Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

I am trying to define a macro that would take a string and paint its ending, for example:

\def\paintString#1{%
  \saveexploremode%
  \exploregroups%
  \textcolor{white}{\StrGobbleRight{#1}{5}}%
  \StrGobbleRight{#1}{2}[\tmp]%
  \textcolor{yellow}{\StrRight{\tmp}{3}}%
  \textcolor{white}{\StrRight{#1}{2}}%
  \restoreexploremode%
}

This works like intended for the ordinary text (everything white, except chars 4-5 from the right, which are yellow). However, it breaks if the argument contains formula or \cite{}:

\paintString{Blah \cite{reference}}
\paintString{Blah $\Sigma$}

To this, LaTeX reports

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

which I assume happens because I break formula (or \cite) and put \textcolor{...}{ inside and the corresponding } outside.

Is there a way to force the expansion in a way that these special things are treated as a single char by the xstring macros?

I need to put some kind of expansion explicitly (hence \exploregroups), because without it the strings are not parsed properly. Furthermore, this is in Beamer theme and one of the places this is called from are frame titles which, if unexpanded, report \StrLength 1, so the above painting doesn't occur at all.

Edit: This is a bit safer to use:

\def\paintString#1{%
  \saveexploremode%
  \exploregroups%
  {\color{white}\StrGobbleRight{#1}{5}%
  \StrGobbleRight{#1}{2}[\tmp]%
  \color{yellow}\StrRight{\tmp}{3}%
  \StrRight{#1}{2}}%
  \restoreexploremode%
}

However, it still fails with \cite.

share|improve this question
2  
I'm afraid you're out of luck: \cite performs many actions before laying out the characters to print and most of these actions are not expandable. –  egreg Apr 19 '13 at 21:10
    
@egreg: I expected that. Besides, \cite is not the only problem (formulas and, I'd expect, other stuff as well). That's why I expect the right solution for this is to teach xstring functions to treat that stuff as a single char/token. It is not a priority that it is painted as described. I'd even go for a solution "don't paint if it raises an error, just don't break". Interesting enough, it breaks even if it has more than 5 characters after \cite. On the other hand, formula with more than 5 chars behind does not confuse it. –  Vedran Šego Apr 19 '13 at 22:56
1  
xstring works on strings of characters; formulas are not simply strings of characters. –  egreg Apr 19 '13 at 22:59
    
@egreg: I don't insist on xstring. It's just the only way I know how to do such painting. –  Vedran Šego Apr 19 '13 at 23:13
    
Another way to handle this might be to somehow achieve that \paintString{some text} gets processed, but \paintString{some \paintString{already painted} text} is returned as it is, so "some" and "text" are not specially painted, but "already painted" is. This would give user an easy way to control problematic chunks of text. Any ideas how to do that? As I expected, IfSubStr didn't help me with this. –  Vedran Šego Apr 20 '13 at 9:55

1 Answer 1

up vote 1 down vote accepted

Here is a satisfactory solution:

\def\paintString#1{%
  \IfSubStr{\expandafter\detokenize\expandafter{#1}}{\detokenize{\paintString}}{%
    #1%
  }{%
    \saveexploremode%
    \exploregroups%
    {\color{white}\StrGobbleRight{#1}{5}%
    \StrGobbleRight{#1}{2}[\tmp]%
    \color{yellow}\StrRight{\tmp}{3}%
    \color{white}\StrRight{#1}{2}}%
    \restoreexploremode%
  }%
}

This works as I suggested in my previous comment to the question:

Another way to handle this might be to somehow achieve that \paintString{some text} gets processed, but \paintString{some \paintString{already painted} text} is returned as it is, so "some" and "text" are not specially painted, but "already painted" is. This would give user an easy way to control problematic chunks of text.

So, I can freely put \frametitle{Some text} (\frametitle is redefined to use \painString for the title) and it is properly painted, as before.

However, \frametitle{Author \cite{reference}} will still fail, so I go around it with \frametitle{\paintString{Author} \cite{reference}}, thus painting the "Author" and leaving \cite{reference} as it is (i.e., just appending it to the painted author's name).

For the additional safety, I have added some additional code which suppresses the painting altogether, without even checking the value of the argument:

\def\paintString#1{%
  \ifnum\suppressPainting=0%
    \IfSubStr{\expandafter\detokenize\expandafter{#1}}{\detokenize{\paintString}}{%
      #1%
    }{%
      \saveexploremode%
      \exploregroups%
      {\color{white}\StrGobbleRight{#1}{5}%
      \StrGobbleRight{#1}{2}[\tmp]%
      \color{yellow}\StrRight{\tmp}{3}%
      \color{white}\StrRight{#1}{2}}%
      \restoreexploremode%
    }%
  \else%
    {\color{white}#1}%
  \fi%
}

So, putting \def\suppressPainting{1} somewhere before the frame will suppress the painting regardless of the argument value. There might be a bit TeX-ier solution, but I'm not aware of it.

Of course, an automatic solution (not requiring user's intervention with a manual call to \paintString) would be nicer, but I don't think it is possible (as @egreg suggested in the comments above), so this is probably the best possible way to handle the problem.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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