summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPgBiel <9021226+PgBiel@users.noreply.github.com>2024-03-11 07:24:51 -0300
committerGitHub <noreply@github.com>2024-03-11 10:24:51 +0000
commit288f7da4d08ede7583e920cab0ac126506d92d3e (patch)
tree2f259fe3be2392f59c803ba379963d1f7f1fc290
parentc29db5f27e9f7b6865dc540a63d9fe269dfaac1b (diff)
Small fixes for table line priority in headers/footers (#3602)
-rw-r--r--crates/typst/src/layout/grid/layout.rs66
-rw-r--r--crates/typst/src/layout/grid/lines.rs4
-rw-r--r--tests/ref/layout/grid-footers-5.pngbin5769 -> 17228 bytes
-rw-r--r--tests/ref/layout/grid-headers-4.pngbin36452 -> 41733 bytes
-rw-r--r--tests/typ/layout/grid-footers-5.typ13
-rw-r--r--tests/typ/layout/grid-headers-4.typ41
6 files changed, 105 insertions, 19 deletions
diff --git a/crates/typst/src/layout/grid/layout.rs b/crates/typst/src/layout/grid/layout.rs
index 354e6814..772c107e 100644
--- a/crates/typst/src/layout/grid/layout.rs
+++ b/crates/typst/src/layout/grid/layout.rs
@@ -1731,21 +1731,44 @@ impl<'a> GridLayouter<'a> {
})
.unwrap_or(LinePosition::Before);
+ // FIXME: In the future, directly specify in 'self.rrows' when
+ // we place a repeated header rather than its original rows.
+ // That would let us remove most of those verbose checks, both
+ // in 'lines.rs' and here. Those checks also aren't fully
+ // accurate either, since they will also trigger when some rows
+ // have been removed between the header and what's below it.
+ let is_under_repeated_header = self
+ .grid
+ .header
+ .as_ref()
+ .and_then(Repeatable::as_repeated)
+ .zip(prev_y)
+ .is_some_and(|(header, prev_y)| {
+ // Note: 'y == header.end' would mean we're right below
+ // the NON-REPEATED header, so that case should return
+ // false.
+ prev_y < header.end && y > header.end
+ });
+
// If some grid rows were omitted between the previous resolved
// row and the current one, we ensure lines below the previous
// row don't "disappear" and are considered, albeit with less
// priority. However, don't do this when we're below a header,
// as it must have more priority instead of less, so it is
- // chained later instead of before.
+ // chained later instead of before. The exception is when the
+ // last row in the header is removed, in which case we append
+ // both the lines under the row above us and also (later) the
+ // lines under the header's (removed) last row.
let prev_lines = prev_y
.filter(|prev_y| {
prev_y + 1 != y
- && !self
- .grid
- .header
- .as_ref()
- .and_then(Repeatable::as_repeated)
- .is_some_and(|header| prev_y + 1 == header.end)
+ && (!is_under_repeated_header
+ || self
+ .grid
+ .header
+ .as_ref()
+ .and_then(Repeatable::as_repeated)
+ .is_some_and(|header| prev_y + 1 != header.end))
})
.map(|prev_y| get_hlines_at(prev_y + 1))
.unwrap_or(&[]);
@@ -1765,15 +1788,16 @@ impl<'a> GridLayouter<'a> {
&[]
};
- // The header lines, if any, will correspond to the lines under
- // the previous row, so they function similarly to 'prev_lines'.
- let expected_header_line_position = expected_prev_line_position;
+ let mut expected_header_line_position = LinePosition::Before;
let header_hlines = if let Some((Repeatable::Repeated(header), prev_y)) =
self.grid.header.as_ref().zip(prev_y)
{
- if prev_y + 1 != y
- && prev_y + 1 == header.end
- && !self.grid.has_gutter
+ if is_under_repeated_header
+ && (!self.grid.has_gutter
+ || matches!(
+ self.grid.rows[prev_y],
+ Sizing::Rel(length) if length.is_zero()
+ ))
{
// For lines below a header, give priority to the
// lines originally below the header rather than
@@ -1783,10 +1807,18 @@ impl<'a> GridLayouter<'a> {
// lines being normally laid out then will be
// precisely the lines below the header.
//
- // Additionally, we don't append header lines when
- // gutter is enabled, since, in that case, there will
- // be a gutter row between header and content, so no
- // lines should overlap.
+ // Additionally, we don't repeat lines above the row
+ // below the header when gutter is enabled, since, in
+ // that case, there will be a gutter row between header
+ // and content, so no lines should overlap. The
+ // exception is when the gutter at the end of the
+ // header has a size of zero, which happens when only
+ // column-gutter is specified, for example. In that
+ // case, we still repeat the line under the gutter.
+ expected_header_line_position = expected_line_position(
+ header.end,
+ header.end == self.grid.rows.len(),
+ );
get_hlines_at(header.end)
} else {
&[]
diff --git a/crates/typst/src/layout/grid/lines.rs b/crates/typst/src/layout/grid/lines.rs
index 1249c3e9..c976da2d 100644
--- a/crates/typst/src/layout/grid/lines.rs
+++ b/crates/typst/src/layout/grid/lines.rs
@@ -547,7 +547,7 @@ pub(super) fn hline_stroke_at_column(
// Ensure the row above us is a repeated header.
// FIXME: Make this check more robust when headers at arbitrary
// positions are added.
- local_top_y + 1 == header.end && y != header.end
+ local_top_y < header.end && y > header.end
});
// Prioritize the footer's top stroke as well where applicable.
@@ -559,7 +559,7 @@ pub(super) fn hline_stroke_at_column(
// Ensure the row below us is a repeated footer.
// FIXME: Make this check more robust when footers at arbitrary
// positions are added.
- local_top_y.unwrap_or(0) + 1 != footer.start && y == footer.start
+ local_top_y.unwrap_or(0) + 1 < footer.start && y >= footer.start
});
let (prioritized_cell_stroke, deprioritized_cell_stroke) =
diff --git a/tests/ref/layout/grid-footers-5.png b/tests/ref/layout/grid-footers-5.png
index b58ed266..0cfd2d66 100644
--- a/tests/ref/layout/grid-footers-5.png
+++ b/tests/ref/layout/grid-footers-5.png
Binary files differ
diff --git a/tests/ref/layout/grid-headers-4.png b/tests/ref/layout/grid-headers-4.png
index e60877d8..be6080f7 100644
--- a/tests/ref/layout/grid-headers-4.png
+++ b/tests/ref/layout/grid-headers-4.png
Binary files differ
diff --git a/tests/typ/layout/grid-footers-5.typ b/tests/typ/layout/grid-footers-5.typ
index db2489fd..874fcd2e 100644
--- a/tests/typ/layout/grid-footers-5.typ
+++ b/tests/typ/layout/grid-footers-5.typ
@@ -26,3 +26,16 @@
gutter: 3pt,
table.footer[a][b][c]
)
+
+---
+// Test footer stroke priority edge case
+#set page(height: 10em)
+#table(
+ columns: 2,
+ stroke: black,
+ ..(table.cell(stroke: aqua)[d],) * 8,
+ table.footer(
+ table.cell(rowspan: 2, colspan: 2)[a],
+ [c], [d]
+ )
+)
diff --git a/tests/typ/layout/grid-headers-4.typ b/tests/typ/layout/grid-headers-4.typ
index 13fd41dd..6ede601c 100644
--- a/tests/typ/layout/grid-headers-4.typ
+++ b/tests/typ/layout/grid-headers-4.typ
@@ -56,3 +56,44 @@
),
[a\ b]
)
+
+---
+// Test header stroke priority edge case (last header row removed)
+#set page(height: 8em)
+#table(
+ columns: 2,
+ stroke: black,
+ gutter: (auto, 3pt),
+ table.header(
+ [c], [d],
+ ),
+ ..(table.cell(stroke: aqua)[d],) * 8,
+)
+
+---
+// Yellow line should be kept here
+#set text(6pt)
+#table(
+ column-gutter: 3pt,
+ inset: 1pt,
+ table.header(
+ [a],
+ table.hline(stroke: yellow),
+ ),
+ table.cell(rowspan: 2)[b]
+)
+
+---
+// Red line should be kept here
+#set page(height: 6em)
+#set text(6pt)
+#table(
+ column-gutter: 3pt,
+ inset: 1pt,
+ table.header(
+ table.hline(stroke: red, position: bottom),
+ [a],
+ ),
+ [a],
+ table.cell(stroke: aqua)[b]
+)