I wish to mark several pairs of from/to anchor locations in text, and use tikz
to draw lines between these. The anchors are not in absolute page locations; the text determines where the endpoints of the line should be. However, the drawn line must not obscure the text. Rather, the line must appear as though it were drawn under the text instead of above.
Failed Attempt #1: Line Obscures Text
The following small example document defines \StrokeFrom
and \StrokeTo
macros, with the latter completing the line to the former. But the stroked line is above the text, obscuring the text below. That is what I want to change. (Run pdflatex
twice for the arrows to move to their final locations.)
\documentclass{article}
\usepackage{tikz}
\tikzset{stroke/.style = {->, yellow, line width = 1ex}}
\newcommand{\StrokeAnchor}[1]{\ensuremath{\vcenter{\hbox{\tikz[overlay, remember picture]{\coordinate (stroke #1) ;}}}}}
\newcommand{\StrokeFrom}[0]{\StrokeAnchor{from}}
\newcommand{\StrokeTo}[0]{\StrokeAnchor{to}\tikz[overlay, remember picture]{\draw [stroke] (stroke from) -- (stroke to) ;}}
\begin{document}
This text is \StrokeFrom before the figure both in the \LaTeX{} source
and in the \StrokeTo rendered document.
\begin{figure}[p]
\StrokeFrom Once upon a time. \StrokeTo
\end{figure}
This text is after the figure in the \LaTeX{} source but before
\StrokeFrom it in the rendered document. \StrokeTo
\end{document}
Failed Attempt #2: Transparent Line Pollutes Text Color
I have also considered simply making the stroked line partially transparent. Unfortunately, a partially-transparent colored line atop black text changes the color of that black text. It also makes fully-saturated colors (such as yellow, above) unusable.
Failed Attempt #3: Line Does Not Stay With Float
This problem was originally inspired by my answer to a question about highlighting text in a code listing while also keeping syntax highlighting. In that case I give the from/to anchors unique-per-page names, then paint all of the required lines from within \AtBeginShipout{\AtBeginShipoutUpperLeft{...}}
so that the lines go onto the page before the text does. (Run pdflatex
twice for the arrows to move to their final locations.)
\documentclass{article}
\usepackage{atbegshi,ifthen,listings,tikz}
\usetikzlibrary{calc}
\tikzset{stroke/.style = {->, yellow, line width = 1ex}}
\newcounter{stroke}[page]
\newcommand{\StrokeAnchor}[1]{\ensuremath{\vcenter{\hbox{\tikz[remember picture, overlay]{\coordinate (#1 stroke \arabic{stroke});}}}}}
\newcommand{\StrokeFrom}[0]{\stepcounter{stroke}\StrokeAnchor{begin}}
\newcommand{\StrokeTo}[0]{\StrokeAnchor{end}}
\AtBeginShipout{\AtBeginShipoutUpperLeft{\ifthenelse{\value{stroke} > 0}{\tikz[remember picture, overlay]{\foreach \stroke in {1,...,\arabic{stroke}} \draw[stroke] (begin stroke \stroke) -- (end stroke \stroke);}}{}}}
\begin{document}
This text is \StrokeFrom before the figure both in the \LaTeX{} source
and in the \StrokeTo rendered document.
\begin{figure}[p]
\StrokeFrom Once upon a time. \StrokeTo
\end{figure}
This text is after the figure in the \LaTeX{} source but before
\StrokeFrom it in the rendered document. \StrokeTo
\end{document}
Unfortunately, this strategy misbehaves if the stroke anchors are in a float which is processed on one page but actually placed on a subsequent page. The line is drawn on the page which was active when the float was being processed, not on the later page where the float actually appears. A fully-satisfactory solution must be able to handle floats that move to different pages, such as the \begin{figure}[p]...\end{figure}
float in the example document above.
Best Answer
@Martin is an TeX ninja. His answer, above, is the first fully-working solution to my original question. The use of
zref-abspos
andzref-abspage
is smart, and it was quite informative to see how to convert fromzref-abspos
positions totikz
coordinates. I can offer some incremental improvements on his proof-of-concept, but he gets big kudos for showing how this can be done. Thanks, Martin!My Modifications
My version, which appears below, makes the following improvements to Martin's original:
The magic
yshift=.5ex
position adjustment is now computed automatically using\vcenter
. This should make stroke positioning more robust in the face of line height changes.Page comparisons are always done between absolute page numbers, never between an absolute and a regular page number. This should improve robustness in complex documents with nontrivial page numbering schemes.
Conversion from
zref
positions totikz
coordinates now takes advantage of the standardshift=
diagram option instead of calculating offsets explicitly. This improves readability and avoids loading up thetikz
calc
library.I use
atbegshi
instead ofeverypage
for my per-page hook. Thezref-abspage
package already needsatbegshi
anyway, so this avoids bringing in an additional package. A second benefit toatbegshi
is that\baselineskip
can now be used to set thetikz
line width, which does not work well witheverypage
.I loop over the numbered highlights using
\forloop
from theforloop
package instead of a recursive macro call. (There is still a recursive macro call deep inside\forloop
, but that is hidden away as an implementation detail.) Likewise, I'm using\ifthenelse
from theifthen
package instead of lower-level TeX conditionals. I generally prefer to program up at the LaTeX layer as much as possible for better error checking and better long-term readability.I've factored out some common code for extracting the absolute page and the
tikz
coordinates of azref
reference, again for better long-term readability.I've added the warning for cross-page highlights that Martin suggested in his solution.
I've generally replaced the term "stroke" with "highlight" to better reflect the intended use of this code, and included
@
symbols in all counter and macro names that are internal to the implementation, not intended for use by the document author.Sample Document
I'll offer my revised solution and the example document separately, to make it easier for folks to extract just the former for their own future use. Here's the example document, which is the same as that Martin used:
Highlighter Implementation
And here is the implementation of the solution, which should be saved as
highlighter.sty
before rendering the example document:Again, big thanks to Martin for showing us how this could be done. I'm just putting the final polish on his gemstone.