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.

Staves
  • 595
  • @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 '12 at 00:08
  • In a sense, yes, but I chose the more general tag. Feel free to replace [tag:notes] with [tag:todonotes]. – lockstep Mar 25 '12 at 00:10
  • @lockstep: Ah ok, thanks. I think I shall change it as, although [tag:todonotes] shows the problem I'm having with nameref, it's not actually the cause. There may be a [tag:todonotes] related solution, but it's not really a tag:notes problem, if that makes sense. – Staves Mar 25 '12 at 00:17
  • @egreg: Although I'm not sure if this might be slightly against the consensus on accepting solutions (somewhat relevant - http://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 '12 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 '21 at 16:31

2 Answers2

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

  • +1 for mentioning that \nameref has built-in functionality to keep it from operating on certain commands, such as \label. – Mico Mar 25 '12 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 '12 at 12:17
  • @Staves: Exactly, you can iterate the patch as often as you wish. – Stephan Lehmke Mar 25 '12 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 '12 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. – Stephan Lehmke Mar 25 '12 at 13:13
  • Ah, understood - cheers again! – Staves Mar 25 '12 at 13:14
  • Ok, even more generic. Give the number of arguments (up to 9) as the second argument to \addnamedisablecommand. Tested only for 1 :-) Although this is getting a bit absurd for the purpose... – Stephan Lehmke Mar 25 '12 at 13:22
  • Crikey, all singing all dancing :P I was just about to ask how to deal with arguments and this is now very much in line with a generalised approach that I was asking about in the question :D Well done sir! – Staves Mar 25 '12 at 13:42
  • At the risk of more absurdity, is there any way to deal with optional arguments? Currently changing the \todo call to be \todo[linecolor=blue]{A todonote about the bar section} results in the reference text being produced as linecolor=blue]A todonote about the bar sectionbar – Staves Mar 25 '12 at 13:50
  • Next edit: Now you can put into the second argument the argument definition used by \newcommand. In the example for a macro with two arguments, one of which is optional. – Stephan Lehmke Mar 25 '12 at 15:29
  • Sweet, that's brilliant! I owe you a beer(/beverage of choice) ^^ – Staves Mar 25 '12 at 15:45
  • @StephanLehmke I still get Token not allowed in a PDF string (PDFDocEncoding): removing `\todo', but it's probably unavoidable. – egreg Mar 25 '12 at 15:45
  • @egreg: Do you get that error when compiling Stephan's example code as it is (i.e. without modification)? – Staves Mar 25 '12 at 15:58
  • @egreg: True, but that's a different matter altogether. It comes from hyperref's \pdfstringdef which converts the sectiontitle to text for the bookmark. You'll notice some garbage there produced by the todo command. I've now added a \pdfstringdefDisableCommands for good measure, but note it has to work in an expandable way, so it cannot cope with optional arguments; no chance here. @Staves, if you want to get rid of both, you must define a second pseudo-\todo command with two arguments and use that whenever you'd use \todo with optional argument. – Stephan Lehmke Mar 25 '12 at 16:16
  • Gotcha, cheers Stephan :) – Staves Mar 25 '12 at 17:35
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.

egreg
  • 1,121,712
  • That's really much simpler ;-) – Stephan Lehmke Mar 25 '12 at 16:34
  • @StephanLehmke Yours was a very good inspiration. – egreg Mar 25 '12 at 16:40
  • Just saw egreg's comment on the question, so leaving Stephan's answer as the accepted one. (@StephanLehmke) – Staves Mar 25 '12 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 http://tex.stackexchange.com/q/48516/5001. Any and all help would be very much appreciated. – Mico Mar 26 '12 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). – Stephan Lehmke Mar 26 '12 at 13:34
  • @egreg: In regards to the warning (Token not allowed in a PDF string), I tried adding \addnamedisablecommand{\todo} after \addnamedisablecommand{\todo[2][]} This did prevent the warning but a \nameref to the label in the following \section[{\todo[linecolor=blue]{A todonote about the bar section}}bar]{bar}\label{sec:bar} came out as [linecolor=blue]A todonote about the bar sectionbar I think I understand why this is happening, the second disable \addnamedisablecommand{\todo} appears to override the first, but can the warning be prevented without messing up the reference? – Staves Mar 26 '12 at 17:47
  • @Staves: As I said above, you need to define a command \newcommand\totwo[2]{\todo[#1]{#2}} and use that instead of \todo with optional argument. Then you can normally disable \totwo[2]. – Stephan Lehmke Mar 26 '12 at 20:40
  • @Staves: I recommend to accept egreg's answer. People finding this later will want to use the simpler solution. – Stephan Lehmke Mar 26 '12 at 20:42
  • @StephanLehmke: Ah ok, I shall define an intermediary command as you suggest and I have now accepted egreg's answer - cheers for all your help :) – Staves Mar 26 '12 at 23:19