(这些线不应该是虚线。我正在使用unicode 方框绘制字符,但显然该网站使用的字体使绘图看起来是虚线。)
┌──────┐ ┌────┐ ┌───────┐ ┌───┐ ┌────┐ ┌───────┐
│ some ├─┤ or ├─┤ a lot ├─┤ ├─┤ of ├─┤ words │
├──────┤ ├────┤ ├───────┤ ├───┤ ├────┤ ├───────┤
│ │ │ │ │ │ │ │ │ │ │ │
│ │ │ │ │ even │ │ │ │ │ │ │
│ foo │ │ x │ │ multi │ │ │ │ │ │ │
│ │ │ │ │ line │ │ │ │ │ │ │
│ │ │ │ │ │ │ │ │ │ │ │
└──────┘ └────┘ └───────┘ └───┘ └────┘ └───────┘
称其为图表似乎有点过分。
我想我仍然记得定位是如何工作的但是我对 TikZ 总体上有点生疏,目前我不知道如何根据以下要求绘制此图:
- 上方框中的文本不是多行,因此上方框都具有相同的固定高度。
- 下面框中的文本可以是多行;我可以手动输入
\\
,但最好有自动换行符。 - 根据内容的不同,下方的框具有其中最需要的框的高度。
- 每个下部框具有与对应的上部框相同的宽度;从某种意义上说,它只是一个分成两个的框。
- 连接各个框的线长度均相同,并且垂直位于上部框的中间。
- 盒子的宽度由上部盒子的内容决定。
- 整个绘图跨越整个文本宽度。
(假设页面足够宽。)
笔记:不幸的是,在发布问题之前,我没有给我的大脑足够的时间去思考,所以我错误地设计了其中一个要求。以下 2 点
- 盒子的宽度由上部盒子的内容决定。
- 下面框中的文本可以是多行;我可以手动输入
\\
,但最好有自动换行符。
应该
盒子的宽度由上部的内容决定降低盒子。- 下面框中的文本可以手工制作多行,例如通过明确
\\
或类似方式;我可以手动输入\\
,但是最好有自动换行符。
所以我误导了答案,顺便说一句,这些答案都很棒。
因此,我已对所有这些方案投了赞成票,但我会接受能根据我想要实现的目标提供最佳结果的方案。我为我的错误道歉。
答案1
有点不靠谱,但可以使用这个tabularray
包。这个想法是绘制一条简单的规则和一个表格,表格前面的列之间有间隙。
这里有两个例子,一个是等宽框,另一个是相对大小的框:
\documentclass{article}
\usepackage{xcolor}
\usepackage{tabularray}
\begin{document}
\noindent\rule{\linewidth}{0.7pt}\vskip-.8\baselineskip
\noindent\begin{tblr}{
colspec={|X||X||X||X||X||X|},
hlines = {1}{-}{leftpos = 0, rightpos = 0},
cells={bg=white,valign=m,halign=c},
row{2}={rowsep+=10pt},
rulesep=10pt,
}
some & or & a lot & & of & words\\
foo & x & even multi line & & &\\
\end{tblr}
\bigskip
\noindent\rule{\linewidth}{0.7pt}\vskip-.8\baselineskip
\noindent\begin{tblr}{
colspec={|X[-1]||X[-1]||X[-1]||X[-1]||X[-1]||X[-1]|},
hlines = {1}{-}{leftpos = 0, rightpos = 0},
cells={bg=white,valign=m,halign=c},
row{2}={rowsep+=10pt},
rulesep=10pt,
}
some & or & a lot & & of & words\\
foo & x & even multi line even multi line even multi line even multi line even multi line & & &\\
\end{tblr}
\end{document}
答案2
nicematrix
带有包和的解决方案tikz
。
\documentclass{article}
%\usepackage[showframe]{geometry}
\usepackage{tikz}
\usetikzlibrary{decorations}
\usepackage{nicematrix}
\setlength{\parindent}{0pt}
\begin{document}
\begin{NiceTabular}[width=\textwidth]{cXcXcXcXcXc}[cell-space-limits=5pt]
some && or && a lot &&&& of && words \\
foo && x && \begin{tabular}{@{}>{\centering}p{1cm}@{}}
even multi line
\end{tabular} &&&&&& \\
\CodeAfter
\begin{tikzpicture}[mydash/.style={dash pattern=on 5pt off 2pt,dash expand off}]
\def\myex{5pt}
\foreach \x in {2,4,6,8,10} \draw (1.5 -| \x) -- (1.5 -| \inteval{1+\x});
\foreach \x in {1,3,5,7,9,11} {
\draw ([yshift=-\myex]1 -| \x) -- (1 -| \x) -- (1 -| \inteval{1+\x}) -- ([yshift=-\myex]1 -| \inteval{1+\x});
\draw (2 -| \x) -- (2 -| \inteval{1+\x});
\draw ([yshift=-\myex]2 -| \x) -- ++(0,2*\myex);
\draw ([yshift=-\myex]2 -| \inteval{1+\x}) -- ++(0,2*\myex);
\draw ([yshift=\myex]last -| \x) -- (last -| \x) -- (last -| \inteval{1+\x}) -- ([yshift=\myex]last -| \inteval{1+\x});
\draw[mydash] ([yshift=-\myex]1 -| \x) -- ([yshift=\myex]2 -| \x);
\draw[mydash] ([yshift=-\myex]1 -| \inteval{1+\x}) -- ([yshift=\myex]2 -| \inteval{1+\x});
\draw[mydash] ([yshift=-\myex]2 -| \x) -- ([yshift=\myex]last -| \x);
\draw[mydash] ([yshift=-\myex]2 -| \inteval{1+\x}) -- ([yshift=\myex]last -| \inteval{1+\x});
}
\end{tikzpicture}
\end{NiceTabular}
\end{document}
编辑(无虚线)
\documentclass{article}
%\usepackage[showframe]{geometry}
\usepackage{tikz}
\usepackage{nicematrix}
\setlength{\parindent}{0pt}
\begin{document}
\begin{NiceTabular}[width=\textwidth]{cXcXcXcXcXc}[cell-space-limits=5pt]
some && or && a lot &&&& of && words \\
foo && x && \begin{tabular}{@{}>{\centering}p{1cm}@{}}
even multi line
\end{tabular} &&&&&& \\
\CodeAfter
\begin{tikzpicture}
\foreach \x in {2,4,...,10} \draw (1.5 -| \x) -- (1.5 -| \inteval{1+\x});
\foreach \x in {1,3,...,11} {
\draw (2 -| \x) -- (2 -| \inteval{1+\x});
\draw (1 -| \x) rectangle (last -| \inteval{1+\x});
}
\end{tikzpicture}
\end{NiceTabular}
\end{document}
答案3
以下不使用 Ti钾Z,而是利用原始的\valign
。
不过绘制水平连接规则相当棘手。为了做到这一点,代码需要测量并记住每列的宽度和每行的最大高度。
使用这个输入应该相当容易,唯一违反直觉的是输入是按行进行的而输出是按列进行的(例如转置tabular
)。
在strangethingy
环境中\\
,跳转到下一列并插入垂直规则以及\hfill
。水平线是在\valign
运出后绘制的,并通过在幕后对每个&
和进行的所有测量进行对齐\\
。最后一行一定不结束\\
(它会引发错误)。
代码可能需要一些清理(特别是名称)但应该是可行的。
在您的示例中,该列a lot & even multi line
是有问题的,因为单词的multi
长度超过了a lot
导致水平盒子过满的长度。
\documentclass{article}
\usepackage{showframe}
\newlength\mycolwd
\newlength\mytotalwd
\newlength\mycolht
\newlength\mymaxhtupper
\newlength\mymaxhtlower
\newcommand*\mywdlist{}
\newlength\strangethingywd
\ExplSyntaxOn
\cs_new_eq:NN \mymap \tl_map_tokens:Nn
\cs_new_eq:NN \mycount \tl_count:N
\ExplSyntaxOff
\newenvironment{strangethingy}[1]
{%
\def\\%
{%
\endlowerbox
\noalign
{%
\vrule
\hfill
\vrule
}%
}%
\def\endlowerbox
{%
\ifhmode\unskip\strut\fi\par\egroup\egroup % setbox0=vbox
\htmeasurehelper\mymaxhtlower
\hbox{\hskip\tabcolsep\box0\hskip\tabcolsep}%
\cr
}%
\def\htmeasurehelper##1%
{%
\mycolht=\dimexpr\ht0+\dp0\relax
\global##1=\ifdim\mycolht>##1\mycolht\else##1\fi
}%
\global\mytotalwd=0pt
\global\mymaxhtlower=0pt
\global\mymaxhtupper=0pt
\global\let\mywdlist\empty
\strangethingywd=\dimexpr#1\relax
\leavevmode
\hbox to\strangethingywd\bgroup
\valign\bgroup
\hrule
\setbox0=\hbox{{\strut\ignorespaces##\unskip}}%
% measure the box and keep a record of some dimensions
\global\mycolwd=\wd0
\ifx\mywdlist\empty
% still empty, but `\long` hence the above test will yield false now
\long\gdef\mywdlist{}%
\else
\xdef\mywdlist{\mywdlist{\the\dimexpr\mycolwd+2\tabcolsep+.8pt}}%
\fi
\global\advance\mytotalwd\dimexpr\mycolwd+2\tabcolsep+.8pt\relax
\htmeasurehelper\mymaxhtupper
\vfil
\hbox{\hskip\tabcolsep\box0\hskip\tabcolsep}%
\vfil
\hrule
&
\vfil
\setbox0=\vbox\bgroup\bgroup
\hsize=\mycolwd\sloppy
\noindent\strut\ignorespaces##%
\vfil
\hrule
\cr
\noalign{\vrule}%
}
{%
\endlowerbox
\noalign{\vrule}%
\egroup
\llap
{%
\edef\myhorizontalrule
{%
\vrule
width\dimexpr(\strangethingywd-\mytotalwd)/\mycount\mywdlist\relax
height\dimexpr\mymaxhtlower+.5\mymaxhtupper+.8pt+.2pt\relax
depth-\dimexpr\mymaxhtlower+.5\mymaxhtupper+.8pt-.2pt\relax
}%
\mymap\mywdlist
{%
\myhorizontalrule
\hspace
}%
}%
\egroup
}
\begin{document}
\noindent
\begin{strangethingy}{\linewidth}% width the strangethingy should take
some & foo \\
or & x \\
a lot & even multi line \\
& \\
of & \\
words & % last block must not end in \\
\end{strangethingy}
\end{document}
答案4
我喜欢这个tabularray
解决方案,但无论如何这里只有一个 TikZ 解决方案。
代码
\documentclass{article}
\usepackage{tikz}
\usepackage{showframe}
\usetikzlibrary{matrix, ext.node-families}
\tikzset{
upper lower/.style={
/utils/row1/.initial/.expand once=\csname @gobble\endcsname,
/utils/row2/.initial/.expand once=\csname @gobble\endcsname,
/utils/exec=%
\def\tikzUpperWidth{0pt}%
\def\tikzLowerHeight{0pt}%
\def\tikzNumberOfBoxes{0},
/utils/temp/.code args={##1/##2}{%
\pgfkeysaddvalue{/utils/row1}{}{\pgfmatrixnextcell ##1}%
\pgfkeysaddvalue{/utils/row2}{}{\pgfmatrixnextcell ##2\nolinebreak\strut}%
\edef\tikzNumberOfBoxes{\pgfinteval{\tikzNumberOfBoxes+1}}%
\pgfmathsetlengthmacro\tikzupperwidth{width("##1")}%
\tikzset{column \tikzNumberOfBoxes/.append style/.expanded=
{text width=\tikzupperwidth}}%
\pgfmathsetlengthmacro\tikzUpperWidth{%
\tikzUpperWidth+\tikzupperwidth
+2*(\pgfkeysvalueof{/pgf/inner xsep})+\pgflinewidth}},
/utils/temp/.list={#1},
column sep/.evaluated={(\linewidth-\tikzUpperWidth)/(\tikzNumberOfBoxes-1)},
row sep=-\pgflinewidth,
every outer matrix/.append style={inner sep=+0pt, outer sep=+0pt}}}
\newcommand\tikzUpperLower[2][]{%
\par\noindent\noindent\begin{tikzpicture}[%
#1, % all values like inner sep and line width needs to be before upper lower
upper lower={#2}]
\matrix (m) [
nodes={draw, anchor=center, align=center, font=\strut},
matrix of nodes, nodes in empty cells,
row 2/.append style={node family/height=row2},
node contents/.expanded={%
\unexpanded\expandafter\expandafter\expandafter
{\pgfkeysvalueof{/utils/row1}}%
\noexpand\pgfmatrixendrow
\unexpanded\expandafter\expandafter\expandafter
{\pgfkeysvalueof{/utils/row2}}%
\noexpand\pgfmatrixendrow
}];
\foreach \target in {2, ..., \tikzNumberOfBoxes} % line width not important
\draw[upper lower/lines/.try] (m-1-\pgfinteval{\target-1}) -- (m-1-\target);
\end{tikzpicture}\par}
\begin{document}
\tikzUpperLower[
ultra thick,% can't be individual
upper lower/lines/.style=thick,
]{
some/foo,
or/x,
a lot/even multiline,
/,
of/,
words/%
}
\end{document}