From d13617ed9b9dd95376b8d068b85513cba8b1b702 Mon Sep 17 00:00:00 2001 From: PgBiel <9021226+PgBiel@users.noreply.github.com> Date: Sat, 21 Jun 2025 23:36:31 -0300 Subject: [PATCH] skip layout of redundant gutter at the top of footer --- crates/typst-layout/src/grid/layouter.rs | 2 +- crates/typst-layout/src/grid/repeated.rs | 20 ++++++++++++++++--- tests/ref/grid-footer-gutter-short-lived.png | Bin 0 -> 321 bytes tests/suite/layout/grid/footers.typ | 14 +++++++++++++ 4 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 tests/ref/grid-footer-gutter-short-lived.png diff --git a/crates/typst-layout/src/grid/layouter.rs b/crates/typst-layout/src/grid/layouter.rs index fd846b6e9..8e810e32b 100644 --- a/crates/typst-layout/src/grid/layouter.rs +++ b/crates/typst-layout/src/grid/layouter.rs @@ -232,7 +232,7 @@ pub(super) enum Row { impl Row { /// Returns the `y` index of this row. - fn index(&self) -> usize { + pub(super) fn index(&self) -> usize { match self { Self::Frame(_, y, _) => *y, Self::Fr(_, y, _) => *y, diff --git a/crates/typst-layout/src/grid/repeated.rs b/crates/typst-layout/src/grid/repeated.rs index 86506df79..6a76cb71a 100644 --- a/crates/typst-layout/src/grid/repeated.rs +++ b/crates/typst-layout/src/grid/repeated.rs @@ -679,9 +679,23 @@ impl<'a> GridLayouter<'a> { let footer_len = footer.range.end - footer.range.start; self.unbreakable_rows_left += footer_len; - // TODO(subfooters): also consider omitted gutter before the footer - // when there is a header right before it taking it. - for y in footer.range.clone() { + let footer_start = if self.grid.is_gutter_track(footer.range.start) + && self + .current + .lrows + .last() + .is_none_or(|r| self.grid.is_gutter_track(r.index())) + { + // Skip gutter at the top of footer if there's already a gutter + // from a repeated header right before it in the current region. + // Normally, that shouldn't happen as it indicates we have a widow, + // but we can't fully prevent widows anyway. + footer.range.start + 1 + } else { + footer.range.start + }; + + for y in footer_start..footer.range.end { self.layout_row_with_state( y, engine, diff --git a/tests/ref/grid-footer-gutter-short-lived.png b/tests/ref/grid-footer-gutter-short-lived.png new file mode 100644 index 0000000000000000000000000000000000000000..2eb74a8615bcd06de1de819fadd855cb4622a9a4 GIT binary patch literal 321 zcmV-H0lxl;P)98e>n@aRCL)m{Wv_fhW@e!VXY#or8&aaABl`=7|Xz!(2{}6Y8t7N%d+({Ez|IAw$htl zxn%+a{~9bco-r^mF!6=>LVO{<5MM;%3n!NW_~8#^;9~HUftQsn@rC#T3=B+s;pGAb zCcY3~h%dw!;tTP`w}s+MkP`Zi{iF<>3x&SpnimfzUR3@TKNkV+T)@D TiOG=>00000NkvXXu0mjf{4 literal 0 HcmV?d00001 diff --git a/tests/suite/layout/grid/footers.typ b/tests/suite/layout/grid/footers.typ index 286928b85..b79841219 100644 --- a/tests/suite/layout/grid/footers.typ +++ b/tests/suite/layout/grid/footers.typ @@ -41,6 +41,20 @@ ) ) +--- grid-footer-gutter-short-lived --- +// Gutter, no repetition, short-lived +#set page(height: 6em) +#set text(6pt) +#set table(inset: 2pt, stroke: 0.5pt) +#table( + gutter: 2pt, + align: center + horizon, + table.header([a]), + table.footer([b]), + table.footer([c]), + [d], +) + --- grid-cell-override-in-header-and-footer --- #table( table.header(table.cell(stroke: red)[Hello]),