[Tex/LaTex] Suppress a page break before a breakable tcolorbox

page-breakingtcolorbox

I'm trying to avoid page breaks between a sectioning command and a following tcolorbox. With "normal" boxes I can use \nopagebreak, e.g. in the before option:

\documentclass[]{article}
\usepackage[many]{tcolorbox}
\usepackage{lipsum,needspace}
\tracingpages=1
\begin{document}
\lipsum[1-4]

\section{title}
\begin{tcolorbox}[before=\nopagebreak,
                  %breakable, %
                  ]
\rule{1cm}{10\baselineskip}
\lipsum[1]
\end{tcolorbox}

\end{document}

But this fails if I activate the breakable option. There is a break point that I can't identify. Has someone an idea where it is and how I can suppress it? \Needspace helps but one has to know the size of objects at the begin of the tcolorbox.

Best Answer

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}

enter image description here

Related Question