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.

6
  • @user202729, updated. I start to think that a grid-based approach is probably the way...
    – Enlico
    Dec 4, 2022 at 9:57
  • 3
    Just use ASCII art, looks way better anyway :P
    – Skillmon
    Dec 4, 2022 at 10:03
  • @Skillmon, that's blasphemy! :P
    – Enlico
    Dec 4, 2022 at 10:08
  • Maybe something like this? tex.stackexchange.com/questions/78310/… Dec 4, 2022 at 11:14
  • 1
    @projetmbc, this
    – Enlico
    Dec 12, 2022 at 14:46

4 Answers 4

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

10
  • To have to vertical rules dashed, you could add: colspec={|X||[dashed]X||[dashed]X||[dashed]X||[dashed]X||[dashed]X|} and vlines={dashed}
    – CarLaTeX
    Dec 4, 2022 at 16:23
  • 1
    @CarLaTeX I'm not sure if the rules are supposed to be dashed. For me the ASCI art in the question looks like this i.stack.imgur.com/Bxww4.png I gather from the comments to the other answer that it might look different on different systems? Dec 4, 2022 at 19:23
  • You're right, maybe it's only ASCII art :)
    – CarLaTeX
    Dec 4, 2022 at 19:29
  • @CarLaTeX ... however isn't it great that tabularray is so flexible that one could get dashed rules with just two lines of code? I love this package :) Dec 4, 2022 at 19:31
  • 1
    @Enlico Then add a \vskip.8\baselineskip above the rule to compensate Dec 7, 2022 at 21:12
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

5
  • 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 4, 2022 at 15:20
  • 1
    @Skillmon you are right, I thought it was a photo.
    – polyn
    Dec 4, 2022 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 6, 2022 at 6:56
  • 1
    @Enlico If your system is not updated add to the preamble \usepackage{xfp}
    – polyn
    Dec 6, 2022 at 8:14
  • @polyn, hard to believe an up-to-date Archlinux installation is not updated, but you're right.
    – Enlico
    Dec 6, 2022 at 16:54
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

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

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .