4

I've come across a situation where a breakable tcolorbox is getting covered up by text that follows. I think the vertical stretchable space is lining up just right so that the page breaking and box breaking algorithms get confused. I realize that I could put a \clearpage after the box, but that requires that I notice that this has happened in a fairly long document (and if the box moves, then the \clearpage would be incorrect).

The following document:

\documentclass[10pt]{article}

\usepackage[textheight=7in,textwidth=320pt]{geometry}
\usepackage{tcolorbox}
\tcbuselibrary{breakable}

\newcommand{\onelineoftext}{this is just about one line of text after I make it long enough for my purposes}

\usepackage{lipsum}

\begin{document}

\lipsum[1-4]

\begin{tcolorbox}[size=minimal,breakable]
One\\Two\\Three
% the page breaks here
\begin{enumerate}
\item   one
\item   two
\item   three
\item   four
\end{enumerate}
Text
\begin{enumerate}
    \item   \onelineoftext\onelineoftext\onelineoftext
    \onelineoftext\onelineoftext\onelineoftext\onelineoftext
    \onelineoftext\onelineoftext\onelineoftext\onelineoftext
    \item   One\\two
    \item   Displayed equation
    \[\lim_{x\to0}\cos\cos\cos\cos\cos\frac00\]
    \item   \onelineoftext\onelineoftext\onelineoftext\onelineoftext
    \[\lim_x e^x\]
    One     
    \item   One\\two
    \[\frac00\]
    This text will be overwritten by the next paragraph and completely covered.
    I don't understand why.
    It seems to involve lots of vertical spacing that's just right,
    so I can't eliminate much else.
\end{enumerate}
\end{tcolorbox}
%\clearpage % solves this page, but not the underlying problem

\onelineoftext\onelineoftext\onelineoftext\onelineoftext
\end{document}

has the following at the bottom of the second page (with no bad boxes)

overwritten output

How can I prevent this overlap from happening, or at least have TeX warn me that something went wrong?

Update

From @muzimuzhiZ: Below is a reduced example showing this problem might be caused by too much shrinkable space in tcolorbox. Text after tcolorbox is in blue, in order to emphasis the overlapping.

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

\begin{document}
\lipsum[1-4]

\begin{tcolorbox}[size=minimal,breakable]
  \begin{enumerate}
    \item
    \lipsum[1-3]
    \vskip 60pt plus 0pt minus 100pt
    \lipsum[1-2]
  \end{enumerate}
\end{tcolorbox}

\color{blue}\lipsum[1]
\end{document}

enter image description here

7
  • I have not understood why you think you have to use \clearpage here and there, but these command here are not imperfect solutions but the problem. Just remove the first and the second.
    – Fran
    Jun 26, 2020 at 16:01
  • The first one does not appear in my actual code, but was needed to get the mwe to show the error. My real question is why does the text end up overlapping? It's almost like tcolorbox and latex are using two different page breaking algorithms to decide how tall the box should be.
    – Teepeemm
    Jun 26, 2020 at 16:07
  • Many things could cause the same error. The minimal working example is not to see the error, but the true origin of this error. If you show us a fake origin, then is a work for the crystal ball. You have to reduce your real document, eliminating everything you can, but without solving the error (and without add another) and only then change carefully the real text by dummy text. BTW, to this use the lipsum package. Simpler and better.
    – Fran
    Jun 26, 2020 at 16:58
  • @Fran My assumption was that what came before the page in question was less relevant, and would only make a longer example. But I have been able to remove the clearpage and still see the same output. I am aware of the utility of lipsum for a mwe, but it is too course grained for this purpose. Even deleting one of the five cos causes the output to adjust and there to be less severe overlap.
    – Teepeemm
    Jun 26, 2020 at 20:48
  • I guess the problem is caused by too much shrinkable space in tcolorbox. longtable package, which constructs a long and breakable tabular, encounters similar problem, and a workaround/solution for longtable given by David Carlisle can be seen here. Jun 26, 2020 at 22:12

2 Answers 2

3

Suprisingly, I have not encountered this situation before.

As commented before, there seems to be to much shrinkable space inside the box. The break algorithm detects that the box cannot be broken further, but the resulting last box seems to be too large to fit on the page (not really true here). So, the bounding box for the last box part is made smaller to fit on the page (we would get a nasty empty page otherwise).

Here, the reduction is way too large leading to the seen overlap.

Preferably, the break algorithm should be more cunning (?!), but at least I can offer a solution which inserts a \clearpage automatically:

\makeatletter
\def\tcb@split@force@last{%
  \tcb@split@setstate@last%
  \ifdim\tcb@h@total>\tcb@h@page\relax%
    \gdef\tcb@after@lastbox{\clearpage}%
    \tcbdimto\kvtcb@bbbottom{\kvtcb@bbbottom+\tcb@h@page-\tcb@h@total}%
  \fi%
}
\makeatother

This patch should do the trick. The only (minor) drawback is that it removes user code given by the after key. But, IMHO this key is intended for spacing after the box and is not relevant for a page break.

0

I've not found a way to prevent the overlap from happening, but I have figured out how to know if it occurs (kind of). The idea is to call \pdfsavepos so that \pdflastypos knows the end of the tcolorbox and the beginning of what comes after. Then we can compare them and type out a warning if something is wrong. The only downside is that using the tcolorbox box content addition hooks causes the content to shift by a fraction of a line.

The following document:

\documentclass{article}

\usepackage[breakable]{tcolorbox}
\usepackage{etoolbox}

\usepackage{lipsum}

\newcommand{\boxendpage}{-1}
\newcommand{\boxendypos}{-1}

\newcommand{\boxendat}[2]{%
 \renewcommand{\boxendpage}{#1}%
 \renewcommand{\boxendypos}{#2}%
}
\newcommand{\afterboxat}[3]{}%
\newcounter{warnlineno}
\AtEndDocument{%
 \renewcommand{\afterboxat}[3]{%
  \ifboolexpr{ test{ \ifnumequal{#1}{\boxendpage} }
           and test{ \ifnumgreater{#2}{\boxendypos} } }{%
   \setcounter{warnlineno}{#3}
   \addtocounter{warnlineno}{-1}
   \typeout{^^JLaTeX Warning: box overlaps on page #1, near line \arabic{warnlineno}.}%
  }{}%
 }%
}

\makeatletter
\newcommand{\markboxend}{%
    \unskip%
    \pdfsavepos%
    \write\@auxout{\string\boxendat{\thepage}{\the\pdflastypos}}%
}
\newcommand{\markafterbox}{%
    \pdfsavepos%
    \write\@auxout{\string\afterboxat{\thepage}{\the\pdflastypos}{\the\inputlineno}}%
}
\makeatother

\begin{document}

\lipsum[1-4]

\begin{tcolorbox}[size=minimal,breakable,
after upper=\markboxend,after lower=\markboxend,after=\markafterbox
]
  \begin{enumerate}
    \item
    \lipsum[1-3]
    \vskip 60pt plus 0pt minus 60pt
    \lipsum[1-2]
  \end{enumerate}
\end{tcolorbox}

\color{blue}\lipsum[1] % line 56

\end{document}

has the barely different output from above:

overlayed output

But the log file now says "LaTeX Warning: box overlaps on page 2, near line 56."

You must log in to answer this question.

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