3

I need to format a string resulting from a dynamic variable set in a separate program for a LaTeX generated report. In particular, if a variable has a negative value (starts with a minus sign), I want to show the variable as red without the minus sign. Here is a minimum working example:

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

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

% Output from variables file
\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{\inputnum{First}{DollarAmount}}

This number should be printed in red, without the minus sign: \ChgFmt{\inputnum{Second}{DollarAmount}}
\end{document} 

I have tried to use \@gobble as well as \StrSubstitute but neither of these produce the desired result due to the presence of the \$ in the dynamic variables.

I would like also to have the flexibility to format the string more. For example, I'd like to prepend a $\uparrow$ to positive numbers and prepend a $\downarrow$ to negative numbers. The solution provided by Steven B. Segletes is elegant but not flexible enough to add these (at least naively):

\documentclass{article}
\usepackage{amsmath,xcolor}
\def\ChgFmt#1{\textcolor{red}{\if-#1\else\textcolor{green}{$\uparrow$#1}\fi}}

% Output from variables file
\newcommand{\FirstDollarAmount}{\$1\text{ million}}
\newcommand{\SecondDollarAmount}{-\$1\text{ million}}
\newcommand{\inputnum}[2]{\csname #1#2\endcsname}

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

This number should be printed in red, without the minus sign: \ChgFmt{\inputnum{Second}{DollarAmount}}
\end{document}

Any other ideas? Thank you in advance for your help.

2

Would this one-line parsing suffice, without use of xstring? This problem is really tailor-made for an \if-#1 construct for two reasons:

  1. \if fully expands its arguments before the test (which matters because the - is embedded several expansions down inside #1); and

  2. a "positive" test strips the leading token from the argument, which is exactly what the OP wants.

I set the string red to start and use an \if-#1...\else...\fi construct to decide whether the leading token of the argument is a -. If a leading - is found, it is stripped by the \if, while leaving the remaining text red. If a leading - is not found, then the whole argument is output in green.

\documentclass{article}
\usepackage{amsmath,xcolor}
\def\ChgFmt#1{\textcolor{red}{\if-#1\else\textcolor{green}{#1}\fi}}

% Output from variables file
\newcommand{\FirstDollarAmount}{\$1\text{ million}}
\newcommand{\SecondDollarAmount}{-\$1\text{ million}}
\newcommand{\inputnum}[2]{\csname #1#2\endcsname}

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

This number should be printed in red, without the minus sign: \ChgFmt{\inputnum{Second}{DollarAmount}}
\end{document}

enter image description here

UPDATE

To handle the OP's revised formulation, one can perform a test inside a temporary box, which is then discarded, but not before it \gdef's the desired outcome (a green \uparrow or a red \downarrow) into a temporary variable. Then, prepend that temporary variable, followed by the prior test described in the initial solution.

\documentclass{article}
\usepackage{amsmath,xcolor}
\def\ChgFmt#1{\setbox0=\hbox{%
  \if-#1\gdef\tmp{\textcolor{red}{$\downarrow$}}\else%
  \gdef\tmp{\textcolor{green!60!black}{$\uparrow$}}\fi%
  }\tmp%
  \textcolor{red}{\if-#1\else\textcolor{green!60!black}{#1}\fi}%
}

% Output from variables file
\newcommand{\FirstDollarAmount}{\$1\text{ million}}
\newcommand{\SecondDollarAmount}{-\$1\text{ million}}
\newcommand{\inputnum}[2]{\csname #1#2\endcsname}

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

This number should be printed in red, without the minus sign: \ChgFmt{\inputnum{Second}{DollarAmount}}
\end{document}

enter image description here

2
  • Your solution is elegant, but not flexible enough to address my application. See the revised question above. – DrTRD Aug 18 '15 at 13:46
  • @DrTRD Please see UPDATE – Steven B. Segletes Aug 18 '15 at 14:14
1

Here's a modification of the former solution that uses a different workaround:

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

\makeatletter
\newcommand{\ChgFmt}[1]{%
  \ensuremath{%
    \begingroup
    \expandarg
    \protected@edef\temp{#1}%
    \IfSubStr{\temp}{-}
      {\StrSubstitute{\temp}{-}{}[\temp]\@firstoftwo}
      {\@secondoftwo}%
    {\color{red}{\downarrow}\temp\endgroup}
    {\color{green}{\uparrow}\temp\endgroup}%
  }%
}

\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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.