Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

The macro \tikzmark has been incredibly useful to me, but do not like the default behavior upon first run.

So, I would like to be able to determine if this is a first run (where there coordinates of the nodes are not accurately available), of if it is a subsequent run (and the coordinates are available, but might not have reached the final value).

So, in the MWE below, I would like only the green line upon first invocation, and only the red line upon subsequent processing:

enter image description here

As an aside, I have no idea why I needed the magic numbers (which vary depending on \paperheight) -0.70 and 1.30 below. I thought that:

\draw (0,-\paperheight) -- (\paperwidth,0);

should have produced a line form the bottom left to the top right. You can uncomment this line in the code and see where the blue line is. Perhaps it is just a simple matter of me not understanding where the origin is -- Had thought it was a the top left of the page.

Notes

  • In my actual usage, the red line goes from the bottom left to the top right of the text, but did not want to clutter the MWE with that additional computation. So, the green line serves as a useful first guess.

References:

Code:

\documentclass{article}
% Adjust paperheight for more useful image
\usepackage[showframe,paperheight=10.0cm]{geometry}
\usepackage{tikz}
\usepackage{lipsum}

\newcommand{\tikzmark}[1]{\tikz[overlay,remember picture,baseline] \node [anchor=base] (#1) {};}%

\newcommand{\MarkText}[3][]{%
    \begin{tikzpicture}[overlay,remember picture]%
        \draw [ultra thick, red, #1] (#2) -- (#3);% Only if subsequent run

        %%% Why the magc numbers needed here?
        \draw [ultra thick, green, #1]%             Only if first run
            (-0.70\paperwidth,-\paperheight) --
            (1.30\paperwidth,\paperheight);

        %%%  Why this not yield line form bottom left to top right???
        %\draw [ultra thick, blue, #1] (0,-\paperheight) -- (\paperwidth,0);
    \end{tikzpicture}%
}%

\begin{document}
    \tikzmark{top}
        \lipsum[1]
    \tikzmark{bottom}
    \MarkText{top}{bottom}
\end{document}
share|improve this question
3  
The easiest way to reference positions on the whole page is the the (current page) node (see section 16.13.2 of the v2.10 manual). Then you don't need to worry about where TeX decides to place the origin. –  Caramdir Mar 29 '12 at 21:54
    
Though that also needs two runs. –  Caramdir Mar 29 '12 at 22:00
    
@Caramdir: Yep I tried that as well before moving on to using the fixed settings of \paperheight and \paperwidth. I don't quite understand why the values of (current page) aren't already available in the first run. –  Peter Grill Mar 29 '12 at 22:07
    
I think this is because PGF always references relative to where on the page is called/positioned. And it only knows that after it has completed drawing and created the box with its contents. So it can only know after the picture has been completed how to access the page boundaries relative to its position. –  Caramdir Mar 29 '12 at 22:24
add comment

2 Answers 2

up vote 23 down vote accepted

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:

First tikzmark run

Second run:

Second tikzmark run

share|improve this answer
    
Thanks for such a detailed explanation. After reading it the third time it is sinking in (that's not to say that your explanation is not good, just my lack of knowledege about the inner workings of tikz). I have come to the conclusion that this is really only complicated as it is not know where the origin is, otherwise a simple (0,-\paperheight) -- (\paperwidth,0) would have achieved the line from bottom left to top right that I wanted. –  Peter Grill Mar 30 '12 at 23:00
    
It will be interesting to see what applications of this enhanced ability to access the \tikzmark nodes before they are placed will come up. –  Peter Grill Mar 30 '12 at 23:03
    
@PeterGrill take a look at tex.stackexchange.com/a/46471/86 to see why I made this change in the code. –  Loop Space Mar 31 '12 at 8:13
add comment

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.

share|improve this answer
    
Seems to work. Wonder how the mapping of pgfid1 to the original names top, bottom is done. –  Peter Grill Mar 29 '12 at 22:13
1  
If you replace pgfid1 by \pgfpictureid it works more generally. –  Caramdir Mar 29 '12 at 22:14
1  
@PeterGrill I think these things are on different levels. The position saved by \pgfsyspdfmark is that of the picture, while the node names are assigned anew in every TeX run. –  Stephan Lehmke Mar 29 '12 at 22:21
1  
I doubt you could access the node (top) before it's defined even on the N'th TeX run. The \tikzmark will always be executed before \MarkText in every run, defining the node (top). The remember picture just assures that pgf knows where the picture is placed, an information unavailable during the TeX run itself because \pdflastxpos only works at \shipout time. –  Stephan Lehmke Mar 29 '12 at 22:29
1  
More precisely, inside a run of TeX, node information is stored in \pgf@sh@ns@⟨name⟩ (the shape), \pgf@sh@np@⟨name⟩ (the saved points), \pgf@sh@na@⟨name⟩ (the anchors (?)), \pgf@sh@nt@⟨name⟩ (the coordinate transform) and \pgf@sh@np@⟨name⟩ (the picture id) –  Caramdir Mar 29 '12 at 22:39
show 3 more comments

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.