diff --git a/crates/typst-layout/src/grid/lines.rs b/crates/typst-layout/src/grid/lines.rs index 973f0d21c..0ea113ac1 100644 --- a/crates/typst-layout/src/grid/lines.rs +++ b/crates/typst-layout/src/grid/lines.rs @@ -641,7 +641,7 @@ mod test { vec![], vec![], vec![], - None, + vec![], entries, ) } @@ -1179,7 +1179,7 @@ mod test { vec![], vec![], vec![], - None, + vec![], entries, ) } diff --git a/crates/typst-library/src/layout/grid/resolve.rs b/crates/typst-library/src/layout/grid/resolve.rs index 78f5cd42f..1e607ccca 100644 --- a/crates/typst-library/src/layout/grid/resolve.rs +++ b/crates/typst-library/src/layout/grid/resolve.rs @@ -697,7 +697,7 @@ impl<'a> CellGrid<'a> { cells: impl IntoIterator>, ) -> Self { let entries = cells.into_iter().map(Entry::Cell).collect(); - Self::new_internal(tracks, gutter, vec![], vec![], vec![], None, entries) + Self::new_internal(tracks, gutter, vec![], vec![], vec![], vec![], entries) } /// Generates the cell grid, given the tracks and resolved entries. @@ -707,7 +707,7 @@ impl<'a> CellGrid<'a> { vlines: Vec>, hlines: Vec>, headers: Vec>, - footer: Option>, + footers: Vec>, entries: Vec>, ) -> Self { let mut cols = vec![]; @@ -754,7 +754,6 @@ impl<'a> CellGrid<'a> { rows.pop(); } - let footers: Vec> = footer.into_iter().collect(); let sorted_footers = simulate_footer_repetition(&footers); Self { @@ -991,6 +990,7 @@ struct RowGroupData { /// /// This stays as `None` for fully empty headers and footers. range: Option>, + #[allow(dead_code)] // TODO: should we remove this? span: Span, kind: RowGroupKind, @@ -1048,11 +1048,7 @@ impl<'x> CellGridResolver<'_, '_, 'x> { let has_gutter = self.gutter.any(|tracks| !tracks.is_empty()); let mut headers: Vec> = vec![]; - - // Stores where the footer is supposed to end, its span, and the - // actual footer structure. - let mut footer: Option<(usize, Span, Footer)> = None; - let mut repeat_footer = false; + let mut footers: Vec> = 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. @@ -1074,10 +1070,11 @@ impl<'x> CellGridResolver<'_, '_, 'x> { // automatically-positioned cell. let mut auto_index: usize = 0; - // The next header after the latest auto-positioned cell. This is used - // to avoid checking for collision with headers that were already - // skipped. + // The next header and footer after the latest auto-positioned cell. + // These are used to avoid checking for collision with headers that + // were already skipped. let mut next_header = 0; + let mut next_footer = 0; // We have to rebuild the grid to account for fixed cell positions. // @@ -1100,10 +1097,10 @@ impl<'x> CellGridResolver<'_, '_, 'x> { &mut pending_hlines, &mut pending_vlines, &mut headers, - &mut footer, - &mut repeat_footer, + &mut footers, &mut auto_index, &mut next_header, + &mut next_footer, &mut resolved_cells, &mut at_least_one_cell, child, @@ -1121,11 +1118,10 @@ impl<'x> CellGridResolver<'_, '_, 'x> { row_amount, )?; - let footer = self.finalize_headers_and_footers( + self.finalize_headers_and_footers( has_gutter, &mut headers, - footer, - repeat_footer, + &mut footers, row_amount, at_least_one_cell, )?; @@ -1136,7 +1132,7 @@ impl<'x> CellGridResolver<'_, '_, 'x> { vlines, hlines, headers, - footer, + footers, resolved_cells, )) } @@ -1156,10 +1152,10 @@ impl<'x> CellGridResolver<'_, '_, 'x> { pending_hlines: &mut Vec<(Span, Line, bool)>, pending_vlines: &mut Vec<(Span, Line)>, headers: &mut Vec>, - footer: &mut Option<(usize, Span, Footer)>, - repeat_footer: &mut bool, + footers: &mut Vec>, auto_index: &mut usize, next_header: &mut usize, + next_footer: &mut usize, resolved_cells: &mut Vec>>, at_least_one_cell: &mut bool, child: ResolvableGridChild, @@ -1212,6 +1208,12 @@ impl<'x> CellGridResolver<'_, '_, 'x> { &mut (*next_header).clone() }; + let local_next_footer = if matches!(child, ResolvableGridChild::Item(_)) { + next_footer + } else { + &mut (*next_footer).clone() + }; + // The first row in which this table group can fit. // // Within headers and footers, this will correspond to the first @@ -1249,10 +1251,6 @@ impl<'x> CellGridResolver<'_, '_, 'x> { (Some(items), None) } ResolvableGridChild::Footer { repeat, span, items, .. } => { - if footer.is_some() { - bail!(span, "cannot have more than one footer"); - } - row_group_data = Some(RowGroupData { range: None, span, @@ -1396,10 +1394,11 @@ impl<'x> CellGridResolver<'_, '_, 'x> { colspan, rowspan, headers, - footer.as_ref(), + footers, resolved_cells, local_auto_index, local_next_header, + local_next_footer, first_available_row, columns, row_group_data.is_some(), @@ -1621,23 +1620,23 @@ impl<'x> CellGridResolver<'_, '_, 'x> { RowGroupKind::Footer => { // Only check if the footer is at the end later, once we know // the final amount of rows. - *footer = Some(( - group_range.end, - row_group.span, - Footer { - // Later on, we have to correct this number in case there - // is gutter, but only once all cells have been analyzed - // and the header's and footer's exact boundaries are - // known. That is because the gutter row immediately - // before the footer might not be included as part of - // the footer if it is contained within the header. - start: group_range.start, - end: group_range.end, - level: 1, - }, - )); + let data = Footer { + // Later on, we have to correct this number in case there + // is gutter, but only once all cells have been analyzed + // and the header's and footer's exact boundaries are + // known. That is because the gutter row immediately + // before the footer might not be included as part of + // the footer if it is contained within the header. + start: group_range.start, + end: group_range.end, + level: row_group.repeatable_level.get(), + }; - *repeat_footer = row_group.repeat; + footers.push(if row_group.repeat { + Repeatable::Repeated(data) + } else { + Repeatable::NotRepeated(data) + }); } } } @@ -1807,24 +1806,30 @@ impl<'x> CellGridResolver<'_, '_, 'x> { &self, has_gutter: bool, headers: &mut [Repeatable
], - footer: Option<(usize, Span, Footer)>, - repeat_footer: bool, + footers: &mut [Repeatable