5

I have the following table

\documentclass{article}
\usepackage{array}
\usepackage{lipsum}
\usepackage{booktabs}
\usepackage[a4paper]{geometry}
\geometry{left=1cm,right=1cm,top=1cm,bottom=1cm}

\begin{document}

\begin{table}[ht]
    \centering
    \begin{tabular}{
            |p{5cm}|m{5cm}|p{4cm}|
        }
        \hline
        Column 1 & Column 2 & Column 3 \\
        \hline
        align me at bottom & align me vertically & \lipsum*[3] \\
        \hline
    \end{tabular}%
\end{table}

\end{document}

What I would like to do is have the first column aligned at the bottom and the second centered vertically. I played around with m, b and p, but could not achieve what I want. I read somewhere those columns don't interact nicely with each other, so that might be the reason. Also if switching to tabularx or other packages helps, I am fine with that. Thanks for any hints.

3

This is a minor modification of AboAmmar's answer where one measures the height of the third column using a savebox.

\documentclass{article}
\usepackage{array}
\usepackage{lipsum}
\usepackage{booktabs}
\usepackage[a4paper]{geometry}
\geometry{left=1cm,right=1cm,top=1cm,bottom=1cm}

\begin{document}

\begin{table}[ht]

    \centering
    \begin{tabular}{|b{5cm}|l|l|}
        \hline
        Column 1 & Column 2 & Column 3 \\
        \hline
        \global\setbox0=\hbox{\parbox[b]{4cm}{\strut\lipsum*[3]}}% actually part of first column
        align me at bottom & \raisebox{0.5\ht0}{\parbox[c]{5cm}{align me vertically}} & \usebox0 \\
        \hline
        \global\setbox0=\hbox{\parbox[b]{4cm}{\strut\lipsum*[4]}}% actually part of first column
        align me at bottom & \raisebox{0.5\ht0}{\parbox[c]{5cm}{align me vertically}} & \usebox0 \\
        \hline
    \end{tabular}%
\end{table}

\end{document}
  • thanks that's great. I see you are saving the height in \sbox0{\parbox[b]{4cm}{\strut\lipsum*[3]}}. So to apply this for more rows, I would have to measure the height every time like this? Because in my real table, I am not having lipsum*[3], but rather large bulks of text. This would blow up my code if I am not mistaken atm. Still better than nothing if it works. – ghx Jun 15 at 14:19
  • I revised the solution to allow you to reuse \box0 in each row. Also, while the \vbox version was simpler, it wasn't quite right if the second column had more than one line. – John Kormylo Jun 16 at 16:05
  • Thanks out of all the solutions, this one seems to be the simplest and still generally useable – ghx Jun 16 at 19:12
4

Without measuring anything. The trick is to use a new line for the bottom-aligned text.

\documentclass{article}
\usepackage{array}
\usepackage{lipsum}
\usepackage{booktabs}
\usepackage{adjustbox}
\usepackage[a4paper]{geometry}
\geometry{left=1cm,right=1cm,top=1cm,bottom=1cm}

\begin{document}

\begin{table}[ht]
    \centering
    \begin{tabular}{
            |p{5cm}|m{5cm}|p{4cm}|
        }
        \hline
        Column 1 & Column 2 & Column 3 \\
        \hline
         & \adjustbox{valign=c,minipage=5cm}{align me
        vertically} & \adjustbox{valign=c,minipage=4cm}{\lipsum*[3]
        \vspace*{-0.9\baselineskip}}
        \\
         align me at bottom & & \\
        \hline
    \end{tabular}%
\end{table}
\end{document}

enter image description here

  • Thanks, this also seems to work. I upvoted you (but John was faster) – ghx Jun 16 at 19:13
3

The p, m, and b specifiers are a bit unintuitive, most beginners will think they are for absolute vertical alignment within the cell borders, whereas they are for relative alignment between columns. They can be described like this:

  • p means normal cells, they are like parbox with alignment at the top line,

  • b means alignment at the bottom, so the baseline is at the bottom line, and

  • m means alignment in the vertical center, i.e. the baseline is in the center.

For example, if one writes p{}m{}b{}, then the top of the cell in the first column and the middle of the cell in the second column and the bottom of the cell in the third column will all be at the same row (same height).

In your example, you can use b{} for all columns, then, to center-align the second column you need to know how high the tallest cell is and how high the current cell is. The tallest cell has a height of 29\baselineskips and the current cell has only 1 line, so you need a \vbox of approx. height (29-1)/2 = 14\baselineskip to write the contents of the cell inside.

\documentclass[a4paper]{article}
\usepackage{array}
\usepackage{lipsum}
\usepackage{booktabs}
\usepackage[margin=1cm]{geometry}

\begin{document}

\begin{table}[ht]
    \centering
    \begin{tabular}{|b{5cm}|b{5cm}|b{4cm}|}
        \hline
        Column 1 & Column 2 & Column 3 \\
        \hline
        align me at bottom & \vbox to 14\baselineskip{align me vertically} & \lipsum*[3]  \\
        \hline
    \end{tabular}%
\end{table}

\end{document}

enter image description here

  • 1
    thanks a lot for the explanation, that helps. However, the height will not always be 29\baselineskip, will it? So a more generally applicable solution would have to calculate the height of the tallest and the current cell I assume? The issue is that my real table is much larger, and has many rows, so the height is varying per row/case. – ghx Jun 15 at 12:37
3

LaTeX's tabular is based on the \halign TeX primitive, however this task is more naturally suited to \valign:

\documentclass{article}
\usepackage{lipsum}

\newcommand*{\firstcolbox}{\hbox to 5cm }
\newcommand*{\secondcolbox}{\hbox to 5cm }

\newlength{\thirdcolwidth}
\setlength{\thirdcolwidth}{4cm}

\begin{document}
\noindent
\hbox{\openup0pt
\vrule % left of the table
\valign{\tabskip=0pt
        \hrule#&\vfil#&\hrule#&\kern 2pt#&\vfil#\vfil&\kern 2pt#&\hrule#\tabskip=0pt\cr
% 2pt = padding space left of the first column text (right of \vrule)
width 2pt&&width 2pt&&&&width 2pt\cr
&\kern2pt \firstcolbox{\strut Column 1\hss}\kern2pt &&& \vfill\firstcolbox{align me at bottom\hss}&&\cr\noalign{\vrule}
width 2pt&&width 2pt&&&&width 2pt\cr
&\kern2pt \secondcolbox{\strut Column 2\hss}\kern2pt &&& \secondcolbox{align me at center\hss}&&\cr\noalign{\vrule}
width 2pt&&width 2pt&&&&width 2pt\cr
&\kern2pt \hbox to \thirdcolwidth{\strut Column 3\hss}\kern2pt &&&
\vbox{\sloppy\hsize=\thirdcolwidth \noindent\strut\lipsum*[3]\strut}&&\cr
% 2pt = padding space right of the last column text (left of \vrule)
width 2pt&&width 2pt&&&&width 2pt\cr
}% of \valign
\vrule % right of the table
}
\end{document}

The syntax is “a bit” heavy, I admit; read chapter 22 of the TeXbook and you'll see this is the norm for alignments written using TeX primitives, especially when rules have to be added everywhere. The advantage of this approach is that there is no kludge of counting lines, relying on the last column text to always be higher, using approximate negative \vskips, etc.

Screenshot

Here is the same, but with all the alignment code wrapped inside an environment defined with xparse. You can use it without looking inside, it's less scary this way. Also, it should be quite useful if you have several tables that require the same presentation.

\documentclass{article}
\usepackage{xparse}
\usepackage{lipsum}

\ExplSyntaxOn

% Default values
\tl_set:Nn \l_ghx_special_array_pre_text_tl  { \ignorespaces }
\tl_set:Nn \l_ghx_special_array_post_text_tl { \unskip }

\cs_new_protected:Npn \ghx_lrbox:nn #1#2
  {
    \hbox_to_wd:nn {#1} { \strut #2 \hfil }
  }

% #1: declarations used at the start of the text in the third column
% #2: width of first column
% #3: width of second column
% #4: width of third column
% #5: header of first column
% #6: header of second column
% #7: header of third column
% #8: text in first column (typeset in LR mode)
% #9: text in second column (typeset in LR mode)
\cs_new_protected:Npn \ghx_start_special_array:nnnnnnnnn #1#2#3#4#5#6#7#8#9
  {
    \hbox \bgroup
    \openup 0pt
    \vrule         % left of the table
    \valign \bgroup
    % Preamble
    \tabskip=0pt \hrule## & \vfil## & \hrule## & \kern 2pt## &
      \vfil##\vfil & \kern 2pt## & \hrule## \tabskip=0pt\cr
    % First “column” (little \hrules for embellishment [“before” padding])
    % 2pt = padding space left of the first column text (right of \vrule)
    width 2pt && width 2pt &&&& width 2pt\cr
    % First real column
    & \kern2pt \ghx_lrbox:nn {#2} {#5} \kern2pt &&&
      \vfill \ghx_lrbox:nn {#2} {#8}&& \cr \noalign{\vrule}
    % Padding
    width 2pt && width 2pt &&&& width 2pt\cr
    % Second real column
    & \kern2pt \ghx_lrbox:nn {#3} {#6} \kern2pt &&&
      \ghx_lrbox:nn {#3} {#9} && \cr \noalign{\vrule}
    % Padding
    width 2pt && width 2pt &&&& width 2pt\cr
    % Start of the third real column
    & \kern2pt \ghx_lrbox:nn {#4} {#7} \kern2pt &&&
      \vbox \bgroup \hsize=#4 \relax #1 \strut
  }

\cs_new_protected:Npn \ghx_stop_special_array:
  {
    % Finish the third real column
    \strut \egroup % of \vbox
    && \cr
    % 2pt = padding space right of the last column text (left of \vrule)
    width 2pt && width 2pt &&&& width 2pt\cr
    \egroup                 % of \valign
    \vrule                  % right of the table
    \egroup                 % of \hbox
  }

\NewDocumentEnvironment { myspecialarray }
  { O{\sloppy\noindent} m m m m m m m m }
  {
    \ghx_start_special_array:nnnnnnnnn
      {#1} {#2} {#3} {#4} {#5} {#6} {#7} {#8} {#9}
    \tl_use:N \l_ghx_special_array_pre_text_tl
  }
  {
    \tl_use:N \l_ghx_special_array_post_text_tl
    \ghx_stop_special_array:
  }

\ExplSyntaxOff

\begin{document}
\noindent
\begin{myspecialarray}{5cm}{5cm}{4cm}{Column 1}{Column 2}{Column 3}%
                      {align me at bottom}{align me at center}
  \lipsum*[3]
\end{myspecialarray}
\end{document}

(same screenshot as before, thus not adding any)

  • Thanks, this seems to be the most powerful of all solutions (the way to go in TeX I assume), but the syntax is also most complicated, that's why I am preferring John's way atm. – ghx Jun 16 at 19:15
  • 1
    Okay, I understand. You may want to have a look at my last edit, though: I wrapped the code inside an environment, so it's very easy to use if you have several tables using the same presentation. – frougon Jun 16 at 21:14
2

Using cals, such tabulars are easy to set up:

\documentclass{article}
\usepackage{ragged2e, cals, lipsum}


\begin{document}

\begin{table}[ht]

\begin{calstable}[c] % Centre the tabular

% Defining columns relative to the margin 
\colwidths{{\dimexpr(\columnwidth)/14*5\relax}
            {\dimexpr(\columnwidth)/14*5\relax}
            {\dimexpr(\columnwidth)/14*4\relax}
            }

% Set up the tabular
\makeatletter
\def\cals@framers@width{0.4pt}   % Outside frame rules, reduce if the rule is too heavy
\def\cals@framecs@width{0.4pt}
\def\cals@cs@width{0.4pt}             % Inside rules, reduce if the rule is too heavy
\def\cals@rs@width{0.4pt}

% R1
\thead{\bfseries
\brow
    \alignL\cell{\vfil Column 1}
    \cell{\vfil Column 2}
    \cell{\vfil Column 3}
\erow
\mdseries
}
% R2 Body
\brow
    \cell{\vfill Align me at bottom}
    \cell{\vfil Align me vertically}
    \cell{\RaggedRight\lipsum*[3]}   % Ragged right looked better
\erow
\makeatletter
\end{calstable}\par % \par to align the tabular
\end{table}

\end{document}

enter image description here

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.