Given 3 columns, a tcolorbox box starts in column 2. Why?

Consider the following LaTeX document:

\documentclass{article}
\usepackage{lipsum,multicol}
\usepackage[breakable]{tcolorbox}
\begin{document}

\begin{multicols*}{3}
A is for Amy who fell down the stairs.

\begin{tcolorbox}[title={Lorem Ipsum},breakable]
\lipsum[1]
\end{tcolorbox}

B is for Basil assaulted by bears.
\end{multicols*}

\end{document}


This is how it is rendered as pdf:

Why does the box start in the second column? How can I force it to start following the paragraph in the first column?

A suggested explanation and my reservations thereto

According to Prof. Sturm's comment below, the behavior described above is not a bug; it results from the fact that

tcolorbox does not know the available space inside a multicols environment.

But then consider the modified document obtained from the original one by reducing the tcolorbox's contents to the first sentence of the paragraph. The resulting pdf is as follows:

If tcolorbox does not know the available space inside a multicols environment then why doesn't the tcolorbox start on the second column as before?

There are limits to what is possible in the interaction between two packages that both try to split a galley into columns without knowing much about each other, e.g., if multicol runs in balancing mode it does its balancing only after any tcolorbox inside has done its magic and thus will only see the breakpoints left by tcolorbox. Thus there is not much chance for automation unless the two packages are really interconnected (ie one implements the features of the other) which is not going to happen. So that means here one has to supply the breakpoints for tcolorboxmanually.

However, in the case of multicols* the situation is somewhat different as tcolorbox could know the available space. It does however simply assumes that it will be \textheight and therefore shows the undesired behavior. Don't think one can call this a bug but it is an unnecessary limitation given that tcolorboxalready tries to be multicol-aware.

Fact is that the available space is known (or could be): it is \@colroom minus whatever has been already typeset in the multicol environment, i.e., \pagetotal(as long as we are in the first column). If we are further down then some level of adjustement is needed but as the code below shows, it seems to be doable.

Now I don't know much about tcolorbox internals (in fact I have looked at the code for the first time today) so my "fix" may need some further adjustments, but the direction it probably ok.

So here is the code and test file:

\documentclass{article}
\usepackage{lipsum,multicol}
\usepackage[breakable]{tcolorbox}

\makeatletter
\newif\iftcb@latermulticol  % are we in a later column?

% remaining height
\def\tcb@comp@h@page{%
\tcb@breakat@next%
\ifdim\tcb@breakat@dim>0pt\relax%
\tcbdimto\tcb@h@page{\tcb@breakat@dim-\kvtcb@shrinkbreakgoal}%
\else%
\ifx\kvtcb@float\@empty%
\iftcb@multicol%
%
% currently tcolorbox always assumes that \textheight is available
%
%        \tcbdimto\tcb@h@page{\textheight-\kvtcb@shrinkbreakgoal}%
%
% but we do know what is available inside a multicol:
%
% it is \@colroom minus what is already set (which is \pagetotal)
%
% However this is only true for the first column as multicol scans all
% column material and only then breaks it into columns
% So for the next columns we need to tell tcolorbox that the value
% is \@colroom (without any substractions)
%
% Thus we distinguish the two cases via a boolean
%
\iftcb@latermulticol
\tcbdimto\tcb@h@page{\@colroom-\kvtcb@shrinkbreakgoal}%
\else
\tcb@latermulticoltrue
\ifdim\pagegoal=16383.99998pt
\tcbdimto\tcb@h@page{\@colroom-\kvtcb@shrinkbreakgoal}%
\else%
%
% If we start not in the first column then \pagetotal is already
% the material for more than a column so we substract \@colroom
% until we are in range (which is not quite accurate but close)
%
\@tempdima\pagetotal
\tcb@comp@compress%
\tcbdimto\tcb@h@page{\@colroom-\@tempdima+\tcb@compress@height -\kvtcb@shrinkbreakgoal}%
\fi%
\fi
%
% end of mod
%
\else%
\ifdim\pagegoal=16383.99998pt
\tcbdimto\tcb@h@page{\vsize-\kvtcb@shrinkbreakgoal}% detects floating objects
\else%
\tcb@comp@compress%
\tcbdimto\tcb@h@page{\pagegoal-\pagetotal+\tcb@compress@height-\kvtcb@shrinkbreakgoal}%
\fi%
\fi%
\else%
\tcbdimto\tcb@h@page{\textheight-\kvtcb@shrinkbreakgoal}%
\fi%
\fi%
}
\makeatother

\begin{document}

\begin{multicols*}{3}
A is for Amy who fell down the stairs.

\begin{tcolorbox}[title={Lorem Ipsum},breakable]
\lipsum[1]
\end{tcolorbox}

B is for Basil assaulted by bears.
\end{multicols*}

\begin{multicols*}{3}
Starting with tcolorbox in the second column \ldots
Starting with tcolorbox in the second column \ldots
Starting with tcolorbox in the second column \ldots
Starting with tcolorbox in the second column \ldots
Starting with tcolorbox in the second column \ldots
Starting with tcolorbox in the second column \ldots
Starting with tcolorbox in the second column \ldots

\lipsum[1]

\begin{tcolorbox}[title={Lorem Ipsum},breakable]
\lipsum[1]
\end{tcolorbox}

B is for Basil assaulted by bears.
\end{multicols*}

The balancing multicol of course still fails without manual help as
that would require both packages interact with each other

\begin{multicols}{3}
A is for Amy who fell down the stairs.

\begin{tcolorbox}[title={Lorem Ipsum},breakable]
\lipsum[1]
\end{tcolorbox}

B is for Basil assaulted by bears.
\end{multicols}

some more text

\end{document}


If we typeset this then we get the following three pages:

• Your patch sounds very promising to me. I will test it in the near future and I intend to incorporate it into tcolorbox, if no unexpected problems arise :-) – Thomas F. Sturm May 30 '17 at 15:41
• @ThomasF.Sturm it could probably be streamlined a bit as far as the logic is concerned. Also I haven't really checked what \tcb@comp@compress does, only guessed from the name :-) – Frank Mittelbach May 30 '17 at 19:44
• \tcb@comp@compress inserts (optionally) \pageshrink or something similar. Inside multicols, I would rather omit this for safety reasons. – Thomas F. Sturm May 31 '17 at 9:46
• I played with the code and some adaptions. For single page examples, the result is superior to the current implementation. But, if the example spreads over several pages and the space for multicol on the first page is lower than \textheight, a new problem arises. The first two boxes of the following page are too short since \@colroom still contains the space value from the (smaller) first page. Currently, my only idea is to detect the page break by labelling and to replace \col@room by \textheight on a page break. I'm open to any better idea ... – Thomas F. Sturm May 31 '17 at 9:56
• Unfortunately, I cannot find a stable solution for multi-pages where the first page contains some stuff before multicol. I have to postpone further experiments for at least a week, but I will come back to it later. – Thomas F. Sturm May 31 '17 at 10:43

Based on Frank Mittelbachs patch, I experimented a lot to get an algorithm which can do the breaking inside a multicols environment in a fully automated way. But I found too many situations which fool an automatism in a way which is worse than not having such an automatism. Meanwhile, I do not see any success in future.

Nevertheless, a half-automated breaking is possible which is superior to the old implementation. I adapted the patch of Frank Mittelbach and implemented a changed version for tcolorbox from version 4.10 (2017/07/05).

The main (new) characteristics are:

• The default break height is the current column height at begin of breaking. If the height column height changes from one page to the next, the default break height is not changed (actually, it changes, but not for the first column on the next page).

• The height of the first box part is estimated as in Frank Mittelbachs patch. There is a fair change that this estimation gives a too-large value causing the first box part to slip from the current column to the next one.

To compensate the possible named shortcuts, a break at can be added. break at also was modified to support easier corrections.

Let's look at some example code:

\documentclass{article}
\usepackage{lipsum,multicol}
\usepackage[showframe]{geometry}
\usepackage[breakable]{tcolorbox}

\begin{document}

\begin{multicols*}{3}
A is for Amy who fell down the stairs.

\begin{tcolorbox}[title={Lorem Ipsum},breakable]
\lipsum[1-2]
\end{tcolorbox}

B is for Basil assaulted by bears.
\end{multicols*}

\begin{multicols*}{3}
\lipsum[1-3]

\begin{tcolorbox}[title={Lorem Ipsum},breakable]
\lipsum[1]
\end{tcolorbox}

B is for Basil assaulted by bears.
\end{multicols*}

The two examples above worked without manual help, but this will be true
only for lucky situations. If a multicols environment starts on one page
with previous content (like this one) and continues to a second page,
the automatism will fail.
In this case, use the \texttt{break at} option:

\begin{multicols*}{3}
A is for Amy who fell down the stairs.
\lipsum[1-2]

\begin{tcolorbox}[title={Lorem Ipsum},breakable,height fixed for=first and middle,
break at=-\baselineskip/0pt/\textheight,
]
\lipsum[1-5]
\end{tcolorbox}

B is for Basil assaulted by bears.
\end{multicols*}

\end{document}


The first two multicols* environments with an embedded breakable tcolorbox did not need any manual corrections, but the third one is tricky. Here, the following was added to get the (good) result:

break at=-\baselineskip/0pt/\textheight


The meaning is as follows:

• -\baselineskip: The first column is sized according to the estimated space minus one \baselineskip, because the actual space is smaller than the estimated one. Prior to version 4.10 (2017/07/05), negative values had the same meaning as 0pt.

• 0pt: The second column is sized to get the current column height. Prior to version 4.10 (2017/07/05), 0pt meant \textheight.

• \textheight: The third column and all following columns are sized to \textheight, because on our example page 4 the height of the columns changes.

• Thank you very much. This is a great improvement. Two questions, though. (1) I get the impression that the settings break at=-\baselineskip/0pt/\textheight will normally be the ones the user would like to use, then why can't it be the default? My only guess is that your code isn't able to figure out the total number of columns and the ordinal number of the column where the tcolorbox begins. Then maybe you can add a simplified way of informing your code about this, e.g. col_pos=2/3 meaning that there are 3 column on this page and the tcolor box starts at the 2nd one. – Evan Aad Jul 6 '17 at 14:20
• (2) What holds you back from providing a fully automated solution? Is there anything that Frank Mittelbach can change in his implementation of multicols or in the API he exposes that would enable you to do it? Or is it something more fundamental than that? – Evan Aad Jul 6 '17 at 14:22
• @EvanAad As I said, a fully automated solution does not work stable. I invested a lot of time to tweak the algorithm, but the algortihm variants all fail too often. I think the problem is fundamental and neither Frank Mittelbach nor I can solve it. – Thomas F. Sturm Jul 6 '17 at 14:49
• @EvanAad break at=-\baselineskip/0pt/\textheight is a good start, but depending on the situation it may be break at=-2\baselineskip/0pt/\textheight, break at=-\baselineskip/0pt/0pt/\textheight, break at=-\baselineskip/\textheight, etc. ... – Thomas F. Sturm Jul 6 '17 at 14:51
• @EvanAad You never should say never :-) But the base of the problem is that a breakable tcolorbox+multicols technically is a \vsplit in a \vsplit which is not really supported by TeX. The same problem exists for a breakable tcolorbox inside a breakable tcolorbox which one should never use... – Thomas F. Sturm Jul 7 '17 at 15:18

It seems that a paragraph before the tcolorbox inside a multicol environment breaks the way tcolorbox computes the height of the first fragment and places it on the second column. I think this a bug but I'll wait for other comments/opinions. In the mean time the undesired behaviour can be avoid with a manually computed break at= option.

\documentclass{article}
\usepackage{lipsum,multicol}
\usepackage[breakable]{tcolorbox}
\begin{document}

\begin{multicols}{3}
A is for Amy who fell down the stairs.

\begin{tcolorbox}[title={Lorem Ipsum}, breakable, break at=18cm/0pt]
\lipsum[1-2]
\end{tcolorbox}

B is for Basil assaulted by bears.
\end{multicols}

\end{document}


• Thanks. What do you mean by 'I don't know what happens with your code'? Does my code work on your computer? – Evan Aad May 29 '17 at 10:21
• It's not a bug, it's just the way it is ... tcolorbox does not know the available space inside a multicols environment. And multicol does not know how to balance the break points for tcolorbox. Sadly, it has to be done manually. – Thomas F. Sturm May 29 '17 at 11:19
• @EvanAad Inside a multicol environment, the default assumed space is the \textheight. After the complete box is broken into several parts with \textheight and the remaining last one, the boxed are arranged by multicol. So, if the first break is adapted manually (if needed), the overall result is quite pleasing. Still, the first break remains to be set manually. – Thomas F. Sturm May 30 '17 at 5:34
• @ThomasF.Sturm as far as I can see it can be automated with a little bit more code in \tcb@comp@h@page – Frank Mittelbach May 30 '17 at 10:35
• @EvanAad No problem at all. In fact my answer was just a workaround that could work while waiting for better solutions. – Ignasi May 30 '17 at 12:47