tikz 和组中的 \settowidth 问题

tikz 和组中的 \settowidth 问题

我刚刚遇到了以下问题 -\settowidth在 a 中使用长度tikzpicture会将该长度重置为 0?这是一个(完全命令行)MWE,使用以下方式编译pdflatex test.tex

\documentclass[12pt]{article}

\usepackage{tikz}

\begin{document}

\newlength{\mypgVTitleWidth}
\newbox\tbxa

\setlength{\mypgVTitleWidth}{1.0pt}
\typeout{ = A: \mypgVTitleWidth is \the\mypgVTitleWidth }

\ %

\settowidth{\mypgVTitleWidth}{MMMMM}
\typeout{ = B: \mypgVTitleWidth is \the\mypgVTitleWidth }

\setbox\tbxa\hbox{{MMMMM}}
\mypgVTitleWidth\wd\tbxa
\typeout{ = B2: \mypgVTitleWidth is \the\mypgVTitleWidth }


\fontsize{10}{10}\selectfont
\ %
\settowidth{\mypgVTitleWidth}{MMMMM}
\typeout{ = C: \mypgVTitleWidth is \the\mypgVTitleWidth }

{

\fontsize{30}{30}\selectfont
\ %
\settowidth{\mypgVTitleWidth}{MMMMM}
\typeout{ = D: \mypgVTitleWidth is \the\mypgVTitleWidth }

}

\begin{tikzpicture}
\typeout{ = E: \mypgVTitleWidth is \the\mypgVTitleWidth }

\fontsize{30}{30}\selectfont
\ %
\settowidth{\mypgVTitleWidth}{MMMMM}
\typeout{ = F: \mypgVTitleWidth is \the\mypgVTitleWidth }
\end{tikzpicture}

\end{document}

第一个问题

使用该代码,tikzpicture将生成以下日志:

 = A: \mypgVTitleWidth is 1.0pt
 = B: \mypgVTitleWidth is 53.83278pt
 = B2: \mypgVTitleWidth is 53.83278pt
 = C: \mypgVTitleWidth is 45.83344pt

LaTeX Font Warning: Font shape `OT1/cmr/m/n' in size <30> not available
(Font)              size <24.88> substituted on input line 30.

 = D: \mypgVTitleWidth is 105.74562pt
 = E: \mypgVTitleWidth is 45.83344pt
 = F: \mypgVTitleWidth is 0.0pt

值得注意的是,轨迹 F 显示 0.0pt。注释掉开始和结束tikzpicture- 日志中将显示以下内容:

 ...
 = D: \mypgVTitleWidth is 105.74562pt
 = E: \mypgVTitleWidth is 45.83344pt
 = F: \mypgVTitleWidth is 105.74562pt

...正如预期的那样。

第二个问题

如果我在一个组中使用 \fontsize..\selectfont 进行临时更改来计算长度;我会在{...}组内获得一个长度 - 并且在我退出时获得前一个长度:

 = D: \mypgVTitleWidth is 105.74562pt
 = E: \mypgVTitleWidth is 45.83344pt

... 好的,我猜这就是该组的真正目的(以确保更改是本地的) - 但如果我想保存这个长度以便在代码中进一步使用(例如在 tikzpicture 中),我该怎么做?我也尝试将第二个\newlength设置为 0 \addtolength,它也会自行重置。

知道为什么会发生这种情况吗?如何解决:\settowidth加入小组tikz?并阅读\settowidth

答案1

正如解释的那样如何在 TikZ 环境中使用 hbox 进行文本尺寸测量?PGF/TikZ 将图片环境中的当前字体设置为\nullfont忽略任何文本。\node {..};然后使用 . 恢复字体\pgfinterruptpicture\endpgfinterruptpicture

为了完成\settowidth工作,您需要添加\pgfinterruptpicture .. \endpgfinterruptpicture以下内容:

\settowidth{\somelength}{\pgfinterruptpicture Text\endpgfinterruptpicture}

甚至可以修补底层宏以\settowidth自动添加这些宏。有两个 TikZ 宏可将某些宏(如\node等)的定义更改为图片内的 TikZ 定义,然后再改回来。这些可用于仅在图片内部使用重新定义的版本,而在外部使用正常版本:

\documentclass{article}

%\usepackage{calc}
\usepackage{tikz}

\usepackage{etoolbox}

\makeatletter
\let\normal@settodim\@settodim
\let\tikz@settodim\@settodim
\patchcmd{\tikz@settodim}{\setbox\@tempboxa\hbox}{\my@tikz@setbox}{}{}
\def\my@tikz@setbox#1{%
    \setbox\@tempboxa\hbox{\pgfinterruptpicture #1\endpgfinterruptpicture}%
}
\appto\tikz@installcommands{%
    \let\@settodim\tikz@settodim
}
\appto\tikz@uninstallcommands{%
    \let\@settodim\normal@settodim
}
\makeatother

\begin{document}

\newlength\mylength
\settowidth{\mylength}{Test}
\the\mylength

\begin{tikzpicture}
    \settowidth{\mylength}{Test}
    \node{\the\mylength};
\end{tikzpicture}

\end{document}

答案2

也许楼主还有其他需求电子工具箱包,但是下面的解决方案不需要它。

\documentclass{article}
\usepackage{tikz}
\makeatletter
\let\sda@ltx@settodim\@settodim
\protected\def\sda@settodim#1#2#3{%
  \setbox\@tempboxa\hbox{\pgfinterruptpicture{#3}\endpgfinterruptpicture}%
  #2#1\@tempboxa\setbox\@tempboxa\box\voidb@x
}
% If you're wary of LaTeX's \g@addto@macro, then use:
\protected\def\l@addto@macro#1#2{%
  \begingroup
  \toks1\expandafter{#1}\toks@{#2}%
  \edef#1{\endgroup\edef\noexpand#1{%
    \noexpand\unexpanded{\the\toks1 \the\toks@}}}#1%
}
\l@addto@macro\tikz@installcommands{\let\@settodim\sda@settodim}
\l@addto@macro\tikz@uninstallcommands{\let\@settodim\sda@ltx@settodim}

% Alternative. No need to pre-declare a dimension when calling the following:
\def\tikzsetcmdtoheight{\tikz@cmd@settodim\ht}
\def\tikzsetcmdtodepth {\tikz@cmd@settodim\dp}
\def\tikzsetcmdtowidth {\tikz@cmd@settodim\wd}
\protected\def\tikz@cmd@settodim#1#2#3{%
  \setbox\@tempboxa\hbox{\pgfinterruptpicture{#3}\endpgfinterruptpicture}%
  \edef#2{\the#1\@tempboxa}\setbox\@tempboxa\box\voidb@x
}
\makeatother

\newlength\mylength
\begin{document}
\begin{tikzpicture}
  \settowidth{\mylength}{Test}
  \tikzsetcmdtowidth\cmda{Test}
  \draw (0,0) node(a) [draw] {{\tt\string\mylength} result: \the\mylength}
    (1.5,1.5) node(b) [draw] {{\tt\string\cmda} result: \cmda};
  \draw[color=red] (a.east) -| (5,1.5) -| (b.east);
\end{tikzpicture}
\end{document}

编辑

另一种方法。调用以下命令时无需预先声明维度。也无需加载电子工具箱包中。这些命令可以在 的内部和外部使用\tikz,甚至可以在蒂克兹未加载。

\documentclass{article}
\usepackage{tikz}
\makeatletter
\protected\def\setcmdtoheight{\cmd@settodim\ht}
\protected\def\setcmdtodepth{\cmd@settodim\dp}
\protected\def\setcmdtowidth{\cmd@settodim\wd}
\def\cmd@settodim#1#2#3{%
  \setbox\@tempboxa\hbox{%
    \@ifundefined{pgfpictureid}{{#3}}{%
      \pgfinterruptpicture{#3}\endpgfinterruptpicture
    }%
  }%
  \edef#2{\the#1\@tempboxa}\setbox\@tempboxa\box\voidb@x
}
\makeatother

% For comparison later, let us set \mylength:
\newlength\mylength
\settowidth{\mylength}{Test}

\begin{document}
\def\print#1#2{Value of {\tt\string#2}: \if#1c\the\fi#2}
\setcmdtowidth\cmda{Test}
Outside {\tt\string\tikz}:\par
\print c\mylength, \print n\cmda
\par\bigskip
Inside {\tt\string\tikz}:\par\medskip
\begin{tikzpicture}
  \setcmdtowidth\cmda{Test}
  \draw (0,0) node(a) [draw,color=red,rounded corners=5pt] {\print c\mylength}
    (1.5,1.5) node(b) [draw,color=blue,rounded corners=5pt] {\print n\cmda};
  \draw[color=red] (a.east) -| (5,1.5) -| (b.east);
  \draw[color=blue] (a.west) -| (-4,1.5) -| (b.west);
  \fill [gray!25!purple!25](5,0) circle [x radius=1cm, y radius=5mm, rotate=30];
  \fill [gray!25!red!25](-4,1.5) circle [x radius=1cm, y radius=5mm, rotate=30];
\end{tikzpicture}
\end{document}

在此处输入图片描述

答案3

感谢@egreg的评论,我找到了帖子“如何在 TikZ 环境中使用 hbox 进行文本尺寸测量?“几乎是一样的。答案解释了有关nullfont,并提出了一种\let\origselectfont\selectfont...基于字体的文本框宽度测量方法tikz

对于我的第二个问题 - 似乎有效的是mypgVTitleWidth使用 将“长度”“类型”转换为“字符串” \edef;但这仍会导致局部变化 - 除非在前面加上\global。所以现在我有以下代码片段:

....
\def\txxx{INIT}

{
  \fontsize{30}{30}\selectfont
  \settowidth{\mypgVTitleWidth}{MMMMM}
  \typeout{ = D: \mypgVTitleWidth is \the\mypgVTitleWidth }
  \global\edef\txxx{\the\mypgVTitleWidth} % "length to string"
  \setbox\tbxa\hbox{{MMMMM}}
  \typeout{ = D2: \tbxa is \the\wd\tbxa, \txxx }
}


\typeout{ = D3: \tbxa is \the\wd\tbxa, \txxx }
\setlength{\mypgVTitleWidth}{\txxx} % "string to length"

\begin{tikzpicture}
\typeout{ = E: \mypgVTitleWidth is \the\mypgVTitleWidth }
....

这似乎对我有用:

 = C: \mypgVTitleWidth is 45.83344pt
 = D: \mypgVTitleWidth is 105.74562pt
 = D2: \tbxa is 105.74562pt, 105.74562pt
 = D3: \tbxa is 53.83278pt, 105.74562pt
 = E: \mypgVTitleWidth is 105.74562pt

好吧,...我希望我理解得对:)
干杯!

附言:还有精彩内容:

相关内容