The following patch can be used for tcolorbox
version 3.32
and prior. It is incorporated in tcolorbox
version 3.33 (2014/12/11)
and higher. To revert to the old behavior before 3.33
, the option ignore nobreak
can be applied.
As I wrote in my comments, I fear that there is no global solution for the problem. But I experimented with a patch which might be helpful for many use cases. The patch code tries to estimate the space needed after a heading. If it is very probable that the box or the first part of the box does not fit on the current page, the \vspace{...}\null\vspace{-...}
trick of yours is applied to move everything to the next page including the heading.
The space estimation can fail especially, if the box contains a large unbreakable content as in your example. In this case, a warning is given to detect such problems in long documents. One manual solution is to enlarge the lines before break
value to match the unbreakable content. Currently, I do not see an automatic way.
The following code is an experimental patch. If it prooves to be stable, I want to put it into an official version, but there have to be many tests before that. If you see (new) problems with it or if you can improve it, feel free to comment. The main part is \tcb@handle@nobreak
, the rest is some slightly changed current code.
\documentclass[]{article}
\usepackage[many]{tcolorbox}
\usepackage{lipsum}
\tracingpages=1
\makeatletter
\def\tcb@do@nobreak@warning{%
\tcb@warning{Using nobreak failed. Try to enlarge `lines before break' or set page breaks manually}%
}
\def\tcb@handle@nobreak{%
\tcb@enlbreak@init%
\tcb@breakat@init%
\tcb@comp@h@page%
\tcbdimto\tcb@h@page{\tcb@h@page-\baselineskip}% security surcharge
\tcb@comp@h@total@standalone%
\tcb@check@for@final@box%
\iftcb@final@box%
\else%
\ifdim\dimexpr\tcb@h@page-\tcb@h@padding-\tcb@h@padtitle<\kvtcb@breakminlines\baselineskip\relax%
\tcbdimto\tcb@h@page{\tcb@h@page+3\baselineskip}%
\vspace{\tcb@h@page}%
{\parskip\z@\nointerlineskip\noindent}%
\vspace{-\tcb@h@page}%
\fi%
\fi%
}
\def\tcb@prepare@break@operation{%
\let\tcb@nobreak@warning\@empty%
\@ifundefined{columnbreak}{\tcb@multicolfalse}{%
\ifnum\doublecol@number>\z@ %
\tcb@multicoltrue%
\else%
\tcb@multicolfalse%
\fi}%
\ifx\kvtcb@float\@empty%
\if@nobreak%
\tcb@handle@nobreak%
\let\tcb@nobreak@warning\tcb@do@nobreak@warning%
\else%
\kvtcb@beforebox%
\fi%
\def\kvtcb@beforebox{\parskip\z@\nointerlineskip\noindent}%
\noindent% applies parskip
\vskip\lineskip{\parskip\z@\par\vskip\z@}\bgroup\@nobreakfalse\addpenalty\z@\egroup% force update vertical list
\tcb@break@allowedtrue%
\tcb@enlbreak@init%
\else%
\tcb@break@allowedfalse%
\fi%
}
\def\tcb@split@start{%
\tcb@breakat@init%
\tcb@comp@h@page%
% height calculation
\tcb@comp@h@total@standalone%
%
\let\tcb@split@next=\relax%
\tcb@check@for@final@box%
\iftcb@final@box%
\tcb@drawcolorbox@standalone%
\else
\iftcb@break@allowed%
\ifdim\dimexpr\tcb@h@page-\tcb@h@padding-\tcb@h@padtitle<\kvtcb@breakminlines\baselineskip\relax%
\iftcb@multicol\columnbreak\else\pagebreak\fi%
\tcb@nobreak@warning%
\tcb@enlbreak@init\tcb@break@allowedfalse%
\tcb@comp@h@page%
\tcb@check@for@final@box%
\iftcb@final@box%
\tcb@drawcolorbox@standalone%
\else
\let\tcb@split@next=\tcb@split@first%
\fi
\else%
\let\tcb@split@next=\tcb@split@first%
\fi
\else%
\let\tcb@split@next=\tcb@split@first%
\fi%
\fi%
\tcb@split@next%
}
\def\tcb@split@first{%
\setcounter{tcbbreakpart}{1}%
\ifx\kvtcb@float\@empty%
\iftcb@vfillbeforefirst%
\let\tcb@before@first=\vfill%
\else%
\let\tcb@before@first=\@empty%
\fi%
\let\tcb@before@middle=\@empty%
\let\tcb@before@last=\@empty%
\iftcb@multicol%
\long\def\tcb@after@first{\par}% using \columnbreak here kills multicols' algortihm sometimes
\long\def\tcb@after@middle{\par\vfill}%
\else%
\long\def\tcb@after@first{\par\pagebreak\tcb@enlbreak@next}%
\long\def\tcb@after@middle{\par\vfill\eject\tcb@enlbreak@next}%
\fi%
\let\tcb@after@last=\par%
\else%
\edef\tcb@before@first{\noexpand\tcb@float@env@begin{tcbfloat}[\kvtcb@float]\noexpand\kvtcb@everyfloat}%
\let\tcb@before@middle=\tcb@before@first%
\let\tcb@before@last=\tcb@before@first%
\let\tcb@after@first=\tcb@float@env@end%
\let\tcb@after@middle=\tcb@float@env@end%
\let\tcb@after@last=\tcb@float@env@end%
\fi%
%
\edef\kvtcb@skin@@first{\kvtcb@skin@first}%
\edef\kvtcb@skin@@middle{\kvtcb@skin@middle}%
\edef\kvtcb@skin@@last{\kvtcb@skin@last}%
%
\let\tcb@h@breaksep@first=\kvtcb@bottomsep%
\tcbdimto\tcb@h@breaksep@middle{\kvtcb@bottomsep+\kvtcb@topsep}%
\let\tcb@h@breaksep@last=\kvtcb@topsep%
\tcbdimto\tcb@h@padding@first{\kvtcb@bbtop@stand+\kvtcb@bbbottom@break+\kvtcb@top@rule@stand+\kvtcb@bottom@rule@break+\tcb@h@breaksep@first+\kvtcb@boxsep*2+\kvtcb@top+\kvtcb@bottom}%
\tcbdimto\tcb@h@padding@middle{\kvtcb@bbtop@break+\kvtcb@bbbottom@break+\kvtcb@top@rule@break+\kvtcb@bottom@rule@break+\tcb@h@breaksep@middle+\kvtcb@boxsep*2+\kvtcb@top+\kvtcb@bottom}%
\tcbdimto\tcb@h@padding@last{\kvtcb@bbtop@break+\kvtcb@bbbottom@stand+\kvtcb@top@rule@break+\kvtcb@bottom@rule@stand+\tcb@h@breaksep@last+\kvtcb@boxsep*2+\kvtcb@top+\kvtcb@bottom}%
%
\tcb@split@setstate@first%
%
\splittopskip=0pt%
\splitmaxdepth=0pt%
\edef\tcb@thevfuzz{\the\vfuzz}%
\edef\tcb@thevbadness{\the\vbadness}%
%
\tcb@swap{\tcb@totalupperbox}{\tcb@upperbox}%
\tcb@swap{\tcb@totallowerbox}{\tcb@lowerbox}%
%
\let\tcb@split@next=\relax%
\tcbdimto\tcb@split@dim{\tcb@h@page-\tcb@h@padding@first-\tcb@h@padtitle}%
\ifdim\tcb@split@dim<\tcb@h@upper\relax%
\tcb@split@upper@box%
\ifdim\wd\tcb@totalupperbox=0pt\relax%
\iftcb@lowerspace%
\tcb@sdraw@U%
\let\tcb@split@next=\tcb@split@SL%
\else%
\tcb@split@force@standalone%
\tcb@drawcolorbox@standalone%
\fi%
\else%
\ifdim\dimexpr\tcb@h@upper=0pt\relax%
\iftcb@break@allowed
\tcb@swap{\tcb@totalupperbox}{\tcb@upperbox}%
\tcb@swap{\tcb@totallowerbox}{\tcb@lowerbox}%
\tcbdimto\tcb@h@upper{\ht\tcb@upperbox+\dp\tcb@upperbox}%
\tcbdimto\tcb@h@lower{\ht\tcb@lowerbox+\dp\tcb@lowerbox}%
\iftcb@multicol\columnbreak\else\pagebreak\fi%
\tcb@nobreak@warning%
\tcb@enlbreak@init\tcb@break@allowedfalse%
\let\tcb@split@next=\tcb@split@start%
\else%
\tcb@sdraw@U%
\let\tcb@split@next=\tcb@split@USL%
\fi%
\else%
\tcb@sdraw@U%
\let\tcb@split@next=\tcb@split@USL%
\fi%
\fi%
\else%
\tcb@swap{\tcb@totalupperbox}{\tcb@upperbox}%
\tcbdimto\tcb@split@dim{\tcb@split@dim-\tcb@h@upper-\tcb@h@segment}
\ifdim\tcb@split@dim<\kvtcb@breakminlines\baselineskip\relax%
\tcb@sdraw@U%
\iftcb@lowerspace%
\let\tcb@split@next=\tcb@split@SL%
\else%
\let\tcb@split@next=\tcb@split@USL%
\fi%
\else
\tcb@split@lower@box%
\ifdim\wd\tcb@totallowerbox=0pt\relax%
\tcb@split@force@standalone%
\tcb@drawcolorbox@standalone%
\else%
\tcb@sdraw@USL%
\let\tcb@split@next=\tcb@split@L%
\fi%
\fi%
\fi%
\tcb@split@next%
}
\makeatother
\begin{document}
\lipsum[1-4]
\section{title}
\begin{tcolorbox}[enhanced,breakable,%
lines before break=10,% commenting this issues a warning
]
\rule{1cm}{10\baselineskip}
\lipsum[1]
\end{tcolorbox}
\clearpage
\lipsum[1-4]
\rule{1cm}{6mm}
\section{Normal case}%
\begin{tcolorbox}[enhanced,breakable]
\lipsum[1]
\end{tcolorbox}
\end{document}
\pagebreak
is inserted and splitting starts at the next page. This is fine in most situations, but here a lonely heading is left behind. I do not have an automatic solution for that; one has to put a\pagebreak
before the heading by hand in this situation (or make this boxunbreakable
).before=\if@nobreak\enlargethispage{-5cm}\fi,
or\if@nobreak\vspace{5cm}\null\vspace{-5cm}\fi
but inside\tcb@split@first
the\enlargethispage
(and the test) no longer works. Is there a place where the size needed for the first split is already known and where one could insert an\enlargethispage
or the\vspace
with a sensible value?\@nobreaktrue
is set. Also, a minimal space after a heading could be checked if atcolorbox
follows; something like\if@nobreak\vspace{5cm}\null\vspace{-5cm}\fi
you mentioned before. This would not prevent the critical situation completely, but it will become more unlikely. On the other side, small boxes may flush to the next page more often (but with the heading).