tikzmark 在首次运行时会有不同的行为(并且标记位置尚不可用)

tikzmark 在首次运行时会有不同的行为(并且标记位置尚不可用)

这个宏\tikzmark对我来说非常有用,但不喜欢第一次运行时的默认行为。

因此,我希望能够确定这是否是第一次运行(其中节点的坐标不准确),或者是否是后续运行(并且坐标可用,但可能未达到最终值)。

因此,在下面的 MWE 中,我希望仅有的首次调用时显示绿线,以及仅有的后续处理后的红线:

在此处输入图片描述

顺便说一句,我不知道为什么我需要下面的魔法数字(这取决于\paperheight) 。我认为:-0.701.30

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

应该产生一条从左下角到右上角的线。您可以在代码中取消注释此行,然后查看蓝线的位置。也许这只是我不明白原点在哪里的问题——我以为它是页面的左上角。

笔记

  • 在我的实际使用中,红线从文本的左下角延伸到右上角,但我不希望额外的计算使 MWE 变得混乱。因此,绿线是一个有用的初步猜测。

参考:

代码:

\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}

答案1

这是增强版的\tikzmark解决了这个问题中的一些问题(尽管在功能上,这与 Stephan 的答案没有太大区别)。为了解决主要的首先失败:使用 TikZ/PGF,在第一次运行时无法引用页面的绝对定位。这是因为 Caramdir 对这个问题的评论是绝对正确的:TikZ/PGF 相对于本地坐标系工作,并在使用之前将全局定位转换为当前定位。因此,为了使用绝对坐标,它需要知道当前图片在页面上的位置,以便它可以计算出从绝对到相对的转换。它只有在页面发送出去后才知道这一点,因为就 TeX 而言,TikZ/PGF 图片只是盒子,因此在将它们放在页面上时会受到 TeX 的奇思妙想的影响。因此,对于您的“默认操作”,我建议选择不依赖于绝对页面定位的操作。

此代码解决的主要问题是能够在声明 tikzmark 之前引用它。这在第二次和后续运行中有效,因为 TikZ/PGF 处理记忆的方式。同样,Caramdir 和 Stephan 的评论完全正确。TikZ/PGF 将图片的来源保存到辅助文件中。然后就可以使用了自始至终后续运行中的文档(尽管与所有辅助内容一样,使用的坐标是技术上上次运行的坐标。希望这会稳定下来。)节点位置是相对于其图片原点来记住的,并且其图片 ID 也与它们一起记住,因此当您在其图片之外调用节点时,TikZ/PGF 会查找相对于其图片原点的节点坐标,然后根据图片原点的相对间隔来调整它们(同样,这里有两个坐标系之间的转换,这就是为什么两个都图片需要remember picture密钥)(此外,完全可以使用尚未保存的另一张图片中的节点,但返回的是原始图片中节点的坐标,而无需转换到本地坐标系。这有时很有用。)。

现在,在页面上标记位置时,我们实际上只需要记住一个坐标。但是记住一个节点涉及两个:记住图片位置和节点的相对偏移量。所以我们应该只记住图片位置。这样做的好处是它在整个文档中都可用,而不仅仅是在声明标记之后。为了使其更易于使用,我们可以声明一个新的坐标系,它接受图片 ID 并返回其相对于当前原点的坐标。如果图片 ID 尚未关联(即,如果我们是第一次运行),我们甚至可以稍微花哨一点并添加一个默认位置的选项。

加上一些样式,这提供了您想要的大部分功能。主要缺少的是引用页面上的绝对定位的能力。但这显然是为了草稿选项,我猜主要的事情就是让它明显地表明这是第一次运行,所以我选择了一些可以清楚说明这一点的东西。

该系统的进一步改进是能够在路径中添加一个键,该键表示“如果首次运行,则忽略此路径”。我认为这并不难。

这样做的主要缺点是,\tikzmark现在引用的是调用它的行的基数。这可能需要事后进行一些调整才能完全正确:例如,在您的代码中,如果不进行调整,我的红线就会往下一行。只需进行一些calc调整,这个问题就很容易解决了。

代码如下:

\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}

(请注意,我把\MarkText 唇形图显示这是可能的。这意味着线条在下面文本。)

第一次运行:

第一次 tikzmark 运行

第二次运行:

第二次 tikzmark 运行

答案2

请参阅 Caramdir 对魔法数字的评论。如果我理解正确的话,通过全局设置,您将创建一个没有边界框的 tikzpicture,因此所有明确的位置都相对于页面上放置(没有大小)overlay的点。\MarkText

对于主要问题,一个可行的解决方案是

  \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

编辑:根据 Caramdir 的建议变得更加通用。

相关内容