From 3cbbcaa7346fa1f48c2b69f5f0445e20d5bc8ff1 Mon Sep 17 00:00:00 2001 From: PgBiel <9021226+PgBiel@users.noreply.github.com> Date: Sat, 10 May 2025 16:57:56 -0300 Subject: [PATCH] use may_progress_with_repeats for rowspans --- crates/typst-layout/src/grid/layouter.rs | 13 ----- crates/typst-layout/src/grid/rowspans.rs | 48 +++++++++++------- ...large-repeating-orphan-before-relative.png | Bin 437 -> 491 bytes 3 files changed, 29 insertions(+), 32 deletions(-) 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 dfcac850083b082e6eccff867e8662f64f74244d..9688c438792a9d9fb2f2500ec25063561ae8d1b0 100644 GIT binary patch delta 478 zcmV<40U`di1M35j7k{A$0{{R3JBy140002YP)t-s|NsC0K{kARd@wLD`uh6*{{AE+ zBy4PKva+(y&d!aEjhB~~n3$MSQc_DxOI1}>qobpShKA71OL>+9>FprDG1itOy{?d|Q>*4Dwn!A3?#TYpLr<$6YySuyg_V(!L=;7hv*x1;E zgM-o0(ZY3U zWN~wQcQ1$N{o_Fn)xl>>4z=Xh>x&HHiTGQ{rv&y}_$-Ux}~r@&Fi; zF(f0*kc=T2Lo$YB02q=nBx6X%kc=T2L$b}BtPv(mm@r|&gb5QSOqeiX!T`2o1@Wpi Uuj-tkGXMYp07*qoM6N<$g5nN_KX3@$vDmudh;4Qka;S z>+9>{;^KaOewUY*oSdBL>FJG)jpgO#Gcz;w_4UHS!p6qNr+=rXySuxZnwo%sfcEzG z=;-L-;o;cW*n@+E(b3UKNl7p;FnoM`+1c5NiHYp&?CtIC*4EaFii(AWg~-Ur&d$!) z*VoD_MKv2r(vS4_9D6Fu;3M;Iz!XXMPtgylgE3B}He8(Hs5Lc^x S0TxRD0000