![使用定位库和长度预先计算均匀分布 tikz 元素会导致错误的结果](https://linux22.com/image/395406/%E4%BD%BF%E7%94%A8%E5%AE%9A%E4%BD%8D%E5%BA%93%E5%92%8C%E9%95%BF%E5%BA%A6%E9%A2%84%E5%85%88%E8%AE%A1%E7%AE%97%E5%9D%87%E5%8C%80%E5%88%86%E5%B8%83%20tikz%20%E5%85%83%E7%B4%A0%E4%BC%9A%E5%AF%BC%E8%87%B4%E9%94%99%E8%AF%AF%E7%9A%84%E7%BB%93%E6%9E%9C.png)
我正在尝试在文本区域的长度上均匀分布tikz
由一个\pic
(= 一个图标)和一个文本(= 标签)组成的多个元素/组。我使用一个图层在其上绘制元素的虚拟对象,测量它们的宽度,累计减去它们,然后除以元素/组的数量,以获得我的组之间的间隔。然后我使用tikz 库以及预先计算的间隔长度来放置我的组。但由于某种原因,这不会导致我的组实际上填满文本区域的整个宽度。\node
discard
\textwidth
positioning
这是一个简化的 MWE,其中我还引入了一个测试长度(通过添加到图片中的读取线可视化),这似乎可以验证我计算的所有部分实际上加起来等于\textwidth
。但我的组并没有填满整个文本区域的宽度:
\documentclass[12pt,a4paper,ngerman,oneside]{scrlttr2}
\def\fontscalingfac{1}
% \renewcommand{\fontscalingfac}{0.85}
\usepackage[sfdefault,scaled=\fontscalingfac]{FiraSans}
\usepackage[LGR,T1]{fontenc}
\usepackage[utf8]{inputenx}
\usepackage{tikz}
\usepackage{tikzpagenodes}
\usetikzlibrary{calc}
\usetikzlibrary{positioning}
\usepackage{showframe}
\newlength{\iconcircleradius}
\setlength{\iconcircleradius}{15pt}
\newlength{\iconcirclelinewidth}
\setlength{\iconcirclelinewidth}{1.5pt}
\newlength{\iconlabelsep}
\setlength{\iconlabelsep}{0.1cm}
\newlength{\headermatrixsep}
\setlength{\headermatrixsep}{\textwidth}
\newlength{\testlen}
\setlength{\testlen}{0pt}
\def\textA{Text piece A}
\def\textB{Text piece B}
\def\textC{Text piece C}
\begin{document}
\begin{tikzpicture}[remember picture,overlay]
% calculate separation lengths for icon-text elements such that they
% are evenly distributed across the \textwidth
% discard the nodes required for the calculations afterwards
\pgfdeclarelayer{discard}
\begin{pgfonlayer}{discard}
\foreach \infoelement in {\textA, \textB, \textC}
{%
\node[circle, line width=\iconcirclelinewidth, anchor=west, minimum height=2\iconcircleradius] (icon circle) at (0,0) {};
\node[font=\small, right=\iconlabelsep of icon circle, anchor=west] (text node) {\infoelement};
\newdimen\xEast
\pgfextractx{\xEast}{\pgfpointanchor{text node}{east}}
\newdimen\xWest
\pgfextractx{\xWest}{\pgfpointanchor{icon circle}{west}}
\global\advance\headermatrixsep by \dimexpr(- \xEast + \xWest + 2\iconcirclelinewidth)\relax
% introduce a test length to check if it accumulates to `\textwidth`
\global\advance\testlen by \dimexpr(\xEast - \xWest - 2\iconcirclelinewidth)\relax
}
\end{pgfonlayer}
\pgfmathsetlength{\headermatrixsep}{\headermatrixsep / (3*\fontscalingfac)}
\pgfmathsetlength{\testlen}{\testlen + (3*\fontscalingfac)*\headermatrixsep}
%
% place icon-text elements evenly across text width
% Icon A
\node[draw, circle, line width=\iconcirclelinewidth, anchor=west, minimum height=2\iconcircleradius] (icon A) at (current page text area.north west) {};
\node[color=black, font=\small, right=\iconlabelsep of icon A, anchor=west] (text A) {\textA};
% Icon B
\node[draw, circle, line width=\iconcirclelinewidth, minimum height=2\iconcircleradius, right=\headermatrixsep of text A, anchor=west] (icon B) {};
\node[color=black, font=\small, right=\iconlabelsep of icon B, anchor=west] (text B) {\textB};
% Icon C
\node[draw, circle, line width=\iconcirclelinewidth, minimum height=2\iconcircleradius, right=\headermatrixsep of text B, anchor=west] (icon C) {};
\node[color=black, font=\small, right=\iconlabelsep of icon C, anchor=west] (text C) {\textC};
\draw[red] ($(current page text area.north west) - (0, 10pt)$) -- ($(current page text area.north west) + (\testlen, -10pt)$);
\end{document}
这导致了下面的图片
引入该变量\fontscalingfac
是因为在我的实际示例中,引入该变量几乎解决了间距问题(我缩放字体以使其 xheight 与newtxsf
我使用的包相匹配)。在我的 MWE 中,缩放字体实际上会导致间距略有不同,我也想了解原因。但我的主要问题是:为什么我将绘图元素均匀分布在页面上并填充整个宽度的方法会失败?
答案1
该解决方案首先将图像/页面细分为三个范围,然后在范围内绘制文本等。
请注意,[overlay] 会关闭所有边界框,甚至是局部边界框,这就是我必须将其关闭的原因。
\documentclass[12pt,a4paper,ngerman,oneside]{scrlttr2}
\def\fontscalingfac{1}
% \renewcommand{\fontscalingfac}{0.85}
%\usepackage[sfdefault,scaled=\fontscalingfac]{FiraSans}% it is easier for me just to comment these out
%\usepackage[LGR,T1]{fontenc}
%\usepackage[utf8]{inputenx}
\usepackage{tikz}
%\usepackage{tikzpagenodes}
%\usetikzlibrary{calc}
%\usetikzlibrary{positioning}
\usepackage{showframe}
\newlength{\boxwidth}
\setlength{\boxwidth}{0.333\textwidth}
\newlength{\boxheight}
\setlength{\boxheight}{1cm}
\def\textA{Text piece A}
\def\textB{Text piece B}
\def\textC{Text piece C}
\begin{document}
\noindent\begin{tikzpicture}
% place icon-text elements evenly across text width
\begin{scope}[local bounding box=box A]
\clip (0,0) rectangle (\boxwidth,-\boxheight);
% Icon A
\node[draw, circle, right] at (0,-0.5\boxheight) (icon A) {};
\node[color=black, font=\small, right] at (icon A.east) (text A) {\textA};
\end{scope}
\draw[red] (box A.north west) rectangle (box A.south east);
%
\begin{scope}[shift=(box A.north east), local bounding box=box B]
\clip (0,0) rectangle (\boxwidth,-\boxheight);
% Icon A
\node[draw, circle, right] at (0,-0.5\boxheight) (icon B) {};
\node[color=black, font=\small, right] at (icon B.east) (text B) {\textB};
\end{scope}
\draw[green] (box B.north west) rectangle (box B.south east);
%
\begin{scope}[shift=(box B.north east), local bounding box=box C]
\clip (0,0) rectangle (\boxwidth,-\boxheight);
% Icon A
\node[draw, circle, right] at (0,-0.5\boxheight) (icon C) {};
\node[color=black, font=\small, right] at (icon C.east) (text C) {\textC};
\end{scope}
\draw[blue] (box C.north west) rectangle (box C.south east);
\end{tikzpicture}
\end{document}
答案2
事实证明,在我具体而复杂的用例中,我偏离了真正的问题。我的方法确实有效,但我的推理中有一个相当简单的错误:我计算了我想要放置的每个组的宽度,并连续减去这些值,这些值\textwidth
应该会给我“空白”的总量,我可以将这些空白分配给我的组,以便它们填满整个文本区域的宽度。到目前为止一切顺利,但随后我将这个长度除以我想要放置的组数 - 我们称之为 N(在我的 MWE 中为 3) - 而我更应该将其除以 N-1,因为我的两个最外面的组与它们各自的边界对齐。这基本上解决了我的问题。
我还需要进行另外两项小修正:首先,在计算组的宽度时,我两次减去了圆节点的线宽,即2\iconcirclelinewidth
。但实际上我只需要这样做一次。此外,我的实际用例文档让我怀疑我的字体的缩放因子可能以某种方式与 产生不良影响tikz
,这就是为什么我在计算中引入了字体缩放因子\fontscalingfac
。然而,这是错误的,字体缩放不会影响计算。
因此,当我相应地修正我的 MWE 时,我得到了我想要的结果:
\documentclass[12pt,a4paper,ngerman,oneside]{scrlttr2}
\usepackage[sfdefault,scaled=0.85]{FiraSans}
\usepackage[LGR,T1]{fontenc}
\usepackage[utf8]{inputenx}
\usepackage{tikz}
\usepackage{tikzpagenodes}
\usetikzlibrary{calc}
\usetikzlibrary{positioning}
\usepackage{showframe}
\newlength{\iconcircleradius}
\setlength{\iconcircleradius}{15pt}
\newlength{\iconcirclelinewidth}
\setlength{\iconcirclelinewidth}{1.5pt}
\newlength{\iconlabelsep}
\setlength{\iconlabelsep}{0.1cm}
\newlength{\headermatrixsep}
\setlength{\headermatrixsep}{\textwidth}
\newlength{\testlen}
\setlength{\testlen}{0pt}
\def\textA{Text piece A}
\def\textB{Text piece B}
\def\textC{Text piece C}
\begin{document}
\begin{tikzpicture}[remember picture,overlay]
% calculate separation lengths for icon-text elements such that they
% are evenly distributed across the \textwidth
% discard the nodes required for the calculations afterwards
\pgfdeclarelayer{discard}
\begin{pgfonlayer}{discard}
\foreach \infoelement in {\textA, \textB, \textC}
{%
\node[circle, line width=\iconcirclelinewidth, anchor=west, minimum height=2\iconcircleradius] (icon circle) at (0,0) {};
\node[font=\small, right=\iconlabelsep of icon circle, anchor=west] (text node) {\infoelement};
\newdimen\xEast
\pgfextractx{\xEast}{\pgfpointanchor{text node}{east}}
\newdimen\xWest
\pgfextractx{\xWest}{\pgfpointanchor{icon circle}{west}}
\global\advance\headermatrixsep by \dimexpr(- \xEast + \xWest + \iconcirclelinewidth)\relax
% introduce a test length to check if it accumulates to `\textwidth`
\global\advance\testlen by \dimexpr(\xEast - \xWest - \iconcirclelinewidth)\relax
}
\end{pgfonlayer}
\pgfmathsetlength{\headermatrixsep}{\headermatrixsep / 2}
\pgfmathsetlength{\testlen}{\testlen + 2*\headermatrixsep}
%
% place icon-text elements evenly across text width
% Icon A
\node[draw, circle, line width=\iconcirclelinewidth, anchor=west, minimum height=2\iconcircleradius] (icon A) at (current page text area.north west) {};
\node[color=black, font=\small, right=\iconlabelsep of icon A, anchor=west] (text A) {\textA};
% Icon B
\node[draw, circle, line width=\iconcirclelinewidth, minimum height=2\iconcircleradius, right=\headermatrixsep of text A, anchor=west] (icon B) {};
\node[color=black, font=\small, right=\iconlabelsep of icon B, anchor=west] (text B) {\textB};
% Icon C
\node[draw, circle, line width=\iconcirclelinewidth, minimum height=2\iconcircleradius, right=\headermatrixsep of text B, anchor=west] (icon C) {};
\node[color=black, font=\small, right=\iconlabelsep of icon C, anchor=west] (text C) {\textC};
\draw[red] ($(current page text area.north west) - (0, 10pt)$) -- ($(current page text area.north west) + (\testlen, -10pt)$);
\end{tikzpicture}
\end{document}