8

I defined a macro that typesets a technical name using math symbols, in my example $\alpha$-thingy. When used in a \section, \hyperref would complain that it cannot use the symbol to compose the string in the pdf's index. So I defined the macro so it uses \texorpdfstring to provide the alternate text-only form alpha-thingy for the index. This works very well unless the macro has an optional argument. This MWE shows the issue:

\documentclass{memoir}

\usepackage{hyperref}

\newcommand{\working}{\texorpdfstring{$\alpha$-thingy}{alpha-thingy}}
\newcommand{\nonworking}[1][thing]{\texorpdfstring{$\alpha$-#1}{alpha-#1}}

\begin{document}
  \section{Example \working}
  Hello \working.
  \section{Example \nonworking}
  Hello \nonworking.
  \section{Example \nonworking[thingy]}
  Hello \nonworking[thingy].
\end{document}

In the log I get two warnings:

W: mwe.tex:11 Token not allowed in a PDF string (PDFDocEncoding):(hyperref) removing `\nonworking'
W: mwe.tex:13 Token not allowed in a PDF string (PDFDocEncoding):(hyperref) removing `\nonworking'

How can I make hyperref happy without having to use \texorpdfstring directly in the \section's argument each time?

1 Answer 1

5

You cannot use optional arguments for bookmarks, because LaTeX's implementation uses \futurelet, which is not expandable. Bookmarks strings are not typeset by TeX, only converted to a string.

Since the macro with the optional argument does not have a following mandatory argument, there is no way detecting the optional argument using a macro-based approach.

The follwing example defines \test with an optional and a mandatory argument. The mandatory argument is not used, thus this is more an artificial example to show the way:

\documentclass{memoir}

\usepackage[pdfencoding=auto]{hyperref}
\usepackage{bookmark}
\bookmarksetup{numbered, open}

\newcommand{\working}{\texorpdfstring{$\alpha$-thingy}{\textalpha-thingy}}

\makeatletter
\newcommand{\test}{}
\def\test#1#{%
  \ifx\\#1\\%
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi
  {\testA{thing}}%
  {\expandafter\testA\expandafter{\stripbrackets#1}}%
}
\newcommand*{\testA}[2]{%
  \texorpdfstring{$\alpha$-#1}{\textalpha-#1}%
}
\newcommand*{\stripbrackets}{}
\long\def\stripbrackets[#1]{#1}
\makeatother

\begin{document}
  \section{Example \working}
  Hello \working.
  \section{Example \test{}}
  Hello \test{}.
  \section{Example \test[thingy]{}}
  Hello \test[thingy]{}.
\end{document}

Result

Bookmarks:

Bookmarks

Remarks:

  • Package bookmark improves the bookmark handling and adds some features.
  • Option pdfencoding=auto uses Unicode for the bookmarks and PDFDocEncoding, if Unicode is not necessary for the bookmark string.
  • The basic trick is \def\test#1#. This puts the optional argument including the square brackets inside #1, if it is given. If the mandatory argument is directly following \test, then #1 is empty, which is tested by \ifx\\#1\\.
6
  • 1
    wow, so much black magic...could you explain where does the second # in \def\test#1# come from?
    – Bordaigorl
    Feb 7, 2015 at 23:07
  • @Bordaigorl The second # is a special case. At the end of the parameter text, it matches/expects an opening curly brace. Feb 7, 2015 at 23:29
  • just two more questions: 1) where can I read more about this use of #? 2) suppose I do not need to support the first use of \nonworkin in my MWE but I can assume I always specify the optional argument when using the macro as a \section's argument. Is there a less obscure way to handle that?
    – Bordaigorl
    Feb 11, 2015 at 11:54
  • Couldn't one redefine futurelet in this context since the very restrictions that are causing problems here would presumably make it easier to fake futurelet in some way no? But the more I think about it the more I'm unsure. Nov 6, 2020 at 23:23
  • @PeterGerdes \futurelet is a non-expandable primitive command (like \let, \def, \edef) that cannot be simulated by macros. Nov 7, 2020 at 20:01

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .