# How to fit a tikz node to a latex table containing multirows?

I am trying to create a latex table the way i am used to (no tikz matrices), but now want to use that table in tikz and draw lines from specific cells to others. Therefore I need to fit tikz nodes to my cells which also contain multicolumns and multirows.

I wonder if I can, somehow, determine the row height of the table to set the nodes minimum height to that. This at least worked for the width (since I have fixed cell widths, it was possible to calculate this).

My current code looks like this (might look disturbing):

\documentclass{standalone}
\usepackage{tikz,forloop,tabularx,tabulary,multicol,multirow,pgfmath}
\usetikzlibrary{positioning,arrows,chains,matrix,scopes,fit,calc}

\makeatletter

% 1. cols
% 2. pos
% 3. node name
% 4. text
\newcommand{\multicolx}[4]{
\multicolumn{#1}{#2}{\tikz[remember picture]{ \node[anchor=base,inner sep=0pt, draw, color=red, minimum width=#1*4mm] (#3) {#4};}}
}

\makeatother
\begin{document}
\begin{tikzpicture}

\node[] (tableipv4) at (0,0)
{
\setlength{\tabcolsep}{0.0pt}
\begin{tabular}{|*{32}{m{4mm}}|}

\multicolx{4}{|c}{tbl-ver}{Version} & \multicolx{4}{|c}{tbl-ihl}{IHL} & \multicolx{8}{|c}{tbl-tos}{Type of Service} & \multicolx{16}{|c|}{tbl-len}{Total Length}\\\hline

\multicolumn{16}{|c}{Identification} & \multicolumn{3}{|c}{Flags} & \multicolumn{13}{|c|}{Fragment Offset} \\\hline

\end{tabular}
};

\end{tikzpicture}
\end{document}


Picture:

Is there any way to achieve fitting without expanding the row heights? Preferably using a latex table... But if there is something similar to multicols for tikz matrices I would consider switching :)

Thank you very much in advance

EDIT: With John Kormylo's improvements my line heights are now fine, as you can see:

However since i have changed the remaining multicolumns to my custom multicolx I realized that it wasn't such a great idea to just multiply the length of a "bit" by the number of spanning columns. Now some nodes aren't wide enough to fill the outer multicolumn...

Updated source code:

\documentclass{standalone}
\usepackage{tikz,forloop,tabularx,tabulary,multicol,multirow,pgfmath}
\usetikzlibrary{positioning,arrows,chains,matrix,scopes,fit,calc}

\makeatletter

% 1. cols
% 2. pos
% 3. node name
% 4. text
\newcommand{\multicolx}[4]{
\multicolumn{#1}{#2}{\tikz[remember picture,baseline=(#3.text)]{ \node[anchor=base,inner sep=0pt, draw, color=red, minimum width=#1*4mm] (#3) {\strut #4};}}
}

\makeatother
\begin{document}
\begin{tikzpicture}

\node[] (tableipv4) at (0,0)
{
\setlength{\tabcolsep}{0.0pt}
\begin{tabular}{|*{32}{m{4mm}}|}

\multicolx{4}{|c}{tbl-ver}{Version} & \multicolx{4}{|c}{tbl-ihl}{IHL} & \multicolx{8}{|c}{tbl-tos}{Type of Service} & \multicolx{16}{|c|}{tbl-len}{Total Length}\\\hline

\multicolx{16}{|c}{tbl-id}{Identification} & \multicolx{3}{|c}{tbl-flag}{Flags} & \multicolx{13}{|c|}{tbl-frag}{Fragment Offset} \\\hline

\end{tabular}
};

\end{tikzpicture}
\end{document}

• it is far more simple to draw header as pure TikZ image ... welcome to TeX.SE! May 24, 2017 at 19:13
• @noodles do you know the bytefield package? Because your example looks like the perfect job for it.
– Mike
May 24, 2017 at 19:25
• @Mike I wish I had known before^^ May 24, 2017 at 20:58
• Although, I really like the result of Zarko's answer. Before marking the question solved, I would still like to known whether it is possible to expand a nodes dimensions to fit a multicolumn (or how to get a rows height to do that manually). May 24, 2017 at 21:04
• The default row height is \arraystretch\ht\strutbox and the default depth is \arraystretch\dp\strutbox. It appears that the default value of \arraystretch is 1. May 24, 2017 at 21:44

You can place the baseline correctly using [baseline=(#3.text)]. You can make all the nodes the same height by including a \strut inside the node. The [anchor=base] has no effect here since there is only one node in each tikzpicture.

\documentclass{standalone}
\usepackage{tikz,forloop,tabularx,tabulary,multicol,multirow,pgfmath}
\usetikzlibrary{positioning,arrows,chains,matrix,scopes,fit,calc}

\makeatletter

% 1. cols
% 2. pos
% 3. node name
% 4. text
\newcommand{\multicolx}[4]{
\multicolumn{#1}{#2}{\tikz[remember picture,baseline=(#3.text)]{\node[anchor=base,inner sep=0pt, draw, color=red, minimum width=#1*4mm] (#3) {\strut #4};}}
}

\makeatother
\begin{document}
\begin{tikzpicture}

\node[] (tableipv4) at (0,0)
{
\setlength{\tabcolsep}{0.0pt}
\begin{tabular}{|*{32}{m{4mm}}|}

\multicolx{4}{|c}{tbl-ver}{Version} & \multicolx{4}{|c}{tbl-ihl}{IHL} & \multicolx{8}{|c}{tbl-tos}{Type of Service} & \multicolx{16}{|c|}{tbl-len}{Total Length}\\\hline

\multicolumn{16}{|c}{Identification} & \multicolumn{3}{|c}{Flags} & \multicolumn{13}{|c|}{Fragment Offset} \\\hline

\end{tabular}
};

\end{tikzpicture}
\end{document}


The problem with the revised MWE has to do with the width of the lines being drawn not being included in the calculation. The following isn't a perfect fit since the correction should differ for {|c|} and {c|}.

\documentclass{standalone}
\usepackage{tikz,forloop,tabularx,tabulary,multicol,multirow,pgfmath}
\usetikzlibrary{positioning,arrows,chains,matrix,scopes,fit,calc}

\makeatletter

% 1. cols
% 2. pos
% 3. node name
% 4. text
\newcommand{\multicolx}[4]{
\multicolumn{#1}{#2}{\tikz[remember picture,baseline=(#3.text)]{ \node[inner sep=0pt,draw, color=red,minimum width={#1*4mm-.75pt}] (#3) {\strut #4};}}
}

\makeatother
\begin{document}
\begin{tikzpicture}

\node[] (tableipv4) at (0,0)
{
\setlength{\tabcolsep}{0.0pt}
\begin{tabular}{|*{32}{m{4mm}}|}

\multicolx{4}{|c}{tbl-ver}{Version} & \multicolx{4}{|c}{tbl-ihl}{IHL} & \multicolx{8}{|c}{tbl-tos}{Type of Service} & \multicolx{16}{|c|}{tbl-len}{Total Length}\\\hline

\multicolx{16}{|c}{tbl-id}{Identification} & \multicolx{3}{|c}{tbl-flag}{Flags} & \multicolx{13}{|c|}{tbl-frag}{Fragment Offset} \\\hline

\end{tabular}
};

\end{tikzpicture}
\end{document}


• Yet so simple and yet so useful! Thank you for pointing that out :) I have added both modifications and applied multicolx to all other multicolumn entries... height is fine but my calculated width isn't perfect - especially in longer lines (rounding errors?)... Is there a simple (and more elaborate) trick to force nodes to use the whole line width? May 24, 2017 at 21:26
• Not really. You can use a \makebox inside the node, but you still have to give it the width. May 24, 2017 at 22:00
• Could it be, that the problem isn't rounding errors (values are treated as lengths->no truncation) but rather I did not regard the widths of the vertical lines :/? My approach becomes less promising every second :P May 24, 2017 at 22:20
• You might find that the [draw] option is making the boxes slightly larger. Tikz centers lines on the edges, so half the line width is outside. May 24, 2017 at 22:31
• So in the end the calculation I would have to do depends on the number of nodes itself. edit: meh! not on the number of nodes but on their position May 24, 2017 at 23:04

Far more simple is drawn your table/image:

For example, your MWE (with correct IP v4 header structure ... consider RFC 1122, RFC 2474 and RFC 2481) as pure TikZ image:

\documentclass[tikz,margin=3mm]{standalone}
\usetikzlibrary{chains, positioning}

\begin{document}
\begin{tikzpicture}[
node distance = 0mm,
start chain = going right,
N/.style = {draw,
minimum width=#1 mm,
outer sep=0pt,
text height=2ex, text depth=0.5ex,
on chain},
]
%--- 1
\node (n11) [N=20,right] at (0,0)        {version};
\node (n12) [N=20]  {IHL};
\node (n13) [N=30]  {DS fiesld};
\node (n14) [N=10]  {ECN};
\node (n15) [N=80]  {Total Length};
%--- 2
\node (n21) [N=80,
below right=of n11.south west]    {Identification};
\node (n22) [N=15]  {Flags};
\node (n23) [N=65]  {Fragment Offset};
%--- 3
\node (n31) [N=40,
below right=of n21.south west]    {Time to Leave};
\node (n32) [N=40]  {Protocol};
\node (n33) [N=80]  {Header Checksum};
%--- 4,5,6
\node (n41) [N=160,
below right=of n31.south west]    {Source Address};
\node (n51) [N=160,below=of n41]        {Destination Address};
\node (n61) [N=160,below=of n51]        {Options + Padding (if any)};
%----------------
\foreach \i in {0,4,8,14,16,19,32}
\draw (5*\i/10,0.4) -- ++ (0,0.2)
\ifnum\i<31
node[midway,right=-1pt,font=\tiny] {\i}
\else
node[midway,left=-1pt,font=\tiny] {31}
\fi;
\node (00) [above=4mm of n14.north east] {IPv4 Header};
%---
\end{tikzpicture}
\end{document}

• Neat :) I would not have thought it could be done that easy by using chains. And thanks for pointing me at the RFC updates... I have just copied the header from the origin of origins RFC791 May 24, 2017 at 20:53