How to allow blocks to break only if their length is more than one page?

These are probably related. The warning means the compiler stopped after five times where the layout keeps changing, leading to you observing an “intermediate” result. It’s similar to the issue in this post:

In your case the problem is a bit more subtle; you don’t have get()update() state dependence, but pagebreak()remainingupdate() dependence that cascades over multiple layout attempts.


I think I have something that works:

#show par: it => layout(size => {
  let height = measure(width: size.width, it).height
  let fitting = height < size.height
  it
  [#metadata(fitting)<fitting>]
})

#show par: it => context {
  let fitting = query(selector(<fitting>).after(here()))
    .at(0, default: none)
  if fitting == none {
    it
  } else if fitting.value {
    block(breakable: false, it)
  } else {
    pagebreak()
    it
  }
}

#set page(paper: "a7")

#lorem(30)

#lorem(30)

#lorem(30)

#lorem(50)

#lorem(90)

#lorem(30)

#lorem(30)

Basically instead of deciding for each paragraph at a specific position on the page whether it should break or not, I say:

  • if the paragraph is short enough to fit on a single page, it should be unbreakable
  • if it isn’t, only then I do a manual page break - that’s necessary whatever the position is - and allow breaking the paragraph across different pages.

Not having page breaks depend on the layout allows this to work without running into the dreaded “layout did not converge within 5 attempts” warning.

There are some strange details about this code:

  • I tried to do the same with state instead of metadata, but state.get() was off-by-one (it returned none for the first paragraph instead of the value that was set)
  • I also tried to avoid the state at all and do it in one show rule, but you can’t do pagebreak() inside layout(), it seems. but splitting it into two show rules works!
2 Likes