当环境定义涉及标签时,如何允许正确的环境嵌套?

当环境定义涉及标签时,如何允许正确的环境嵌套?

下面的代码展示了this answer用波浪线标记(突出显示)某个段落(\item{...}),以便稍后注意。环境tikzborder使用 TikZ 装饰在文本右侧绘制波浪线(即使出现分页符);可选参数控制文本和线之间的分离(文档必须处理三次)。

这个想法很简单:在环境的开始和结束处放置标记,然后在标记之间绘制装饰;在开始和结束时使用标签来判断标记是否在同一页面并采取相应的行动。

\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}
  \addtocounter{borderpages}{-1}%
  \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}

问题是现在我想嵌套tikzborder环境,但这不起作用,正如通过取消注释上面代码中的内部环境并重新处理文档所看到的那样。

我认为我需要某种机制来堆叠标记,但我不知道如何实现这种机制。当然,也许另一种允许正确嵌套环境的方法会更好,我也愿意听听。

答案1

解决方案是跟踪嵌套深度并为每个深度定义宏。除了\tikzmark根据嵌套深度命名之外,还需要根据嵌套深度存储水平偏移量。

这与我使用的方法类似将枚举列表中的条目括在括号中。但是,这里一个关键的区别是,您需要从设置这些设置的环境之外访问这些设置。为此,我定义了全局宏来存储它们。

为了直观地看到发生了什么,我必须根据嵌套深度添加颜色,因此要设置绘图选项,可以使用

\renewcommand*{\DrawOptions}{}

指定此项,此设置将应用于特定级别的波浪线。这也根据嵌套深度全局存储。

所以最终的结果是:

在此处输入图片描述

进一步增强:

  • 可能有一些代码清理工作可以完成。

代码:

\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%
  %\addtocounter{bordercntr}{-1}%
  \addtocounter{NestingDepthCounter}{-1}
}

% 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}
  \addtocounter{borderpages}{-1}%
  \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}

相关内容