mirror of
https://github.com/typst/typst
synced 2025-08-23 03:04:14 +08:00
WIP [no ci] tag table headers
This commit is contained in:
parent
1fa54b751a
commit
46229e73ba
@ -2,6 +2,7 @@ use std::num::{NonZeroU32, NonZeroUsize};
|
||||
use std::sync::Arc;
|
||||
|
||||
use ecow::EcoString;
|
||||
use typst_macros::Cast;
|
||||
use typst_utils::NonZeroExt;
|
||||
|
||||
use crate::diag::{bail, HintedStrResult, HintedString, SourceResult};
|
||||
@ -361,15 +362,40 @@ fn show_cellgrid_html(grid: CellGrid, styles: StyleChain) -> Content {
|
||||
|
||||
impl Show for Packed<TableElem> {
|
||||
fn show(&self, engine: &mut Engine, styles: StyleChain) -> SourceResult<Content> {
|
||||
Ok(if TargetElem::target_in(styles).is_html() {
|
||||
let elem = 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();
|
||||
show_cellgrid_html(table_to_cellgrid(self, engine, locator, styles)?, styles)
|
||||
} else {
|
||||
BlockElem::multi_layouter(self.clone(), engine.routines.layout_table).pack()
|
||||
}
|
||||
.spanned(self.span()))
|
||||
let children = self
|
||||
.children
|
||||
.iter()
|
||||
.map(|c| match c.clone() {
|
||||
TableChild::Header(header) => {
|
||||
let mut header = header.unpack();
|
||||
header.children = header
|
||||
.children
|
||||
.into_iter()
|
||||
.map(|item| match item {
|
||||
TableItem::Cell(cell) => {
|
||||
let mut cell = cell.unpack();
|
||||
cell.push_header_scope(Smart::Custom(TableHeaderScope::Column));
|
||||
TableItem::Cell(Packed::new(cell))
|
||||
}
|
||||
item => item,
|
||||
})
|
||||
.collect();
|
||||
TableChild::Header(Packed::new(header))
|
||||
}
|
||||
child => child,
|
||||
})
|
||||
.collect();
|
||||
let mut table = self.clone().unpack();
|
||||
table.children = children;
|
||||
BlockElem::multi_layouter(Packed::new(table), engine.routines.layout_table).pack()
|
||||
};
|
||||
Ok(elem.spanned(self.span()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -810,6 +836,9 @@ pub struct TableCell {
|
||||
#[fold]
|
||||
pub stroke: Sides<Option<Option<Arc<Stroke>>>>,
|
||||
|
||||
// TODO: feature gate
|
||||
pub header_scope: Smart<TableHeaderScope>,
|
||||
|
||||
/// Whether rows spanned by this cell can be placed in different pages.
|
||||
/// When equal to `{auto}`, a cell spanning only fixed-size rows is
|
||||
/// unbreakable, while a cell spanning at least one `{auto}`-sized row is
|
||||
@ -847,3 +876,10 @@ impl From<Content> for TableCell {
|
||||
value.unpack::<Self>().unwrap_or_else(Self::new)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Cast)]
|
||||
pub enum TableHeaderScope {
|
||||
Both,
|
||||
Column,
|
||||
Row,
|
||||
}
|
||||
|
@ -5,14 +5,14 @@ use krilla::page::Page;
|
||||
use krilla::surface::Surface;
|
||||
use krilla::tagging::{
|
||||
ArtifactType, ContentTag, Identifier, Node, SpanTag, TableCellSpan, TableDataCell,
|
||||
TableHeaderCell, TableHeaderScope, Tag, TagBuilder, TagGroup, TagKind, TagTree,
|
||||
TableHeaderCell, Tag, TagBuilder, TagGroup, TagKind, TagTree,
|
||||
};
|
||||
use typst_library::foundations::{Content, LinkMarker, Packed, StyleChain};
|
||||
use typst_library::foundations::{Content, LinkMarker, Packed, StyleChain, Smart};
|
||||
use typst_library::introspection::Location;
|
||||
use typst_library::layout::RepeatElem;
|
||||
use typst_library::model::{
|
||||
Destination, FigureCaption, FigureElem, HeadingElem, Outlinable, OutlineBody,
|
||||
OutlineEntry, TableCell, TableElem,
|
||||
OutlineEntry, TableCell, TableElem, TableHeaderScope,
|
||||
};
|
||||
use typst_library::pdf::{ArtifactElem, ArtifactKind, PdfTagElem, PdfTagKind};
|
||||
use typst_library::visualize::ImageElem;
|
||||
@ -141,10 +141,14 @@ impl TableCtx {
|
||||
|
||||
// TODO: possibly set internal field on TableCell when resolving
|
||||
// the cell grid.
|
||||
let is_header = false;
|
||||
let header_scope = cell.header_scope(StyleChain::default());
|
||||
let span = TableCellSpan { rows: rowspan as i32, cols: colspan as i32 };
|
||||
let tag = if is_header {
|
||||
let scope = TableHeaderScope::Column; // TODO
|
||||
let tag = if let Smart::Custom(scope) = header_scope {
|
||||
let scope = match scope {
|
||||
TableHeaderScope::Both => krilla::tagging::TableHeaderScope::Both,
|
||||
TableHeaderScope::Column => krilla::tagging::TableHeaderScope::Column,
|
||||
TableHeaderScope::Row => krilla::tagging::TableHeaderScope::Row,
|
||||
};
|
||||
TagKind::TH(TableHeaderCell::new(scope).with_span(span))
|
||||
} else {
|
||||
TagKind::TD(TableDataCell::new().with_span(span))
|
||||
@ -167,14 +171,37 @@ impl TableCtx {
|
||||
fn build_table(self, mut nodes: Vec<TagNode>) -> Vec<TagNode> {
|
||||
// Table layouting ensures that there are no overlapping cells, and that
|
||||
// any gaps left by the user are filled with empty cells.
|
||||
let mut row_chunk = Vec::new();
|
||||
let mut is_header_chunk = false;
|
||||
for row in self.rows.into_iter() {
|
||||
let mut row_nodes = Vec::new();
|
||||
for (_, tag, nodes) in row.into_iter().flatten() {
|
||||
row_nodes.push(TagNode::Group(tag, nodes));
|
||||
}
|
||||
let mut is_header_row = true;
|
||||
let row_nodes = row
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.map(|(cell, tag, nodes)| {
|
||||
is_header_row &= cell.header_scope(StyleChain::default()).is_custom();
|
||||
TagNode::Group(tag, nodes)
|
||||
})
|
||||
.collect();
|
||||
|
||||
// TODO: generate `THead`, `TBody`, and `TFoot`
|
||||
nodes.push(TagNode::Group(TagKind::TR.into(), row_nodes));
|
||||
let row = TagNode::Group(TagKind::TR.into(), row_nodes);
|
||||
if row_chunk.is_empty() {
|
||||
is_header_chunk = is_header_row;
|
||||
row_chunk.push(row);
|
||||
} else if is_header_chunk == is_header_row {
|
||||
row_chunk.push(row);
|
||||
} else {
|
||||
let tag = if is_header_chunk { TagKind::THead } else { TagKind::TBody };
|
||||
nodes.push(TagNode::Group(tag.into(), std::mem::take(&mut row_chunk)));
|
||||
|
||||
is_header_chunk = is_header_row;
|
||||
row_chunk.push(row);
|
||||
}
|
||||
}
|
||||
|
||||
if !row_chunk.is_empty() {
|
||||
let tag = if is_header_chunk { TagKind::THead } else { TagKind::TBody };
|
||||
nodes.push(TagNode::Group(tag.into(), row_chunk));
|
||||
}
|
||||
|
||||
nodes
|
||||
|
Loading…
x
Reference in New Issue
Block a user