mirror of
https://github.com/typst/typst
synced 2025-08-23 19:24:14 +08:00
Compare commits
No commits in common. "edd213074f476b283374c4e6bcb39ede5cf17e39" and "7d5b9a716f48df3f7e9ca35839defcc2ee131b33" have entirely different histories.
edd213074f
...
7d5b9a716f
@ -22,7 +22,7 @@ use typst_syntax::Span;
|
|||||||
use typst_utils::NonZeroExt;
|
use typst_utils::NonZeroExt;
|
||||||
|
|
||||||
use crate::introspection::SplitLocator;
|
use crate::introspection::SplitLocator;
|
||||||
use crate::pdf::{TableCellKind, TableHeaderScope};
|
use crate::model::{TableCellKind, TableHeaderScope};
|
||||||
|
|
||||||
/// Convert a grid to a cell grid.
|
/// Convert a grid to a cell grid.
|
||||||
#[typst_macros::time(span = elem.span())]
|
#[typst_macros::time(span = elem.span())]
|
||||||
@ -226,7 +226,7 @@ impl ResolvableCell for Packed<TableCell> {
|
|||||||
let breakable = cell.breakable(styles).unwrap_or(breakable);
|
let breakable = cell.breakable(styles).unwrap_or(breakable);
|
||||||
let fill = cell.fill(styles).unwrap_or_else(|| fill.clone());
|
let fill = cell.fill(styles).unwrap_or_else(|| fill.clone());
|
||||||
|
|
||||||
let kind = cell.kind().copied().unwrap_or_default().or(kind);
|
let kind = cell.kind(styles).or(kind);
|
||||||
|
|
||||||
let cell_stroke = cell.stroke(styles);
|
let cell_stroke = cell.stroke(styles);
|
||||||
let stroke_overridden =
|
let stroke_overridden =
|
||||||
|
@ -2,13 +2,14 @@ use std::num::{NonZeroU32, NonZeroUsize};
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use ecow::EcoString;
|
use ecow::EcoString;
|
||||||
|
use typst_macros::Cast;
|
||||||
use typst_utils::NonZeroExt;
|
use typst_utils::NonZeroExt;
|
||||||
|
|
||||||
use crate::diag::{bail, HintedStrResult, HintedString, SourceResult};
|
use crate::diag::{bail, HintedStrResult, HintedString, SourceResult};
|
||||||
use crate::engine::Engine;
|
use crate::engine::Engine;
|
||||||
use crate::foundations::{
|
use crate::foundations::{
|
||||||
cast, elem, scope, Content, NativeElement, Packed, Show, Smart, StyleChain,
|
cast, dict, elem, scope, Content, Dict, NativeElement, Packed, Show, Smart,
|
||||||
TargetElem,
|
StyleChain, TargetElem,
|
||||||
};
|
};
|
||||||
use crate::html::{attr, tag, HtmlAttrs, HtmlElem, HtmlTag};
|
use crate::html::{attr, tag, HtmlAttrs, HtmlElem, HtmlTag};
|
||||||
use crate::introspection::{Locatable, Locator};
|
use crate::introspection::{Locatable, Locator};
|
||||||
@ -19,7 +20,6 @@ use crate::layout::{
|
|||||||
TrackSizings,
|
TrackSizings,
|
||||||
};
|
};
|
||||||
use crate::model::Figurable;
|
use crate::model::Figurable;
|
||||||
use crate::pdf::TableCellKind;
|
|
||||||
use crate::text::LocalName;
|
use crate::text::LocalName;
|
||||||
use crate::visualize::{Paint, Stroke};
|
use crate::visualize::{Paint, Stroke};
|
||||||
|
|
||||||
@ -811,8 +811,7 @@ pub struct TableCell {
|
|||||||
#[fold]
|
#[fold]
|
||||||
pub stroke: Sides<Option<Option<Arc<Stroke>>>>,
|
pub stroke: Sides<Option<Option<Arc<Stroke>>>>,
|
||||||
|
|
||||||
#[internal]
|
// TODO: feature gate
|
||||||
#[synthesized]
|
|
||||||
pub kind: Smart<TableCellKind>,
|
pub kind: Smart<TableCellKind>,
|
||||||
|
|
||||||
/// Whether rows spanned by this cell can be placed in different pages.
|
/// Whether rows spanned by this cell can be placed in different pages.
|
||||||
@ -852,3 +851,65 @@ impl From<Content> for TableCell {
|
|||||||
value.unpack::<Self>().unwrap_or_else(Self::new)
|
value.unpack::<Self>().unwrap_or_else(Self::new)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
|
pub enum TableCellKind {
|
||||||
|
Header(NonZeroU32, TableHeaderScope),
|
||||||
|
Footer,
|
||||||
|
#[default]
|
||||||
|
Data,
|
||||||
|
}
|
||||||
|
|
||||||
|
cast! {
|
||||||
|
TableCellKind,
|
||||||
|
self => match self {
|
||||||
|
Self::Header(level, scope) => dict! { "level" => level, "scope" => scope }.into_value(),
|
||||||
|
Self::Footer => "footer".into_value(),
|
||||||
|
Self::Data => "data".into_value(),
|
||||||
|
},
|
||||||
|
"header" => Self::Header(NonZeroU32::ONE, TableHeaderScope::default()),
|
||||||
|
"footer" => Self::Footer,
|
||||||
|
"data" => Self::Data,
|
||||||
|
mut dict: Dict => {
|
||||||
|
// TODO: have a `pdf.header` function instead?
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Cast)]
|
||||||
|
enum HeaderKind {
|
||||||
|
Header,
|
||||||
|
}
|
||||||
|
dict.take("kind")?.cast::<HeaderKind>()?;
|
||||||
|
let level = dict.take("level").ok().map(|v| v.cast()).transpose()?;
|
||||||
|
let scope = dict.take("scope").ok().map(|v| v.cast()).transpose()?;
|
||||||
|
dict.finish(&["kind", "level", "scope"])?;
|
||||||
|
Self::Header(level.unwrap_or(NonZeroU32::ONE), scope.unwrap_or_default())
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The scope of a table header cell.
|
||||||
|
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash, Cast)]
|
||||||
|
pub enum TableHeaderScope {
|
||||||
|
/// The header cell refers to both the row and the column.
|
||||||
|
Both,
|
||||||
|
/// The header cell refers to the column.
|
||||||
|
#[default]
|
||||||
|
Column,
|
||||||
|
/// The header cell refers to the row.
|
||||||
|
Row,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TableHeaderScope {
|
||||||
|
pub fn refers_to_column(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
TableHeaderScope::Both => true,
|
||||||
|
TableHeaderScope::Column => true,
|
||||||
|
TableHeaderScope::Row => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn refers_to_row(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
TableHeaderScope::Both => true,
|
||||||
|
TableHeaderScope::Column => false,
|
||||||
|
TableHeaderScope::Row => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,14 +1,11 @@
|
|||||||
use std::num::NonZeroU32;
|
|
||||||
|
|
||||||
use ecow::EcoString;
|
use ecow::EcoString;
|
||||||
use typst_macros::{cast, elem, func, Cast};
|
use typst_macros::{cast, elem, Cast};
|
||||||
use typst_utils::NonZeroExt;
|
|
||||||
|
|
||||||
use crate::diag::SourceResult;
|
use crate::diag::SourceResult;
|
||||||
use crate::engine::Engine;
|
use crate::engine::Engine;
|
||||||
use crate::foundations::{Content, NativeElement, Packed, Show, Smart, StyleChain};
|
use crate::foundations::{Content, Packed, Show, StyleChain};
|
||||||
use crate::introspection::Locatable;
|
use crate::introspection::Locatable;
|
||||||
use crate::model::TableCell;
|
use crate::model::TableHeaderScope;
|
||||||
|
|
||||||
// TODO: docs
|
// TODO: docs
|
||||||
#[elem(Locatable, Show)]
|
#[elem(Locatable, Show)]
|
||||||
@ -213,68 +210,3 @@ impl Show for Packed<ArtifactElem> {
|
|||||||
Ok(self.body.clone())
|
Ok(self.body.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: feature gate
|
|
||||||
/// Explicity define this cell as a header cell.
|
|
||||||
#[func]
|
|
||||||
pub fn header_cell(
|
|
||||||
#[named]
|
|
||||||
#[default(NonZeroU32::ONE)]
|
|
||||||
level: NonZeroU32,
|
|
||||||
#[named]
|
|
||||||
#[default]
|
|
||||||
scope: TableHeaderScope,
|
|
||||||
/// The table cell.
|
|
||||||
cell: TableCell,
|
|
||||||
) -> Content {
|
|
||||||
cell.with_kind(Smart::Custom(TableCellKind::Header(level, scope)))
|
|
||||||
.pack()
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: feature gate
|
|
||||||
/// Explicity define this cell as a data cell.
|
|
||||||
#[func]
|
|
||||||
pub fn data_cell(
|
|
||||||
/// The table cell.
|
|
||||||
cell: TableCell,
|
|
||||||
) -> Content {
|
|
||||||
cell.with_kind(Smart::Custom(TableCellKind::Data)).pack()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
|
|
||||||
pub enum TableCellKind {
|
|
||||||
Header(NonZeroU32, TableHeaderScope),
|
|
||||||
Footer,
|
|
||||||
#[default]
|
|
||||||
Data,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The scope of a table header cell.
|
|
||||||
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash, Cast)]
|
|
||||||
pub enum TableHeaderScope {
|
|
||||||
/// The header cell refers to both the row and the column.
|
|
||||||
Both,
|
|
||||||
/// The header cell refers to the column.
|
|
||||||
#[default]
|
|
||||||
Column,
|
|
||||||
/// The header cell refers to the row.
|
|
||||||
Row,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TableHeaderScope {
|
|
||||||
pub fn refers_to_column(&self) -> bool {
|
|
||||||
match self {
|
|
||||||
TableHeaderScope::Both => true,
|
|
||||||
TableHeaderScope::Column => true,
|
|
||||||
TableHeaderScope::Row => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn refers_to_row(&self) -> bool {
|
|
||||||
match self {
|
|
||||||
TableHeaderScope::Both => true,
|
|
||||||
TableHeaderScope::Column => false,
|
|
||||||
TableHeaderScope::Row => true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -15,7 +15,5 @@ pub fn module() -> Module {
|
|||||||
pdf.define_elem::<EmbedElem>();
|
pdf.define_elem::<EmbedElem>();
|
||||||
pdf.define_elem::<PdfTagElem>();
|
pdf.define_elem::<PdfTagElem>();
|
||||||
pdf.define_elem::<ArtifactElem>();
|
pdf.define_elem::<ArtifactElem>();
|
||||||
pdf.define_func::<header_cell>();
|
|
||||||
pdf.define_func::<data_cell>();
|
|
||||||
Module::new("pdf", pdf)
|
Module::new("pdf", pdf)
|
||||||
}
|
}
|
||||||
|
@ -181,7 +181,7 @@ pub(crate) fn handle_end(gc: &mut GlobalContext, loc: Location) {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
table_ctx.insert(&cell, entry.nodes);
|
table_ctx.insert(cell, entry.nodes);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
StackEntryKind::Link(_, link) => {
|
StackEntryKind::Link(_, link) => {
|
||||||
|
@ -7,8 +7,7 @@ use krilla::tagging::{
|
|||||||
};
|
};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use typst_library::foundations::{Packed, Smart, StyleChain};
|
use typst_library::foundations::{Packed, Smart, StyleChain};
|
||||||
use typst_library::model::TableCell;
|
use typst_library::model::{TableCell, TableCellKind, TableHeaderScope};
|
||||||
use typst_library::pdf::{TableCellKind, TableHeaderScope};
|
|
||||||
|
|
||||||
use crate::tags::{TableId, TagNode};
|
use crate::tags::{TableId, TagNode};
|
||||||
|
|
||||||
@ -55,12 +54,12 @@ impl TableCtx {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn insert(&mut self, cell: &TableCell, nodes: Vec<TagNode>) {
|
pub(crate) fn insert(&mut self, cell: Packed<TableCell>, nodes: Vec<TagNode>) {
|
||||||
let x = cell.x(StyleChain::default()).unwrap_or_else(|| unreachable!());
|
let x = cell.x(StyleChain::default()).unwrap_or_else(|| unreachable!());
|
||||||
let y = cell.y(StyleChain::default()).unwrap_or_else(|| unreachable!());
|
let y = cell.y(StyleChain::default()).unwrap_or_else(|| unreachable!());
|
||||||
let rowspan = cell.rowspan(StyleChain::default());
|
let rowspan = cell.rowspan(StyleChain::default());
|
||||||
let colspan = cell.colspan(StyleChain::default());
|
let colspan = cell.colspan(StyleChain::default());
|
||||||
let kind = cell.kind().copied().expect("kind to be set after layouting");
|
let kind = cell.kind(StyleChain::default());
|
||||||
|
|
||||||
// Extend the table grid to fit this cell.
|
// Extend the table grid to fit this cell.
|
||||||
let required_height = y + rowspan.get();
|
let required_height = y + rowspan.get();
|
||||||
@ -85,7 +84,7 @@ impl TableCtx {
|
|||||||
x: x.saturating_as(),
|
x: x.saturating_as(),
|
||||||
y: y.saturating_as(),
|
y: y.saturating_as(),
|
||||||
rowspan: rowspan.try_into().unwrap_or(NonZeroU32::MAX),
|
rowspan: rowspan.try_into().unwrap_or(NonZeroU32::MAX),
|
||||||
colspan: colspan.try_into().unwrap_or(NonZeroU32::MAX),
|
colspan: rowspan.try_into().unwrap_or(NonZeroU32::MAX),
|
||||||
kind,
|
kind,
|
||||||
headers: TagIdRefs::NONE,
|
headers: TagIdRefs::NONE,
|
||||||
nodes,
|
nodes,
|
||||||
@ -134,7 +133,7 @@ impl TableCtx {
|
|||||||
|
|
||||||
// Explicitly set the headers attribute for cells.
|
// Explicitly set the headers attribute for cells.
|
||||||
for x in 0..width {
|
for x in 0..width {
|
||||||
let mut column_header = Vec::new();
|
let mut column_header = None;
|
||||||
for y in 0..height {
|
for y in 0..height {
|
||||||
self.resolve_cell_headers(
|
self.resolve_cell_headers(
|
||||||
(x, y),
|
(x, y),
|
||||||
@ -144,7 +143,7 @@ impl TableCtx {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for y in 0..height {
|
for y in 0..height {
|
||||||
let mut row_header = Vec::new();
|
let mut row_header = None;
|
||||||
for x in 0..width {
|
for x in 0..width {
|
||||||
self.resolve_cell_headers(
|
self.resolve_cell_headers(
|
||||||
(x, y),
|
(x, y),
|
||||||
@ -225,7 +224,7 @@ impl TableCtx {
|
|||||||
fn resolve_cell_headers<F>(
|
fn resolve_cell_headers<F>(
|
||||||
&mut self,
|
&mut self,
|
||||||
(x, y): (usize, usize),
|
(x, y): (usize, usize),
|
||||||
current_header: &mut Vec<(NonZeroU32, TagId)>,
|
current_header: &mut Option<(NonZeroU32, TagId)>,
|
||||||
refers_to_dir: F,
|
refers_to_dir: F,
|
||||||
) where
|
) where
|
||||||
F: Fn(&TableHeaderScope) -> bool,
|
F: Fn(&TableHeaderScope) -> bool,
|
||||||
@ -233,24 +232,26 @@ impl TableCtx {
|
|||||||
let table_id = self.id;
|
let table_id = self.id;
|
||||||
let Some(cell) = self.get_mut(x, y) else { return };
|
let Some(cell) = self.get_mut(x, y) else { return };
|
||||||
|
|
||||||
let mut new_header = None;
|
if let Some((prev_level, cell_id)) = current_header.clone() {
|
||||||
if let TableCellKind::Header(level, scope) = cell.unwrap_kind() {
|
// The `Headers` attribute is also set for parent headers.
|
||||||
if refers_to_dir(&scope) {
|
let mut is_parent_header = true;
|
||||||
// Remove all headers that are the same or a lower level.
|
if let TableCellKind::Header(level, scope) = cell.unwrap_kind() {
|
||||||
while current_header.pop_if(|(l, _)| *l >= level).is_some() {}
|
if refers_to_dir(&scope) {
|
||||||
|
is_parent_header = prev_level < level;
|
||||||
let tag_id = table_cell_id(table_id, cell.x, cell.y);
|
}
|
||||||
new_header = Some((level, tag_id));
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if let Some((_, cell_id)) = current_header.last() {
|
if is_parent_header && !cell.headers.ids.contains(&cell_id) {
|
||||||
if !cell.headers.ids.contains(&cell_id) {
|
|
||||||
cell.headers.ids.push(cell_id.clone());
|
cell.headers.ids.push(cell_id.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
current_header.extend(new_header);
|
if let TableCellKind::Header(level, scope) = cell.unwrap_kind() {
|
||||||
|
if refers_to_dir(&scope) {
|
||||||
|
let tag_id = table_cell_id(table_id, cell.x, cell.y);
|
||||||
|
*current_header = Some((level, tag_id));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -338,24 +339,21 @@ mod tests {
|
|||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn test(table: TableCtx, exp_tag: TagNode) {
|
fn test(table: TableCtx, exp_tag: TagNode) {
|
||||||
let tag = table.build_table(Vec::new());
|
let tag = table.build_table(Vec::new());
|
||||||
assert_eq!(exp_tag, tag);
|
assert_eq!(tag, exp_tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn table<const SIZE: usize>(cells: [TableCell; SIZE]) -> TableCtx {
|
fn table<const SIZE: usize>(cells: [TableCell; SIZE]) -> TableCtx {
|
||||||
let mut table = TableCtx::new(TableId(324), Some("summary".into()));
|
let mut table = TableCtx::new(TableId(324), Some("summary".into()));
|
||||||
for cell in cells {
|
for cell in cells {
|
||||||
table.insert(&cell, Vec::new());
|
table.insert(Packed::new(cell), Vec::new());
|
||||||
}
|
}
|
||||||
|
|
||||||
table
|
table
|
||||||
}
|
}
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn header_cell(
|
fn header_cell(x: usize, y: usize, level: u32, scope: TableHeaderScope) -> TableCell {
|
||||||
(x, y): (usize, usize),
|
|
||||||
level: u32,
|
|
||||||
scope: TableHeaderScope,
|
|
||||||
) -> TableCell {
|
|
||||||
TableCell::new(Content::default())
|
TableCell::new(Content::default())
|
||||||
.with_x(Smart::Custom(x))
|
.with_x(Smart::Custom(x))
|
||||||
.with_y(Smart::Custom(y))
|
.with_y(Smart::Custom(y))
|
||||||
@ -365,14 +363,6 @@ mod tests {
|
|||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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 {
|
fn cell(x: usize, y: usize) -> TableCell {
|
||||||
TableCell::new(Content::default())
|
TableCell::new(Content::default())
|
||||||
.with_x(Smart::Custom(x))
|
.with_x(Smart::Custom(x))
|
||||||
@ -380,36 +370,26 @@ mod tests {
|
|||||||
.with_kind(Smart::Custom(TableCellKind::Data))
|
.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<const SIZE: usize>(nodes: [TagNode; SIZE]) -> TagNode {
|
fn table_tag<const SIZE: usize>(nodes: [TagNode; SIZE]) -> TagNode {
|
||||||
let tag = TagKind::Table(Some("summary".into()));
|
let tag = TagKind::Table(Some("summary".into()));
|
||||||
TagNode::Group(tag.into(), nodes.into())
|
TagNode::Group(tag.into(), nodes.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn thead<const SIZE: usize>(nodes: [TagNode; SIZE]) -> TagNode {
|
fn header<const SIZE: usize>(nodes: [TagNode; SIZE]) -> TagNode {
|
||||||
TagNode::Group(TagKind::THead.into(), nodes.into())
|
TagNode::Group(TagKind::THead.into(), nodes.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tbody<const SIZE: usize>(nodes: [TagNode; SIZE]) -> TagNode {
|
fn body<const SIZE: usize>(nodes: [TagNode; SIZE]) -> TagNode {
|
||||||
TagNode::Group(TagKind::TBody.into(), nodes.into())
|
TagNode::Group(TagKind::TBody.into(), nodes.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tfoot<const SIZE: usize>(nodes: [TagNode; SIZE]) -> TagNode {
|
fn row<const SIZE: usize>(nodes: [TagNode; SIZE]) -> TagNode {
|
||||||
TagNode::Group(TagKind::TFoot.into(), nodes.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn trow<const SIZE: usize>(nodes: [TagNode; SIZE]) -> TagNode {
|
|
||||||
TagNode::Group(TagKind::TR.into(), nodes.into())
|
TagNode::Group(TagKind::TR.into(), nodes.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn th<const SIZE: usize>(
|
fn header_cell_tag<const SIZE: usize>(
|
||||||
(x, y): (u32, u32),
|
x: u32,
|
||||||
|
y: u32,
|
||||||
scope: TableHeaderScope,
|
scope: TableHeaderScope,
|
||||||
headers: [(u32, u32); SIZE],
|
headers: [(u32, u32); SIZE],
|
||||||
) -> TagNode {
|
) -> TagNode {
|
||||||
@ -426,7 +406,7 @@ mod tests {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn td<const SIZE: usize>(headers: [(u32, u32); SIZE]) -> TagNode {
|
fn cell_tag<const SIZE: usize>(headers: [(u32, u32); SIZE]) -> TagNode {
|
||||||
let ids = headers
|
let ids = headers
|
||||||
.map(|(x, y)| table_cell_id(TableId(324), x, y))
|
.map(|(x, y)| table_cell_id(TableId(324), x, y))
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@ -441,9 +421,9 @@ mod tests {
|
|||||||
fn simple_table() {
|
fn simple_table() {
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
let table = table([
|
let table = table([
|
||||||
header_cell((0, 0), 1, TableHeaderScope::Column),
|
header_cell(0, 0, 1, TableHeaderScope::Column),
|
||||||
header_cell((1, 0), 1, TableHeaderScope::Column),
|
header_cell(1, 0, 1, TableHeaderScope::Column),
|
||||||
header_cell((2, 0), 1, TableHeaderScope::Column),
|
header_cell(2, 0, 1, TableHeaderScope::Column),
|
||||||
|
|
||||||
cell(0, 1),
|
cell(0, 1),
|
||||||
cell(1, 1),
|
cell(1, 1),
|
||||||
@ -456,21 +436,21 @@ mod tests {
|
|||||||
|
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
let tag = table_tag([
|
let tag = table_tag([
|
||||||
thead([trow([
|
header([row([
|
||||||
th((0, 0), TableHeaderScope::Column, []),
|
header_cell_tag(0, 0, TableHeaderScope::Column, []),
|
||||||
th((1, 0), TableHeaderScope::Column, []),
|
header_cell_tag(1, 0, TableHeaderScope::Column, []),
|
||||||
th((2, 0), TableHeaderScope::Column, []),
|
header_cell_tag(2, 0, TableHeaderScope::Column, []),
|
||||||
])]),
|
])]),
|
||||||
tbody([
|
body([
|
||||||
trow([
|
row([
|
||||||
td([(0, 0)]),
|
cell_tag([(0, 0)]),
|
||||||
td([(1, 0)]),
|
cell_tag([(1, 0)]),
|
||||||
td([(2, 0)]),
|
cell_tag([(2, 0)]),
|
||||||
]),
|
]),
|
||||||
trow([
|
row([
|
||||||
td([(0, 0)]),
|
cell_tag([(0, 0)]),
|
||||||
td([(1, 0)]),
|
cell_tag([(1, 0)]),
|
||||||
td([(2, 0)]),
|
cell_tag([(2, 0)]),
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
]);
|
]);
|
||||||
@ -482,111 +462,35 @@ mod tests {
|
|||||||
fn header_row_and_column() {
|
fn header_row_and_column() {
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
let table = table([
|
let table = table([
|
||||||
header_cell((0, 0), 1, TableHeaderScope::Column),
|
header_cell(0, 0, 1, TableHeaderScope::Column),
|
||||||
header_cell((1, 0), 1, TableHeaderScope::Column),
|
header_cell(1, 0, 1, TableHeaderScope::Column),
|
||||||
header_cell((2, 0), 1, TableHeaderScope::Column),
|
header_cell(2, 0, 1, TableHeaderScope::Column),
|
||||||
|
|
||||||
header_cell((0, 1), 1, TableHeaderScope::Row),
|
header_cell(0, 1, 1, TableHeaderScope::Row),
|
||||||
cell(1, 1),
|
cell(1, 1),
|
||||||
cell(2, 1),
|
cell(2, 1),
|
||||||
|
|
||||||
header_cell((0, 2), 1, TableHeaderScope::Row),
|
header_cell(0, 2, 1, TableHeaderScope::Row),
|
||||||
cell(1, 2),
|
cell(1, 2),
|
||||||
cell(2, 2),
|
cell(2, 2),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
let tag = table_tag([
|
let tag = table_tag([
|
||||||
trow([
|
row([
|
||||||
th((0, 0), TableHeaderScope::Column, []),
|
header_cell_tag(0, 0, TableHeaderScope::Column, []),
|
||||||
th((1, 0), TableHeaderScope::Column, []),
|
header_cell_tag(1, 0, TableHeaderScope::Column, []),
|
||||||
th((2, 0), TableHeaderScope::Column, []),
|
header_cell_tag(2, 0, TableHeaderScope::Column, []),
|
||||||
]),
|
]),
|
||||||
trow([
|
row([
|
||||||
th((0, 1), TableHeaderScope::Row, [(0, 0)]),
|
header_cell_tag(0, 1, TableHeaderScope::Row, [(0, 0)]),
|
||||||
td([(1, 0), (0, 1)]),
|
cell_tag([(1, 0), (0, 1)]),
|
||||||
td([(2, 0), (0, 1)]),
|
cell_tag([(2, 0), (0, 1)]),
|
||||||
]),
|
]),
|
||||||
trow([
|
row([
|
||||||
th((0, 2), TableHeaderScope::Row, [(0, 0)]),
|
header_cell_tag(0, 2, TableHeaderScope::Row, [(0, 0)]),
|
||||||
td([(1, 0), (0, 2)]),
|
cell_tag([(1, 0), (0, 2)]),
|
||||||
td([(2, 0), (0, 2)]),
|
cell_tag([(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)]),
|
|
||||||
]),
|
|
||||||
]),
|
]),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user