tikzmark to have different behaviour if first run (and mark locations not yet available)
Here's a souped-up version of \tikzmark
that addresses some of the issues in this question (though in functionality there's not a huge difference between this and Stephan's answer). To address the main failing first: it is not possible, using TikZ/PGF, to reference the absolute positioning of the page on the first run. This is because Caramdir's comment on the question is absolutely correct: TikZ/PGF works relative to the local coordinate system and converts the global positioning to the current one before using it. So in order to use absolute coordinates, it needs to know where the current picture is located on the page so that it can work out the transformation from absolute to relative. It only knows this after the page has been shipped out since TikZ/PGF pictures are simply boxes, as far as TeX is concerned, and thus subject to the whims and fancies of TeX as it puts them on the page. So for your "default action", I recommend choosing something that doesn't depend on absolute page positioning.
The main issue that this code addresses is being able to reference the tikzmark before it is declared. This works, on second and subsequent runs, because of how TikZ/PGF deals with the remembering. Again, the comments by Caramdir and Stephan are completely correct. TikZ/PGF saves the origin of the picture into the auxfile. This is then available throughout the document on subsequent runs (though, as with all aux stuff, the coordinates used are technically the coordinates of the previous run. Hopefully this stabilises.). The node positions are remembered relative to their picture origin and their picture id is remembered with them, so when you invoke a node outside its picture then TikZ/PGF looks up the node coordinates which are relative to its picture origin, and then adjusts them by the relative separations of the picture origins (again, there are conversions between two coordinate systems here which is why both pictures require the remember picture
key) (also, it's perfectly possible to use a node from another picture that hasn't been saved, but what is returned is the coordinates of the node in the original picture without being transformed to the local coordinate system. This can sometimes be useful.).
Now when marking a position on a page, we really only need to remember one coordinate. But remembering a node involves two: remembering the picture position and the relative offset of the node. So instead we should just remember the picture position. This has the advantage that it is available throughout the document, not just after the mark has been declared. To make this more usable, we can declare a new coordinate system that takes in a picture id and returns its coordinates relative to the current origin. We can even get a bit fancy and add in an option for a default position if that picture id hasn't been associated as yet (ie if we're on the first run).
Together with a bit of styling, this provides most of the functionality that you want. The main bit missing is the ability to reference the absolute positioning on the page. But as this is clearly for a draft option, I guess that the main thing is simply to make it blindingly obvious that this is the first run so I've chosen something that makes that clear.
A further refinement to this system would be to be able to add a key to the path which said "if on first run, ignore this path". I don't think that would be hard.
The main drawback of this is that the \tikzmark
now references the base of the line on which it is called. This can require a little adjustment afterwards to get things exactly right: for example in your code then my red line is one line too far down without adjustment. A bit of calc
adjustment makes this trivial to fix.
Here's the code:
\documentclass{article}
% Adjust paperheight for more useful image
\usepackage[showframe,paperheight=10.0cm]{geometry}
\usepackage{tikz}
\usetikzlibrary{calc}
\usepackage[nopar]{lipsum}
\makeatletter
\tikzset{%
remember picture with id/.style={%
remember picture,
overlay,
save picture id=#1,
},
save picture id/.code={%
\edef\pgf@temp{#1}%
\immediate\write\pgfutil@auxout{%
\noexpand\savepointas{\pgf@temp}{\pgfpictureid}}%
},
if picture id/.code args={#1#2#3}{%
\@ifundefined{save@pt@#1}{%
\pgfkeysalso{#3}%
}{
\pgfkeysalso{#2}%
}
}
}
\def\savepointas#1#2{%
\expandafter\gdef\csname save@pt@#1\endcsname{#2}%
}
\def\tmk@labeldef#1,#2\@nil{%
\def\tmk@label{#1}%
\def\tmk@def{#2}%
}
\tikzdeclarecoordinatesystem{pic}{%
\pgfutil@in@,{#1}%
\ifpgfutil@in@%
\tmk@labeldef#1\@nil
\else
\tmk@labeldef#1,(0pt,0pt)\@nil
\fi
\@ifundefined{save@pt@\tmk@label}{%
\tikz@scan@one@point\pgfutil@firstofone\tmk@def
}{%
\pgfsys@getposition{\csname save@pt@\tmk@label\endcsname}\save@orig@pic%
\pgfsys@getposition{\pgfpictureid}\save@this@pic%
\pgf@process{\pgfpointorigin\save@this@pic}%
\pgf@xa=\pgf@x
\pgf@ya=\pgf@y
\pgf@process{\pgfpointorigin\save@orig@pic}%
\advance\pgf@x by -\pgf@xa
\advance\pgf@y by -\pgf@ya
}%
}
\newcommand\tikzmark[2][]{%
\tikz[remember picture with id=#2] #1;}
\makeatother
\newcommand{\MarkText}[3][]{%
\begin{tikzpicture}[overlay,remember picture]%
\path (pic cs:#2,{(0,\paperheight)}) +(0,.7\baselineskip) coordinate (a);
\path (pic cs:#3,{(0,-\paperheight)}) +(0,-.3\baselineskip) coordinate (b);
\draw [ultra thick,
if picture id={#2}{red}{line width=1cm,green,opacity=.5},
#1]
(a) -- ($(b)!(a)!($(b)+(1,0)$)$);
\end{tikzpicture}%
}%
\begin{document}
\MarkText{top}{bottom}
\tikzmark{top}
\lipsum[1]
\tikzmark{bottom}
\end{document}
(Note that I've put the \MarkText
before the lipsum to show that that is possible. This will mean that the lines go under the text.)
First run:
Second run:
See the comment of Caramdir on the magic numbers thing. If I understand correctly, by globally setting overlay
you are creating a tikzpicture without bounding box, hence all explicit positions are relative to the point where \MarkText
(having no size) is placed on the page.
For the main question, a working solution is
\ifcsname pgf@sys@pdf@mark@pos@\pgfpictureid\endcsname
\draw [ultra thick, red, #1] (#2) -- (#3);% Only if subsequent run
\else
%%% Why the magc numbers needed here?
\draw [ultra thick, green, #1]% Only if first run
(-0.70\paperwidth,-\paperheight) --
(1.30\paperwidth,\paperheight);
\fi
Edit: Made more general by Caramdir's suggestion.