6

I know it is possible to place TikZ nodes in positions specified relative to the page, as shown in Positioning relative to page in TikZ.

I'm also aware that it is possible to obtain nodes relative to the margins, as in Aligning tikz node at bottom of page (not paper; including margin).

However, I'd like to place a tikz node not at the bottom of the page, neither at its margin, but at the lowest place actually filled by the text block while using raggedbottom (thus calculations from the margins are not really helpful, as far as I can tell). More precisely, how could I supply tikz with the y coordinate of the baseline of the last typeset line in the current page with raggedbottom? Due to what I'm trying to do with this, a manual specification of the last line with a tikzmark would not be a viable alternative (as far as I can see), so what I want to specify is the coordinate of the last line, whatever that happens to be when typeset.

For the sake of having a MWE, I supply the following which may be convenient but does not actually try to address the question, for I'm at a loss on this:

\documentclass{article}
\usepackage{tikz}
\usepackage[paperheight=8cm,paperwidth=12cm]{geometry}
\usepackage[defaultlines=4,all]{nowidow}
\usepackage{blindtext}
\raggedbottom

\begin{document}
\thispagestyle{empty}
\begin{tikzpicture}[remember picture,overlay]
  \node[anchor=south] at (current page.south) {I am south-bound...};
  \draw[thick] (current page.south west) rectangle (current page.north east);
\end{tikzpicture}
\blindtext
\end{document}

Update: I initially tried to keep the question more abstract but, as suggested in the comments, perhaps more context might be helpful. What I'm trying to do is to polish Gonzalo Medina's solution to Mark (highlight) a paragraph (\item{…}) with a squiggly line for later attention to a raggedbottom context. In cases in which the lateral line is drawn through a page break, the y bottom coordinate of the part in the first page is calculated as y of current page.center -0.5\textheight, so that if the page is shorter under raggedbottom the line stretches too far. The following code, slightly adapted from Gonzalo Medina's answer illustrates the problem:

\documentclass{article}
\usepackage{refcount}
\usepackage{tikz}
\usetikzlibrary{decorations.pathmorphing,calc}
\usepackage{lipsum}
\usepackage[defaultlines=6,all]{nowidow}
\raggedbottom

\newcounter{tmp}
\newcommand\tikzmark[1]{%
  \tikz[overlay,remember picture] \node (#1) {};}

\newcommand\Startsquiggly{%
  \stepcounter{tmp}%
  \tikzmark{a}\label{a\thetmp}%
  \ifnum\getpagerefnumber{a\thetmp}=\getpagerefnumber{b\thetmp} \else
  \begin{tikzpicture}[overlay, remember picture]
    \draw [decoration={coil,aspect=0},decorate,ultra thick,gray]
      let \p1 = (a.north), \p2 = (b), \p3 = (current page.center) in
      ( $ (\x3,\y1) + (.55\textwidth,0) $ ) --  ( $ (\x3,\y3) + (0.55\textwidth,-0.5\textheight) $ );
  \end{tikzpicture}%
  \fi%
}

\newcommand\Endsquiggly{%
\tikzmark{b}\label{b\thetmp}
  \ifnum\getpagerefnumber{a\thetmp}=\getpagerefnumber{b\thetmp}
  \begin{tikzpicture}[overlay, remember picture]
    \draw [decoration={coil,aspect=0},decorate,ultra thick,gray]
      let \p1 = (a.north), \p2 = (b), \p3 = (current page.center) in
      ( $ (\x3,\y1) + (.55\textwidth,0) $ ) --  ( $ (\x3,\y2) + (.55\textwidth,0) $ );
  \end{tikzpicture}%
  \else
  \begin{tikzpicture}[overlay, remember picture]
    \draw [decoration={coil,aspect=0},decorate,ultra thick,gray]
      let \p1 = (a.north), \p2 = (b), \p3 = (current page.center) in
      ( $ (\x3,\y3) + (.55\textwidth,.5\textheight) $ ) -- ( $ (\x3,\y2) + (.55\textwidth,0) $ );
  \end{tikzpicture}%
  \fi
}

\newcommand\Squ[1]{\Startsquiggly#1\Endsquiggly}
\begin{document}

\begin{itemize}
\item \lipsum*[1]
\item \Startsquiggly\lipsum*[1]\Endsquiggly
\item \lipsum*[1]
\item \Startsquiggly\lipsum*[1]\Endsquiggly
\end{itemize}

\end{document}

The over-stretching line looks like this:

enter image description here

Either ideas to address the initial general question, or an alternative approach that works for the specific situation are welcome.

  • Either use tikzmark library or do the equivalent, if you prefer, and put \tikzmark{mymark} at an appropriate place. Then you can use it in the picture definition using ({pic cs:mymark}). – cfr Aug 25 '17 at 23:15
  • @cfr, thank you for your comment. If I understand well, I'd have to build the document, see what the last line is, and then place a tikzmark on it. Is that it? That would be too manual for my purposes. If that is the only way, I'd probably give up. Or am I getting wrong how to use the tikzmark? If you think it is necessary, I can give more details on the context and what I'm trying to do with this coordinate. – gusbrs Aug 26 '17 at 0:11
  • There might be another way. See what people say. Details of the context would be good. Somebody might be able to suggest an alternative approach. (That is, it might be an XY problem.) – cfr Aug 26 '17 at 3:08
  • @cfr, I've added some context. If that sparks any further thoughts, they'd be very welcome. – gusbrs Aug 26 '17 at 3:57
1

This is a very tentative first version of an answer with many typeouts. Actually, it is merely a long addendum to our chat. And this is not really my answer, all the tough problems have been solved by Heiko Oberdiek in his amazing answer. The basic idea is to make use of the lineno package, which won't put a line number on an empty line. Heiko was able to put a tikz node on the last line number. This provides us with the vertical position of the last line. The rest is straightforward modulo freaking out because of seemingly causality violating TeX shipout procedure.

\documentclass{article}
\usepackage{refcount}
\usepackage{tikz,tikzpagenodes}
\usetikzlibrary{decorations.pathmorphing,calc}
\usepackage{lipsum}
\usepackage[defaultlines=6,all]{nowidow}
\raggedbottom


\usepackage{atbegshi}
\usepackage[pagewise]{lineno}
\newcommand{\Later}{}

\makeatletter
\newcount\AbsolutePageNumber % zero-based
\AbsolutePageNumber=\z@
\newcommand*{\UnknownLineNumber}{??}
\let\FirstLineNumber\UnknownLineNumber
\let\LastLineNumber\UnknownLineNumber
\newcommand*{\GetLineNumbers}{%
  \begingroup
    \let\nextLN\relax
    \def\pageLN##1##2##3{%
      \ifnum##1=\AbsolutePageNumber
        \xdef\FirstLineNumber{\the\numexpr##3+1}%
        \global\let\LastLineNumber\@LastLineNumber
      \fi
      \ifnum##1>\AbsolutePageNumber
        \let\nextLN\relax
      \fi
    }%
    \let\firstLN\@gobble
    \def\lastLN##1{%
      \def\@LastLineNumber{\the\numexpr##1+1}%
    }%
    \LN@Pfirst
  \endgroup
}
\AtBeginShipout{%
  \GetLineNumbers
  \AtBeginShipoutAddToBox{%
    \ifx\FirstLineNumber\UnknownLineNumber
    \else
%       \fbox{%
%         \the\numexpr\LastLineNumber + 1 - \FirstLineNumber\relax
%         ~(\FirstLineNumber--\LastLineNumber)%
%       }%
    \fi
    \expandafter\ifx\csname Anchor\LastLineNumber\endcsname\relax
    \else
%       \tikz[overlay,remember picture]{%
%         \draw[thick,blue,latex-](ln\LastLineNumber)--++(-2cm,0);%
%       }%
    \fi
  }%
  \global\advance\AbsolutePageNumber\@ne
}

\renewcommand\thelinenumber{%
  %\arabic{linenumber}%
  \GetLineNumbers
  \ifx\LastLineNumber\UnknownLineNumber
  \else
    \ifnum\LastLineNumber=\numexpr\FirstLineNumber+\value{linenumber}-1\relax
      \tikz[overlay,remember picture]{%
        \node(ln\LastLineNumber){\strut};%
      }%
      \typeout{done\space with\space ln\LastLineNumber}
      \Later
      \renewcommand{\Later}{}
      \global\expandafter\let\csname Anchor\LastLineNumber\endcsname\@empty
    \fi
  \fi
}
\makeatother

\newcounter{tmp}
\newcommand\tikzmark[1]{%
  \tikz[overlay,remember picture] \node (#1) {};}

\newcommand\Startsquiggly{%
  \stepcounter{tmp}%
  \typeout{executing\space Startsquiggly\space\thetmp}
  \tikzmark{a}\label{a\thetmp}%
  \ifnum\getpagerefnumber{a\thetmp}=\getpagerefnumber{b\thetmp} \else
  \typeout{\getpagerefnumber{a\thetmp},\getpagerefnumber{b\thetmp}}
  \GetLineNumbers
  \xdef\NoWay{0}
  % here we define a command that will be executed at page shipout
  \renewcommand{\Later}{\typeout{executing\space Later\space \thetmp\space on \space page\space\thepage}
  \ifnum\NoWay=0
  \begin{tikzpicture}[overlay, remember picture]
  \draw [decoration={coil,aspect=0},decorate,ultra thick,gray]
  ([xshift=3mm]current page text area.east |- a.north)
  --([xshift=3mm]current page text area.east |- ln\LastLineNumber);
  \end{tikzpicture}
  \fi
  \xdef\NoWay{1}
  }
%   \begin{tikzpicture}[overlay, remember picture]
%     \draw [decoration={coil,aspect=0},decorate,ultra thick,gray]
%       let \p1 = (a.north), \p2 = (b), \p3 = (current page.center) in
%       ( $ (\x3,\y1) + (.55\textwidth,0) $ ) --  ( $ (\x3,\y3) + (0.55\textwidth,-0.5\textheight) $ );
%   \end{tikzpicture}%
  \fi%
}

\newcommand\Endsquiggly{%
\tikzmark{b}\label{b\thetmp}
  \ifnum\getpagerefnumber{a\thetmp}=\getpagerefnumber{b\thetmp}
  \typeout{endcase1:\getpagerefnumber{a\thetmp},\getpagerefnumber{b\thetmp}}
  \begin{tikzpicture}[overlay, remember picture]
    \draw [decoration={coil,aspect=0},decorate,ultra thick,gray]
    ([xshift=3mm]current page text area.east |- a.north)
    --([xshift=3mm]current page text area.east |- b.south);
%       let \p1 = (a.north), \p2 = (b), \p3 = (current page.center) in
%       ( $ (\x3,\y1) + (.55\textwidth,0) $ ) --  ( $ (\x3,\y2) + (.55\textwidth,0) $ );
  \end{tikzpicture}%
  \else
  \typeout{endcase2:\getpagerefnumber{a\thetmp},\getpagerefnumber{b\thetmp}}
  \begin{tikzpicture}[overlay, remember picture]
     \draw [decoration={coil,aspect=0},decorate,ultra thick,gray]
    ([xshift=3mm]current page text area.north east)
    --([xshift=3mm]current page text area.east |- b.south);
%       let \p1 = (a.north), \p2 = (b), \p3 = (current page.center) in
%       ( $ (\x3,\y3) + (.55\textwidth,.5\textheight) $ ) -- ( $ (\x3,\y2) + (.55\textwidth,0) $ );
  \end{tikzpicture}%
  \fi
}

\newcommand\Squ[1]{\Startsquiggly#1\Endsquiggly}
\begin{document}
\linenumbers
\begin{itemize}
\item \lipsum*[1]
\item \Startsquiggly\lipsum*[1]\Endsquiggly
\item \lipsum*[1]
\item \Startsquiggly\lipsum*[1]\Endsquiggly
\end{itemize}

\end{document}

Note that I made use of the tikzpagenodes package to draw the squiggly line. As a result, the line got a bit shifted, but you can readjust the horizontal positions by playing with [xshift=3mm]. I'll be happy to assist you in making stylistic improvements.

  • To quote a famous rodent: "WOW! Impressive!" Indeed, Oberdiek's approach is very ingenious. But if found here a cunning application. I did not fully grasp the code (alas, my limitations are numerous), but I do get the general idea, and in my tests with it, it looks very nice indeed. Unfortunately, lineno did collide with my settings in my real case application. But that's not a problem. I do think this question is very interesting, and I'm glad it finally found its "mate". So it's really appreciated. Thank you very much! – gusbrs Apr 20 '18 at 1:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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