diff --git a/R/typst_style.R b/R/typst_style.R index 53bcf98f0..0310396d6 100644 --- a/R/typst_style.R +++ b/R/typst_style.R @@ -240,6 +240,14 @@ typst_hlines <- function(x, lin) { tmp <- split(lin, list(lin$i, lin$line, lin$line_color_mapped, lin$line_width)) tmp <- Filter(function(x) nrow(x) > 0, tmp) tmp <- lapply(tmp, function(k) { + # Drop duplicate cell-level entries. These arise routinely whenever + # theme_tinytable() and a user style_tt() declare the same line (same + # i, j, line, color, width, trim). Without deduplication, + # typst_split_chunks() sees phantom breaks in diff(x) != 1 and emits a + # chain of fragmented table.hline()/table.vline() segments that cover + # the same span as a single line. See test-typst-line-dedup.R. + k <- k[!duplicated(k), , drop = FALSE] + # Split chunks based on consecutive columns AND line_trim boundaries # line_trim marks group boundaries - split chunks at these points chunks <- typst_split_chunks(k$j) @@ -462,6 +470,8 @@ typst_vlines <- function(x, lin) { lin <- split(lin, list(lin$j, lin$line, lin$line_color_mapped, lin$line_width)) lin <- Filter(function(x) nrow(x) > 0, lin) lin <- lapply(lin, function(k) { + # Drop duplicate cell-level entries; see typst_hlines() for rationale. + k <- k[!duplicated(k), , drop = FALSE] ymin <- typst_split_chunks(k$i)$min ymax <- typst_split_chunks(k$i)$max xmin <- k$j[1] diff --git a/inst/tinytest/_tinysnapshot/typst-issue592.typ b/inst/tinytest/_tinysnapshot/typst-issue592.typ index ba40cf261..b4d19daec 100644 --- a/inst/tinytest/_tinysnapshot/typst-issue592.typ +++ b/inst/tinytest/_tinysnapshot/typst-issue592.typ @@ -78,11 +78,7 @@ block[ // start block table.hline(y: 14, start: 1, end: 2, stroke: 0.1em + black), table.hline(y: 14, start: 3, end: 7, stroke: 0.1em + black), table.hline(y: 15, start: 1, end: 2, stroke: 0.1em + black), table.hline(y: 15, start: 3, end: 7, stroke: 0.1em + black), table.hline(y: 16, start: 2, end: 3, stroke: 0.1em + black), - table.vline(x: 0, start: 0, end: 1, stroke: 0.1em + black), - table.vline(x: 0, start: 0, end: 2, stroke: 0.1em + black), - table.vline(x: 0, start: 1, end: 7, stroke: 0.1em + black), - table.vline(x: 0, start: 6, end: 12, stroke: 0.1em + black), - table.vline(x: 0, start: 11, end: 16, stroke: 0.1em + black), + table.vline(x: 0, start: 0, end: 16, stroke: 0.1em + black), table.vline(x: 1, start: 0, end: 16, stroke: 0.1em + black), table.vline(x: 2, start: 0, end: 1, stroke: 0.1em + black), table.vline(x: 3, start: 0, end: 1, stroke: 0.1em + black), @@ -129,4 +125,4 @@ table.cell(rowspan: 3)[3], [x], [80.9%], [78.4%], [ ], [55.6%], // tinytable align-figure after ] // end block -) // end figure +) // end figure \ No newline at end of file diff --git a/inst/tinytest/test-bugfix.R b/inst/tinytest/test-bugfix.R index 7478049b3..907556306 100644 --- a/inst/tinytest/test-bugfix.R +++ b/inst/tinytest/test-bugfix.R @@ -58,3 +58,36 @@ local({ expect_true(grepl("1,23", typst, fixed = TRUE)) expect_true(grepl("1,23", tabulator, fixed = TRUE)) }) + + +# Regression test: typst_hlines/typst_vlines used to emit fragmented chains of +# table.hline()/table.vline() segments covering the same span whenever +# theme_tinytable() and the user declared the same line (or any line was +# declared twice with identical properties). This produced redundant, ugly +# output and slowed down rendering. The fix deduplicates cell-level entries +# inside the typst line processors. + +# Case 1: user re-declares the theme's middle "below colnames" line. +# Before the fix this produced 6 fragmented hline segments at y=1. +tab <- tt(mtcars[1:5, 1:5]) |> + style_tt(i = 0, line = "b", line_color = "black", line_width = 0.05) +out <- save_tt(tab, "typst") +hlines <- strsplit(out, "\n", fixed = TRUE)[[1]] +hlines <- hlines[grepl("table.hline", hlines, fixed = TRUE)] +middle <- hlines[grepl("y: 1,", hlines, fixed = TRUE)] +expect_true(length(middle) >= 1) +# The whole row should contain exactly one table.hline(y: 1, ...) segment +n_middle_segments <- lengths(regmatches(middle, gregexpr("table[.]hline", middle))) +expect_true(n_middle_segments == 1) + +# Case 2: user adds the same line twice -> should still produce a single segment +tab <- tt(mtcars[1:5, 1:5]) |> + style_tt(i = 2, line = "b", line_color = "pink") |> + style_tt(i = 2, line = "b", line_color = "pink") +out <- save_tt(tab, "typst") +hlines <- strsplit(out, "\n", fixed = TRUE)[[1]] +hlines <- hlines[grepl("table.hline", hlines, fixed = TRUE)] +dup <- hlines[grepl("y: 3,", hlines, fixed = TRUE)] +expect_true(length(dup) >= 1) +n_dup_segments <- lengths(regmatches(dup, gregexpr("table[.]hline", dup))) +expect_true(n_dup_segments == 1)