diff --git a/crates/typst-layout/src/grid/layouter.rs b/crates/typst-layout/src/grid/layouter.rs index 879e435d1..dbe31c30b 100644 --- a/crates/typst-layout/src/grid/layouter.rs +++ b/crates/typst-layout/src/grid/layouter.rs @@ -254,7 +254,7 @@ impl<'a> GridLayouter<'a> { if y >= footer.start { if y == footer.start { self.layout_footer(footer, engine, self.finished.len())?; - self.flush_pending_headers(); + self.flush_orphans(); } y = footer.end; continue; @@ -266,7 +266,6 @@ impl<'a> GridLayouter<'a> { // After the first non-header row is placed, pending headers are no // longer orphans and can repeat, so we move them to repeating // headers. - self.flush_pending_headers(); // // Note that this is usually done in `push_row`, since the call to // `layout_row` above might trigger region breaks (for multi-page @@ -275,6 +274,7 @@ impl<'a> GridLayouter<'a> { // visible output and thus does not push any rows even though it // was successfully laid out, in which case we additionally flush // here just in case. + self.flush_orphans(); y += 1; } @@ -328,7 +328,7 @@ impl<'a> GridLayouter<'a> { self.layout_relative_row(engine, disambiguator, v, y)? } Sizing::Fr(v) => { - self.current.lrows_orphan_snapshot = None; + self.flush_orphans(); self.lrows.push(Row::Fr(v, y, disambiguator)) } } @@ -1443,7 +1443,7 @@ impl<'a> GridLayouter<'a> { fn push_row(&mut self, frame: Frame, y: usize, is_last: bool) { // There is now a row after the rows equipped with orphan prevention, // so no need to remove them anymore. - self.current.lrows_orphan_snapshot = None; + self.flush_orphans(); self.regions.size.y -= frame.height(); self.lrows.push(Row::Frame(frame, y, is_last)); } diff --git a/crates/typst-layout/src/grid/repeated.rs b/crates/typst-layout/src/grid/repeated.rs index f90aaf4f9..bc1d82152 100644 --- a/crates/typst-layout/src/grid/repeated.rs +++ b/crates/typst-layout/src/grid/repeated.rs @@ -35,7 +35,7 @@ impl<'a> GridLayouter<'a> { // headers afterwards, which basically are not headers, for all intents // and purposes. It is therefore guaranteed that all new headers have // been placed at least once. - self.flush_pending_headers(); + self.flush_orphans(); // Layout each conflicting header independently, without orphan // prevention (as they don't go into 'pending_headers'). @@ -139,10 +139,26 @@ impl<'a> GridLayouter<'a> { Ok(()) } + /// This function should be called each time an additional row has been + /// laid out in a region to indicate that orphan prevention has succeeded. + /// + /// It removes the current orphan snapshot and flushes pending headers, + /// such that a non-repeating header won't try to be laid out again + /// anymore, and a repeating header will begin to be part of + /// `repeating_headers`. + pub fn flush_orphans(&mut self) { + self.current.lrows_orphan_snapshot = None; + self.flush_pending_headers(); + } + /// Indicates all currently pending headers have been successfully placed /// once, since another row has been placed after them, so they are /// certainly not orphans. pub fn flush_pending_headers(&mut self) { + if self.pending_headers.is_empty() { + return; + } + for header in self.pending_headers { if let Repeatable::Repeated(header) = header { // Vector remains sorted by increasing levels: