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 Answers 2

3

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, 2015 at 13:46
  • @DrTRD Please see UPDATE Aug 18, 2015 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

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .