6

I have frequent enumerated lists where the items in the list are of uneven heights, normally because of display-math items like dfrac. (Yes, I'm aware of the typographic issues with display math presented inline, but this layout is a requirement for the document type I'm preparing.) To give the list even spacing, I'm currently using vphantom, as shown in the MWE below:

\documentclass{article}
\usepackage{enumitem}
\usepackage{mathtools}
\begin{document}

\begin{enumerate}[label=\Alph*)]
    \item $x = y^2 + 4y + 13 \vphantom{\sqrt{\dfrac{x-4}{2}}}$
    \item $x = 2\left(y + 1\right)^2 + 4 \vphantom{\sqrt{\dfrac{x-4}{2}}}$
    \item $y = 2\left(x + 1 \right)^2 - 4 \vphantom{\sqrt{\dfrac{x-4}{2}}}$
    \item $y = -\sqrt{\dfrac{x-4}{2}}+2$
\end{enumerate}

\end{document}

What I would like to do is dispense with the end-user needing to remember to add vphantom all the time. As best as I can tell, I need a command that will keep track of the height of each line in the list, save the largest one to the aux file, and insert a strut in each item that takes its size from that saved value. Is there a package that already does this? Or alternatively, how would I create such a command? (I still haven't mastered the the aux file.)

Edit:

Heiko's solution gave me the clue I needed to finish the job. Here's what I did to wrap the whole thing so that I don't have to fuss with definitions each time:

\documentclass{article}
\usepackage{enumitem}
\usepackage{mathtools}
\usepackage{etoolbox}
\usepackage{xparse}

\newcommand{\choicestext}{}
\newcommand{\phantomtext}{}

\newcommand{\choice}[1]{%
    \xappto\choicestext{\unexpanded{\item #1\vph}}%
    \xappto\phantomtext{\unexpanded{\{#1\}}}
}

\newenvironment{choices}{%
    \renewcommand{\choicestext}{}%
    \renewcommand{\phantomtext}{}%
}{%
    \def\vph{\vphantom{\smash[b]\phantomtext}}%
    \begin{enumerate}[label=\Alph*)]
        \choicestext
    \end{enumerate}
}

\begin{document}

\begin{choices}
    \choice{$x = y^2 + 4y + 13$}
    \choice{$x = 2\left(y + 1\right)^2 + 4$}
    \choice{$y = 2\left(x + 1 \right)^2 - 4$}
    \choice{$y = -\sqrt{\dfrac{x-4}{2}}+2$}
\end{choices}

\end{document}
3
  • A simple and flexible (partially-automated) solution would be to define a \newcommand*{\MaxSize}{\vphantom{\sqrt{\dfrac{x-4}{2}}}} and i modify \item to insert \MaxSize on each line so that you don't have to manually add this. Aug 19, 2017 at 23:22
  • My problem is that I have many different lists in a document (these are test questions), and so the relevant \MaxSize will be different for each question. In other words, no single, predefined value is going to work for the whole document.
    – Karl Hagen
    Aug 19, 2017 at 23:25
  • Yep, each list would redefine \MaxSize. Thus partially-automated Aug 19, 2017 at 23:27

1 Answer 1

10

The formulas can be put in locally defined macros. Then, \vphantom just uses all of them without the need to know, which formula has the largest height and depth.

\documentclass{article}
\usepackage{enumitem}
\usepackage{mathtools}
\begin{document}

\begin{enumerate}[label=\Alph*)]
    \def\fa{$x = y^2 + 4y + 13$}
    \def\fb{$x = 2\left(y + 1\right)^2 + 4$}
    \def\fc{$y = 2\left(x + 1 \right)^2 - 4$}
    \def\fd{$y = -\sqrt{\dfrac{x-4}{2}}+2$}
    \def\vph{\vphantom{\fa\fb\fc\fd}}
    \item\fa\vph
    \item\fb\vph
    \item\fc\vph
    \item\fd\vph
\end{enumerate}

\end{document}

Result

As can be seen in the image, using the largest formula wastes lots of vertical space, because the depth of the fourth formula is quite large and not relevant for the spacing between the formulas. This can be fixed by:

\documentclass{article}
\usepackage{enumitem}
\usepackage{mathtools}
\begin{document}

\begin{enumerate}[label=\Alph*)]
    \def\fa{$x = y^2 + 4y + 13$}
    \def\fb{$x = 2\left(y + 1\right)^2 + 4$}
    \def\fc{$y = 2\left(x + 1 \right)^2 - 4$}
    \def\fd{$y = -\sqrt{\dfrac{x-4}{2}}+2$}
    \def\vph{\vphantom{\smash[b]{\fa\fb\fc\fd}}}
    \item\fa\vph
    \item\fb\vph
    \item\fc\vph
    \item\fd\vph
\end{enumerate}

\end{document}

Result

As the previous example shows, the approach in using the same maximum height and depth for all formulas usually wastes space. The better goal is to optimize the space between the formulas, the sum of the depth with the following height, or in other words \baselineskip.

The line spacing algorithm of TeX can be exploited: If two lines are too narrow (\lineskiplimit), then TeX does not use \baselineskip for even spacing, but inserts \lineskip instead. The following algorithm sets the stuff two times with different settings of \lineskip to detect uneven spacing. Then, \baselineskip is increased until it is large enough for even spacing.

With this approach, the code to be evenly spaced should not set \baselineskip or \lineskip explicitly, otherwise the optimization will fail.

\documentclass{article}
\usepackage{enumitem}
\usepackage{mathtools}

\makeatletter
\newif\ifEvenSpacing@Uneven
\newcommand*{\EvenSpacing}[1]{%
  \par
  \begingroup
    \EvenSpacing@Try{#1}%
    \@whilesw\ifEvenSpacing@Uneven\fi{%
      \advance\baselineskip by .1pt
      \advance\normalbaselineskip by .1pt
      \EvenSpacing@Try{#1}%
    }%
    #1\par
  \endgroup
}
\newcommand*{\EvenSpacing@Try}[1]{%
  \lineskip=0pt
  \normallineskip=0pt
  \settototalheight{\dimen0}{\parbox{\linewidth}{#1}}%
  \lineskip=1pt
  \normallineskip=1pt
  \settototalheight{\dimen2}{\parbox{\linewidth}{#1}}%
  \ifdim\dimen0=\dimen2
    \EvenSpacing@Unevenfalse
  \else
    \EvenSpacing@Uneventrue
  \fi
}
\makeatother

\begin{document}

\EvenSpacing{\begin{enumerate}[label=\Alph*)]
  \item $x = y^2 + 4y + 13$
  \item $x = 2\left(y + 1\right)^2 + 4$
  \item $y = 2\left(x + 1 \right)^2 - 4$
  \item $y = -\sqrt{\dfrac{x-4}{2}}+2$
\end{enumerate}}

\end{document}

Result

The algorithm can be improved by using exponential search until the \baselineskip is too large. Then, binary search finds the optimal \baselineskip.

3
  • +1 Check \ht and \settototalheight too. (Just for similar questions-searches with figures, tikz or whatever \vphantom will not be a solution) -I maen that somebody may find this QA in future but he could be looking to real calculate the height and not to just use the value of height in his text-
    – koleygr
    Aug 20, 2017 at 0:02
  • Your third solution is awesome, and I may have use for it at some point. I do, though, alter \baselineskip in my documents, so for the time being, I've adapted your second version, as it works well for my purposes.
    – Karl Hagen
    Aug 20, 2017 at 0:30
  • @KarlHagen The second solution is not a general solution. If you want to exclude the first height and latest depth you would need something like \vphantom{\smash[t]{\fa\fb\fc}\smash[b]{\fb\fc\fd}} for the formulas \fa to \fd. Still, the spacing can be larger than an optimized \baselineskip. Aug 20, 2017 at 0:47

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .