summaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorPgBiel <9021226+PgBiel@users.noreply.github.com>2024-03-13 06:15:16 -0300
committerGitHub <noreply@github.com>2024-03-13 09:15:16 +0000
commit48820fe69b8061bd949847afc343bf160d05c924 (patch)
tree8b51ce9b682fb0ac3c79810f649b8c7b9a60c91f /crates
parentfd2eb0ceb25270e5ea738b76a2f5271e84234667 (diff)
Fix table cells and rowspans wrongly assuming full page height available (#3637)
Diffstat (limited to 'crates')
-rw-r--r--crates/typst/src/layout/grid/layout.rs7
-rw-r--r--crates/typst/src/layout/grid/rowspans.rs57
2 files changed, 58 insertions, 6 deletions
diff --git a/crates/typst/src/layout/grid/layout.rs b/crates/typst/src/layout/grid/layout.rs
index 645b68b6..46993989 100644
--- a/crates/typst/src/layout/grid/layout.rs
+++ b/crates/typst/src/layout/grid/layout.rs
@@ -2599,7 +2599,12 @@ impl<'a> GridLayouter<'a> {
let width = self.cell_spanned_width(cell, x);
let size = Size::new(width, height);
let mut pod = Regions::one(size, Axes::splat(true));
- if self.grid.rows[y] == Sizing::Auto {
+ if self.grid.rows[y] == Sizing::Auto
+ && self.unbreakable_rows_left == 0
+ {
+ // Cells at breakable auto rows have lengths relative
+ // to the entire page, unlike cells in unbreakable auto
+ // rows.
pod.full = self.regions.full;
}
let frame = cell.layout(engine, self.styles, pod)?.into_frame();
diff --git a/crates/typst/src/layout/grid/rowspans.rs b/crates/typst/src/layout/grid/rowspans.rs
index 764a2b70..f4df3e4a 100644
--- a/crates/typst/src/layout/grid/rowspans.rs
+++ b/crates/typst/src/layout/grid/rowspans.rs
@@ -10,12 +10,16 @@ use super::layout::{in_last_with_offset, points, Repeatable, Row, RowPiece};
/// All information needed to layout a single rowspan.
pub(super) struct Rowspan {
- // First column of this rowspan.
+ /// First column of this rowspan.
pub(super) x: usize,
- // First row of this rowspan.
+ /// First row of this rowspan.
pub(super) y: usize,
- // Amount of rows spanned by the cell at (x, y).
+ /// Amount of rows spanned by the cell at (x, y).
pub(super) rowspan: usize,
+ /// Whether all rows of the rowspan are part of an unbreakable row group.
+ /// This is true e.g. in headers and footers, regardless of what the user
+ /// specified for the parent cell's `breakable` field.
+ pub(super) is_effectively_unbreakable: bool,
/// The horizontal offset of this rowspan in all regions.
pub(super) dx: Abs,
/// The vertical offset of this rowspan in the first region.
@@ -96,7 +100,16 @@ impl<'a> GridLayouter<'a> {
engine: &mut Engine,
) -> SourceResult<()> {
let Rowspan {
- x, y, dx, dy, first_region, region_full, heights, ..
+ x,
+ y,
+ rowspan,
+ is_effectively_unbreakable,
+ dx,
+ dy,
+ first_region,
+ region_full,
+ heights,
+ ..
} = rowspan_data;
let [first_height, backlog @ ..] = heights.as_slice() else {
// Nothing to layout.
@@ -110,9 +123,20 @@ impl<'a> GridLayouter<'a> {
// Prepare regions.
let size = Size::new(width, *first_height);
let mut pod = Regions::one(size, Axes::splat(true));
- pod.full = region_full;
pod.backlog = backlog;
+ if !is_effectively_unbreakable
+ && self.grid.rows[y..][..rowspan]
+ .iter()
+ .any(|spanned_row| spanned_row == &Sizing::Auto)
+ {
+ // If the rowspan spans an auto row and is breakable, it will see
+ // '100%' as the full page height, at least at its first region.
+ // This is consistent with how it is measured, and with how
+ // non-rowspan cells behave in auto rows.
+ pod.full = region_full;
+ }
+
// Push the layouted frames directly into the finished frames.
let fragment = cell.layout(engine, self.styles, pod)?;
let (current_region, current_rrows) = current_region_data.unzip();
@@ -172,6 +196,9 @@ impl<'a> GridLayouter<'a> {
x,
y,
rowspan,
+ // The field below will be updated in
+ // 'check_for_unbreakable_rows'.
+ is_effectively_unbreakable: !cell.breakable,
dx,
// The four fields below will be updated in 'finish_region'.
dy: Abs::zero(),
@@ -227,9 +254,29 @@ impl<'a> GridLayouter<'a> {
{
self.finish_region(engine)?;
}
+
+ // Update unbreakable rows left.
self.unbreakable_rows_left = row_group.rows.len();
}
+ if self.unbreakable_rows_left > 1 {
+ // Mark rowspans as effectively unbreakable where applicable
+ // (if all of their spanned rows would be in the same unbreakable
+ // row group).
+ // Not needed if only one unbreakable row is left, since, then,
+ // no rowspan will be effectively unbreakable, at least necessarily.
+ // Note that this function is called after 'check_for_rowspans' and
+ // potentially updates the amount of remaining unbreakable rows, so
+ // it wouldn't be accurate to only check for this condition in that
+ // function. We need to check here instead.
+ for rowspan_data in
+ self.rowspans.iter_mut().filter(|rowspan| rowspan.y == current_row)
+ {
+ rowspan_data.is_effectively_unbreakable |=
+ self.unbreakable_rows_left >= rowspan_data.rowspan;
+ }
+ }
+
Ok(())
}