summaryrefslogtreecommitdiff
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
parentfd2eb0ceb25270e5ea738b76a2f5271e84234667 (diff)
Fix table cells and rowspans wrongly assuming full page height available (#3637)
-rw-r--r--crates/typst/src/layout/grid/layout.rs7
-rw-r--r--crates/typst/src/layout/grid/rowspans.rs57
-rw-r--r--tests/ref/layout/grid-headers-4.pngbin41733 -> 42192 bytes
-rw-r--r--tests/ref/layout/grid-rowspan-basic.pngbin98653 -> 99968 bytes
-rw-r--r--tests/ref/layout/grid-rowspan-split-3.pngbin99690 -> 100103 bytes
-rw-r--r--tests/typ/layout/grid-headers-4.typ13
-rw-r--r--tests/typ/layout/grid-rowspan-basic.typ20
7 files changed, 91 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(())
}
diff --git a/tests/ref/layout/grid-headers-4.png b/tests/ref/layout/grid-headers-4.png
index be6080f7..1f3e4b10 100644
--- a/tests/ref/layout/grid-headers-4.png
+++ b/tests/ref/layout/grid-headers-4.png
Binary files differ
diff --git a/tests/ref/layout/grid-rowspan-basic.png b/tests/ref/layout/grid-rowspan-basic.png
index 783991b3..b464d8b4 100644
--- a/tests/ref/layout/grid-rowspan-basic.png
+++ b/tests/ref/layout/grid-rowspan-basic.png
Binary files differ
diff --git a/tests/ref/layout/grid-rowspan-split-3.png b/tests/ref/layout/grid-rowspan-split-3.png
index 3d809123..c3ff4bd1 100644
--- a/tests/ref/layout/grid-rowspan-split-3.png
+++ b/tests/ref/layout/grid-rowspan-split-3.png
Binary files differ
diff --git a/tests/typ/layout/grid-headers-4.typ b/tests/typ/layout/grid-headers-4.typ
index 6ede601c..6ab0d612 100644
--- a/tests/typ/layout/grid-headers-4.typ
+++ b/tests/typ/layout/grid-headers-4.typ
@@ -97,3 +97,16 @@
[a],
table.cell(stroke: aqua)[b]
)
+
+---
+#set page(height: 7em)
+#set text(6pt)
+#let full-block = block(width: 2em, height: 100%, fill: red)
+#table(
+ columns: 3,
+ inset: 1.5pt,
+ table.header(
+ [a], full-block, table.cell(rowspan: 2, full-block),
+ [b]
+ )
+)
diff --git a/tests/typ/layout/grid-rowspan-basic.typ b/tests/typ/layout/grid-rowspan-basic.typ
index 1cc7289b..bbd1c047 100644
--- a/tests/typ/layout/grid-rowspan-basic.typ
+++ b/tests/typ/layout/grid-rowspan-basic.typ
@@ -230,3 +230,23 @@
[f],
[g]
)
+
+---
+// Block below shouldn't expand to the end of the page, but stay within its
+// rows' boundaries.
+#set page(height: 9em)
+#table(
+ rows: (1em, 1em, 1fr, 1fr, auto),
+ table.cell(rowspan: 2, block(width: 2em, height: 100%, fill: red)),
+ table.cell(rowspan: 2, block(width: 2em, height: 100%, fill: red)),
+ [a]
+)
+
+---
+#set page(height: 7em)
+#table(
+ columns: 3,
+ [], [], table.cell(breakable: true, rowspan: 2, block(width: 2em, height: 100%, fill: red)),
+ table.cell(breakable: false, block(width: 2em, height: 100%, fill: red)),
+ table.cell(breakable: false, rowspan: 2, block(width: 2em, height: 100%, fill: red)),
+)