6

This has been answered here for text outputs with the use of \IfEqCase from the xstring package. But if I want to use the output as a value to set a parameter in a function, it fails.

Here is my MWE:

\documentclass[a4paper]{article} 

\usepackage{lettrine,xstring}

\newcommand*{\Lettrine}[1]{%
    \lettrine[lines=1, findent=\IfEqCase{#1}{%
        {P}{-0.8em}%
        {Q}{1em}%
    }[\PackageError{Lettrine}{Undefined option to Lettrine: #1}{}]%
    ]{\textit{#1}}{}%
}

\newcommand*{\LettrineSimply}[1]{\lettrine[lines=1, findent=-0.8em]{\textit{#1}}{}}

\begin{document} 
\LettrineSimply{P} erfectly working !
\Lettrine{P} ossibly it will work !
\end{document}

\LettrineSimply works, but I want to use different findent depending on the letter. In my attempt with the use of \IfEqCase, I have the following compilation error:

$ pdflatex MWE_lettrine_command.tex 
This is pdfTeX, Version 3.1415926-2.5-1.40.14 (TeX Live 2013)
 restricted \write18 enabled.
entering extended mode
(./MWE_lettrine_command.tex
LaTeX2e <2011/06/27>
Babel <3.9f> and hyphenation patterns for 15 languages loaded.
(/usr/share/texmf-dist/tex/latex/base/article.cls
Document Class: article 2007/10/19 v1.4h Standard LaTeX document class
(/usr/share/texmf-dist/tex/latex/base/size10.clo))
(/usr/share/texmf-dist/tex/latex/lettrine/lettrine.sty
(/usr/share/texmf-dist/tex/latex/graphics/keyval.sty)
Loading lettrine.cfg
(/etc/texmf/tex/latex/lettrine.d/lettrine.cfg))
(/usr/share/texmf-dist/tex/generic/xstring/xstring.sty
(/usr/share/texmf-dist/tex/generic/xstring/xstring.tex))
(./MWE_lettrine_command.aux)
! Missing number, treated as zero.
<to be read again> 
                   \let 
l.17 \Lettrine{P}
                  ossibly it will work !
? 

If I just ENTER:

! Illegal unit of measure (pt inserted).
<to be read again> 
                   \let 
l.17 \Lettrine{P}
                  ossibly it will work !

Do I try to assign a string to the findent parameter? How can I solve that?

Otherwise, is it possible to define tables or associative maps inside commands?

lalebarde
  • 769

5 Answers5

6

As your \Lettrine command certainly does some un-expandable things, we don't really need a fully expandable version of \IfEqCase.

lettrine

\documentclass[a4paper]{article} 

\usepackage{lettrine,xstring}

\makeatletter
\newcommand*{\Lettrine}[1]{%
    \IfEqCase{#1}{%
        {P}{\def\@@temp{findent=-0.8em,}}%
        {Q}{\def\@@temp{findent=1em,}}%
    }[\PackageError{Lettrine}{Undefined option to Lettrine: #1}{}%
      \def\@@temp{}]%
    \expandafter\lettrine\expandafter[\@@temp lines=1]{\textit{#1}}{}%
}
\makeatother

\newcommand*{\LettrineSimply}[1]
   {\lettrine[lines=1, findent=-0.8em]{\textit{#1}}{}}

\begin{document}\thispagestyle{empty}

\LettrineSimply{P} erfectly working !
\Lettrine{P} ossibly it will work !
\Lettrine{Q} uite certainly it does work !
%\Lettrine{R} arely are errors encountered ! % generates error as expected
\end{document}
  • What about if I need to use multiple conditions/variables for the same case, for example CASE1: if a=1 and /orb=2 : then do this and so on.Identifying multiple variables in the same case – Silva Feb 20 '20 at 21:08
5

You need the \ifeqcase to be defined as expandable macro. My solution uses only TeX primitives, no xparse package is needed:

\documentclass[a4paper]{article}
\usepackage{lettrine}

\def\ifeqcase#1#2{\ifeqcaseA#1#2\end}
\def\ifeqcaseA#1#2#3#4{%
   \ifx#1#2#3\expandafter\ifeqcaseE\expandafter#4%
   \else\ifx#4\end
        \else \expandafter\expandafter\expandafter
              \ifeqcaseA\expandafter\expandafter\expandafter#1%
                        \expandafter\expandafter\expandafter#4% 
   \fi \fi
}
\def\ifeqcaseE#1\end#2{}

\newcommand*{\Lettrine}[1]{%
    \lettrine[lines=1, findent=\ifeqcase{#1}{%
        {P}{-0.8em}% 
        {Q}{1em}%
    }{\PackageError{Lettrine}{Undefined option to Lettrine: #1}{}}%
    ]{\textit{#1}}{}%
}
\newcommand*{\LettrineSimply}[1]{\lettrine[lines=1, findent=-0.8em]{\textit{#1}}{}}

\begin{document}   
\LettrineSimply{P}erfectly working !
\Lettrine{P}ossibly it will work !
\Lettrine{Q}next test.
\end{document}

See that the different syntax is used for \ifcaseeq: the else part is in normal TeX braces.

I know that the question was LaTeX-type but we can compare the code which generates the same effect in plainTeX (using \eqifcase by @jfbu).

\input opmac

\def\eqifcase #1#2#3{\eqifcaseA #1#2#1{#3}\end}
\def\eqifcaseA #1#2#3{\ifx #1#2\eqifcaseE{#3}\fi \eqifcaseA #1}
\def\eqifcaseE #1\fi #2\end{\fi #1}

\def\letterfirst#1{\vskip.7\baselineskip\noindent{\it\thefontsize[24.7]#1}%
   \kern\eqifcase#1{P{-0.5em} Q{1em} R{.1em} S{.4em}}
   {0pt\opwarning{\string\letterfirs{#1} in unspaced}}%
}

\letterfirst Possibly it will work!
\letterfirst Qnext text.

\end

That is the beauty of TeX.

wipet
  • 74,238
  • I cannot compile it: `$ pdflatex MWE_lettrine_command3.tex This is pdfTeX, Version 3.1415926-2.5-1.40.14 (TeX Live 2013) [cutted] LaTeX2e <2011/06/27> Runaway argument? lines=1, findent=-0.8em {\textit {P}}{}erfectly working ! \Lettrine {\ETC. ! Paragraph ended before @lettrine was complete. \par l.29

    ? `

    – lalebarde May 20 '14 at 18:19
  • I actually need at least 15 cases. – lalebarde May 20 '14 at 18:21
  • 1
    @lalebarde: I missed one square backet in the second \newcommand after findent=-0.8em while I copied and pasted my code to stackexchange. Sorry. I have corrected it now. – wipet May 20 '14 at 20:20
  • 1
    @egreg: You are not right. my macro \ifcaseeq works with arbirtary number of parameters. You can try it... – wipet May 20 '14 at 20:21
  • @wipet I was using the first version. Now it works as advertised. – egreg May 20 '14 at 20:48
  • @egreg: My first version was the same as the second (i.e. accepts arbitrary parameters). Only the missing ] in LaTeX part of the code was corrected. – wipet May 20 '14 at 21:43
  • The second solution is interresting but cannot be compared easily since you substitute also lettrine. I don't want to substitute lettrine by a hack because I want to be able to use the full capabilities of lettrine. – lalebarde May 21 '14 at 07:24
5

This is more of a comment on wipet's answer but is a bit too long.

Here is another way to do an expandable \ifeqcase. The conditions are the same: we only compare single tokens. We use \eqifcase as name.

Added note: initial motivation in revisiting wipet's interesting \ifeqcase was to move the execution of the selected branch to after the case-switch has been cleared. This being done it is just one step to make the \eqifcase into a macro which expands in only two steps, before executing the selected code. And another aspect (independent of the added "expand in only two steps") worth pointing out is that \eqifcase can be used as argument to macros which expand their argument via \romannumeral-`0. I have added an illustration of this below.

eqifcase

\documentclass{article}

\def\eqifcase #1#2#3{\eqifcaseA #1#2#1{#3}\end }

\def\eqifcaseA #1#2#3{\ifx #1#2\eqifcaseDo{#3}\fi \eqifcaseA #1}

\def\eqifcaseDo #1\fi #2\end{\fi #1}

\def\test #1{%
    \eqifcase {#1}{{A}{aa}{B}{bb}{C}{cc}}{not found}}


\begin{document}\thispagestyle{empty}
\test{A}

\test{B}

\test{C}

\test{D}
\end{document}

Illustration (using within macros expanding their arguments via f-expansion) and Extension (case-switch expands in only two steps; this aspect is not illustrated here)

\documentclass{article}

\def\eqifcase #1#2#3{\romannumeral0\eqifcaseA #1#2#1{#3}\end }

\def\eqifcaseA #1#2#3{\ifx #1#2\eqifcaseDo{#3}\fi \eqifcaseA #1}

\def\eqifcaseDo #1\fi #2\end{\fi \space #1}

\usepackage {xintfrac}

\makeatletter
\newcommand{\DOOPERATION}[1]{\eqifcase {#1}
    {+\xintAdd
     *\xintMul
     -\xintSub
     /\xintDiv
     T\xintTrunc}{\@gobbletwo}}
\makeatother

\begin{document}

Demonstrating the use within macros applying f-expansion to their arguments:

\xintIrr {\DOOPERATION -{113/355}{22/7}}

\xintIrr {\DOOPERATION *{1024/243}{243/1024}}

\xintIrr {\DOOPERATION /{\DOOPERATION *{1001}{999}}{\DOOPERATION *{77}{111}}}

\DOOPERATION {T}{18}{113/355}


\end{document}

ifcase extended

  • OK, you have better code (only three lines) with the same result. – wipet May 20 '14 at 21:39
  • @wipet I have commented more on \eqifcase which was motivated by your interesting \ifeqcase –  May 21 '14 at 07:13
4

You should use an expandable command as the value of findent, but \IfEqCase isn't. Here's a working version with xparse; I've added \ignorespaces so that it's immaterial if you leave a space after \Lettrine{P} or not.

\documentclass[a4paper]{article} 

\usepackage{lettrine,xparse}

\ExplSyntaxOn
\NewDocumentCommand{\Lettrine}{m}
 {
  \lettrine[lines=1, findent=\lalebarde_case:n { #1 }]{\textit{#1}}{}
  \ignorespaces
}

\cs_new:Npn \lalebarde_case:n #1
 {
  \str_case:nnF { #1 }
   {
    {P}{-0.8em}
    {Q}{1em}
   }
   {% none of the above
    \PackageError{Lettrine}{Undefined option to Lettrine: #1}{}
   }
 }
\ExplSyntaxOff

\newcommand*{\LettrineSimply}[1]{\lettrine[lines=1, findent=-0.8em]{\textit{#1}}{}\ignorespaces}

\begin{document} 
\LettrineSimply{P} erfectly working!

\Lettrine{P}erfectly working!

\Lettrine{Q}uestion: will it work?
\end{document}

You should be able to add whatever letter you want; the order is almost unimportant; for greater efficiency, put the most frequent letters first.

enter image description here

egreg
  • 1,121,712
1

Here is the final code, from jfbu solution, mixed with what I have learnt from wipet and egreg:

\documentclass{article}
\usepackage{lettrine}

\def\eqifcase #1#2#3{\eqifcaseA #1#2#1{#3}\end }

\def\eqifcaseA #1#2#3{\ifx #1#2\eqifcaseDo{#3}\fi \eqifcaseA #1}

\def\eqifcaseDo #1\fi #2\end{\fi #1}

\def\Lettrine #1{\vspace{-4ex}\lettrine[lines=1,findent=%
    \eqifcase {#1}{{P}{-0.8em}{T}{-0.6em}}{-0.1em}%
    ]{\textit{#1}}{}}


\begin{document}\thispagestyle{empty}
\section{section 1}
\Lettrine{P}owerfull macro !
\section{section 2}
\Lettrine{D}efault.
\section{section 3}
\Lettrine{T}remendous!
\section{section 4}
without lettrine to show that thanks to \textit{\\vspace\{-4ex\}} in the macro, we keep the same vertical space between the text baseline and the section title.
\end{document}

enter image description here Big thanks to all the three of you, you are amazing ! EDIT: update thanks to remarks.

lalebarde
  • 769
  • 1
    you can remove the \expandafter in your \Lettrine... ;-) –  May 21 '14 at 08:02
  • 1
    compiling the code with \vspace{-4ex} makes the big letters overlap on my installation –  May 21 '14 at 08:05
  • @jfbu, yes, I have not changed the snapshot. Lettrines are intended to be used after sections. \vspace{-4ex} enables to keep the same space between the section title and the text, without consideration for the lettrine. – lalebarde May 21 '14 at 09:01
  • Updated. Of course, this vspace depends on the sections specifications. Here, 2ex would have been better. – lalebarde May 21 '14 at 09:13
  • you could perhaps use \smash to kill the vertical space occupied by the lettrine. No need for a \vspace then. –  May 21 '14 at 10:28
  • Thanks for the suggestion. I have just tested it. \smash before \lettrine does not compile, and before \textit has no effect. – lalebarde May 21 '14 at 11:59
  • I had not tested. Using \lettrine with lines=1 seems to use little of lettrine package. What about simply \noindent\smash{\Huge\textit{T}}\kern-0.6em\relax remendous! possibly made into a macro? –  May 21 '14 at 12:38
  • Continuation of this Lettrine story here with a new problem when I try to add an optional argument. – lalebarde May 21 '14 at 13:04