diff options
| author | PgBiel <9021226+PgBiel@users.noreply.github.com> | 2025-06-14 19:31:27 -0300 |
|---|---|---|
| committer | PgBiel <9021226+PgBiel@users.noreply.github.com> | 2025-06-28 22:39:35 -0300 |
| commit | 315612b1f7be637c0c1a6a123f8802e09bee332a (patch) | |
| tree | a05bb073aaef653318c4148f0ef502b3773e450d | |
| parent | f3cc3bdae77793e73d4a72bab55ed3331b23e2a1 (diff) | |
detect short lived headers and footers at the table edges
even if headers and footers are interleaved
| -rw-r--r-- | crates/typst-library/src/layout/grid/resolve.rs | 83 |
1 files changed, 40 insertions, 43 deletions
diff --git a/crates/typst-library/src/layout/grid/resolve.rs b/crates/typst-library/src/layout/grid/resolve.rs index d51b5551..1fe7ae27 100644 --- a/crates/typst-library/src/layout/grid/resolve.rs +++ b/crates/typst-library/src/layout/grid/resolve.rs @@ -1042,9 +1042,11 @@ impl<'x> CellGridResolver<'_, '_, 'x> { let mut headers: Vec<Repeatable<Header>> = vec![]; let mut footers: Vec<Repeatable<Footer>> = vec![]; - // If true, there has been at least one cell besides headers and - // footers. When false, footers at the end are forced to not repeat. - let mut at_least_one_cell = false; + // The first and last rows containing a cell outside a row group, that + // is, outside a header or footer. Headers after the last such row and + // footers before the first such row have no "children" cells and thus + // are not repeated. + let mut first_last_cell_rows = None; // We can't just use the cell's index in the 'cells' vector to // determine its automatic position, since cells could have arbitrary @@ -1094,7 +1096,7 @@ impl<'x> CellGridResolver<'_, '_, 'x> { &mut next_header, &mut next_footer, &mut resolved_cells, - &mut at_least_one_cell, + &mut first_last_cell_rows, child, )?; } @@ -1115,7 +1117,7 @@ impl<'x> CellGridResolver<'_, '_, 'x> { &mut headers, &mut footers, row_amount, - at_least_one_cell, + first_last_cell_rows, )?; Ok(CellGrid::new_internal( @@ -1149,7 +1151,7 @@ impl<'x> CellGridResolver<'_, '_, 'x> { next_header: &mut usize, next_footer: &mut usize, resolved_cells: &mut Vec<Option<Entry<'x>>>, - at_least_one_cell: &mut bool, + first_last_cell_rows: &mut Option<(usize, usize)>, child: ResolvableGridChild<T, I>, ) -> SourceResult<()> where @@ -1260,13 +1262,7 @@ impl<'x> CellGridResolver<'_, '_, 'x> { (Some(items), None) } - ResolvableGridChild::Item(item) => { - if matches!(item, ResolvableGridItem::Cell(_)) { - *at_least_one_cell = true; - } - - (None, Some(item)) - } + ResolvableGridChild::Item(item) => (None, Some(item)), }; let items = header_footer_items.into_iter().flatten().chain(simple_item); @@ -1448,6 +1444,13 @@ impl<'x> CellGridResolver<'_, '_, 'x> { // no longer appear at the top. *top_hlines_end = Some(pending_hlines.len()); } + } else { + // This is a cell outside a row group. + *first_last_cell_rows = Some( + first_last_cell_rows + .map(|(first, last)| (first.min(y), last.max(y))) + .unwrap_or((y, y)), + ); } // Let's resolve the cell so it can determine its own fields @@ -1788,42 +1791,41 @@ impl<'x> CellGridResolver<'_, '_, 'x> { /// an adjacent gutter row to be repeated alongside that header or /// footer, if there is gutter; /// 3. Wrap headers and footers in the correct [`Repeatable`] variant. - #[allow(clippy::type_complexity)] fn finalize_headers_and_footers( &self, has_gutter: bool, headers: &mut [Repeatable<Header>], footers: &mut [Repeatable<Footer>], row_amount: usize, - at_least_one_cell: bool, + first_last_cell_rows: Option<(usize, usize)>, ) -> SourceResult<()> { // Mark consecutive headers right before the end of the table, or the - // final footer, as short lived, given that there are no normal rows - // after them, so repeating them is pointless. - // - // TODO(subfooters): take the last footer if it is at the end and - // backtrack through consecutive footers until the first one in the - // sequence is found. If there is no footer at the end, there are no - // haeders to turn short-lived. + // footers at the end, as short lived, given that there are no normal + // rows after them, so repeating them is pointless. // - // TODO: interleaved headers and footers? - let mut first_end_footer = row_amount; - for end_footer in footers.iter().rev() { - if end_footer.range.end != first_end_footer { - break; + // Same for consecutive footers right after the start of the table or + // any initial headers. + if let Some((first_cell_row, last_cell_row)) = first_last_cell_rows { + for header in + headers.iter_mut().rev().take_while(|h| h.range.start > last_cell_row) + { + header.short_lived = true; } - first_end_footer = end_footer.range.start; - } - - let mut consecutive_header_start = first_end_footer; - for header_at_the_end in headers.iter_mut().rev().take_while(move |h| { - let at_the_end = h.range.end == consecutive_header_start; - - consecutive_header_start = h.range.start; - at_the_end - }) { - header_at_the_end.short_lived = true; + for footer in footers.iter_mut().take_while(|f| f.range.end <= first_cell_row) + { + // TODO(subfooters): short lived + footer.repeated = false; + } + } else { + // No cells outside headers or footers, so nobody repeats! + for header in &mut *headers { + header.short_lived = true; + } + for footer in &mut *footers { + // TODO(subfooters): short lived + footer.repeated = false; + } } // Repeat the gutter below a header (hence why we don't @@ -1891,11 +1893,6 @@ impl<'x> CellGridResolver<'_, '_, 'x> { // It also keeps us within the total amount of rows, so we // don't need to '.min()' later. footer.range.end = (2 * footer.range.end).saturating_sub(1); - - if !at_least_one_cell { - // TODO: short-lived (and remove this?) - footer.repeated = false; - } } } |
