Fix table cells and rowspans wrongly assuming full page height available (#3637)

This commit is contained in:
PgBiel 2024-03-13 06:15:16 -03:00 committed by GitHub
parent fd2eb0ceb2
commit 48820fe69b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 91 additions and 6 deletions

View File

@ -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();

View File

@ -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(())
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

After

Width:  |  Height:  |  Size: 98 KiB

View File

@ -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]
)
)

View File

@ -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)),
)