2

My need

I want to create a command that returns a color depending on the value of the argument.

My attempt

I've tried the following but it does not work.

The error is Argument of \XC@col@rlet has an extra }.

\documentclass{article}

\usepackage{xcolor}
\usepackage{xstring}
\usepackage{tikz}

\newcommand{\colorForLetter}[1]
% does not work
{\IfStrEqCase{#1}%
    {%
    {A}{blue}%
    {C}{green}%
    }[pink]%
}
% work
%{blue}

\newcommand{\print}[1]
{
\colorlet{varLocalColor}{\colorForLetter{#1}}
\textcolor{varLocalColor!50}{#1}
}


\begin{document}

\def\letters{
A,
B,
B,
C
}

\foreach \xx in \letters
{
    \print{\xx}
}

\end{document}

My questions

What is the cause of the error? How to proceed then?

3
  • 2
    How many colors / cases do you have? You could just \definecolor for each one. Commented May 20 at 11:54
  • 3
    xstring commands a are in general not expandable, use expl3, or a simple definecolor Commented May 20 at 11:55
  • Thank you David. Can you give me more details in an answer? I'm not able to make it work with your comment.
    – Colas
    Commented May 20 at 12:04

3 Answers 3

4

We can use \colorlet to specify a color for each case. The \@ifundefined below checks if the case color is undefined (from here), and uses a default if so, to match \IfStrEqCase{arg}{...cases...}[default] from the question.

EDIT: from your comment, I've adjusted to explicitly define a color called case to use as needed.

MWE

\documentclass{article}
\usepackage{xcolor}
\colorlet{case}{black}
\colorlet{A}{red}
\colorlet{B}{green}
\colorlet{C}{blue}
\makeatletter
\newcommand{\caseclr}[1]{% set the case color with default
  \@ifundefined{\string\color@#1}%
    {\colorlet{case}{black}}% default
    {\colorlet{case}{#1}}}% if case is defined
\makeatother
\newcommand{\print}[1]{% just an example macro
  \caseclr{#1}% set the case color
  \textcolor{case!50}{#1}}% print the argument in case color
\begin{document}
hello
\print{A}\print{B}\print{C}\print{X}
\end{document}

Result

result

4
  • I really need a command that returns the color so that I can use it at different places, in other commands. I tried to modify your code but it does not work. Can you help me?
    – Colas
    Commented May 20 at 12:16
  • Here what I did: \colorlet{A}{red} \colorlet{B}{green} \colorlet{C}{blue} \makeatletter \newcommand{\conditionalColor}[1]{\@ifundefined{\string\color@#1}{black}} \makeatother \begin{document} hello \textcolor{\conditionalColor{A}}{coucou} \end{document}
    – Colas
    Commented May 20 at 12:18
  • 1
    I see. I've updated the solution to hopefully do what you want. Commented May 20 at 12:41
  • It works! Thank you
    – Colas
    Commented May 20 at 12:51
4

Probably expl3's \str_case:nnF does the trick—with \textcolor and \colorlet and the like arguments are expanded, thus you can use \str_case:nnF both when specifying colors in the argument of \textcolor and when defining colors via \colorlet.

In case the letter denoting the color is to be passed via a macro which serves as a variable, e.g., within a \foreach-loop, you can use the variant \str_case:enF:

\documentclass{article}

\usepackage{xcolor}
\usepackage{tikz}

\ExplSyntaxOn
% Only very recent releases of expl3 have \str_case:enF, so let's generate the
% variant in case it is not already there:
\cs_generate_variant:Nn {\str_case:nnF}{e}
\cs_new:Npn \colorForLetter #1
  {
    %\exp_args:Ne \str_case:nnF {#1}
    \str_case:enF{#1}
      {
        {A}{blue}
        {C}{green}
      }
      {pink}
  }
\ExplSyntaxOff

\begin{document}

\textcolor{\colorForLetter{A}!50}{A}%
\textcolor{\colorForLetter{B}!50}{B}%
\textcolor{\colorForLetter{B}!50}{B}%
\textcolor{\colorForLetter{C}!50}{C}%

\medskip
\hrule
\medskip

\colorlet{varLocalColorA}{\colorForLetter{A}}%
\colorlet{varLocalColorB}{\colorForLetter{B}!50}%
\colorlet{varLocalColorC}{\colorForLetter{C}!50}%
\textcolor{varLocalColorA!50}{A}%
\textcolor{varLocalColorB}{B}%
\textcolor{varLocalColorB}{B}%
\textcolor{varLocalColorC}{C}%

\end{document}

If you like playing with \foreach, you can, e.g., do:

\documentclass{article}

\usepackage{xcolor}
\usepackage{tikz}

\ExplSyntaxOn
% Only very recent releases of expl3 have \str_case:enF, so let's generate the
% variant in case it is not already there:
\cs_generate_variant:Nn {\str_case:nnF}{e}
\cs_new:Npn \colorForLetter #1
  {
    %\exp_args:Ne \str_case:nnF {#1}
    \str_case:enF{#1}
      {
        {A}{blue}
        {C}{green}
      }
      {pink}
  }
\ExplSyntaxOff

\begin{document}

\foreach \xx in {A,B,B,C}%
                {%
                  \textcolor{\colorForLetter{\xx}!50}{\xx}%
                }%

\medskip
\hrule
\medskip

% \xx = color name; \yy = letter for color; \zz = additional things like !50
\foreach \xx/\yy in {%
                      {varLocalColorA}/{\colorForLetter{A}},
                      {varLocalColorB}/{\colorForLetter{B}!50},
                      {varLocalColorC}/{\colorForLetter{C}!50}%
                    }%
                    {%
                      \xglobal\colorlet{\xx}{\yy}%
                    }

\textcolor{varLocalColorA!50}{A}%
\textcolor{varLocalColorB}{B}%
\textcolor{varLocalColorB}{B}%
\textcolor{varLocalColorC}{C}%

\end{document}

If you also like macros for calling \textcolor or \colorlet, you can do:

\documentclass{article}

\usepackage{xcolor}
\usepackage{tikz}

\ExplSyntaxOn
% Only very recent releases of expl3 have \str_case:enF, so let's generate the
% variant in case it is not already there:
\cs_generate_variant:Nn {\str_case:nnF}{e}
\cs_new:Npn \colorForLetter #1
  {
    %\exp_args:Ne \str_case:nnF {#1}
    \str_case:enF{#1}
      {
        {A}{blue}
        {C}{green}
      }
      {pink}
  }
\ExplSyntaxOff

\newcommand{\print}[1]{%
  \textcolor{\colorForLetter{#1}!50}{#1}%
}

\newcommand{\letcolorbyLetter}[3]{%
  % #1 = new color name; #2 = letter for color; #3 = additional things 
  \colorlet{#1}{\colorForLetter{#2}#3}%
}

\begin{document}

\foreach \xx in {A,B,B,C}{\print{\xx}}

\medskip
\hrule
\medskip

% \xx = color name; \yy = letter for color; \zz = additional things like !50
\foreach \xx/\yy/\zz in {varLocalColorA/A/,
                         varLocalColorB/B/!50,
                         varLocalColorC/C/!50}{\expandafter\xglobal\letcolorbyLetter{\xx}{\yy}{\zz}}

\textcolor{varLocalColorA!50}{A}%
\textcolor{varLocalColorB}{B}%
\textcolor{varLocalColorB}{B}%
\textcolor{varLocalColorC}{C}%

\end{document}

enter image description here

2
  • I took the liberty of streamlining the code. This exploits the fact that \textcolor fully expands its first argument.
    – egreg
    Commented May 23 at 7:05
  • \colorlet{somecolor}{\colorForLetter{A}!50} works.
    – egreg
    Commented May 23 at 11:00
2

With the following definition, \colorForLetter can be used anywhere a color name is expected.

\documentclass{article}

\usepackage{xcolor}
\usepackage{tikz}

\ExplSyntaxOn
\NewExpandableDocumentCommand{\colorForLetter}{m}
 {
  \str_case:enF{#1}
   {
    {A}{blue}
    {C}{green}
  }{pink}
 }
\ExplSyntaxOff

\newcommand{\print}[1]{%
  \textcolor{\colorForLetter{#1}!50}{#1}%
}


\begin{document}

\foreach \xx in {A,B,B,C}{\print{\xx}}

\colorlet{mycolorone}{\colorForLetter{A}}
\colorlet{mycolortwo}{\colorForLetter{B}!50!\colorForLetter{C}}

\textcolor{mycolorone}{abcdef}
\textcolor{mycolortwo}{abcdef}

\end{document}

enter image description here

If you are running a version of LaTeX where \str_case:enF isn't defined, just do

\ExplSyntaxOn
\NewExpandableDocumentCommand{\colorForLetter}{m}
 {
  \str_case:enF{#1}
   {
    {A}{blue}
    {C}{green}
  }{pink}
 }
\cs_generate_variant:Nn \str_case:nnF { e }
\ExplSyntaxOff

The added code does nothing if the variant is already defined.

You must log in to answer this question.

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