0

I think I've stumbled upon a tcolorbox bug: whenever there are two consecutive breakable boxes, neither can fit unbroken in the current page, and the content of the first is unbreakable (e.g. an image) while the content of the second is breakable (e.g. text), then the first is moved to the next page as expected, but for some reason, the second one is not broken and end-up overflowing from the page. Making the first box unbreakable (which should change basically nothing since it can not be broken) fixes the problem, which makes me think that this is due to tcolorbox somehow wrongly applying the decision to not break the first box to the second box.

The actual use case is a PhD thesis where every figure, theorem (and fact, proposition, remark, etc.) and proof is in a breakable tcolorbox. This problem happens at unexpected places because the figures float while the theorems do not, which makes manually making some boxes unbreakable unmanageable.

I've looked at the code for the beakable, tried changing the values of booleans \tcb@break@allowedtrue and \tcb@ignorenobreaktrue and placing the box in a group (to prevent value changes from leaking to the other box), but this had no effect.

\documentclass{article}
\usepackage[most]{tcolorbox}
\usepackage{afterpage}
\usepackage{lipsum}

\tcbset{enhanced}

\newcommand{\breakablecontent}{\lipsum[1-12]} \newcommand{\nonbreakablecontent}{\rule{\columnwidth}{\columnwidth}}

\newcommand{\mytest}[1]{ \lipsum[1-3] \afterpage{ \begin{tcolorbox}[breakable=true] \breakablecontent \end{tcolorbox} } \begin{tcolorbox}[breakable=#1] \nonbreakablecontent \end{tcolorbox} }

% Naive Attempt to fix this: %\makeatletter %\renewcommand{\mytest}[1]{ % \lipsum[1-3] % \afterpage{ % \tcb@break@allowedtrue\tcb@ignorenobreaktrue % \begingroup % \begin{tcolorbox}[breakable=true] % \breakablecontent % \end{tcolorbox} % \endgroup % \tcb@break@allowedtrue\tcb@ignorenobreaktrue % } % \tcb@break@allowedtrue\tcb@ignorenobreaktrue % \begin{tcolorbox}[breakable=#1] % \nonbreakablecontent % \end{tcolorbox} %} %\makeatother

\begin{document}

\section{Expected behavior} \mytest{false}

\clearpage

\section{Unexpected behavior} \mytest{true}

\end{document}

xavierm02
  • 787
  • 1
    I think, the problem really is related to \afterpage where you place the first tcolorbox in. Without this, the breakpoints work as expected. What is the reason you use this? Would \clearpage be a workaround for you, maybe? – Jasper Habicht May 26 '23 at 11:01
  • @JasperHabicht Right. I use this because I want some figures to be in landscape, but did not manage to make this work with floating figures, so I do \afterpage{\begin{landscape}\begin{figure}[H]. Maybe I should just use the landscape option of tcolorbox to rotate only the figure, and then store lists of pages I want to rotate back and use something similar to https://tex.stackexchange.com/questions/451418/floating-landscape-environment – xavierm02 May 26 '23 at 12:03
  • But there ought to be some simpler way of doing it. Surely, saving the state of some well-chosen tcolorbox variables just before the afterpage and restoring it at the beginning of the afterpage should work. – xavierm02 May 26 '23 at 12:07
  • There's also the option of using \savebox and \usebox for the second box, which prevents any interleaving with the first box and hence fixes the problem for this example. But I have use cases where the afterpage figure takes several pages so this would not always work (as can be seen if we replace the content of the first box with \nonbreakablecontent\\\nonbreakablecontent\\\nonbreakablecontent\\\nonbreakablecontent). – xavierm02 May 26 '23 at 12:20
  • You could stop using floats and instead use \captionof{figure}{...} (caption and capt-of packages). – John Kormylo May 26 '23 at 12:49
  • @JohnKormylo I don't really use figures, I use tcolorboxes with the float option. And I do need them to float. – xavierm02 May 26 '23 at 13:03
  • What would you expect to happen if both tcolorbox contained breakable content? That tcolorbox somehow interleaves them? Do not forget that the box outside the \afterpage is started and processed first. – Ulrike Fischer May 26 '23 at 13:23
  • @UlrikeFischer Any order would be fine, I just want to avoid overflows and huge empty spaces. Interleaving sounds reasonable since the afterpage box is really a figure while the other one is more like delimited text (and interleaving does happen between a non-floating box and a floating one on a page of floats). In the cases where I use afterpage, the box often takes the whole page (or several whole pages) so I could clearpage after it if needed anyway. – xavierm02 May 26 '23 at 14:27
  • 1
    use the magazine library to split and store the pieces and then output them manually in the order your want. Do not rely on afterpage, it inserts the tcolorbox in the output routine and that confuses tcolorbox which hasn't done all the needed cleaning up yet. – Ulrike Fischer May 26 '23 at 14:49
  • @UlrikeFischer It works! Thanks! (I had searched "precompute" and "prerender" hoping to find such a library in the manual but had missed it :/) – xavierm02 May 26 '23 at 15:37

2 Answers2

2

I think, the problem really is related to \afterpage where you place the first tcolorbox in. Without this, the breakpoints work as expected. I am unsure why you need this. Maybe a workaround could be to place a simple \clearpage right before the following tcolorbox?

MWE:

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

\tcbset{enhanced}

\newcommand{\breakablecontent}{\lipsum[1-12]} \newcommand{\nonbreakablecontent}{\rule{\columnwidth}{\columnwidth}}

\newcommand{\mytest}[1]{ \lipsum[1-3] \clearpage \begin{tcolorbox}[breakable=true] \breakablecontent \end{tcolorbox} \clearpage \begin{tcolorbox}[breakable=#1] \nonbreakablecontent \end{tcolorbox} }

\begin{document}

\section{Expected behavior} \mytest{false}

\clearpage

\section{Unexpected behavior} \mytest{true}

\end{document}

2

Using the magazine library as suggested by Ulrike Fischer fixes the overflow problem but the unbreakable content is inexplicably shifted downwards, outside of its box. Using floats instead of afterpage works.

\documentclass{article}
\usepackage[most]{tcolorbox}
\usepackage{afterpage}
\usepackage{lipsum}

\tcbset{enhanced}

\newcommand{\breakablecontent}{\lipsum[1-12]} \newcommand{\nonbreakablecontent}{\rule{\columnwidth}{\columnwidth}}

\newboxarray{a}

% Does not work weirdly \newcommand{\mytest}[1]{ \lipsum[1-3]\ \begin{tcolorbox}[breakable=true, reset and store to box array=a] \breakablecontent \end{tcolorbox} \afterpage{ \consumeboxarray[a]{1}{} \consumeboxarray[a]{2}{} \consumeboxarray[a]{3}{} } \begin{tcolorbox}[breakable=#1] \nonbreakablecontent \end{tcolorbox} }

% Works \renewcommand{\mytest}[1]{ \lipsum[1-3]\ \begin{tcolorbox}[breakable=true, reset and store to box array=a] \breakablecontent \end{tcolorbox} \begin{figure}[p]\consumeboxarray[a]{1}{}\end{figure} \begin{figure}[p]\consumeboxarray[a]{2}{}\end{figure} \begin{figure}[p]\consumeboxarray[a]{3}{}\end{figure} \begin{tcolorbox}[breakable=#1] \nonbreakablecontent \end{tcolorbox} }

\begin{document}

\section{Expected behavior} \mytest{false}

\clearpage

\section{Unexpected behavior} \mytest{true}

\end{document}

xavierm02
  • 787