The floating object (figure*
) spanning two columns is the culprit here, as it introduces an additional level of boxing of the output page containing the \pdfstartlink
which opens the citation hyperlink.
The page that follows is a "normal" two-column page without a float and thus the closing \pdfendlink
ends up in a box whose level is one less.
To remedy the situation, we could add one boxing level to the offending \pdfendlink
. For this, the example below defines
\addOneNestingLevelEndLink
which is inserted immediately before the offending \citep
command.
The opposite situation of a two-column float placed on the page where the citation hyperlink ends is possible too. Here, the boxing level of the \pdfstartlink
command must be increased. Therefore,
\addOneNestingLevelStartLink ,
likewise to be put in front of the citation command in question, is provided. Either one or the other of these commands may be used at a time.
Example (on ShareLaTeX):
\documentclass[a5paper,twocolumn]{scrartcl}
\usepackage{filecontents}
\begin{filecontents}{btxdoc.bib}
@BOOK{strunk-and-white,
author = "Strunk, Jr., William and E. B. White",
title = "The Elements of Style",
publisher = "Macmillan",
edition = "Third",
year = 1979 }
\end{filecontents}
\pagestyle{empty}
\usepackage{natbib}
\usepackage[colorlinks,citecolor=blue]{hyperref}
% For constructing the MWE
\usepackage{mwe,lipsum}
\bibliographystyle{chicago}
\makeatletter\ifdefined\Hy@StartlinkName
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% \addOneNestingLevelStartLink
% \addOneNestingLevelEndLink
%
% put ONE of \addOneNestingLevel(Start|End)Link in front of offending \cite
% depending on the placement of the double-column float object
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\def\addOneNestingLevelStartLink{%
\gdef\Hy@StartlinkName##1##2{%
\sbox0{\Hy@StartlinkNameOrig{##1}{##2}}\usebox0
\global\let\Hy@StartlinkName\Hy@StartlinkNameOrig%
}%
}
\def\addOneNestingLevelEndLink{%
\gdef\pdfendlink{%
\sbox0{\pdfendlinkOrig}\usebox0%
\global\let\pdfendlink\pdfendlinkOrig%
}%
}
\let\Hy@StartlinkNameOrig\Hy@StartlinkName
\let\pdfendlinkOrig\pdfendlink
\else
\let\addOneNestingLevelStartLink\relax
\let\addOneNestingLevelEndLink\relax
\fi
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\makeatother
\begin{document}
\begin{figure*}[t]
\includegraphics[width=\textwidth]{example-image-golden}
\end{figure*}
\lipsum[1]
\lipsum[1]
\lipsum[2]
Test line break:
\citep{strunk-and-white}
Works for first word.
\citep{strunk-and-white}
And for second.
Conclusion: Line breaks work well.
Now what happens if we force the break at the page boundary?
Let's take a look
We make this just long enough to break the link at page boundary
\addOneNestingLevelEndLink\citep{strunk-and-white}
\bibliography{btxdoc}
\end{document}
The MWE is a good starting point. From there I could simplify it further. frontmatter
sets the title in the optional argument of \twocolumn
. Thus \twocolumn[]
is sufficient. The text can be replaced by vertical spaces.
The class can be replaced by the standard article
class.
MWE:
\documentclass{article}
\usepackage{hyperref}
\begin{document}
\twocolumn[]
\null\vfill\newpage
\null\kern.95\textheight
\href{http://tex.stackexchange.com}{This is a very very
very very very very very very very very very very very very very
very very very very very very very very long link.}
\end{document}
If \twocolumn
is called with the optional argument, then macro \@topnewpage
is called, which puts the stuff of the optional argument in a double column float object for the top of the page. Thus also the following triggers the problem instead of \twocolumn[]
:
\twocolumn
\begin{figure*}\end{figure*}
At some later time in the output routine, macro \@combinedblfloats
combines the top double float object with the normal page contents:
\setbox\@outputbox \vbox to\textheight{%
\unvbox\@tempboxa
\vskip-\dblfloatsep
\ifnum \@dbltopnum>\m@ne
\dblfigrule
\fi
\vskip \dbltextfloatsep
\box\@outputbox
}%
As can be seen, \@outputbox
is boxed again in an additional \vbox
.
Thus the box level of the first part of the link is higher by one in comparison to the second page without the double float object.
The following workaround increases the boxing level except for the first page:
\documentclass{article}
\usepackage{hyperref}
\AtBeginShipout{%
\ifnum\value{page}>1 %
\typeout{* Additional boxing of page `\thepage'}%
\setbox\AtBeginShipoutBox=\hbox{\copy\AtBeginShipoutBox}%
\fi
}
\begin{document}
\twocolumn[]
\null\vfill\newpage
\null\kern.95\textheight
\href{http://tex.stackexchange.com}{This is a very very
very very very very very very very very very very very very very
very very very very very very very very long link.}
\end{document}
The workaround also works with the original MWE of the question.
In real documents, it is not trivial to know, which pages would need
how many additional box levels.
A manual way would be using the following snippet, the first two lines quite early, even before \documentclass
, the remaining part should be executed late, here via \AtBeginDocument
.
\RequirePackage{atbegshi}
\AtBeginShipoutInit
\AtBeginDocument{%
\AtBeginShipout{%
\begingroup
\showboxdepth=\maxdimen
\showboxbreadth=\maxdimen
\tracingonline=1 %
\edef\restoreinteractionmode{\interactionmode=\the\interactionmode}%
\nonstopmode
\showbox\AtBeginShipoutBox
\restoreinteractionmode
\endgroup
}%
}
Then the box listings of the output pages can be analyzed to identify the start part of the link and the end part on the next page. Then the number of dots at the line starts need to be compared.
The next example tries a more automatic solution:
\documentclass[5p]{elsarticle}
\usepackage{kantlipsum}
\usepackage{hyperref}
\usepackage{etoolbox}
\makeatletter
\newcount\c@additionalboxlevel
\setcounter{additionalboxlevel}{0}
\newcount\c@maxboxlevel
\setcounter{maxboxlevel}{1}
\patchcmd\@combinedblfloats{\box\@outputbox}{%
\stepcounter{additionalboxlevel}%
\box\@outputbox
}{}{\errmessage{\noexpand\@combinedblfloats could not be patched}}
\AtBeginShipout{%
\ifnum\value{additionalboxlevel}>\value{maxboxlevel}%
\typeout{Warning: maxboxlevel might be too small, increase to %
\the\value{additionalboxlevel}%
}%
\fi
\@whilenum\value{additionalboxlevel}<\value{maxboxlevel}\do{%
\typeout{* Additional boxing of page `\thepage'}%
\setbox\AtBeginShipoutBox=\hbox{\copy\AtBeginShipoutBox}%
\stepcounter{additionalboxlevel}%
}%
\setcounter{additionalboxlevel}{0}%
}
\makeatother
\begin{document}
\begin{frontmatter}
\end{frontmatter}
\kant*[1-4]
\kant[5]
\href{http://tex.stackexchange.com}{This is a very very
very very very very very very very very very very very very very
very very very very very very very very long link.}
\end{document}
Maybe this issue could also be fixed by replacing \box\@outputbox
by \unvbox\@outputbox
in macro \@combinedblfloats
(maybe the vertical glue assignments might change):
\usepackage{etoolbox}
\makeatletter
\patchcmd\@combinedblfloats{\box\@outputbox}{\unvbox\@outputbox}{}{%
\errmessage{\noexpand\@combinedblfloats could not be patched}%
}%
\makeatother
Best Answer
I figured out the location of the link causing the error using the
[draft]
option in thehyperref
package definition, as explained in this 2002 mailing list post by James A. Bednar:(I tried user22326's useful suggestion to use the
[debug]
option in thehyperref
package definition, however, this didn't help me in finding the location (in the log, or in the latex file) of the link causing the error. Not even after I figured it out using thedraft
option solution.)