Closed Enivex closed 7 months ago
For obvious reasons, hyperref complains when
\zcref
is used in section titles
I'd say expected indeed. But, can you please provide a MWE of the issue? (It spares me the guesswork and the rework. If at all possible, you should always try to provide one when reporting).
There are some workarounds I've seen, but they all revolve around cleveref.
Could you provide links to them?
I'd say expected indeed. But, can you please provide a MWE of the issue? (It spares me the guesswork and the rework. If at all possible, you should always try to provide one when reporting).
My bad
\documentclass{article}
\usepackage{zref-clever}
\usepackage{hyperref}
\begin{document}
\section{Hello}
\label{first}
\section{World \zcref{first}}
\end{document}
will give the following error
Token not allowed in a PDF string (Unicode):
(hyperref) removing `\zcref'.
and the pdf bookmark will be World first
instead of World section 1
However
\documentclass{article}
\usepackage{zref-clever}
\usepackage{hyperref}
\usepackage{cleveref}
\usepackage{crossreftools}
\pdfstringdefDisableCommands{%
\let\zcref\crtcref
}
\begin{document}
\section{Hello}
\label{first}
\section{World \zcref{first}}
\end{document}
gives the desired output
Could you provide links to them?
https://ctan.org/pkg/crossreftools?lang=en provides expandable versions of cleveref commands
I found the workaround in https://github.com/latex3/hyperref/issues/148#issuecomment-660202930
I'm not sure the use case crossreftools
had in mind in offering \crtcref
and \crtCref
but, in my view, using them to do:
\pdfstringdefDisableCommands{%
\let\Cref\crtCref
\let\cref\crtcref
}
is a mistake. Because \cref
and \Cref
can receive a comma separated list of labels, while \crtcref
and \crtCref
can only receive a single label as argument. So, if you happen to try:
\documentclass{article}
\usepackage{hyperref}
\usepackage{cleveref}
\usepackage{crossreftools}
\pdfstringdefDisableCommands{%
\let\cref\crtcref
}
\begin{document}
\section{Hello}
\label{first}
\section{Foo}
\label{second}
\section{World \cref{first,second}}
\end{document}
You get:
And the error is completely silent, since we cannot even issue a warning there, given the macros need to be expandable.
Also, as documented in crossreftools
, besides the fact it can't handle lists of labels, \crtcref
and \crtCref
are not fully general replacements to \cref
and \Cref
since it ignores \crefformat
etc. and only retrieve the \crefname
of the counter for the label. So \crtcref
may differ from \cref
even for a single label.
This point is valid for both cleveref
and zref-clever
. The problem is the same. And I don't think it is possible to provide a fully general expandable version of either \cref
or \zcref
. So, I don't think it is an oversight of cleveref
not to try to provide such macros. If crossreftools
decided to provide some half-baked ones regardless, I personally still don't understand the intended use cases for them. And, as I said above, using them for this particular use case is, imho, a bad idea.
The case is actually worse for \zcref
because it has an optional argument which \crtcref
does not. So if you do \let\zcref\crtcref
, as soon as you do something like \section{\zcref[S]{first} bar}
, you get a broken document.
My recommendation for this is to take the traditional approach, and use \texorpdfstring
, constructing the reference manually. Like so:
\documentclass{article}
\usepackage{zref-clever}
\usepackage{hyperref}
\begin{document}
\section{Hello}
\label{first}
\section{Foo \texorpdfstring{\zcref{first}}{section~\ref{first}}}
\end{document}
(Depending on the case, you may also use the short title/optional arg of the sectioning command. Just, as you know, it affects not only the PDF bookmarks but also things like ToC etc. It depends on what you want.)
You say "this defeats part of the reason for using zref-clever
in the first place". I'd say hardly. It is indeed a known limitation, and working around it requires a little manual work on the occasional use of \zcref
in a section title. Not a significant dent in the grand scheme of things.
If you really, really want to avoid this manual work, you may get away by leveraging \autoref
for your bookmarks with something like:
\documentclass{article}
\usepackage{zref-clever}
\usepackage{hyperref}
\NewExpandableDocumentCommand{\myautoref}{O{}m}{\autoref{#2}}
\pdfstringdefDisableCommands{%
\let\zcref\myautoref
}
\begin{document}
\section{Hello}
\label{first}
\section{World \zcref{first}}
\end{document}
However, this is subject to the same fundamental problem as using \let\cref\crtcref
with cleveref
, plus any differences of output between \zcref
and \autoref
(Well, \crtcref
is arguably closer to \autoref
than to \cref
anyway...). But, you should get reasonable results most of the time. Caveat emptor though, of course.
Naturally, zref-clever
does store the name(s) for a given reference type somewhere, and you can (expandably) retrieve the type of a given label with zref
tools. So it is technically viable to build something like \crtcref
for zref-clever
. (Though there are some subtle differences, e.g.: i) the case is not controlled by the main macro but by an option, which you would not have access to; ii) the name of the variable which stores the reference type name may depend on the declension cases defined for a given language, etc.). Anyway, this is neither recommended, nor supported, so I won't post this here. Of course, as you know, (La)TeX allows you to dig the code and do it regardless.
PS: @muzimuzhi Since you posted the crossreftools
approach at the hyperref
issue tracker, you may be interested on my take on its risks above. :wink: (Don't worry about it, I'm really just pinging because you may be interested).
Edit: A little trick to get a warning in case of multiple labels (at the cost of having to use a dedicated helper macro):
\documentclass{article}
\usepackage{zref-clever}
\usepackage{hyperref}
\NewExpandableDocumentCommand{\myzcrefsection}{O{}m}{%
\texorpdfstring{\refused{#2}\zcref[#1]{#2}}{\autoref{#2}}}
\begin{document}
\section{Foo}
\label{first}
\section{Bar}
\label{second}
% OK
\section{Baz \myzcrefsection{first}}
% Not OK, but at least generates unresolved references warning.
\section{Baz \myzcrefsection{first,second}}
\end{document}
Thanks for the exhaustive reply. I'll probably end up just sticking with \texorpdfstring
. (I know only just about enough to be dangerous.)
You say "this defeats part of the reason for using zref-clever in the first place". I'd say hardly. It is indeed a known limitation, and working around it requires a little manual work on the occasional use of \zcref in a section title. Not a significant dent in the grand scheme of things.
This is true. I was exaggerating for effect.
It's just unfortunate that such a seemingly easy (for the user) thing is so hard to do in LaTeX. I seems like a thing that "should" be possible.
I'll probably end up just sticking with
\texorpdfstring
.
I think that's the wisest. I do get your mumble, but I think you'll get used to it in no time, and it shouldn't bother you much in your actual document. And, even if you occasionally need to set up a cross-ref manually for the bookmark, the actually typeset material can always be kept perfectly consistent with the use of \zcref
.
It's just unfortunate that such a seemingly easy (for the user) thing is so hard to do in LaTeX. I seems like a thing that "should" be possible.
The problem well known and low level, but not that low. I'd call it a limitation of how hyperref
handles bookmarks rather than a "LaTeX limitation". Consider, for example, the following document to generate a tagged PDF:
\DocumentMetadata{testphase=phase-III}
\documentclass{article}
\usepackage{zref-clever}
\usepackage{hyperref}
\begin{document}
\section{Hello}
\label{first}
\section{Foo \texorpdfstring{\zcref{first}}{section~\ref{first}}}
\end{document}
And open the resunting PDF at https://ngpdf.com/loadFile (to inspect the HTML extracted from the tagged structure). You get:
I take from there LaTeX can, in principle, already "see the text generated by protected/non-expandable material and send it to the PDF". (Says the person who has no real idea of how the bookmarks are created... :wink:, so take it with a grain of salt). So things may evolve eventually (of course, the time scale in which things move in the LaTeX world might not be to your liking...).
Thanks for the exhaustive reply.
Thanks for taking the time to report. Cheers! :smile:
For obvious reasons, hyperref complains when
\zcref
is used in section titlesOne way around this is
\texorpdfstring{\zcref{some_theorem}}{Theorem~\ref{some_theorem}}}
, however this defeats part of the reason for using zref-clever in the first place.There are some workarounds I've seen, but they all revolve around cleveref. For instance
However, this only works if cleveref is also loaded.
Is there a better way to solve this?