# How to allow correct environment nesting when the environment definition involves labels?

The code below shows an improved version of the code provided in this answer to Mark (highlight) a paragraph (\item{...}) with a squiggly line for later attention. The environment tikzborder uses a TikZ decoration to draw a squiggly line to the right of the text (even if page breaks occur); the optional argument controls the separation between the text and the line (the document has to be processed three times).

The idea is simple: marks are placed at the beginning and at the end of the environment and then the decoration is drawn between the marks; labels are used at the beginning and at the end, to decide if the marks are in the same page or not and act accordingly.

\documentclass{article}
\usepackage{atbegshi}
\usepackage{refcount}
\usepackage{tikz}
\usetikzlibrary{decorations.pathmorphing,calc}
\usepackage{lipsum}

\newcounter{bordercntr}
\newcounter{borderpages}

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

\newenvironment{tikzborder}[1][0pt]
{%
\gdef\borderspacing{#1}
\stepcounter{bordercntr}%
\tikzmark{start-border}\label{start-border\thebordercntr}%
% if the marks are in the same page, nothing is done
% otherwise, the decoration is drawn from the starting point to the page bottom
% and, if necessary, intermediate pages will also receive the decoration
\ifnum\getpagerefnumber{start-border\thebordercntr}=\getpagerefnumber{end-border\thebordercntr} \else
\begin{tikzpicture}[overlay, remember picture]
\draw [decoration={coil,aspect=0},decorate,ultra thick,gray]
let \p1 = (start-border.north), \p2 = (end-border), \p3 = (current page.center) in%
( $(\x3,\y1) + (.55\textwidth+#1,2pt)$ ) --  ( $(\x3,\y3) + (0.55\textwidth+#1,-0.5\textheight)$ );
\end{tikzpicture}%
\setcounter{borderpages} {\numexpr\getpagerefnumber{end-border\thebordercntr}-\getpagerefnumber{start-border\thebordercntr}}\theborderpages
\ifnum\value{borderpages}>1
\AtBeginShipoutNext{\tikzborderpage[#1]}%
\fi
\fi%
}
{\tikzmark{end-border}\label{end-border\thebordercntr}
% if the marks are in the same page, the decoration is drawn
% otherwise, the decoration is drawn from the top of the page to the end mark
\ifnum\getpagerefnumber{start-border\thebordercntr}=\getpagerefnumber{end-border\thebordercntr}
\begin{tikzpicture}[overlay, remember picture]
\draw [decoration={coil,aspect=0},decorate,ultra thick,gray]
let \p1 = (start-border.north), \p2 = (end-border), \p3 = (current page.center) in
( $(\x3,\y1) + (.55\textwidth+\borderspacing,2pt)$ ) --  ( $(\x3,\y2) + (.55\textwidth+\borderspacing,10pt)$ );
\end{tikzpicture}%
\else
\begin{tikzpicture}[overlay, remember picture]
\draw [decoration={coil,aspect=0},decorate,ultra thick,gray]
let \p1 = (start-border.north), \p2 = (end-border), \p3 = (current page.center) in
( $(\x3,\y3) + (.55\textwidth+\borderspacing,.5\textheight-6pt)$ ) -- ( $(\x3,\y2) + (.55\textwidth+\borderspacing,10pt)$ );
\end{tikzpicture}%
\fi%
}

% the command to draw the decoration in intermediate pages from the top
% to the bottom of the page
\newcommand\tikzborderpage[1][0pt]{%
\begin{tikzpicture}[overlay, remember picture]
\draw [decoration={coil,aspect=0},decorate,ultra thick,gray]
let \p1 = (current page.center) in
( $(\x1,\y1) + (.55\textwidth+#1,0.5\textheight-15pt)$ ) -- ( $(\x1,\y1) + (.55\textwidth+#1,-0.5\textheight)$ );
\end{tikzpicture}
\ifnum\value{borderpages}>1
\AtBeginShipoutNext{\tikzborderpage[#1]}%
\fi%
}

\begin{document}

\begin{tikzborder}
\lipsum[2]
\end{tikzborder}

\begin{tikzborder}[-5pt]
\lipsum[2]
%\begin{tikzborder}
\lipsum[2]
%\end{tikzborder}
\end{tikzborder}

\end{document}


The problem is that now I want to nest tikzborder environments and this is not working, as can be seen by uncommenting out the inner environment in the code above and reprocessing the document.

I think I need some mechanism to stack the marks, but I don't know how to implement this kind of mechanism. Of course, perhaps another approach allowing correct nesting of the environment could be better, and I would also be willing to hear about it.

-
Hmmm.. This seems to work works very nicely. The lines are drawn at the correct vertical spots. Is the problem you are referring to the issue with where squiggly line is placed horizontally? If I manually tweak the location with the optional parameter to tikzborder, this work well. –  Peter Grill Oct 12 '12 at 3:05
@PeterGrill when I uncomment out the inner environment in my code, the border is not drawn correctly. Do you get the correct result after uncommenting the inner environment? –  Gonzalo Medina Oct 12 '12 at 3:11
I guess that depends on what the correct results are. I will post an answer and delete it so you can see. –  Peter Grill Oct 12 '12 at 3:14
@PeterGrill the correct result for paragraphs two and three should be a squiggly line spanning both paragraphs and another squiggly line spanning only the third paragraph. Perhaps the problem can be seen more clearly now that I removed some code from the example. –  Gonzalo Medina Oct 12 '12 at 3:23
Using your updated code, I think that it is even more clearer that the marking is correct -- except for the fact that the outer squiggly does not end at paragraph 3. –  Peter Grill Oct 12 '12 at 3:38

The solution is to keep track of the nesting depth and define macros for each depth. In addition to making the \tikzmark names based on the nesting dept, you also need to store the horizontal offset based on the nesting dept.

This is similar to the approach I used in Enclose an entry in an enumerate list in parentheses. However, one crucial difference here is that you need access to the settings from outside of the environment where these are set. To do this I define global macros to store them.

For me to visually see what was going on I had to add color based on the nesting depth, so to set the draw options one can use

\renewcommand*{\DrawOptions}{}


to specify this, and this setting will be applied to the squiggly for the particular level. This is also stored globally based on the nesting depth.

So final result is:

## Further Enhancements:

• There is probably some code clean that can be done.

## Code:

\documentclass{article}
\usepackage{atbegshi}
\usepackage{refcount}
\usepackage{tikz}
\usetikzlibrary{decorations.pathmorphing,calc}
\usepackage{lipsum}

\newcounter{bordercntr}
\newcounter{borderpages}

% To allow for nesting we need to use different names for each \tikzmark.
% Use we can use the value of this counter to name those.
% This counted is incremented at the \begin{tikzborder} and decremented
% at \end{tikzborder}
\newcounter{NestingDepthCounter}
% Just to ensure that these are not already used.  Need one of these
% for each nesting depth. Assume three for now.
\newcommand{\BorderSpacingA}{}
\newcommand{\BorderSpacingB}{}
\newcommand{\BorderSpacingC}{}

% To allow for different draw options based on nesting depth.
% There here just to ensure that these are not defined previously
\newcommand{\DrawOptions}{}%
\newcommand{\DrawOptionsA}{}
\newcommand{\DrawOptionsB}{}
\newcommand{\DrawOptionsC}{}

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

\newenvironment{tikzborder}[1][0pt]
{%
\stepcounter{NestingDepthCounter}%
\global\expandafter\def\csname BorderSpacing\Alph{NestingDepthCounter}\endcsname{#1}
\global\expandafter\xdef\csname DrawOption\Alph{NestingDepthCounter}\endcsname{\DrawOptions}
%\gdef\borderspacing{#1}
%\stepcounter{bordercntr}%
\tikzmark{start-border\Alph{NestingDepthCounter}}\label{start-border\Alph{NestingDepthCounter}\thebordercntr}%
% if the marks are in the same page, nothing is done
% otherwise, the decoration is drawn from the starting point to the page bottom
% and, if necessary, intermediate pages will also receive the decoration
\ifnum\getpagerefnumber{start-border\Alph{NestingDepthCounter}\thebordercntr}=\getpagerefnumber{end-border\Alph{NestingDepthCounter}\thebordercntr} \else
\def\LocalDrawOptions{\expandafter\csname DrawOption\Alph{NestingDepthCounter}\endcsname}
\begin{tikzpicture}[overlay, remember picture]
\draw [decoration={coil,aspect=0},decorate,ultra thick,gray,\LocalDrawOptions]
let \p1 = (start-border\Alph{NestingDepthCounter}.north), \p2 = (end-border\Alph{NestingDepthCounter}), \p3 = (current page.center) in%
( $(\x3,\y1) + (.55\textwidth+#1,2pt)$ ) --  ( $(\x3,\y3) + (0.55\textwidth+#1,-0.5\textheight)$ );
\end{tikzpicture}%
\setcounter{borderpages} {\numexpr\getpagerefnumber{end-border\Alph{NestingDepthCounter}\thebordercntr}-\getpagerefnumber{start-border\Alph{NestingDepthCounter}\thebordercntr}}\theborderpages
\ifnum\value{borderpages}>1
\AtBeginShipoutNext{\tikzborderpage[#1]}%
\fi
\fi%
}
{\tikzmark{end-border\Alph{NestingDepthCounter}}\label{end-border\Alph{NestingDepthCounter}\thebordercntr}
\def\borderspacing{\expandafter\csname BorderSpacing\Alph{NestingDepthCounter}\endcsname}
\def\LocalDrawOptions{\expandafter\csname DrawOption\Alph{NestingDepthCounter}\endcsname}
% if the marks are in the same page, the decoration is drawn
% otherwise, the decoration is drawn from the top of the page to the end mark
\ifnum\getpagerefnumber{start-border\Alph{NestingDepthCounter}\thebordercntr}=\getpagerefnumber{end-border\Alph{NestingDepthCounter}\thebordercntr}
\begin{tikzpicture}[overlay, remember picture]
\draw [decoration={coil,aspect=0},decorate,ultra thick,gray,\LocalDrawOptions]
let \p1 = (start-border\Alph{NestingDepthCounter}.north), \p2 = (end-border\Alph{NestingDepthCounter}), \p3 = (current page.center) in
( $(\x3,\y1) + (.55\textwidth+\borderspacing,2pt)$ ) --  ( $(\x3,\y2) + (.55\textwidth+\borderspacing,10pt)$ );
\end{tikzpicture}%
\else
\begin{tikzpicture}[overlay, remember picture]
\draw [decoration={coil,aspect=0},decorate,ultra thick,gray,\LocalDrawOptions]
let \p1 = (start-border\Alph{NestingDepthCounter}.north), \p2 = (end-border\Alph{NestingDepthCounter}), \p3 = (current page.center) in
( $(\x3,\y3) + (.55\textwidth+\borderspacing,.5\textheight-6pt)$ ) -- ( $(\x3,\y2) + (.55\textwidth+\borderspacing,10pt)$ );
\end{tikzpicture}%
\fi%
}

% the command to draw the decoration in intermediate pages from the top
% to the bottom of the page
\newcommand\tikzborderpage[1][0pt]{%
\begin{tikzpicture}[overlay, remember picture]
\draw [decoration={coil,aspect=0},decorate,ultra thick,gray]
let \p1 = (current page.center) in
( $(\x1,\y1) + (.55\textwidth+#1,0.5\textheight-15pt)$ ) -- ( $(\x1,\y1) + (.55\textwidth+#1,-0.5\textheight)$ );
\end{tikzpicture}
\ifnum\value{borderpages}>1
\AtBeginShipoutNext{\tikzborderpage[#1]}%
\fi%
}

\begin{document}

\begin{tikzborder}
\lipsum[2]
\end{tikzborder}

\renewcommand*{\DrawOptions}{blue}\color{blue}
\begin{tikzborder}[2.5pt]
\lipsum[2]
\renewcommand*{\DrawOptions}{red}%
\begin{tikzborder}[-10pt]
\color{red}\lipsum[2]
\end{tikzborder}
\end{tikzborder}

\end{document}

-
Thanks, but if I try your solution using \begin{tikzborder}[2.5pt] \lipsum[1-2] \begin{tikzborder}[-10pt] \lipsum[1-2] \end{tikzborder} \lipsum[1-2] \end{tikzborder} I get an error: ! Package pgf Error: No shape named end-borderA is known. –  Gonzalo Medina Oct 12 '12 at 5:02
Yeah that seems to be related to the page breaking issue. I don't fully understand how your solution handles that so am having difficulty figuring out how to adjust for that. The above works as long as the environments start and end on the same page. –  Peter Grill Oct 12 '12 at 6:25