diff --git a/crates/typst-layout/src/grid/layouter.rs b/crates/typst-layout/src/grid/layouter.rs index 9575657a1..002cd10f6 100644 --- a/crates/typst-layout/src/grid/layouter.rs +++ b/crates/typst-layout/src/grid/layouter.rs @@ -1793,16 +1793,3 @@ pub(super) fn points( offset }) } - -/// Checks if the first region of a sequence of regions is not the last usable -/// region, assuming that the last region will always be occupied by some -/// specific offset height, even after calling `.next()`, due to some -/// additional logic which adds content automatically on each region turn (in -/// our case, headers). -pub(super) fn may_progress_with_offset(regions: Regions<'_>, offset: Abs) -> bool { - // Use 'approx_eq' as float addition and subtraction are not associative. - !regions.backlog.is_empty() - || regions - .last - .is_some_and(|height| !(regions.size.y + offset).approx_eq(height)) -} diff --git a/crates/typst-layout/src/grid/rowspans.rs b/crates/typst-layout/src/grid/rowspans.rs index 5bba70761..18ca934e7 100644 --- a/crates/typst-layout/src/grid/rowspans.rs +++ b/crates/typst-layout/src/grid/rowspans.rs @@ -4,7 +4,7 @@ use typst_library::foundations::Resolve; use typst_library::layout::grid::resolve::Repeatable; use typst_library::layout::{Abs, Axes, Frame, Point, Region, Regions, Size, Sizing}; -use super::layouter::{may_progress_with_offset, points, Row}; +use super::layouter::{points, Row}; use super::{layout_cell, Cell, GridLayouter}; /// All information needed to layout a single rowspan. @@ -258,14 +258,7 @@ impl GridLayouter<'_> { // Skip to fitting region. while !self.regions.size.y.fits(row_group.height) - && may_progress_with_offset( - self.regions, - // Use 'repeating_header_height' (ignoring the height of - // non-repeated headers) to allow skipping if the - // non-repeated header is too large. It will become an - // orphan, but when there is no space left, anything goes. - self.current.repeating_header_height + self.current.footer_height, - ) + && self.may_progress_with_repeats() { self.finish_region(engine, false)?; } @@ -931,6 +924,8 @@ impl GridLayouter<'_> { // won't change is safe. self.current.repeating_header_height, self.current.footer_height, + self.current.could_progress_at_top, + self.current.initial_after_repeats, ); let total_spanned_height = rowspan_simulator.simulate_rowspan_layout( @@ -1031,10 +1026,17 @@ struct RowspanSimulator<'a> { finished: usize, /// The state of regions during the simulation. regions: Regions<'a>, - /// The height of the header in the currently simulated region. + /// The total height of headers in the currently simulated region. header_height: Abs, - /// The height of the footer in the currently simulated region. + /// The total height of footers in the currently simulated region. footer_height: Abs, + /// Whether `self.regions.may_progress()` was `true` at the top of the + /// region, indicating we can progress anywhere in the current region, + /// even right after a repeated header. + could_progress_at_top: bool, + /// Available height after laying out repeated headers at the top of the + /// currently simulated region. + initial_after_repeats: Abs, /// The total spanned height so far in the simulation. total_spanned_height: Abs, /// Height of the latest spanned gutter row in the simulation. @@ -1050,12 +1052,16 @@ impl<'a> RowspanSimulator<'a> { regions: Regions<'a>, header_height: Abs, footer_height: Abs, + could_progress_at_top: bool, + initial_after_repeats: Abs, ) -> Self { Self { finished, regions, header_height, footer_height, + could_progress_at_top, + initial_after_repeats, total_spanned_height: Abs::zero(), latest_spanned_gutter_height: Abs::zero(), } @@ -1104,10 +1110,7 @@ impl<'a> RowspanSimulator<'a> { 0, )?; while !self.regions.size.y.fits(row_group.height) - && may_progress_with_offset( - self.regions, - self.header_height + self.footer_height, - ) + && self.may_progress_with_repeats() { self.finish_region(layouter, engine)?; } @@ -1129,10 +1132,7 @@ impl<'a> RowspanSimulator<'a> { let mut skipped_region = false; while unbreakable_rows_left == 0 && !self.regions.size.y.fits(height) - && may_progress_with_offset( - self.regions, - self.header_height + self.footer_height, - ) + && self.may_progress_with_repeats() { self.finish_region(layouter, engine)?; @@ -1252,6 +1252,7 @@ impl<'a> RowspanSimulator<'a> { // header or footer (as an invariant, any rowspans spanning any header // or footer rows are fully contained within that header's or footer's rows). self.regions.size.y -= self.header_height + self.footer_height; + self.initial_after_repeats = self.regions.size.y; Ok(()) } @@ -1268,8 +1269,17 @@ impl<'a> RowspanSimulator<'a> { self.regions.next(); self.finished += 1; + self.could_progress_at_top = self.regions.may_progress(); self.simulate_header_footer_layout(layouter, engine) } + + /// Similar to [`GridLayouter::may_progress_with_repeats`] but for rowspan + /// simulation. + fn may_progress_with_repeats(&self) -> bool { + self.could_progress_at_top + || self.regions.last.is_some() + && self.regions.size.y != self.initial_after_repeats + } } /// Subtracts some size from the end of a vector of sizes. diff --git a/tests/ref/grid-subheaders-too-large-repeating-orphan-before-relative.png b/tests/ref/grid-subheaders-too-large-repeating-orphan-before-relative.png index dfcac8500..9688c4387 100644 Binary files a/tests/ref/grid-subheaders-too-large-repeating-orphan-before-relative.png and b/tests/ref/grid-subheaders-too-large-repeating-orphan-before-relative.png differ