6

I am writing a macro that formats strings based upon their values in a LaTeX report. I have run into an issue in which a string containing a dollar sign (\$) will result in an error. Here is a minimum working example:

\documentclass{article}
\usepackage{color}
\usepackage{xstring}

\newcommand{\ChgFmt}[1]{\IfSubStr{#1}{-}{{\color{red} #1}}{ {\color{green} #1}}}

\begin{document}
This number is printed in green: \ChgFmt{1\%}
This number is printed in red: \ChgFmt{-1\%}
\end{document} 

If the 1\% above is replaced by \$1 million, then I receive the message "TeX capacity exceeded" and no output is produced. I have read that one solution is to enclose the argument of the \newcommand above in \ensuremath as follows:

\documentclass{article}
\usepackage{color}
\usepackage{xstring}

\newcommand{\ChgFmt}[1]{\ensuremath{\IfSubStr{#1}{-}{{\color{red} #1}}{ {\color{green} #1}}}}

\begin{document}
This number is printed in green: \ChgFmt{\$1 million}
\end{document} 

This however formats the "million" as it were a math symbol rather than a string. I have attempted to use @gobble as suggested below, but my code has the additional complication that I am using dynamic variables. In particular, here is a minimum example:

\documentclass{article}
\usepackage{color}

\newcommand{\FirstDollarAmount}{\$1 million}
\newcommand{\SecondDollarAmount}{-\$1 million}
\newcommand{\inputnum}[2]{\expandafter\csname #1#2\endcsname}

\makeatletter
\newcommand{\ChgFmt}[1]{{\@ifnextchar{-}{\color{red}$-$\@gobble}{\color{green}}#1}}
\makeatother

\begin{document}
This number should be printed in green: \ChgFmt{\inputnum{First}{DollarAmount}}

This number should be printed in red: \ChgFmt{\inputnum{Second}{DollarAmount}}
\end{document}

Although output is produced without error, both numbers are printed in green. Are other ideas?

DrTRD
  • 217

1 Answers1

7

You need \fullexpandarg, but \$ and \text would not survive it; the latter command is needed for setting “million” in normal type.

Thus I first neutralize \$ and \text making them equivalent to \relax for the duration of the test; the replacement is done after the test has decided whether to follow the true or the false path.

Other macros might require similar treatment; not \% because it's very robust and survives \edef.

\documentclass{article}
\usepackage{amsmath}
\usepackage{color}
\usepackage{xstring}

\makeatletter
\newcommand{\ChgFmt}[1]{%
  \ensuremath{%
    \begingroup\fullexpandarg
    \let\$\relax \let\text\unexpanded
    \IfSubStr{#1}{-}
      {\endgroup\@firstoftwo}
      {\endgroup\@secondoftwo}%
    {\begingroup\color{red}#1\endgroup}
    {\begingroup\color{green}#1\endgroup}%
  }%
}
\makeatother

\newcommand{\FirstDollarAmount}{\$1\text{ million}}
\newcommand{\SecondDollarAmount}{-\$1\text{ million}}
\newcommand{\inputnum}[2]{\expandafter\csname #1#2\endcsname}

\begin{document}
This number is printed in green: \ChgFmt{\$1 \text{ million}}

This number is printed in red: \ChgFmt{-\$1 \text{ million}}

This number should be printed in green: \ChgFmt{\inputnum{First}{DollarAmount}}

This number should be printed in red: \ChgFmt{\inputnum{Second}{DollarAmount}}
\end{document} 

enter image description here

egreg
  • 1,121,712
  • Thank you, @egreg! Could you give me an example of how to add additional formatting? For example, how would you remove the minus sign from negative values? How would you add an $\uparrow$/$\downarrow$ in front of positive and negative values? – DrTRD Aug 17 '15 at 19:48
  • @DrTRD That's a completely different question. – egreg Aug 17 '15 at 19:50
  • Feel free to add an answer here then @egreg: http://tex.stackexchange.com/questions/261922/formatting-strings-in-dynamic-variables-with-xstring – DrTRD Aug 18 '15 at 11:49