Vertical alignment in table: m-column, row size - problem in last column
In my opinion, it is a bug and should be reported. Tweak for the impatient: add one extra column with zero width and no padding. Remember to pre-pend \\
with &
on the problematic lines!
\documentclass{article}
\usepackage{array}
\begin{document}
\begin{tabular}{|m{0.18cm}|m{0.18cm}|m{0.18cm}|@{}m{0pt}@{}}
\hline
a & b & c &\\[2ex]
\hline
0 & 0 & 0 \\
\hline
\end{tabular}
\end{document}
The question as well as the answer by @tohecz indicate that there is at least one fundamental misconception about what the preamble tokens "m", "b", etc. do and how this relates to \\
and its optional argument.
The "m" token is not centering its material vertically in the available space!
The tokens "c", "l" and "r" produce horizontal cells (one line high) and their alignment point vertically is just the baseline of each cell. Now "p", "m" and "b" generate cells of a certain width and possibly several lines. Their vertical alignment is the first line, the middle of the box and the bottom line, respectively. So if we modify the example a bit and run:
\begin{tabular}{|p{0.18cm}|m{0.18cm}|b{0.18cm}|l|}
\hline
a\newline a & b\newline b & c\newline c\newline c& d \\
\hline
\end{tabular}
and what we get is
So this is all positioned relative to the baseline of the cell containing "d".
Now the optional argument of \\
only extends the space from the alignment point downwards it is not extending the size of the actual cells. Furthermore it is not actually adding this space but only making sure that there is at least that amount of space to the next row. So for example, in the above case \\[2ex]
would have no effect because the cell in the first column goes further down than this.
So coming back to the original question and its input: because each cell has only a single line they should all line up but the "m" would not have the effect of centering the content in the available space. Instead the extra space requested by \\[2ex]
would go after it (or should ... that's the bug).
So let's add an "l" column to avoid the bug and see what happens:
\begin{tabular}{|m{0.18cm}|m{0.18cm}|m{0.18cm}|l|}
\hline
a & b & c & d \\[2ex]
\hline
a\newline a & b & c & d \\[2ex]
\hline
\end{tabular}
Result is
The first row shows that "m" and "l" sit side by side if the "m" has only a single line inside and that the 2ex is between the baseline and the next row.
The second line has an "m" with 2 lines inside and there you see again that the 2ex goes from the row baseline to the next row and "m" is not vertically centered either.
So the suggestion to use an extra (kind of hidden row) like
\begin{tabular}{|m{0.18cm}|m{0.18cm}|m{0.18cm}|@{}m{0pt}@{}}
\hline
a & b & c & x\\[4ex] % x normally being a space
\hline
a\newline a & b & c & x\\[4ex]
\hline
\end{tabular}
is actually making use of the current bug, i.e., the "space" in that hidden question is actually pushed upwards so that the visible rows now appear more or less centered (which I made visible by putting in an "x" above):
So the moment this would get fixed the above trick no longer works.
So what is the bug?
The issue is very subtle. To make a table with rules work it is not possible to vertically skip downwards on \\[2ex]
, instead what happens is that in one of the row an invisible rule/box is placed that has a depth extending the normal depth by 2ex
. If you don't do this then any vertical rule gets a disruption in places where you add additional vertical space.
The downside of this is, that if a row has an unusual depth, any such exstra space may come out short because it is measured in relation to the "normal" depth.
In addition and that is the "bug" in array
putting this invisual strut at the end of the material in the last row, simply goes wrong for m-columns as they are vertically centered. Thus here you see the "c" moving upwards as the strut ends up in some sort of $\vcenter{... c \strut}$
instead of $\vcenter{... c}$\strut
.
The reason for this is the strange behavior of the underlying \halign
mechanism of TeX that is used to build tables: when we reach \\[2ex]
LaTeX has processed $\vcenter{... c
but not yet seen the remaining material that forms the column, i.e., }$
.
So \\[ex]
calculates and adds the strut and then tells TeX that the column is finished (using \cr
internally) and that results in TeX copying the remaining part of the column template (}$
...plus space and rule) into the input stream and bingo you end up with the strut inside vcenter
.
A possible fix would be to delay the strut placement to ensure that this happens only in the right part of the column template. That in turn is a bit tricky as by that time scoping will have changed values back etc. So this needs to be a global operation.
\documentclass{article}
\usepackage{array}
\makeatletter
\def\@classz{\@classx
\@tempcnta \count@
\prepnext@tok
\@addtopreamble{\ifcase \@chnum
\hfil
\d@llarbegin
\insert@column
\d@llarend\fmi@fix \hfil \or
\hskip1sp\d@llarbegin \insert@column \d@llarend\fmi@fix \hfil \or
\hfil\hskip1sp\d@llarbegin \insert@column \d@llarend\fmi@fix \or
$\vcenter
\@startpbox{\@nextchar}\insert@column \@endpbox $\fmi@fix
\or
\vtop \@startpbox{\@nextchar}\insert@column \@endpbox\fmi@fix \or
\vbox \@startpbox{\@nextchar}\insert@column \@endpbox\fmi@fix
\fi}\prepnext@tok}
\def\@mkpream#1{\gdef\@preamble{}\@lastchclass 4 \@firstamptrue
\let\@sharp\relax \let\@startpbox\relax \let\@endpbox\relax
\let\fmi@fix\relax
\@temptokena{#1}\@tempswatrue
\@whilesw\if@tempswa\fi{\@tempswafalse\the\NC@list}%
\count@\m@ne
\let\the@toks\relax
\prepnext@tok
\expandafter \@tfor \expandafter \@nextchar
\expandafter :\expandafter =\the\@temptokena \do
{\@testpach
\ifcase \@chclass \@classz \or \@classi \or \@classii
\or \save@decl \or \or \@classv \or \@classvi
\or \@classvii \or \@classviii
\or \@classx
\or \@classx \fi
\@lastchclass\@chclass}%
\ifcase\@lastchclass
\@acol \or
\or
\@acol \or
\@preamerr \thr@@ \or
\@preamerr \tw@ \@addtopreamble\@sharp \or
\or
\else \@preamerr \@ne \fi
\def\the@toks{\the\toks}}
\def\@xargarraycr#1{\gdef\fmi@fix
{\@tempdima #1\advance\@tempdima \dp\@arstrutbox
\vrule \@depth\@tempdima \@width\z@\global\let\fmi@fix\relax}\cr}
\let\fmi@fix\relax
\makeatother
\begin{document}
\begin{tabular}{|m{0.18cm}|m{0.18cm}|}
\hline
a&c \\[2ex]
\hline
0& 0 \\
\hline
\end{tabular}
\end{document}
As I said this is rather delicate so I'm not sure it is safe to make that type of change without breaking a lot of other stuff (after all array
is a really old package (25+ years) which is used quite a lot and that issue is in there from the beginning.
Anyway with the above change we now get:
which is what we should but perhaps not what people hoped when they thought they specify an "m" column.
Update
As of 2018-04 release of LaTeX (or even one release earlier) the actual bug has now been corrected as outline above. That means (unfortunately) that the trick suggested above and marked as the correct answer is not longer working, but as I explained it was based on a wrong assumption of what the specification of "m" is.