[Tex/LaTex] Removing an “Overfull \vbox” message caused by a nested table

boxestableswarnings

I am trying to typeset a nested table, like this:

\documentclass{article}
\usepackage{bigstrut}
\usepackage{multirow}

\begin{document}

\begin{tabular}{|lll|}
  \hline
   A & \multirow{2}{*}{
      \begin{minipage}{3cm}
        \begin{tabular}{|l|l|}
          \hline
          A & \hfill 1\textsuperscript{st}\bigstrut[t] \\
          \hline
          A & A \\
          \hline
        \end{tabular}
      \end{minipage}
        } &  A \\
   A & {} & A \\
   A & A  & A \\
  \hline
\end{tabular}

\end{document}

This causes a bad box: Overfull \vbox (3.19998pt too high) detected at line 19

I have been trying for a while to detect the precise source of this bad box (it's caused partly by the \bigstrut), but I do not know how to remove this warning. I tried various fixes of the sort \\[3.5pt], but I am not sure what's going wrong and how to address the issue. It might be the case that typesetting the table in an entirely different way turns out to be the "best" way, but for now I am interested in the source of the bad box and how to get rid of it.

Note: In this minimized example, the bottom middle A overlaps with the nested table, but even if I do something like \\[1cm] for the second-to-last line, the bad (overfull) \vbox message persists (with incidentally the same point size indication).


EDIT

Here's is an alternate MWE which creates a similar overfull error without having to read through a nested tabular environment:

\documentclass{article}
\usepackage{xcolor}
\usepackage{multirow}
\setlength{\parindent}{0pt}
\newcommand{\vstrut}[3][blue]{{\color{#1}\rule[-#2]{0.4pt}{#3}}}
\newcommand{\hstrut}[2][blue]{{\color{#1}\makebox[0pt][l]{\rule[-0.4pt]{#2}{0.4pt}}}}
\begin{document}

\begin{tabular}{|lll|}
  \hline
   A\vstrut{0ex}{5ex} & \multirow{2}{3cm}{%
      \begin{minipage}[t]{3cm}
            Trial \vstrut[red]{18pt}{18pt}  text
      \end{minipage}} 
                                          & A \\
   A\vstrut{3ex}{3ex}\hstrut{1.65in} & {} & A \\
   A                                 & A  & A \\
  \hline
\end{tabular}

\end{document}

Best Answer

TeX is right: the overfull box (3.2pt too high) should be there: the embedded table you are trying to fit in the height of two outer table rows is just too high. What made it too high? First, you have used the \bigstrut[t] in the embedded table. This has made it 2pt higher. Second, you have used three \hlines, each being \arrayrulewidth=0.4pt high: this accounts for the remaining 1.2pt.

A test: The inner table fits perfectly if the bigstrut[t] and \hlines are removed:

no bigstrut or hline

\documentclass{article}
\usepackage{bigstrut}
\usepackage{multirow}
\begin{document}
\begin{tabular}{|lll|}
  \hline
   A & \multirow{2}{*}{
        \begin{tabular}{|l|l|}%|
          %\hline
          A & \hfill 1\textsuperscript{st}%
          %\bigstrut[t]
          \\
          %\hline
          A & A \\
          %\hline
        \end{tabular}
        } &  A\\
   A & {} & A\\
   A & A  & A \\
  \hline
\end{tabular}
\end{document}

There is only one way of dealing with this: creating more space in the outer table. Given that you would probably like the rows of the inner and outer table to be aligned, I suggest parallelism. Whenever you use a \bigstrut in the inner table, use it in the outer table as well, in the same row.

There's a catch however. Whenever you make a row of the outer table higher than normal, you need to tell multirow about it in order to not get an overfull box. To do this, multirow provides the optional argument bigstruts that increases the height of the multirow cell for bigstruts * \bigstrutjot. Thus (I've cleaned up the MWE a bit), an example with (only) \bigstrut in the inner table, and consequently a \bigstrut in the outer table plus the optional argument [1].

bigstrut, no hlines

\documentclass{article}
\usepackage{bigstrut}
\usepackage{multirow}
\usepackage{xcolor}
\begin{document}
\begin{tabular}{|lll|}
  \hline
   A & \multirow{2}[1]{*}{\color{red}%
        \begin{tabular}{|l|l|}%|
          A & A\bigstrut[t] \\
          A & A\\
        \end{tabular}} &  A\bigstrut[t]  \\
   A & {} & A \\
   A & A  & A \\
  \hline
\end{tabular}
\end{document}

Next, \hline in the embedded table. First, parallelism: we have to get extra vertical space into the outer table as well. I propose using a strut of appropriate depth.

\def\hlinestrut{\rule[\dimexpr-\dp\strutbox-\arrayrulewidth]{\dimexpr\dp\strutbox+\arrayrulewidth}{0pt}}

However, this is not enough: again, we also have to tell multirow that the rows are higher than normal. However, we are faced with a problem. The only way to increase the height of the multirow cell is by the optional argument bigstruts, which takes only integer values, but the height of a \hline is not an integer multiple of \bigstrutjot. By default, \arrayrulewidth is one fifth of \bigstrutjot. I see no other way but patch multirow to accept floats for the bigstruts argument. Furtunately, this is quite easy, all we have to change is this:

%\ifnum#2>0 \advance\@tempdima\bigstrutjot \fi
\ifdim#2pt>0pt \advance\@tempdima\bigstrutjot \fi

The full example now:

bigstrut and one hline

\documentclass{article}
\usepackage{bigstrut}
\usepackage{multirow}
\usepackage{xcolor}
\def\hlinestrut{\rule[\dimexpr-\dp\strutbox-\arrayrulewidth]{\dimexpr\dp\strutbox+\arrayrulewidth}{0pt}}
\makeatletter
\def\@xmultirow#1[#2]#3[#4]#5{\@tempcnta=#1%
  \@tempdima\@tempcnta\ht\@arstrutbox
  \advance\@tempdima\@tempcnta\dp\@arstrutbox
  \ifnum\@tempcnta<0\@tempdima=-\@tempdima\fi
  \advance\@tempdima#2\bigstrutjot
  \if*#3\setbox0\vtop to \@tempdima{\vfill\multirowsetup
    \hbox{\strut#5\strut}\vfill}%
  \else
      \setbox0\vtop to \@tempdima{\hsize#3\@parboxrestore
                \vfill \multirowsetup \strut#5\strut\par\vfill}%
  \fi
  \ht0\z@\dp0\z@
  \ifnum\@tempcnta<0\advance\@tempdima-\dp\@arstrutbox
  \else\@tempdima\ht\@arstrutbox
     %\ifnum#2>0 \advance\@tempdima\bigstrutjot \fi
     \ifdim#2pt>0pt \advance\@tempdima\bigstrutjot \fi
  \fi
  \advance\@tempdima#4\relax\leavevmode\vtop{\vskip-\@tempdima\box0\vss}}
\@ifundefined{bigstrutjot}{\newdimen\bigstrutjot \bigstrutjot\jot}{}
\makeatother
\begin{document}
\begin{tabular}{|lll|}
  \hline
   A & \multirow{2}[1.2]{*}{\color{red}%
        \begin{tabular}{|l|l|}%|
          A & A\bigstrut[t] \\
          \hline
          A & A\\
        \end{tabular}} &  A\bigstrut[t]\hlinestrut  \\
   A & {} & A \\
   A & A  & A \\
  \hline
\end{tabular}
\end{document}

To go fully back to the original example (with three \hlines), one final complication: the top \hlines of the two tables should overlap. First, we increase bigstruts to 1.6 (one \bigstrut and three \hlines). Second: the inner \hline is a part of the outer cell! So, if we do nothing, the inner table will be pushed 0.4pt down. (And this will be too close to the cell in the third row.) The solution is to use \multirow's fixup optional parameter, which shifts the cell. (Omitting this optional parameter [\arrayrulewidth] means that the inside table is not shifted to have their top lines overlap.)

bigstrut and three hlines

\documentclass{article}
\usepackage{bigstrut}
\usepackage{multirow}
\usepackage{xcolor}
\def\hlinestrut{\rule[\dimexpr-\dp\strutbox-\arrayrulewidth]{\dimexpr\dp\strutbox+\arrayrulewidth}{0pt}}
\makeatletter
\def\@xmultirow#1[#2]#3[#4]#5{\@tempcnta=#1%
  \@tempdima\@tempcnta\ht\@arstrutbox
  \advance\@tempdima\@tempcnta\dp\@arstrutbox
  \ifnum\@tempcnta<0\@tempdima=-\@tempdima\fi
  \advance\@tempdima#2\bigstrutjot
  \if*#3\setbox0\vtop to \@tempdima{\vfill\multirowsetup
    \hbox{\strut#5\strut}\vfill}%
  \else
      \setbox0\vtop to \@tempdima{\hsize#3\@parboxrestore
                \vfill \multirowsetup \strut#5\strut\par\vfill}%
  \fi
  \ht0\z@\dp0\z@
  \ifnum\@tempcnta<0\advance\@tempdima-\dp\@arstrutbox
  \else\@tempdima\ht\@arstrutbox
     %\ifnum#2>0 \advance\@tempdima\bigstrutjot \fi
     \ifdim#2pt>0pt \advance\@tempdima\bigstrutjot \fi
  \fi
  \advance\@tempdima#4\relax\leavevmode\vtop{\vskip-\@tempdima\box0\vss}}
\@ifundefined{bigstrutjot}{\newdimen\bigstrutjot \bigstrutjot\jot}{}
\makeatother
\begin{document}
\begin{tabular}{|lll|}
  \hline
   A & \multirow{2}[1.6]{*}[\arrayrulewidth]{\color{red}%
        \begin{tabular}{|l|l|}%|
          \hline
          A & A\bigstrut[t] \\
          \hline
          A & A\\
          \hline
        \end{tabular}} &  A\bigstrut[t]\hlinestrut  \\
   A & {} & A \\
   A & A  & A \\
  \hline
\end{tabular}
\end{document}

Another complication arises when you use (parallel) \bigstruts but not in the first row. The thing is, \multirow automatically shifts the table for \bigstrutjot whenever the bigstruts argument is positive. However, this only makes sense if the \bigstrut occurs in the first row (I believe this is due to the fact that multirow uses \vtop top construct the cell content). So, in the situation described above, one must "unshift" the table using fixup, like so:

\begin{tabular}{|lll|}
  \hline
   A & \multirow{2}[1.6]{*}[\dimexpr\arrayrulewidth-\bigstrutjot]{\color{red}%
        \begin{tabular}{|l|l|}%|
          \hline
          A & A \\
          \hline
          A & A\bigstrut[t]\\
          \hline
        \end{tabular}} &  A\hlinestrut  \\
   A & {} & A\bigstrut[t] \\
   A & A  & A \\
  \hline
\end{tabular}