12

Background (simplified)

I'm using the todonotes package to add todonotes to sections in my documents. I have created a macro to add the todonotes to sections such that they appear alongside the section's entry in the table of contents. I am also using the nameref macro from the hyperref package to refer to various sections of my document by name. Partly because of this, my macro also allows a label to be specified for the section. Here's a somewhat simplified version of the output my macro produces:

\section[{\todo{A todonote about the foo section}}{foo}]{foo}%
\label{sec:foo}

The problem

The trouble with this is that when using the \nameref to refer to a section with a todonote contained in the title, as above, the todonote also gets attached to the reference.

MWE:

\documentclass{article}

\usepackage{todonotes}
\usepackage[colorlinks,linkcolor=blue]{hyperref}

\begin{document}

\tableofcontents

\section{foo}
\begin{itemize}
    \item A reference to the \nameref{sec:bar} section
\end{itemize}

\section[{\todo{A todonote about the bar section}}{bar}]{bar}
\label{sec:bar}
A section called bar

\section{baz}
A section called baz

\end{document}

Which produces (after three or four compiles/builds for good measure):

A screenshot of the output from the MWE demonstrating the problem

What's happening:

So the call to \nameref{sec:bar} is presumably producing a reference which includes {\todo{A todonote about the bar section}}{bar} and todonotes is (correctly but undesirably) producing a duplicate todonote because of this. To clarify, I would like the reference to the bar section to be produced as just bar without the duplicate todonote.

Approach to finding a solution

Although this is my use case, I would prefer to know how to make nameref ignore any commands within the section title as I feel this would probably be more generally useful (and not just for me).

As a side note, an alternative solution to ignoring the commands could possibly be to define some nameref-specific reference text for the section somehow. However, this is less desirable as it would require a little maintenance to keep it relevant to the section name.

5
  • @lockstep: Just curious, what's the notes tag intended to be used for/mean? (Is it in reference to the todonotes package?)
    – Staves
    Mar 25, 2012 at 0:08
  • In a sense, yes, but I chose the more general tag. Feel free to replace notes with todonotes.
    – lockstep
    Mar 25, 2012 at 0:10
  • @lockstep: Ah ok, thanks. I think I shall change it as, although todonotes shows the problem I'm having with nameref, it's not actually the cause. There may be a todonotes related solution, but it's not really a notes(-caused) problem, if that makes sense.
    – Staves
    Mar 25, 2012 at 0:17
  • @egreg: Although I'm not sure if this might be slightly against the consensus on accepting solutions (somewhat relevant - meta.tex.stackexchange.com/q/535/11869) I strongly agree that Stephan deserves a lot of credit. If you're saying that this is cool then I shall leave it as it (now) is, otherwise I shall accept your answer so that it will be the first a new user/visitor sees.
    – Staves
    Mar 25, 2012 at 17:57
  • @Staves: Although it may require more maintenance (as you say), those that are willing to manually add the name can avoid \nameref entirely and just use \hyperref[sec:section]{Section Name} as in this post.
    – teichert
    Jun 11, 2021 at 16:31

2 Answers 2

6

A simplified version of Stefan Lehmke's solution:

\documentclass{article}

\usepackage{etoolbox}
\usepackage{todonotes}
\usepackage[colorlinks,linkcolor=blue]{hyperref}

\makeatletter
\newcommand\@namedisablecommands{}
\newcommand{\addnamedisablecommand}[1]{%
  \g@addto@macro\@namedisablecommands{\renewcommand#1{}}%
  \pdfstringdefDisableCommands{\renewcommand#1{}}%
}
\AtBeginDocument{ 
  \patchcmd{\T@nameref}
  {\let\label\@gobble}
  {\let\label\@gobble\@namedisablecommands}
  {}{}
}
\makeatother

\addnamedisablecommand{\todo[2][]}

Notice the changed syntax; to keep Stefan's write

\newcommand{\addnamedisablecommand}[2]{%
  \g@addto@macro\@namedisablecommands{\renewcommand#1#2{}}%
  \pdfstringdefDisableCommands{\renewcommand#1#2{}}%
}

instead.

After doing this, \@namedisablecommands will expand to

\renewcommand\todo[2][]{}

which will neutralize its expansion. The warning

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

is avoided by adding \todo (or other similar commands) in the disabled command list for hyperref.

9
  • That's really much simpler ;-) Mar 25, 2012 at 16:34
  • @StephanLehmke Yours was a very good inspiration.
    – egreg
    Mar 25, 2012 at 16:40
  • Just saw egreg's comment on the question, so leaving Stephan's answer as the accepted one. (@StephanLehmke)
    – Staves
    Mar 25, 2012 at 17:33
  • Really neat solution! I'm very interested in finding out if the method you and @StephanLehmke propose can be extended to limiting the scope of commands other than \T@nameref. I currently have a bounty on an open question that concerns, in part, limiting the scope of a TeX macro that invokes some lua code; see tex.stackexchange.com/q/48516/5001. Any and all help would be very much appreciated.
    – Mico
    Mar 26, 2012 at 13:21
  • I already saw your comment, but I doubt there is a connection. What we did here is a rather standard method for deactivating commands which have "moved" to a place they don't belong, because like in this case they are part of some section heading or such which gets saved by nameref. This is well-known for instance in the context of \pdfstringdefDisableCommands. Unfortunately, I don't see a connection with your case (protect macro names and arguments from search/replace). Mar 26, 2012 at 13:34
12

Interestingly, \nameref seems to know there are some things which should be disabled (for instance, \label), but it doesn't offer an interface for adjusting it.

Hence, patching seems to be a solution. I repeat the full example:

\documentclass{article}

\usepackage{etoolbox}
\usepackage{todonotes}
\usepackage[colorlinks,linkcolor=blue]{hyperref}

\makeatletter
\newcommand\@namedisablecommands{}%
\newcount\@namedisablegenericmacro
\newcommand\addnamedisablecommand[2]
{%
  \global\advance\@namedisablegenericmacro\@ne
  \expandafter\newcommand
  \csname @namedisablegobble\number\@namedisablegenericmacro\endcsname
  #2{}%
  \global\expandafter\let
  \csname @namedisablegobble\number\@namedisablegenericmacro\expandafter\endcsname
  \csname @namedisablegobble\number\@namedisablegenericmacro\endcsname
  \edef\@namedisablecommands{%
    \unexpanded\expandafter{\@namedisablecommands}%
    \let\noexpand#1\expandafter\noexpand
    \csname @namedisablegobble\number\@namedisablegenericmacro\endcsname
  }%
}
\AtBeginDocument
{%
  \patchcmd{\T@nameref}
  {\let\label\@gobble}
  {\let\label\@gobble\@namedisablecommands}
  {}{}%
}
\addnamedisablecommand{\todo}{[2][]}
\expandafter\pdfstringdefDisableCommands\expandafter
{%
  \@namedisablecommands
}%
\makeatother
\begin{document}

\tableofcontents

\section{foo}
\begin{itemize}
\item A reference to the \nameref{sec:bar} section
\end{itemize}

\section[{\todo{A todonote about the bar section}}{bar}]{bar}
\label{sec:bar}
A section called bar

\section{baz}
A section called baz

\end{document}

enter image description here

15
  • +1 for mentioning that \nameref has built-in functionality to keep it from operating on certain commands, such as \label.
    – Mico
    Mar 25, 2012 at 10:20
  • Thanks for this, I have accepted it as it does indeed solve the problem I am experiencing. Presumably this could also be expanded to ignore/gobble any number of commands?
    – Staves
    Mar 25, 2012 at 12:17
  • @Staves: Exactly, you can iterate the patch as often as you wish. Mar 25, 2012 at 12:21
  • Nice one :) Could the \let and \@gobble surrounding \todo be moved into the \addnamedisablecommand macro (around the #1)? - This works for me with \todo it but I'm not sure if there might be ramifications with other commands and so on...
    – Staves
    Mar 25, 2012 at 13:04
  • I thought about that, but the next step of abstraction is a lot more effort. The main complication is to handle commands with differing numbers of arguments. Mar 25, 2012 at 13:13

You must log in to answer this question.

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