3

I'm typesetting a document with PDFLaTeX. In some section titles, I use a macro which has a starred version. In the PDF index (the table of contents that PDF viewers typically show in a side pane), I get the macro's arguments, showing that the macro has been erased somewhere. As it happens, I want to have just the second argument of the macro as the degraded plain text (whether the macro call has a star or not).

That is, I write

\section{\foo{one}{two}}

and I want it to be sort of like

\section[two]{\foo{one}{two}}

except that the table of contents should still have \foo{one}{two} and I want this to be automatic, I don't want to have to do something different in every section title where I use the macro.

This looks to me like a case for \texorpdfstring. But how do I eat up the star? LaTeX complains about \@ifnextchar ending up in a PDF string, which makes sense, because that's where the expandability stops. How can I eat up the star in an expandable way, or whatever it takes to get this right?

Toy example illustrating the problem:

\documentclass{article}
\usepackage[english]{babel}
\usepackage{xr-hyper}
\usepackage[destlabel=true]{hyperref}
\makeatletter
\def\foo@star#1#2{\textsf{#1}-{#2}}
\def\foo@plain#1#2{\textit{#1}-{#2}}
\DeclareRobustCommand{\foo}{\@ifstar\foo@star\foo@plain}
\def\foo@pdf#1#2{#2}
\DeclareRobustCommand{\goo}{\texorpdfstring{\@ifstar\foo@star\foo@plain}{\@ifstar\foo@pdf\foo@pdf}}
\DeclareRobustCommand{\hoo}{\texorpdfstring{\@ifstar\foo@star\foo@plain}{\foo@pdf}}
\makeatother
\begin{document}
\section{\foo{plain}{direct}}
\section{\foo*{starred}{direct}}
\section{\goo{plain}{texorpdfstring+ifstar}}
\section{\goo*{starred}{texorpdfstring+ifstar}}
\section{\hoo{plain}{texorpdfstring}}
\section{\hoo*{starred}{texorpdfstring}}
\end{document}

\foo is the base version, without consideration for PDF strings. \goo is what I wish would work, but doesn't because \@ifstar isn't fully expandable. \hoo works for the unstarred version but doesn't eat the star when it's present.

Warnings from LaTeX:

Package hyperref Warning: Token not allowed in a PDF string (PDFDocEncoding):
(hyperref)                removing `\@ifnextchar' on input line 14.


Package hyperref Warning: Token not allowed in a PDF string (PDFDocEncoding):
(hyperref)                removing `\@ifnextchar' on input line 15.


Package hyperref Warning: Token not allowed in a PDF string (PDFDocEncoding):
(hyperref)                removing `\@ifnextchar' on input line 16.


Package hyperref Warning: Token not allowed in a PDF string (PDFDocEncoding):
(hyperref)                removing `\@ifnextchar' on input line 17.

PDF index:

plaindirect
*starreddirect
plaintexorpdfstring+ifstar
*starredtexorpdfstring+ifstar
texorpdfstring
starredtexorpdfstring

What I want to have there is just the second argument, no first argument, no star.

  • It makes no sense having \@ifstar in the “pdf part” of \texorpdfstring – egreg May 20 '16 at 12:51
  • @egreg I know. It makes no sense to have \@ifstar in the TeX part either since it then gets interpreted as part of the PDF string. – Gilles May 20 '16 at 13:11
  • Even stranger, \edef\temp{\foo{plain}{direct}} also crashes - Argument of KV@def has an extra }. – John Kormylo May 20 '16 at 13:38
2

I think this is what you want:

\documentclass{article}
\usepackage{xparse}
\usepackage{hyperref}

\DeclareExpandableDocumentCommand{\foo}{smm}{%
  \texorpdfstring
    {\IfBooleanTF{#1}{\textsf}{\textit}{#2}--#3}
    {#2-#3}%
}

\begin{document}

\section{This is \foo{A}{B}}

\section{This is \foo*{A}{B}}

\end{document}

enter image description here

0

All you needed was \string.

\documentclass{article}
\usepackage[english]{babel}
\usepackage{xr-hyper}
\usepackage[destlabel=true]{hyperref}
\makeatletter
\newcommand{\foo@star}[2]{\textsf{#1}-{#2}}
\newcommand{\foo@plain}[2]{\textit{#1}-{#2}}
\DeclareRobustCommand{\foo}{\@ifstar\foo@star\foo@plain}
\makeatother

\newcommand{\mysection}[1]{\section[\string #1]{#1}}

\begin{document}
\tableofcontents

\mysection{\foo{plain}{direct}}
\mysection{\foo*{starred}{direct}}

\end{document}

demo

  • How would this solution generalize to a non-toy example where the problematic macro isn't the first thing in the title? – Gilles May 20 '16 at 14:38
  • @Gilles - Further experiments showed no problems using additional text or macros. One can use \protect instead of \string, but I have never found a case where it works any better. – John Kormylo May 20 '16 at 14:58

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.