我刚刚遇到了以下问题 -\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
好吧,...我希望我理解得对:)
,
干杯!
附言:还有精彩内容: