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 want to write a macro in LaTeX2e that can pass \textcolor, which is embedded in another macro, the values of the colour model gray, rgb, or cmyk. The number of arguments specified implies the colour model to be used; 1 argument for gray, 3 for the rgb, and 4 for the cmyk model. I was able to write a macro that does what I want, but with the arguments in the standard manner in braces. This is the code I was able to write by modifying the answers here.

\makeatletter  
\def\setmycolour#1{%  
\@ifnextchar\bgroup%  
    {\docolour{#1}}  
    {\dogray{#1}}  
}  
\def\dogray#1{This is gray hue #1.}  
\def\docolour#1#2#3{%  
\@ifnextchar\bgroup%  
    {\docmyk{#1}{#2}{#3}}  
    {\dorgb{#1}{#2}{#3}}  
}  
\def\dorgb#1#2#3{This is rgb colour #1,#2,#3.}  
\def\docmyk#1#2#3#4{This is cmyk colour #1,#2,#3,#4.}  
\makeatother

I use the macro as

\setmycolour{0.85}\\
\setmycolour{1}{0}{0}\\
\setmycolour{1}{0}{0}{0}\\

I want to use the macro, for example, as \setmycolour{1,0,0} or \setmycolour{0.85}. How do I parse the arguments in the macro definition to do this? Is the above code the best way to get the effect I want?

share|improve this question

5 Answers 5

up vote 8 down vote accepted

The comments what color we have is only for demonstration here.

\documentclass{article}
\usepackage[T1]{fontenc}

\makeatletter  
\def\setmycolour#1{\expandafter\setmycolour@i#1,,,,\@nil}
\def\setmycolour@i#1,#2,#3,#4,#5\@nil{% 
  \ifx$#2$ we have gray => #1 \else
    \ifx$#3$ we have a wrong color setting \else
      \ifx $#4$ we have a rgb setting => #1,#2,#3\else
                we have a cmyk setting =>#1,#2,#3,#4
      \fi
    \fi
  \fi 
}  
\makeatother

\begin{document}

\setmycolour{0.5}\par
\setmycolour{0.5,0.6}\par
\setmycolour{0.5,0.6,0.7}\par
\setmycolour{0.5,0.6,0.7,0.8}\par

\end{document}
share|improve this answer
    
Thanks Herbert, this works fine. I had to add another \if to disallow more than 5 arguments (I'm just splitting hairs now :-)). –  Luis Costa Apr 13 '11 at 14:44
3  
Could you explain the solution? I assume you trickily pass four commas to setmycolour@i to ensure that this macro call always succeeds but depending on the number of commas in #1, you might pass too many (which does not hurt). –  Christian Lindig Apr 13 '11 at 15:27
    
\def\setmycolour@i#1,#2,#3,#4,#5\@nil expects at least 4 commas, the reason why i have t add these ones if there is only one argument, then #2#3#4#5 are all empty. When there are four comma separated arguments given, #5 collects all trhe rest, in this case three commas. #5 is like a garbage can. The other cases are between these two ones. –  Herbert Apr 13 '11 at 15:34
    
What about \ifx$#2$? This seems tricky as well. Is this evaluated as \ifx$$ (true) when #2 is empty? But why does it evaluate to false when #2 is not empty? Could you have used something other than $? –  Christian Lindig Apr 13 '11 at 18:10
1  
@Christian: I guess you could put any character (a-z or A-Z) in place of $. The key, I think, is that two same tokens are place around the argument being tested, say #2. For example, you write \ifx a#2a with "a" in place of "$". So when #2 is empty \ifx will test left-hand "a" and right-hand "a" and evaluate to true and to false when testing left-hand "a" and "contents-of-#2" when #2 isn't empty whence the right-hand "a" is ignored, now it being part of the 'then' execution. –  Luis Costa Apr 18 '11 at 14:52
\documentclass{minimal}

\makeatletter

\def \setmycolour #1{
    \newcount \n
    \n = 0
    \setmycolour@ #1,\stopmarker ,
    \ifnum \n = 1
        This is gray hue (#1).
    \else \ifnum \n = 3
        This is rgb colour (#1).
    \else \ifnum \n = 4
        This is cmyk colour (#1).
    \else
        \message{Wrong number of values.}
    \fi\fi\fi
}

\def \stopmarker{EOV}

\def \setmycolour@ #1,{
    \edef \colorvalue{#1}
    \ifx \colorvalue \stopmarker
        \let \next = \relax
    \else
        \advance \n by 1
        \let \next = \setmycolour@
    \fi
    \next
}
\makeatother

\begin{document}

    \setmycolour{0.5}\par
    \setmycolour{0.5,0.6}\par
    \setmycolour{0.5,0.6,0.7}\par
    \setmycolour{0.5,0.6,0.7,0.8}\par

\end{document}
share|improve this answer
    
This solution is good and can be improved by using \ifcase\n rather than the multiple \ifnum tests. It will scale nicely. –  Bruno Le Floch Apr 13 '11 at 20:21
    
@Bruno: after posting this solution, I've tried to rewrite it using ifcase. Unfortunately, I found it looking awkward, because - as far as I know - there's no possibility to explicitly set constants for ifcase, so you have to write multiple redundant ors. –  Dmitrii Volosnykh Apr 13 '11 at 20:33
    
It seems that \ifcase\n \or "gray" \or \ERROR \or "rgb" \or "cmyk" \else \ERROR \fi is shorter than what you have. But in general, it is true that there is no way of doing anything else than contiguous cases with \ifcase. –  Bruno Le Floch Apr 13 '11 at 23:26
    
This is a very clear and readable solution. –  Luis Costa Apr 18 '11 at 9:23
1  
@DmitryF.Volosnykh: \value is a LaTeX2e command. It is dangerous to redefine primitives or kernel commands outside local groups. –  Ahmed Musa Oct 4 '12 at 11:14

A LaTeX3 solution:

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\setmycolour}{ m }
  {
   \prg_case_int:nnn { \clist_length:n { #1 } }
     {
      {1}{ \dogray{#1} }
      {3}{ \dorgb{#1} }
      {4}{ \docmyk{#1} }
     }
     {OOPS}
  }
\ExplSyntaxOff

\def\dogray#1{This is gray hue #1.}
\def\dorgb#1{This is rgb colour #1.}
\def\docmyk#1{This is cmyk colour #1.}

\begin{document}
\setmycolour{0.85}\\
\setmycolour{1,0,0}\\
\setmycolour{1,0,0,0}

\end{document}

Customizations of the commands performed in the admissible cases are, of course, possible.

Important change

Due to the changes made to expl3 in Summer 2012, the functions

\prg_case_int:nnn
\clist_length:n

should be changed into

\int_case:nnn
\clist_count:n

with the same syntax.

share|improve this answer

Just for fun, a solution with the lpeg parser included in luaTeX.

\documentclass{standalone}
\usepackage{luacode}

\begin{luacode*}
  lpeg = require('lpeg')

  digit = lpeg.R('09')
  dot = lpeg.P('.')
  number = (digit^1 * dot * digit^1) + digit^1
  comma = lpeg.P(',')

  csv = lpeg.Ct(lpeg.C(number) * (comma * lpeg.C(number))^0) / function (t)
     if #t == 1 then
        return '\\dogray{' .. tostring(t[1]) .. '}'
     else if #t == 3 then
           return '\\dorgb{' .. tostring(t[1]) .. '}{' .. tostring(t[2]) .. '}{' .. tostring(t[3]) .. '}'
        else if #t == 4 then
              return '\\docmyk{' .. tostring(t[1]) .. '}{' .. tostring(t[2]) .. '}{' .. tostring(t[3]) .. '}{'  .. tostring(t[4]) .. '}'
           end
        end
     end
     end

  function parse_and_make(s)
     tex.sprint(lpeg.match(csv,s))
  end
\end{luacode*}

\def\dogray#1{This is gray hue #1.}  
\def\dorgb#1#2#3{This is rgb colour #1,#2,#3.}  
\def\docmyk#1#2#3#4{This is cmyk colour #1,#2,#3,#4.}  

\def\setmycolor#1{%
  \directlua{parse_and_make("#1")}}

\begin{document}

\setmycolor{0.85}\\
\setmycolor{1,0,0}\\
\setmycolor{1,0,0,0}\\

\end{document}
share|improve this answer

A variant from the Dmitry's code without counter

\documentclass{minimal}

\makeatletter
\def\expr@{This is gray hue}
\def\expr@@{Error with} 
\def\expr@@@{This is rgb colour}
\def\expr@@@@{This is cmyk colour}
\def\expr@@@@@{Error with} 

\def \setmycolour #1{
    \def\tmp{} 
    \setmycolour@ #1,\stopmarker , 
    \@nameuse{expr\tmp} (#1)
}

\def \stopmarker{EOV}

\def \setmycolour@ #1,{
    \edef \value{#1}
    \ifx \value \stopmarker
        \let \next = \relax
    \else
        \expandafter\def\expandafter\tmp\expandafter{\tmp @}% 
        \let \next = \setmycolour@
    \fi
    \next
}
\makeatother

\begin{document}
    \setmycolour{0.5}\par
    \setmycolour{0.5,0.6}\par
    \setmycolour{0.5,0.6,0.7}\par
    \setmycolour{0.5,0.6,0.7,0.8}\par
    \setmycolour{0.5,0.6,0.7,0.8,0.9}\par  
\end{document} 
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.