diff --git a/crates/typst-pdf/src/tags/table.rs b/crates/typst-pdf/src/tags/table.rs index 362363294..592cb5550 100644 --- a/crates/typst-pdf/src/tags/table.rs +++ b/crates/typst-pdf/src/tags/table.rs @@ -329,269 +329,3 @@ fn table_header_scope(scope: TableHeaderScope) -> krilla::tagging::TableHeaderSc TableHeaderScope::Row => krilla::tagging::TableHeaderScope::Row, } } - -#[cfg(test)] -mod tests { - use pretty_assertions::assert_eq; - use typst_library::foundations::Content; - - use super::*; - - #[track_caller] - fn test(table: TableCtx, exp_tag: TagNode) { - let tag = table.build_table(Vec::new()); - assert_eq!(exp_tag, tag); - } - - #[track_caller] - fn table(cells: [TableCell; SIZE]) -> TableCtx { - let mut table = TableCtx::new(TableId(324), Some("summary".into())); - for cell in cells { - table.insert(&Packed::new(cell), Vec::new()); - } - table - } - - #[track_caller] - fn header_cell( - (x, y): (usize, usize), - level: u32, - scope: TableHeaderScope, - ) -> TableCell { - TableCell::new(Content::default()) - .with_x(Smart::Custom(x)) - .with_y(Smart::Custom(y)) - .with_kind(Smart::Custom(TableCellKind::Header( - NonZeroU32::new(level).unwrap(), - scope, - ))) - } - - #[track_caller] - fn footer_cell(x: usize, y: usize) -> TableCell { - TableCell::new(Content::default()) - .with_x(Smart::Custom(x)) - .with_y(Smart::Custom(y)) - .with_kind(Smart::Custom(TableCellKind::Footer)) - } - - fn cell(x: usize, y: usize) -> TableCell { - TableCell::new(Content::default()) - .with_x(Smart::Custom(x)) - .with_y(Smart::Custom(y)) - .with_kind(Smart::Custom(TableCellKind::Data)) - } - - fn empty_cell(x: usize, y: usize) -> TableCell { - TableCell::new(Content::default()) - .with_x(Smart::Custom(x)) - .with_y(Smart::Custom(y)) - .with_kind(Smart::Auto) - } - - fn table_tag(nodes: [TagNode; SIZE]) -> TagNode { - let tag = Tag::Table.with_summary(Some("summary".into())); - TagNode::Group(tag.into(), nodes.into()) - } - - fn thead(nodes: [TagNode; SIZE]) -> TagNode { - TagNode::Group(Tag::THead.into(), nodes.into()) - } - - fn tbody(nodes: [TagNode; SIZE]) -> TagNode { - TagNode::Group(Tag::TBody.into(), nodes.into()) - } - - fn tfoot(nodes: [TagNode; SIZE]) -> TagNode { - TagNode::Group(Tag::TFoot.into(), nodes.into()) - } - - fn trow(nodes: [TagNode; SIZE]) -> TagNode { - TagNode::Group(Tag::TR.into(), nodes.into()) - } - - fn th( - (x, y): (u32, u32), - scope: TableHeaderScope, - headers: [(u32, u32); SIZE], - ) -> TagNode { - let scope = table_header_scope(scope); - let id = table_cell_id(TableId(324), x, y); - let ids = headers.map(|(x, y)| table_cell_id(TableId(324), x, y)); - TagNode::Group( - Tag::TH(scope) - .with_id(Some(id)) - .with_headers(ids) - .with_location(Some(Span::detached().into_raw())) - .into(), - Vec::new(), - ) - } - - fn td(headers: [(u32, u32); SIZE]) -> TagNode { - let ids = headers.map(|(x, y)| table_cell_id(TableId(324), x, y)); - TagNode::Group( - Tag::TD - .with_headers(ids) - .with_location(Some(Span::detached().into_raw())) - .into(), - Vec::new(), - ) - } - - #[test] - fn simple_table() { - #[rustfmt::skip] - let table = table([ - header_cell((0, 0), 1, TableHeaderScope::Column), - header_cell((1, 0), 1, TableHeaderScope::Column), - header_cell((2, 0), 1, TableHeaderScope::Column), - - cell(0, 1), - cell(1, 1), - cell(2, 1), - - cell(0, 2), - cell(1, 2), - cell(2, 2), - ]); - - #[rustfmt::skip] - let tag = table_tag([ - thead([trow([ - th((0, 0), TableHeaderScope::Column, []), - th((1, 0), TableHeaderScope::Column, []), - th((2, 0), TableHeaderScope::Column, []), - ])]), - tbody([ - trow([ - td([(0, 0)]), - td([(1, 0)]), - td([(2, 0)]), - ]), - trow([ - td([(0, 0)]), - td([(1, 0)]), - td([(2, 0)]), - ]), - ]), - ]); - - test(table, tag); - } - - #[test] - fn header_row_and_column() { - #[rustfmt::skip] - let table = table([ - header_cell((0, 0), 1, TableHeaderScope::Column), - header_cell((1, 0), 1, TableHeaderScope::Column), - header_cell((2, 0), 1, TableHeaderScope::Column), - - header_cell((0, 1), 1, TableHeaderScope::Row), - cell(1, 1), - cell(2, 1), - - header_cell((0, 2), 1, TableHeaderScope::Row), - cell(1, 2), - cell(2, 2), - ]); - - #[rustfmt::skip] - let tag = table_tag([ - trow([ - th((0, 0), TableHeaderScope::Column, []), - th((1, 0), TableHeaderScope::Column, []), - th((2, 0), TableHeaderScope::Column, []), - ]), - trow([ - th((0, 1), TableHeaderScope::Row, [(0, 0)]), - td([(1, 0), (0, 1)]), - td([(2, 0), (0, 1)]), - ]), - trow([ - th((0, 2), TableHeaderScope::Row, [(0, 0)]), - td([(1, 0), (0, 2)]), - td([(2, 0), (0, 2)]), - ]), - ]); - - test(table, tag); - } - - #[test] - fn complex_tables() { - #[rustfmt::skip] - let table = table([ - header_cell((0, 0), 1, TableHeaderScope::Column), - header_cell((1, 0), 1, TableHeaderScope::Column), - header_cell((2, 0), 1, TableHeaderScope::Column), - - header_cell((0, 1), 2, TableHeaderScope::Column), - header_cell((1, 1), 2, TableHeaderScope::Column), - header_cell((2, 1), 2, TableHeaderScope::Column), - - cell(0, 2), - empty_cell(1, 2), // the type of empty cells is inferred from the row - cell(2, 2), - - header_cell((0, 3), 2, TableHeaderScope::Column), - header_cell((1, 3), 2, TableHeaderScope::Column), - empty_cell(2, 3), // the type of empty cells is inferred from the row - - cell(0, 4), - cell(1, 4), - empty_cell(2, 4), - - empty_cell(0, 5), // the type of empty cells is inferred from the row - footer_cell(1, 5), - footer_cell(2, 5), - ]); - - #[rustfmt::skip] - let tag = table_tag([ - thead([ - trow([ - th((0, 0), TableHeaderScope::Column, []), - th((1, 0), TableHeaderScope::Column, []), - th((2, 0), TableHeaderScope::Column, []), - ]), - trow([ - th((0, 1), TableHeaderScope::Column, [(0, 0)]), - th((1, 1), TableHeaderScope::Column, [(1, 0)]), - th((2, 1), TableHeaderScope::Column, [(2, 0)]), - ]), - ]), - tbody([ - trow([ - td([(0, 1)]), - td([(1, 1)]), - td([(2, 1)]), - ]), - ]), - thead([ - trow([ - th((0, 3), TableHeaderScope::Column, [(0, 0)]), - th((1, 3), TableHeaderScope::Column, [(1, 0)]), - th((2, 3), TableHeaderScope::Column, [(2, 0)]), - ]), - ]), - tbody([ - trow([ - td([(0, 3)]), - td([(1, 3)]), - td([(2, 3)]), - ]), - ]), - tfoot([ - trow([ - td([(0, 3)]), - td([(1, 3)]), - td([(2, 3)]), - ]), - ]), - ]); - - test(table, tag); - } -} diff --git a/tests/ref/pdftags/table-tags-basic.yml b/tests/ref/pdftags/table-tags-basic.yml new file mode 100644 index 000000000..035982722 --- /dev/null +++ b/tests/ref/pdftags/table-tags-basic.yml @@ -0,0 +1,68 @@ +- Tag: Table + /BBox: + page: 0 + left: 9.500 + top: 9.500 + right: 76.350 + bottom: 60.240 + /K: + - Artifact + - Artifact + - Artifact + - Artifact + - Artifact + - Artifact + - Artifact + - Artifact + - Tag: THead + /K: + - Tag: TR + /K: + - Tag: TH + /Id: "U1x0y0" + /Scope: Column + /Headers: [] + /K: + - Content: page=0 mcid=0 + - Tag: TH + /Id: "U1x1y0" + /Scope: Column + /Headers: [] + /K: + - Content: page=0 mcid=1 + - Tag: TH + /Id: "U1x2y0" + /Scope: Column + /Headers: [] + /K: + - Content: page=0 mcid=2 + - Tag: TBody + /K: + - Tag: TR + /K: + - Tag: TD + /Headers: ["U1x0y0"] + /K: + - Content: page=0 mcid=3 + - Tag: TD + /Headers: ["U1x1y0"] + /K: + - Content: page=0 mcid=4 + - Tag: TD + /Headers: ["U1x2y0"] + /K: + - Content: page=0 mcid=5 + - Tag: TR + /K: + - Tag: TD + /Headers: ["U1x0y0"] + /K: + - Content: page=0 mcid=6 + - Tag: TD + /Headers: ["U1x1y0"] + /K: + - Content: page=0 mcid=7 + - Tag: TD + /Headers: ["U1x2y0"] + /K: + - Content: page=0 mcid=8 diff --git a/tests/ref/pdftags/table-tags-column-and-row-header.yml b/tests/ref/pdftags/table-tags-column-and-row-header.yml new file mode 100644 index 000000000..eac8100f7 --- /dev/null +++ b/tests/ref/pdftags/table-tags-column-and-row-header.yml @@ -0,0 +1,68 @@ +- Tag: Table + /BBox: + page: 0 + left: 9.500 + top: 9.500 + right: 85.360 + bottom: 60.240 + /K: + - Artifact + - Artifact + - Artifact + - Artifact + - Artifact + - Artifact + - Artifact + - Artifact + - Tag: TR + /K: + - Tag: TH + /Id: "U1x0y0" + /Scope: Column + /Headers: [] + /K: + - Content: page=0 mcid=0 + - Tag: TH + /Id: "U1x1y0" + /Scope: Column + /Headers: [] + /K: + - Content: page=0 mcid=1 + - Tag: TH + /Id: "U1x2y0" + /Scope: Column + /Headers: [] + /K: + - Content: page=0 mcid=2 + - Tag: TR + /K: + - Tag: TH + /Id: "U1x0y1" + /Scope: Row + /Headers: ["U1x0y0"] + /K: + - Content: page=0 mcid=3 + - Tag: TD + /Headers: ["U1x1y0", "U1x0y1"] + /K: + - Content: page=0 mcid=4 + - Tag: TD + /Headers: ["U1x2y0", "U1x0y1"] + /K: + - Content: page=0 mcid=5 + - Tag: TR + /K: + - Tag: TH + /Id: "U1x0y2" + /Scope: Row + /Headers: ["U1x0y0"] + /K: + - Content: page=0 mcid=6 + - Tag: TD + /Headers: ["U1x1y0", "U1x0y2"] + /K: + - Content: page=0 mcid=7 + - Tag: TD + /Headers: ["U1x2y0", "U1x0y2"] + /K: + - Content: page=0 mcid=8 diff --git a/tests/ref/pdftags/table-tags-missing-cells.yml b/tests/ref/pdftags/table-tags-missing-cells.yml new file mode 100644 index 000000000..8b031729d --- /dev/null +++ b/tests/ref/pdftags/table-tags-missing-cells.yml @@ -0,0 +1,115 @@ +- Tag: Table + /BBox: + page: 0 + left: 9.500 + top: 9.500 + right: 76.350 + bottom: 96.820 + /K: + - Artifact + - Artifact + - Artifact + - Artifact + - Artifact + - Artifact + - Artifact + - Artifact + - Artifact + - Artifact + - Artifact + - Tag: THead + /K: + - Tag: TR + /K: + - Tag: TH + /Id: "U1x0y0" + /Scope: Column + /Headers: [] + /K: + - Content: page=0 mcid=0 + - Tag: TH + /Id: "U1x1y0" + /Scope: Column + /Headers: [] + /K: + - Content: page=0 mcid=1 + - Tag: TH + /Id: "U1x2y0" + /Scope: Column + /Headers: [] + /K: + - Content: page=0 mcid=2 + - Tag: TR + /K: + - Tag: TH + /Id: "U1x0y1" + /Scope: Column + /Headers: ["U1x0y0"] + /K: + - Content: page=0 mcid=3 + - Tag: TH + /Id: "U1x1y1" + /Scope: Column + /Headers: ["U1x1y0"] + /K: + - Content: page=0 mcid=4 + - Tag: TH + /Id: "U1x2y1" + /Scope: Column + /Headers: ["U1x2y0"] + /K: + - Content: page=0 mcid=5 + - Tag: TBody + /K: + - Tag: TR + /K: + - Tag: TD + /Headers: ["U1x0y1"] + - Tag: TD + /Headers: ["U1x1y1"] + - Tag: TD + /Headers: ["U1x2y1"] + - Tag: THead + /K: + - Tag: TR + /K: + - Tag: TH + /Id: "U1x0y3" + /Scope: Column + /Headers: ["U1x0y0"] + /K: + - Content: page=0 mcid=6 + - Tag: TH + /Id: "U1x1y3" + /Scope: Column + /Headers: ["U1x1y0"] + /K: + - Content: page=0 mcid=7 + - Tag: TH + /Id: "U1x2y3" + /Scope: Column + /Headers: ["U1x2y0"] + - Tag: TBody + /K: + - Tag: TR + /K: + - Tag: TD + /Headers: ["U1x0y3"] + - Tag: TD + /Headers: ["U1x1y3"] + - Tag: TD + /Headers: ["U1x2y3"] + - Tag: TFoot + /K: + - Tag: TR + /K: + - Tag: TD + /Headers: ["U1x0y3"] + - Tag: TD + /Headers: ["U1x1y3"] + /K: + - Content: page=0 mcid=8 + - Tag: TD + /Headers: ["U1x2y3"] + /K: + - Content: page=0 mcid=9 diff --git a/tests/suite/pdftags/table.typ b/tests/suite/pdftags/table.typ new file mode 100644 index 000000000..c053ee991 --- /dev/null +++ b/tests/suite/pdftags/table.typ @@ -0,0 +1,37 @@ +--- table-tags-basic pdftags --- +#table( + columns: 3, + table.header([H1], [H2], [H3]), + [a1], [a2], [a3], + [b1], [b2], [b3], +) + +--- table-tags-column-and-row-header pdftags --- +#table( + columns: 3, + table.header([H1], [H2], [H3]), + pdf.header-cell(scope: "row")[10:00], [a2], [a3], + pdf.header-cell(scope: "row")[12:30], [b2], [b3], +) + +--- table-tags-missing-cells pdftags --- +#table( + columns: 3, + table.header(level: 1, [H1], [H1], [H1]), + table.header(level: 2, [H2], [H2], [H2]), + + // the middle cell is missing + table.cell(x: 0)[], + table.cell(x: 2)[], + + // the last cell is missing, its type should be inferred from the row + table.header(level: 2, [H2], [H2]), + + // last cell is missing + [], [], + + table.footer( + table.cell(x: 1)[F], + table.cell(x: 2)[F], + ), +)