5

I have made a macro \@Romankern that makes roman numerals with a negative kerning between them to make them closer to each other. I named it \@Romankern to allow it to be used with \pagenumbering.

The macro works fine until I load the lastpage package. When this package is loaded the macro breaks telling me:

! Undefined control sequence.
\@Romankern #1->\edef \temp 
                            {\@Roman {#1}}\expandafter \@romkern \temp ...

I tried to trace the source of the error but failed... miserably. What is wrong with my macro that it is so fragile?

\documentclass{article}

\usepackage{lipsum}
\usepackage{lastpage}

\makeatletter

\def\@Romankern#1{%
  \edef\temp{\@Roman{#1}}%
  \expandafter\@romkern\temp\@empty}
\def\@romkern#1#2{%
  \ifx#2\@empty
    #1%
    \let\next\relax
  \else
    #1\kern-0.07em%
    \let\next\@romkern
  \fi
  \next#2}

\begin{document}

\pagenumbering{Romankern}

\lipsum[1-50]

\end{document}
6

The definition of \@Romankern is not robust, because it contains assignments (\def\temp{...}, \let\next\@romkern, ...). If the label is written to the .aux file, assignments are not carried out and \def\temp{...} becomes to something like \def<the expansion of \temp>{...} with <the expansion of \temp> being the result of expanding the current meaning of \temp. In the case of the question \temp is undefined and therefore raises an error.

The following example defines a macro \@@Romankern that takes a plain number as argument and that will go to the .aux file. The conversion of the argument of \@Romankern, a counter value, to a plain number is done in an expandable and therefore robust way (via \expandafter and \number). The macro \@@Romankern is made robust by e-TeX's \protected that prevents the expansion of \@@Romankern, when writing to the .aux file.

\documentclass{article}

\usepackage{lipsum}
\usepackage{lastpage}

\makeatletter
\def\@Romankern#1{%
  \expandafter\@@Romankern\expandafter{\number#1}%
}
\protected\def\@@Romankern#1{%
  \edef\temp{\@Roman{#1}}%
  \expandafter\@romkern\temp\@empty\@empty
}
\def\@romkern#1#2{%
  \ifx#2\@empty
    #1%
    \let\next\relax
  \else
    #1\kern-0.07em%
    \let\next\@romkern
  \fi
  \next#2}

\begin{document}

\pagenumbering{Romankern}

Last page: \pageref{LastPage}

\lipsum[1-50]

\end{document}

The .aux file contains:

\relax
\newlabel{LastPage}{{}{\@@Romankern {10}}}
\xdef\lastpage@lastpage{\@@Romankern {10}}
\gdef\lastpage@lastpageHy{}

Remarks:

  • The call of \@romkern in

    \def\temp{\@Roman{#1}}%
    \expandafter\@romkern\temp\@empty\@empty
    

    is modified to contain two stop tokens \@empty, because \@romkern always expects two arguments. But \temp is empty for zero or negative numbers.

  • Ooh, now I understand it. Excellent explanation. Thanks a lot :) – John Doe-Nut Apr 8 '18 at 1:12

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.