[Tex/LaTex] Incomplete \ifx

macros

I am trying to define a function Lettrine from the lettrine one. I did it here successfully, but I need to add an optional argument to be able to use more lettrine features like ante:

\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\LettrineX #1#2{\vspace{-4ex}\lettrine[lines=1,findent=-0.1em
    \ifx\dummy#2\dummy\empty\else,#2\fi%
    ]{#1}{}}
\newcommand*{\Lettrine}[2][x]{\LettrineX{#2}{#1}}

\begin{document}\thispagestyle{empty}
\section{section 1}
\Lettrine[ante=«]{P}owerfull macro ! »
\section{section 2}
\Lettrine{P}owerfull macro !

\end{document}

It does not compile, with the error:

$ pdflatex MWE_lettrine_command7.tex 
This is pdfTeX, Version 3.1415926-2.5-1.40.14 (TeX Live 2013)
 restricted \write18 enabled.
entering extended mode
(./MWE_lettrine_command7.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)) (./MWE_lettrine_command7.aux))
! Incomplete \ifx; all text was ignored after line 17.
<inserted text> 
                \fi 
<*> MWE_lettrine_command7.tex

If I add a \expandafter before lettrine, it changes nothing, but I am a newbbie for this kind of stuff. If I comment the \ifx line of code, it does compile and work.

The following \ifx MWE reproduces the same kind of architecture with the ifx expression inside an optional argument in a demonstration function. It compiles and works successfully:

\documentclass{article}
\usepackage{lettrine}

\newcommand*{\Test}[2][xxx]{option=#1; argument=#2}

\def\testX #1#2{%
  \Test[\ifx\dummy#2\dummy\empty\else,#2\fi]{#1}%
}

\newcommand*{\test}[2][]{\testX{#2}{#1}}

\begin{document}\thispagestyle{empty}
With one argument:  "\test{MAIN-ARG---ONLY-ONE}"\par
With two arguments: "\test[OPTION=2]{MAIN-ARG}"
\end{document}

Result:
enter image description here

I have quite no more hair at trying to figure out what I am doing wrong. Any idea please?

Best Answer

The code contains this in inside a key value list:

\ifx\dummy#2\dummy\empty\else,#2\fi

The first element in the list is (assuming #2 does not contain ,):

\ifx\dummy#2\dummy\empty\else

The next is

#2\fi

The \ifx construct is divided at the comma by the key value parser.

The following implementation expands the options partially to get a valid key value option list:

\documentclass{article}
\usepackage{lettrine}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}

\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}

\newcommand*{\Lettrine}[2][]{%
  \vspace{-4ex}%
  \edef\LettrineNext{%
    \noexpand\lettrine[%
      lines=1,
      findent=%
        \eqifcase {#2}{{P}{-0.8em}{T}{-0.6em}}{-0.1em},%
      \unexpanded{#1}%
    ]%
  }%
  \LettrineNext{\textit{#2}}{}%
}

\begin{document}\thispagestyle{empty}
\section{section 1}
\Lettrine[ante=«]{P}owerfull macro ! »
\section{section 2}
\Lettrine{P}owerfull macro !
\end{document}

Result

New implementation to support additional features

  • The capital letter can be hidden inside a macro, e.g. \dropCap.

  • The letter can consist of more than one token, e.g. D'.

Macro \DeclareFindents configures the gap lengths:

\DeclareFindents{-0.1em}{
  P=-0.8em,
  T=-0.6em,
  D'=-1em,
}

The first argument is the default value. Then a key value lists follows; the key is the letter (also several tokens are possible) and the value the gap length for this letter.

Package kvsetkeys provides the frame work for a configurable key value parser that compares the list entries with the actual letter. Both the keys and the letters are run through \protected@edef to expand macros such as \dropCap.

Example file:

\documentclass{article}
\usepackage{lettrine}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}

\usepackage{kvsetkeys}

\makeatletter
\newcommand*{\DeclareFindents}[2]{%
  \def\findent@default{#1}%
  \def\findent@list{#2}%
}
\DeclareFindents{0pt}{}% initializing

\newcommand*{\findent@set}[1]{%
  \protected@edef\findent@letter{#1}%
  \let\findent@value\findent@default
  \expandafter\kv@parse\expandafter{\findent@list}{%
    \protected@edef\kv@key{\kv@key}%
    \ifx\findent@letter\kv@key
      \let\findent@value\kv@value
      \kv@break
    \fi
    \@gobbletwo % key and value arguments are not needed
  }%
}
\newcommand*{\Lettrine}[2][]{%
  \vspace{-4ex}%
  \findent@set{#2}%
  \edef\LettrineNext{%
    \noexpand\lettrine[%
      lines=1,
      findent=\findent@value,
      \unexpanded{#1}%
    ]%
  }%
  \LettrineNext{\textit{#2}}{}%
}
\makeatother

\DeclareFindents{-0.1em}{
  P=-0.8em,
  T=-0.6em,
  D'=-1em,
}
\begin{document}\thispagestyle{empty}
\section{section 1}
\Lettrine[ante=«]{P}owerfull macro ! »
\section{section 2}
\Lettrine{P}owerfull macro !
\section{section 3}
\Lettrine{D'}Artagnan and his friends.
\section{section 4}
\newcommand*{\dropCap}{D'}
\Lettrine\dropCap Artangan does not drop his friends.
\end{document}

Result

Related Question