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}
I propose a partial solution. The main shortcoming is its dependence on later changes made in the .tex
file.
The idea is to introduce a command that ends the current inner box and starts a new one. If a breaking is needed, the command is inserted in the text and the desired split is achieved.
Without forcing the breaking of the inner box (not needed).
In the code below the command is \splitbox
. Moreover, starting with the default values of the inner box, TikZ is used to construct sharp corners at the bottom of the current page and at the top of the next one for the split box.
\documentclass{article}
\usepackage{tikz}
\usepackage[breakable, skins]{tcolorbox}
\usepackage{lipsum}
\newtcolorbox{OuterBox}[1][]{%
breakable,
enhanced,
colback=white,
colframe=blue,
#1
}
\newtcolorbox{InnerBox}[1][]{%
enforce breakable,
enhanced,
colback=yellow,
colframe=yellow,
#1
}
\newcommand{\splitbox}{%
\hfill
\begin{tikzpicture}[remember picture, overlay]
\draw[yellow, line width=1.5ex] (1.55em, -2.2ex) -- +(-1\textwidth-3.1em, 0);
\end{tikzpicture}
\end{InnerBox}
\begin{InnerBox}
\begin{tikzpicture}[remember picture, overlay]
\draw[yellow, line width=1.5ex] (-1.55em, 3.2ex) -- +(1\textwidth+3.1em, 0);
\end{tikzpicture}\hspace{-.7ex}
}
\title{Breaking inner boxes. Trial and error}
\date{\vspace{-8ex}}
\begin{document}
\maketitle
\section{The forced breaking is needed}
\lipsum[7]
\begin{OuterBox}
Just a line of text.
\lipsum[5]
\begin{InnerBox}
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Ut pu-
rus elit, vestibulum ut, placerat ac, adipiscing vitae, felis. Cur-
abitur dictum gravida mauris. Nam arcu libero, nonummy eget,
consectetuer id, vulputate a, magna. Donec vehicula augue eu
neque. Pellentesque habitant morbi tristique senectus et netus et
malesuada fames ac turpis egestas. Mauris ut leo. Cras viverra
metus rhoncus sem. Nulla et lectus vestibulum urna fringilla ultri-
ces. Phasellus eu tellus sit amet tortor gravida placerat. Integer
sapien est, iaculis in, pretium quis, viverra ac, nunc. Praesent
eget sem vel leo ultrices bibendum. Aenean faucibus. Morbi do-
lor nulla, malesuada eu, pulvinar at, mollis ac, nulla. Curabitur
auctor semper nulla. Donec varius orci eget risus. Duis nibh mi,
congue eu, accumsan eleifend, sagittis quis, diam. Duis eget orci
sit amet orci dignissim rutrum.
\splitbox
Nam dui ligula, fringilla a, euismod sodales, sollicitudin vel, wisi.
Morbi auctor lorem non justo. Nam lacus libero, pretium at,
lobortis vitae, ultricies et, tellus. Donec aliquet, tortor sed ac-
cumsan bibendum, erat ligula aliquet magna, vitae ornare odio
metus a mi. Morbi ac orci et nisl hendrerit mollis. Suspendisse
ut massa. Cras nec ante. Pellentesque a nulla. Cum sociis na-
toque penatibus et magnis dis parturient montes, nascetur ridicu-
lus mus. Aliquam tincidunt urna. Nulla ullamcorper vestibulum
turpis. Pellentesque cursus luctus mauris.
\end{InnerBox}
\lipsum[4]
\end{OuterBox}
\lipsum[5-6]
\end{document}
Note that due to \lipsum[n]
behavior with respect to the neighboring paragraphs, I inserted normal text in the inner box (as in a normal text file).
Best Answer
I had the same issue with a document of mine and what fixed the problem for me was this answer from egreg, so I included the code here too.
I hope this is what you were looking for (full MWE below).