diff --git a/crates/typst-library/src/model/table.rs b/crates/typst-library/src/model/table.rs index bdcdf68e7..ba7924422 100644 --- a/crates/typst-library/src/model/table.rs +++ b/crates/typst-library/src/model/table.rs @@ -9,9 +9,9 @@ use crate::foundations::{ cast, elem, scope, Content, NativeElement, Packed, Show, Smart, StyleChain, TargetElem, }; -use crate::html::{tag, HtmlAttr, HtmlAttrs, HtmlElem}; +use crate::html::{tag, HtmlAttr, HtmlAttrs, HtmlElem, HtmlTag}; use crate::introspection::Locator; -use crate::layout::grid::resolve::table_to_cellgrid; +use crate::layout::grid::resolve::{table_to_cellgrid, Cell, CellGrid, Entry}; use crate::layout::{ show_grid_cell, Abs, Alignment, BlockElem, Celled, GridCell, GridFooter, GridHLine, GridHeader, GridVLine, Length, OuterHAlignment, OuterVAlignment, Rel, Sides, @@ -262,34 +262,61 @@ impl TableElem { type TableFooter; } +fn show_cell_html(tag: HtmlTag, cell: &Cell, styles: StyleChain) -> Content { + let cell = cell.body.clone(); + let Some(cell) = cell.to_packed::() else { return cell }; + let mut attrs = HtmlAttrs::default(); + let span = |n: NonZeroUsize| (n != NonZeroUsize::MIN).then(|| n.to_string()); + if let Some(colspan) = span(cell.colspan(styles)) { + attrs.push(HtmlAttr::constant("colspan"), colspan); + } + if let Some(rowspan) = span(cell.rowspan(styles)) { + attrs.push(HtmlAttr::constant("rowspan"), rowspan); + } + HtmlElem::new(tag) + .with_body(Some(cell.body.clone())) + .with_attrs(attrs) + .pack() + .spanned(cell.span()) +} + +fn show_cellgrid_html(grid: CellGrid, styles: StyleChain) -> Content { + let elem = |tag, body| HtmlElem::new(tag).with_body(Some(body)).pack(); + let mut rows: Vec<_> = grid.entries.chunks(grid.cols.len()).collect(); + + let tr = |tag, row: &[Entry]| { + let row = row + .iter() + .flat_map(|entry| entry.as_cell()) + .map(|cell| show_cell_html(tag, cell, styles)); + elem(tag::tr, Content::sequence(row)) + }; + + let footer = grid.footer.map(|ft| { + let rows = rows.drain(ft.unwrap().start..); + elem(tag::tfoot, Content::sequence(rows.map(|row| tr(tag::td, row)))) + }); + let header = grid.header.map(|hd| { + let rows = rows.drain(..hd.unwrap().end); + elem(tag::thead, Content::sequence(rows.map(|row| tr(tag::th, row)))) + }); + + let mut body = Content::sequence(rows.into_iter().map(|row| tr(tag::td, row))); + if header.is_some() || footer.is_some() { + body = elem(tag::tbody, body); + } + + let content = header.into_iter().chain(core::iter::once(body)).chain(footer); + elem(tag::table, Content::sequence(content)) +} + impl Show for Packed { fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult { Ok(if TargetElem::target_in(styles).is_html() { // TODO: This is a hack, it is not clear whether the locator is actually used by HTML. // How can we find out whether locator is actually used? let locator = Locator::root(); - let elem = |tag, body| HtmlElem::new(tag).with_body(Some(body)).pack(); - let grid = table_to_cellgrid(self, engine, locator, styles)?; - let rows = grid.entries.chunks(grid.cols.len()).map(|row| { - let row = row.iter().flat_map(|entry| entry.as_cell()); - elem(tag::tr, Content::sequence(row.map(|cell| cell.body.clone()))) - }); - let mut rows: Vec<_> = rows.collect(); - - let footer = grid.footer.map(|ft| { - elem(tag::tfoot, Content::sequence(rows.drain(ft.unwrap().start..))) - }); - let header = grid.header.map(|hd| { - elem(tag::thead, Content::sequence(rows.drain(..hd.unwrap().end))) - }); - - let mut body = Content::sequence(rows); - if header.is_some() || footer.is_some() { - body = elem(tag::tbody, body); - } - - let content = header.into_iter().chain(core::iter::once(body)).chain(footer); - elem(tag::table, Content::sequence(content)) + show_cellgrid_html(table_to_cellgrid(self, engine, locator, styles)?, styles) } else { BlockElem::multi_layouter(self.clone(), engine.routines.layout_table).pack() } @@ -737,20 +764,7 @@ cast! { impl Show for Packed { fn show(&self, _engine: &mut Engine, styles: StyleChain) -> SourceResult { - if TargetElem::target_in(styles).is_html() { - let mut attrs = HtmlAttrs::default(); - let span = |n: NonZeroUsize| (n != NonZeroUsize::MIN).then(|| n.to_string()); - if let Some(colspan) = span(self.colspan(styles)) { - attrs.push(HtmlAttr::constant("colspan"), colspan); - } - if let Some(rowspan) = span(self.rowspan(styles)) { - attrs.push(HtmlAttr::constant("rowspan"), rowspan); - } - let body = Some(self.body.clone()); - Ok(HtmlElem::new(tag::td).with_body(body).with_attrs(attrs).pack()) - } else { - show_grid_cell(self.body.clone(), self.inset(styles), self.align(styles)) - } + show_grid_cell(self.body.clone(), self.inset(styles), self.align(styles)) } }