8

(The lines are not meant to be dashed. I'm using unicode box drawing characters, but apparently the font used by this site makes the drawing look dashed.)

┌──────┐ ┌────┐ ┌───────┐ ┌───┐ ┌────┐ ┌───────┐
│ some ├─┤ or ├─┤ a lot ├─┤   ├─┤ of ├─┤ words │
├──────┤ ├────┤ ├───────┤ ├───┤ ├────┤ ├───────┤
│      │ │    │ │       │ │   │ │    │ │       │
│      │ │    │ │ even  │ │   │ │    │ │       │
│ foo  │ │ x  │ │ multi │ │   │ │    │ │       │
│      │ │    │ │ line  │ │   │ │    │ │       │
│      │ │    │ │       │ │   │ │    │ │       │
└──────┘ └────┘ └───────┘ └───┘ └────┘ └───────┘

Calling it a graph seems a bit overkill.

I think I still remember how positioning works, but I'm a bit rusty on TikZ overall, and at the moment I don't see how I can make this drawing given the following requirements:

  • Text in the upper boxes is not multiline, so the upper boxes are all the same fixed height.
  • Text in the lower boxes can be multiline; I'm ok with manually putting \\, but an automatic line break would be preferrable.
  • The lower boxes have all the height of the one of them that requires the most, based on its content.
  • Each lower box has the same width as the corresponding upper box; in a way it's just one box split in 2.
  • The lines connecting the boxes have all the same length and are vertically in the middle of the upper boxes.
  • The width of the boxes is determined by the content in the upper boxes.
  • The whole drawing spans the whole textwidth.

(The assumption that the page is wide enough if made.)


Note: Unfortunately I've not given enough time to my brain to think before posting the question, so I misdesigned one of the requirements. The following 2 points

  • The width of the boxes is determined by the content in the upper boxes.
  • Text in the lower boxes can be multiline; I'm ok with manually putting \\, but an automatic line break would be preferrable.

should have actually been

  • The width of the boxes is determined by the content in the upper lower boxes.
  • Text in the lower boxes can be manually made multiline, e.g. via explicit \\ or similar; I'm ok with manually putting \\, but an automatic line break would be preferrable.

So I have misguided the answers, which are all great, by the way.

Therefore I've upvoted all of them, but I'll accept the one that gives the best result according to what I wanted to achieve. I apologize for the mistake.

Enlico
  • 2,592

4 Answers4

11

A solution with packages nicematrix and 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}

enter image description here

Edit (without dashed lines)

\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}

enter image description here

polyn
  • 5,614
  • 4
    I love that you reproduced the dashed vertical rules, but I guess that's just an artefact of the ASCII-art and OP wants solid lines. – Skillmon Dec 04 '22 at 15:20
  • 1
    @Skillmon you are right, I thought it was a photo. – polyn Dec 04 '22 at 15:40
  • I get quite a few errors on this: main.tex|20 error| (Package pgf) No shape named `1+2' is known. See the pgf package documentation for explanation. ... \end{NiceTabular} main.tex|20 error| Undefined control sequence <argument> 1.5 -| \inteval {1+\x } \end{NiceTabular}. Can you say something about how you compiled it? – Enlico Dec 06 '22 at 06:56
  • 1
    @Enlico If your system is not updated add to the preamble \usepackage{xfp} – polyn Dec 06 '22 at 08:14
  • @polyn, hard to believe an up-to-date Archlinux installation is not updated, but you're right. – Enlico Dec 06 '22 at 16:54
11

A bit hacky, but one could use the tabularray package. The idea is to draw a simple rule and a table with gaps between the columns in front of it.

Here are two examples, one with equal width boxes and one with relatively sized ones:

\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}

enter image description here

7

The following doesn't use TikZ, instead it makes use of the primitive \valign.

Drawing the horizontal connection rules is rather tricky though. In order to do so the code needs to measure and remember the width of each column and the maximum height of each row.

Input with this should be rather easy, the only thing that's counter intuitive is that the input is line wise while the output is column wise (transposed to for instance tabular).

Inside the strangethingy environment \\ jumps to the next column and inserts vertical rules as well as a \hfill. The horizontal lines are drawn after the \valign is shipped out and aligned by all the measurements done behind the scenes on each & and \\. The last line must not end in \\ (it'll throw an error).

The code might need some cleanup (especially on names) but should be workable.

In your example the column a lot & even multi line is problematic, since the word multi is longer than a lot resulting in an overfull hbox.

\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}

enter image description here

Skillmon
  • 60,462
5

I like the tabularray solution but here's an only TikZ one anyway.

Code

\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}

Output

enter image description here

Qrrbrbirlbel
  • 119,821