11

The MWE below does exactly what I want via the \TypsetInBlueOnlyIfCurrentColorIsBlack macro:

enter image description here

\TypsetInBlueOnlyIfCurrentColorIsBlack is intended to change the color of its parameter to blue, only if the current color is black. Otherwise, the current color is to be used.

Questions:

  • I expected that xcolor would provide a macro to do this, but was not able to locate it. Thus, wondering if there any obvious problems with this approach, or is there an official interface that is provided.

References:

Code:

\documentclass{article}
\usepackage{xcolor}
\usepackage{xstring}

%% At this point the default color is black so save that.
\edef\BlackColor{\csname\string\color@.\endcsname}

\makeatletter
\newcommand{\@CurrentColor}{}% Make sure we are not using an existing macro
\newcommand*{\TypsetInBlueOnlyIfCurrentColorIsBlack}[1]{%
    \edef\@CurrentColor{\csname\string\color@.\endcsname}% Get current color
    \IfStrEq{\@CurrentColor}{\BlackColor}{%
        {\bfseries\ttfamily\textcolor{blue}{#1}}% current color IS black
    }{%
        {\bfseries\ttfamily#1}%  current color is NOT black
    }%
}
\makeatother

\begin{document}
    Following should be in blue:
    \TypsetInBlueOnlyIfCurrentColorIsBlack{blue}.

\color{red}
    Following should be in red:
    \TypsetInBlueOnlyIfCurrentColorIsBlack{red}.

\color{blue}
    Following should also be in blue:
    \TypsetInBlueOnlyIfCurrentColorIsBlack{blue}.
\end{document}
  • 2
    color stores the current colour in \current@color. So you could do \usepackage{xcolor}{\color{black}\global\let\my@black@color\current@color}\newcommand*\ifblack{\ifx\my@black@color\current@color}. – Skillmon Dec 15 '18 at 22:20
  • @Skillmon Better yet: \sbox0{\color{black}\global\let\my@black@color\current@color} so no whatsit is added to the main vertical list. – egreg Dec 16 '18 at 0:00
8

An interface? Use expl3, of course!

With \storecolor you can store the representation of the colors you need to do comparison with.

I define a generic comparison command and also yours as a special case.

At begin document each color in the list passed to \storecolors and also black are converted in the internal representation and stored in a property list. This is for greater efficiency and not actually necessary: it saves several steps in the evaluation.

The main command converts the current color in the internal representation and compares it with the color given as the first argument (only named colors, but this could be generalized). If the test returns true, the color specified as second argument is used to print the third argument; otherwise no color change is performed.

\documentclass{article}
\usepackage{xcolor}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\perhapschangecolor}{mmm}
 {% #1 = color to compare with current
  % #2 = color to use if match
  % #3 = text
  \peter_color_change:nnn { #1 } { #2 } { #3 }
 }
% the same but with black and blue preset
\NewDocumentCommand{\TypsetInBlueOnlyIfCurrentColorIsBlack}{m}
 {
  \perhapschangecolor{black}{blue}{#1}
 }
\NewDocumentCommand{\storecolors}{m}
 {% black is always stored
  \AtBeginDocument { \peter_color_store:n { black,#1 } }
 }

\prop_new:N \g_peter_color_stored_prop
\tl_new:N \l__peter_color_current_tl

\cs_new:Nn \peter_color_store:n
 {
  \hbox_set:Nn \l_tmpa_box
   {
    \clist_map_inline:nn { #1 }
     {
      \color{##1}
      \driver_color_pickup:N \l_tmpa_tl
      \prop_gput:NnV \g_peter_color_stored_prop { ##1 } \l_tmpa_tl
     }
   }
 }

\cs_new_protected:Nn \peter_color_change:nnn
 {
  \driver_color_pickup:N \l__peter_color_current_tl
  \prop_if_in:NnTF \g_peter_color_stored_prop { #1 }
   {% more efficient test
    \str_if_eq:eeTF
     { \l__peter_color_current_tl }
     { \prop_item:Nn \g_peter_color_stored_prop { #1 } }
     { \textcolor{#2}{#3} }
     { #3 }
   }
   {
    \hbox_set:Nn \l_tmpa_box
     {
      \color{#1}
      \driver_color_pickup:N \l_tmpa_tl
      \tl_gset_eq:NN \g_tmpa_tl \l_tmpa_tl
     }
    \driver_color_pickup:N \l_tmpa_tl
    \str_if_eq:eeTF { \g_tmpa_tl } { \l_tmpa_tl }
     { \textcolor{#2}{#3} }
     { #3 }
   }
 }
\ExplSyntaxOff

\storecolors{red!60}

\begin{document}

    Following should be in blue:
    \TypsetInBlueOnlyIfCurrentColorIsBlack{blue}

\color{red}
    Following should be in red:
    \TypsetInBlueOnlyIfCurrentColorIsBlack{red}

\color{blue}
    Following should be in blue:
    \TypsetInBlueOnlyIfCurrentColorIsBlack{blue}

\color{black}
    Following should be in pale red:
    \perhapschangecolor{black}{red!60}{red!60}

\color{red!60}
    Following should be in pale red:
    \perhapschangecolor{black}{red!60}{red!60} 

\color{blue}
    Following should be in blue:
    \perhapschangecolor{black}{red!60}{blue}

    % blue has not been stored, but the test succeeds as well
    Following should be in pale red:
    \perhapschangecolor{blue}{red!60}{red!60}

\end{document}

enter image description here

  • In \clist_map_inline:nn in \peter_color_store:n you're missing an additional group around \color{##1}. – Skillmon Dec 16 '18 at 8:37
  • @Skillmon It's not necessary, as the code is executed inside a box being built and then unused. – egreg Dec 16 '18 at 10:46
  • I'd still do so, just to not lead to bad practise. Somebody not knowing the limitations and seeing this code might write faulty code because he thinks it's generally safe to use it inside a box. – Skillmon Dec 16 '18 at 13:01

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.