Enumerate in multicols how to move the last element upwards

Use the \raggedcolumns command. Based on a suggestion from David Carlisle in chat I've added a command the adjust the spacing after the enumerate environment. I'm not sure how this works, or why the problem arises in the first place.

\documentclass{article}
\usepackage[a5paper]{geometry}
\usepackage{multicol}
\setlength{\parindent}{0cm}
\newcommand{\fixspacing}{\vspace{0pt plus 1filll}\mbox{}}
\begin{document}
\raggedcolumns
\begin{multicols}{2}
  \begin{enumerate}
  \item text 1
  \item text 2
  \item text 3
  \item text 4
  \item text 5
  \end{enumerate}
  \fixspacing
\end{multicols}
\end{document}

output of code


The normal behavior of the multicols environment is to balance the columns so that they are all of (roughly) equal vertical length. If a the columns contain vertical stretchable space (as it happens in the example) then multicols will use this stretch or shrink component as part of the balancing process. Especially in the last column this may result in some visible holes, es long as the result is still technically within the bounds of specified "badness".

The suggested fix (by David and Alan) in the other answer adds strongly vertical stretchable space at the end of the last column and therefore the other space(s) do not expand.

There is actually a different (and possibly simpler) way to get to the same result: multicols has the parameter \finalcolumnbadness which decides whether or not the last column is being vertically justified or set at its natural height. The idea is that if the last column would get a "badness" > \finalcolumnbadness then justification is abandomed and the column is set to its natural height (i.e., the suggested fix is actually applied internally). Thus all that would be necessary is to set this parameter to a suitable value, e.g.

 \setcounter{finalcolumnbadness}{1000}

would achieve the same as \fixspacing in the other answer.

However, neither is giving the right result if you look closely, the position of text 5 is slightly lower than text 2 in the first column. (I added some rules to make this better visible):

enter image description here

So why is this the case?

We have to think about how multicols does its balancing:

  • It starts with by calculating a minimal column height based on the material it has to balance (in fact it starts a bit lower).
  • Then it cuts off all columns to this height (except for the last).
  • Then it looks at the height of the remaining material. If it is smaller than the height of the other columns - fine.
  • If not, it looks if it is possible to squeeze that material into the wanted column height. If that does work and the resulting badness is not too "bad" (see \finalcolumnbadness above) then the trial is considered a success. There are a few other tests that are done, but for the purpose of this discussion they are unimportant.
  • If the trail was a failure then multicols starts another trial, this time enlarging the trial height by 1pt. And eventually this will result in a successful trial.

So what is the problem with this approach?

The issue is that the moment the first column contains some strechable or shrinkable space (and it does in our example) it is more than likely that a solution will be found where the first column is not set at its natural height, but slightly compressed or stretched. However, with aboce fix will set the last column at its natural height and ergo, the baselines will not end up in the same positions.

Now can anything be done about this?

Not generally if you think about it unless you devices a very complicated algorithm that looks at all columns and their badness and makes those values part of the success/failure. But even then you can easily dream up examples that then result in problems or have no solution whatsoever.

But there is something that can be done which will help in many cases and this is implemented in the patch below.

The answer is simple:

  • Start the trial with a column height that is a multiple of \baselineskip (or rather that plus \topskip for the first line); this is already the case so nothing to be changed here.

  • But then increment not by a point but increment by \baselineskip

This gives a higher chance (not a guarantee) that columns are typeset at their natural height. The code for this looks like this:

\makeatletter
\newcommand\balanceincrement{\baselineskip}

\edef\reset@newlinechar{\newlinechar\the\newlinechar}\newlinechar-1 % hack needed for patching \balance@columns

\patchcmd{\balance@columns}{\advance\@tempdima\dp\mult@box}{}{\typeout{Success}}{\typeout{Fail!!}}
\patchcmd{\balance@columns}{\advance\dimen@-\p@}{\advance\dimen@-\balanceincrement\relax}{\typeout{Success}}{\typeout{Fail!!}}
\patchcmd{\balance@columns}{\advance\dimen@\p@}{\advance\dimen@\balanceincrement\relax}{\typeout{Success}}{\typeout{Fail!!}}

\reset@newlinechar
\makeatother

If we apply this to the example we get:

enter image description here

and that's much more like it should be. This would also help enormously for people who try to use grid based typesetting as now the results from a multicols gets predictable in height.

And if we want a different increment (or the old behavior back) then

\renewcommand\balanceincrement{1pt}

will do the trick.


Just add one extra \item with no text. That worked for me.