Vertical space after minipages

1,902

The explanation for your problem is quite complex. If you'd like, you can just skip down to the solution below.

Explanation

When TeX is typesetting paragraphs, it behaves roughly as follows: It first breaks the paragraph into lines (using a complex algorithm with a number of parameters). These lines placed into a horizontal box (or \hbox) and the boxes are placed on the main vertical list. The main vertical list holds all of the material from which pages are created.

TeX tries to make sure that successive horizontal boxes on the vertical list are spaced such that the baselines of each box are \baselineskip apart. We can use TeX to see this in action.

Here's some plain TeX (compile with tex or pdftex)

\showboxdepth=1
\showboxbreadth=10000
\tracingonline=1

\setbox0=\vbox{
First line\hfil\break
Second line
}
\showbox0
\end

When compiled, it prints out the contents of the \vbox inside which the paragraph has been broken into two lines. The \boxshow... parameters control how TeX prints the contents of the box. Here's the output.

> \box0=
\vbox(18.94444+0.0)x469.75499
.\hbox(6.94444+0.0)x469.75499, glue set 409.81047fil []
.\penalty 300
.\glue(\baselineskip) 5.05556
.\hbox(6.94444+0.0)x469.75499, glue set 420.31046fil []

This can be a little complicated to read at first, but what it's telling us is that \box0 is a \vbox that is 18.94444pt in height, 0pt in depth and 469.75499pt wide. That is, (height+depth)xwidth. Inside that we can see the two \hboxes corresponding to the two lines of the paragraph. Between them, there's a \glue that corresponds to \baselineskip. Note that the depth of the top box plus the glue plus the height of the bottom box is 12pt which is \baselineskip.

Now TeX also doesn't want successive boxes to get to close together to prevent them from overlapping. To prevent this, TeX will insert some space between the boxes when they are too close together. The exact algorithm is unimportant here, but the key is that we can disable this behavior by using \nointerlineskip.

Now you might be wondering how two boxes of text could ever be too close together. This can happen if you have a box with depth m on top of a box with height n such that m + n > \baselineskip.

So what does all of this have to do with your question? Well, TeX has three types of vertical boxes, \vbox, \vtop, and \vcenter. Let's ignore \vcenter since it's a bit strange. \vbox and \vtop differ in where they place their reference point—that is the point that corresponds to their baseline. A \vbox sets its reference point to be the reference point of the last box in the list (i.e., usually the baseline of the last line of the paragraph in the box). In contrast, a \vtop sets its reference point to be the reference point of the first box in the list (i.e., the baseline of the top line in the paragraph).

We can see this in action.

\noindent
\vbox{
    \hsize=1in
    Top line\hfill\break
    Bottom line
}
\vtop{
    \hsize=1in
    Top line\hfill\break
    Bottom line
}
\end

enter image description here

You can see how the bottom line of the left box is aligned with the top line of the right box.

We can print what these two boxes look like as we did before.

The left box:

\vbox(18.94444+0.0)x72.26999
.\hbox(6.94444+1.94444)x72.26999, glue set 16.4366fill []
.\penalty 300
.\glue(\baselineskip) 3.11111
.\hbox(6.94444+0.0)x72.26999, glue set 20.18652fil []

The right box:

\vbox(6.94444+12.0)x72.26999
.\hbox(6.94444+1.94444)x72.26999, glue set 16.4366fill []
.\penalty 300
.\glue(\baselineskip) 3.11111
.\hbox(6.94444+0.0)x72.26999, glue set 20.18652fil []

The key difference is in the height and depth of these two boxes (the top line of each listing).

This leads to a tricky problem. What happens if we use a \vtop and the next line on the page is just a normal line from a paragraph? Let's take a look.

\parindent=0pt
\leavevmode
\vtop{
    \hsize=1in
    Top line\hfill\break
    g Bottom line
}

Next line
\end

enter image description here

I suppressed indentation and started the second line with a g because it descends below the baseline. Let's look at the boxes that get constructed. (I wrapped everything in a \vbox and used \showbox to see the output. Thus, there is an extra outer vbox below. I increased \showboxdepth by 1 to see the next inner box.)

\vbox(28.83333+0.0)x469.75499
.\vbox(6.94444+13.94444)x72.26999
..\hbox(6.94444+1.94444)x72.26999, glue set 36.4366fill []
..\penalty 300
..\glue(\baselineskip) 3.11111
..\hbox(6.94444+1.94444)x72.26999, glue set 11.85318fil []
.\glue(\parskip) 0.0 plus 1.0
.\glue(\lineskip) 1.0
.\hbox(6.94444+0.0)x469.75499, glue set 429.75491fil
..\hbox(0.0+0.0)x0.0
..\tenrm N
..\tenrm e
..\tenrm x
..\tenrm t
..\glue 3.33333 plus 1.66666 minus 1.11111
..\tenrm l
..\tenrm i
..\tenrm n
..\tenrm e
..\penalty 10000
..\glue(\parfillskip) 0.0 plus 1.0fil
..\glue(\rightskip) 0.0

There are several pieces of note here:

  1. The second \vbox corresponds to our \vtop;
  2. A \parskip was inserted between paragraphs; and
  3. No \baselineskip was inserted between the \vtop and the following \hbox which contains Next line. Instead there is a \lineskip. The \lineskip comes from the fact that the depth of the \vtop and the height of the following \hbox are greater than \baselineskip. If TeX had tried to make their baselines \baselineskip apart, they would overlap.

Maybe now you can see the issue with your own code. The minipage with the [t] option produces a \vtop. Thus TeX isn't ensuring that the baseline of the bottom line in the minipage is \baselineskip away from the baseline of the following line. Instead, it's inserting \lineskip which isn't nearly enough space.

You might be thinking that the solution is to switch the minipage to [b], but then you'll have an analogous situation with the line above.

Solution

The normal method for dealing with this is to put a \strut in the last line in the \vtop, turn off interline skip (i.e., inhibit the \lineskip glue), and put a \strut in the first line of the next paragraph.

A \strut inserts a box (namely \box\strutbox) which has height + depth which is usually equal to \baselineskip. However, this isn't going to work in your case. The reason is \onehalfspacing changes the \baselineskip but doesn't touch \box\strutbox.

So what we need to do is end the longer box with a \vskip of the appropriate length, turn off interline skip, and use \strut. So what is the appropriate length? Well, we want \baselineskip = (height of \box\strutbox) + (depth of the last box in the \vbox) + (our explicit \vskip).

We can compute that by setting a temporary \skip register (I picked 0) to be \baselineskip - \ht\strutbox - \prevdepth where \prevdepth is the depth of the last box on the vertical list (so the last line of text).

Two final caveats.

  1. This doesn't work with minipage, I didn't investigate why.
  2. enumerate will add space above it normally. This is inhibited by minipage which executes \@setminipage. I understand how \@setminipage works, but I don't understand how to prevent this behavior just using enumitem's options. So instead, this code executes \@setminipage.

Here's the complete code.

\documentclass[a4paper,DIV=15,oneside,12pt]{scrartcl}
\usepackage[english]{babel}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{blindtext}
\usepackage{setspace}
\usepackage{enumitem}

\parskip 0pt
\parindent 0pt

\begin{document}
\pagestyle{empty}
\onehalfspacing

\blindtext\par
\noindent
\parbox[t]{0.6\textwidth}{
\blindtext

\skip0=\baselineskip
\advance\skip0 by-\prevdepth
\advance\skip0 by-\ht\strutbox\relax
\vskip\skip0
}\hfill
\parbox[t]{0.37\textwidth}{
\csname @setminipage\endcsname
\begin{enumerate}[leftmargin=*,label=\alph{*}),itemsep=0pt,partopsep=0pt,topsep=0pt, parsep=0pt]
\item Test 1
\item Test 2
\end{enumerate}
}
\par
\nointerlineskip
\strut\blindtext
\end{document}

enter image description here

Share:
1,902
Gotti91
Author by

Gotti91

Updated on June 29, 2020

Comments

  • Gotti91
    Gotti91 over 3 years

    I would like to integrate two minipages inside a text, keeping the line spacing. Using the \begin{minipage}[t] option, the space above the minipages is correct (green circle). But unfortunately, the space below is far too short (red circle). How can I adjust it to the "normal baselineskip"?

    enter image description here

    MWE:

    \documentclass[a4paper,DIV=15,oneside,12pt]{scrartcl}
    \usepackage[english]{babel} 
    \usepackage[utf8]{inputenc}
    \usepackage[T1]{fontenc} 
    \usepackage{blindtext}
    \usepackage{setspace}
    \usepackage{enumitem}
    
    \parskip 0pt
    \parindent 0pt
    
    \begin{document}
    \pagestyle{empty}
    \onehalfspacing
    
    \blindtext\par
    \begin{minipage}[t]{0.6\textwidth}
    \blindtext
    \end{minipage}\hfill
    %Minipage 2
    \begin{minipage}[t]{0.37\textwidth}
    %\centering\rule{3cm}{2cm}
    \begin{enumerate}[leftmargin=*,label=\alph{*}),itemsep=0pt,partopsep=0pt,topsep=0pt, parsep=0pt]
    \item Test 1
    \item Test 2
    \end{enumerate}
    \end{minipage}\par
    \blindtext
    \end{document}   
    
    • cfr
      cfr over 6 years
      Put them inside another minipage and set that with b, possibly.
    • David Carlisle
      David Carlisle over 6 years
      @cfr that wouldn't work
    • cfr
      cfr over 6 years
      Possibly not, then. @DavidCarlisle