[Tex/LaTex] Vertically aligning minipages, subfigures, and subtables (not with baseline)

captionsminipagesubcaptionvertical alignment

The goal

My goal is to align side-by-side figures, tables, and text in minipages according to the top, middle and bottom of the largest such minipage. This makes it different (I believe) from the related links and references at the bottom, which detail the baseline of the surrounding text. I don't know which figure is largest.

For example, consider the following figure:

enter image description here

Notice that A and C are aligned to the bottom of C and that the fourth image is aligned to the middle of C, the largest image.

The algorithm for this is quite straightforward:

  • put all of the minipage environments in a box, and measure the height
  • then set each minipage using \begin{minipage}[b][MAX HEIGHT][<alignment>] so that each one is 'anchored' to the bottom, each one has the same height (cruicial), and then the position of each contents can be specified (t, c, or b).

The problem

The problem I'm having is when I try to caption these objects, the numbering falls down. I get, for example, the following:

screenshot

Having read Is there a switch to turn off LaTeX counters from inside a box?, I thought I could fix it with \savecounters@ and restorecounters@.

The question

How can I modify my code so that the numbering of both figures and subfigures is as expected? Solutions would hopefully scale naturally to tables.

I'm very open to alternatives that accomplish the same idea; I have tried with the adjustbox package, for example, but no luck so far.

The MWE

% arara: pdflatex
% !arara: indent: {overwrite: yes}
\documentclass{article}
\usepackage{graphicx}
\usepackage[showframe=true,textwidth=12cm]{geometry}
\usepackage{amsmath}
\usepackage{subcaption}
\usepackage{environ}

\newsavebox{\vabox}
\makeatletter
\NewEnviron{verticallyaligned}{%
    % temporarily set \vamaxheight to nothing during the measurements
    \let\vamaxheight\relax
    % temporarily disable figure numbering
    \savecounters@
    % measure the height of the body
    \begin{lrbox}{\vabox}
        \BODY%
    \end{lrbox}%
    % turn figure numbering back on
    \restorecounters@
    % set the height of the minipage box
    \newlength{\vamaxheight}
    \setlength{\vamaxheight}{\ht\vabox}
    % output the body, which now contains the new height :)
    \noindent\makebox[\linewidth][c]{\mbox{}\hfill\BODY\hfill\mbox{}}%
}

\begin{document}

\begin{verticallyaligned}
    \noindent
    \begin{minipage}[b][\vamaxheight][b]{.10\textwidth}
        \centering
        \includegraphics[width=\textwidth]{example-image-a}
        \captionof{figure}{}
    \end{minipage}%
    \hfill
    \begin{minipage}[b][\vamaxheight][t]{.15\textwidth}
        \centering
        \includegraphics[width=\textwidth]{example-image-b}
        \captionof{figure}{}
    \end{minipage}%
    \hfill
    \begin{minipage}[b][\vamaxheight][t]{.20\textwidth}
        \centering
        \includegraphics[width=\textwidth]{example-image-c}
    \end{minipage}%
    \hfill
    \begin{minipage}[b][\vamaxheight][c]{.10\textwidth}
        \centering
        \includegraphics[width=\textwidth]{example-image}
    \end{minipage}%
    \hfill
    \begin{minipage}[b][\vamaxheight][t]{.20\textwidth}
        \centering
        \includegraphics[width=\textwidth]{example-image}
    \end{minipage}%
\end{verticallyaligned}

\begin{figure}[!htb]
    \begin{verticallyaligned}
        \begin{subfigure}[b][\vamaxheight][t]{.10\textwidth}
            \centering
            \includegraphics[width=\textwidth]{example-image-a}
            \caption{}
        \end{subfigure}%
        \hfill
        \begin{subfigure}[b][\vamaxheight][t]{.15\textwidth}
            \centering
            \includegraphics[width=\textwidth]{example-image-b}
            \caption{}
        \end{subfigure}%
    \end{verticallyaligned}
    \caption{}
\end{figure}

\begin{figure}[!htb]
    \begin{verticallyaligned}
        \begin{subfigure}[b][\vamaxheight][c]{.10\textwidth}
            \centering
            \includegraphics[width=\textwidth]{example-image-a}
            \caption{}
        \end{subfigure}%
        \hfill
        \begin{subfigure}[b][\vamaxheight][c]{.15\textwidth}
            \centering
            \includegraphics[width=\textwidth]{example-image-b}
            \caption{}
        \end{subfigure}%
    \end{verticallyaligned}
    \caption{}
\end{figure}
\end{document}

References

Best Answer

Here I introduce \pushfigure[alignment]{width}{content}{captioningof} to push figures onto a "stack", and then \popfigures to set them all on a line. In all cases, captioning should be done with \captionof for any figure being "pushed". Bottom alignment is the default.

The \pushfigure phase of the operation saves all the parameters, and measures the height (plus any depth) of the content. It saves the maximum height+depth of all the contents that have been pushed.

The \popfigures phase performs a \stackinset of the figure atop a rule of the maximum-content-height, using the saved alignment for the figure. This achieves proper vertical alignment of the figures. Any captioning that was specified is applied below the vertical \rule, which will keep all captions at the same height, regardless of content alignment.

An \hfill is inserted at the beginning and after each figure popped. All parameters (counters/lengths) are reset at the end of the "pop", to prepare for the next sequence of "push"es.

For this MWE, I reduced the value of \abovecaptionskip to 6pt, which I find more visually pleasing when employing subfigures.

EDITED to demonstrate tabular and \parbox content. REEDITED to account for the fact that bottom aligned tabular and \parbox boxes can still have depth, associated with descending letters on the bottom line.

% arara: pdflatex
% !arara: indent: {overwrite: yes}
\documentclass{article}
\usepackage{graphicx}
\usepackage[showframe=true,textwidth=12cm]{geometry}
\usepackage{amsmath}
\usepackage{subcaption}
\usepackage{stackengine,ifthen}
\newcounter{figstack}
\newcounter{figindex}
\newlength\fight
\newcommand\pushfigure[4][b]{%
  \stepcounter{figstack}%
  \expandafter\def\csname %
    figalign\romannumeral\value{figstack}\endcsname{#1}%
  \expandafter\def\csname %
    figwd\romannumeral\value{figstack}\endcsname{#2}%
  \expandafter\def\csname %
    figcontent\romannumeral\value{figstack}\endcsname{#3}%
  \expandafter\def\csname %
    figcap\romannumeral\value{figstack}\endcsname{#4}%
  \setbox0=\hbox{%
    \begin{minipage}{#2}#3\end{minipage}}%
  \ifdim\dimexpr\ht0+\dp0\relax>\fight\global\setlength{\fight}{%
    \dimexpr\ht0+\dp0\relax}\fi%
}
\newcommand\popfigures{%
  \setcounter{figindex}{0}%
  \hfill%
  \whiledo{\value{figindex}<\value{figstack}}{%
    \stepcounter{figindex}%
    \def\tmp{\csname figwd\romannumeral\value{figindex}\endcsname}%
    \begin{minipage}[t]{\tmp}%
      \centering%
      \stackinset{c}{}%
        {\csname figalign\romannumeral\value{figindex}\endcsname}{}%
        {\csname figcontent\romannumeral\value{figindex}\endcsname}%
        {\rule{0pt}{\fight}}\par%
      \csname figcap\romannumeral\value{figindex}\endcsname%
    \end{minipage}%
    \hfill%
  }%
  \setcounter{figstack}{0}%
  \setlength{\fight}{0pt}%
  \hfill%
}
\setlength\abovecaptionskip{6pt}
\begin{document}
\pushfigure{.1\textwidth}
           {\includegraphics[width=\textwidth]{example-image-a}}%
           {\captionof{figure}{}}
\pushfigure[t]{.15\textwidth}
           {\includegraphics[width=\textwidth]{example-image-b}}%
           {\captionof{figure}{}}
\pushfigure[t]{.2\textwidth}
           {\includegraphics[width=\textwidth]{example-image-c}}%
           {}
\pushfigure[c]{.1\textwidth}
           {\includegraphics[width=\textwidth]{example-image}}%
           {}
\pushfigure[c]{.2\textwidth}
           {\includegraphics[width=\textwidth]{example-image}}%
           {}
\noindent\popfigures

\begin{figure}[!htb]
\captionsetup{labelformat=parens}
  \pushfigure[t]{.1\textwidth}
             {\includegraphics[width=\textwidth]{example-image-a}}
             {\captionof{subfigure}{}}
  \pushfigure{.15\textwidth}
%             {\includegraphics[width=\textwidth]{example-image-b}}
             {\begin{tabular}[b]{ccc}a&b&c\\dd&e&fff\\%
               a&b&c\\x&yy& z\end{tabular}}
             {\captionof{subfigure}{}}
\popfigures\par
\captionsetup{labelformat=default}
\caption{}
\end{figure}

\begin{figure}[!htb]
\captionsetup{labelformat=parens}
  \pushfigure[c]{.1\textwidth}
             {\includegraphics[width=\textwidth]{example-image-a}}
             {\captionof{subfigure}{}}
  \pushfigure[c]{.15\textwidth}
%             {\includegraphics[width=\textwidth]{example-image-b}}
             {\parbox{\textwidth}{%
                This is text and a bit more text and some more text}}
             {\captionof{subfigure}{}}
\popfigures\par
\captionsetup{labelformat=default}
\caption{}
\end{figure}
\end{document}

enter image description here


VERSION FOR TABLES

Directly analogous to figure version, but with \poptables (sub)captions placed above the figures. Note, though, that if table captions are multiline, the captions will be bottom-aligned with respect to other captions in the same row.

% arara: pdflatex
% !arara: indent: {overwrite: yes}
\documentclass{article}
\usepackage{graphicx}
\usepackage[showframe=true,textwidth=12cm]{geometry}
\usepackage{amsmath}
\usepackage{subcaption}
\usepackage{stackengine,ifthen}
\newcounter{tblstack}
\newcounter{tblindex}
\newlength\tblht
\newcommand\pushtable[4][b]{%
  \stepcounter{tblstack}%
  \expandafter\def\csname %
    tblalign\romannumeral\value{tblstack}\endcsname{#1}%
  \expandafter\def\csname %
    tblwd\romannumeral\value{tblstack}\endcsname{#2}%
  \expandafter\def\csname %
    tblcontent\romannumeral\value{tblstack}\endcsname{#3}%
  \expandafter\def\csname %
    tblcap\romannumeral\value{tblstack}\endcsname{#4}%
  \setbox0=\hbox{%
    \begin{minipage}{#2}#3\end{minipage}}%
  \ifdim\dimexpr\ht0+\dp0\relax>\tblht\global\setlength{\tblht}{%
    \dimexpr\ht0+\dp0\relax}\fi%
}
\newcommand\poptables{%
  \setcounter{tblindex}{0}%
  \hfill%
  \whiledo{\value{tblindex}<\value{tblstack}}{%
    \stepcounter{tblindex}%
    \def\tmp{\csname tblwd\romannumeral\value{tblindex}\endcsname}%
    \begin{minipage}[b]{\tmp}%
      \centering%
      \csname tblcap\romannumeral\value{tblindex}\endcsname\par%
      \stackinset{c}{}%
        {\csname tblalign\romannumeral\value{tblindex}\endcsname}{}%
        {\csname tblcontent\romannumeral\value{tblindex}\endcsname}%
        {\rule{0pt}{\tblht}}%
    \end{minipage}%
    \hfill%
  }%
  \setcounter{tblstack}{0}%
  \setlength{\tblht}{0pt}%
  \hfill%
}
\setlength\abovecaptionskip{6pt}
\begin{document}
\pushtable{.1\textwidth}
           {\includegraphics[width=\textwidth]{example-image-a}}%
           {\captionof{table}{}}
\pushtable[t]{.15\textwidth}
           {\includegraphics[width=\textwidth]{example-image-b}}%
           {\captionof{table}{}}
\pushtable[t]{.2\textwidth}
           {\includegraphics[width=\textwidth]{example-image-c}}%
           {}
\pushtable[c]{.1\textwidth}
           {\includegraphics[width=\textwidth]{example-image}}%
           {}
\pushtable[c]{.2\textwidth}
           {\includegraphics[width=\textwidth]{example-image}}%
           {}
\noindent\poptables

\bigskip\begin{table}[!htb]
\captionsetup{labelformat=default}
\caption{}
\captionsetup{labelformat=parens}
  \pushtable[t]{.1\textwidth}
             {\includegraphics[width=\textwidth]{example-image-a}}
             {\captionof{subtable}{}}
  \pushtable{.15\textwidth}
%             {\includegraphics[width=\textwidth]{example-image-b}}
             {\begin{tabular}[b]{ccc}a&b&c\\dd&e&fff\\%
               a&b&c\\x&yy& z\end{tabular}}
             {\captionof{subtable}{}}
\poptables\par
\end{table}

\begin{table}[!htb]
\captionsetup{labelformat=default}
\caption{}
\captionsetup{labelformat=parens}
  \pushtable[c]{.1\textwidth}
             {\includegraphics[width=\textwidth]{example-image-a}}
             {\captionof{subtable}{}}
  \pushtable[c]{.15\textwidth}
%             {\includegraphics[width=\textwidth]{example-image-b}}
             {\parbox{\textwidth}{%
                This is text and a bit more text and some more text}}
             {\captionof{subtable}{}}
\poptables\par
\end{table}
\end{document}

enter image description here


Now the tricky part is if one wants table and figures on the same row. The macro \popfigures achieves its vertical alignment by using top-aligned minipages. The \poptables does it by using bottom-aligned minipages. Neither of these approaches will work if a row must accommodate both top and bottom captioning.

That problem has not yet been solved.