![tikzmark 在首次运行时会有不同的行为(并且标记位置尚不可用)](https://linux22.com/image/247793/tikzmark%20%E5%9C%A8%E9%A6%96%E6%AC%A1%E8%BF%90%E8%A1%8C%E6%97%B6%E4%BC%9A%E6%9C%89%E4%B8%8D%E5%90%8C%E7%9A%84%E8%A1%8C%E4%B8%BA%EF%BC%88%E5%B9%B6%E4%B8%94%E6%A0%87%E8%AE%B0%E4%BD%8D%E7%BD%AE%E5%B0%9A%E4%B8%8D%E5%8F%AF%E7%94%A8%EF%BC%89.png)
这个宏\tikzmark
对我来说非常有用,但不喜欢第一次运行时的默认行为。
因此,我希望能够确定这是否是第一次运行(其中节点的坐标不准确),或者是否是后续运行(并且坐标可用,但可能未达到最终值)。
因此,在下面的 MWE 中,我希望仅有的首次调用时显示绿线,以及仅有的后续处理后的红线:
顺便说一句,我不知道为什么我需要下面的魔法数字(这取决于\paperheight
) 。我认为:-0.70
1.30
\draw (0,-\paperheight) -- (\paperwidth,0);
应该产生一条从左下角到右上角的线。您可以在代码中取消注释此行,然后查看蓝线的位置。也许这只是我不明白原点在哪里的问题——我以为它是页面的左上角。
笔记
- 在我的实际使用中,红线从文本的左下角延伸到右上角,但我不希望额外的计算使 MWE 变得混乱。因此,绿线是一个有用的初步猜测。
参考:
\tikzmark
来自在正文旁边添加大括号。
代码:
\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
前唇形图显示这是可能的。这意味着线条在下面文本。)
第一次运行:
第二次运行:
答案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 的建议变得更加通用。