feat: group artifacts

span one artifact tag across all content inside an artifact
This commit is contained in:
Tobias Schmitz 2025-07-09 01:08:34 +02:00
parent 0c09c7d666
commit 8998676acb
No known key found for this signature in database
2 changed files with 48 additions and 13 deletions

View File

@ -107,6 +107,8 @@ fn convert_pages(gc: &mut GlobalContext, document: &mut Document) -> SourceResul
let mut surface = page.surface(); let mut surface = page.surface();
let mut fc = FrameContext::new(typst_page.frame.size()); let mut fc = FrameContext::new(typst_page.frame.size());
tags::page_start(gc, &mut surface);
handle_frame( handle_frame(
&mut fc, &mut fc,
&typst_page.frame, &typst_page.frame,
@ -115,6 +117,8 @@ fn convert_pages(gc: &mut GlobalContext, document: &mut Document) -> SourceResul
gc, gc,
)?; )?;
tags::page_end(gc, &mut surface);
surface.finish(); surface.finish();
tags::add_annotations(gc, &mut page, fc.link_annotations); tags::add_annotations(gc, &mut page, fc.link_annotations);
@ -293,8 +297,8 @@ pub(crate) fn handle_frame(
handle_image(gc, fc, image, *size, surface, *span)? handle_image(gc, fc, image, *size, surface, *span)?
} }
FrameItem::Link(dest, size) => handle_link(fc, gc, dest, *size), FrameItem::Link(dest, size) => handle_link(fc, gc, dest, *size),
FrameItem::Tag(Tag::Start(elem)) => tags::handle_start(gc, elem)?, FrameItem::Tag(Tag::Start(elem)) => tags::handle_start(gc, surface, elem)?,
FrameItem::Tag(Tag::End(loc, _)) => tags::handle_end(gc, *loc), FrameItem::Tag(Tag::End(loc, _)) => tags::handle_end(gc, surface, *loc),
} }
fc.pop(); fc.pop();

View File

@ -31,7 +31,11 @@ use crate::tags::table::TableCtx;
mod outline; mod outline;
mod table; mod table;
pub(crate) fn handle_start(gc: &mut GlobalContext, elem: &Content) -> SourceResult<()> { pub(crate) fn handle_start(
gc: &mut GlobalContext,
surface: &mut Surface,
elem: &Content,
) -> SourceResult<()> {
if gc.tags.in_artifact.is_some() { if gc.tags.in_artifact.is_some() {
// Don't nest artifacts // Don't nest artifacts
return Ok(()); return Ok(());
@ -41,10 +45,10 @@ pub(crate) fn handle_start(gc: &mut GlobalContext, elem: &Content) -> SourceResu
if let Some(artifact) = elem.to_packed::<ArtifactElem>() { if let Some(artifact) = elem.to_packed::<ArtifactElem>() {
let kind = artifact.kind.get(StyleChain::default()); let kind = artifact.kind.get(StyleChain::default());
start_artifact(gc, loc, kind); start_artifact(gc, surface, loc, kind);
return Ok(()); return Ok(());
} else if let Some(_) = elem.to_packed::<RepeatElem>() { } else if let Some(_) = elem.to_packed::<RepeatElem>() {
start_artifact(gc, loc, ArtifactKind::Other); start_artifact(gc, surface, loc, ArtifactKind::Other);
return Ok(()); return Ok(());
} }
@ -103,7 +107,7 @@ pub(crate) fn handle_start(gc: &mut GlobalContext, elem: &Content) -> SourceResu
// first page. Maybe it should be the cell on the last page, but that // first page. Maybe it should be the cell on the last page, but that
// would require more changes in the layouting code, or a pre-pass // would require more changes in the layouting code, or a pre-pass
// on the frames to figure out if there are other footers following. // on the frames to figure out if there are other footers following.
start_artifact(gc, loc, ArtifactKind::Other); start_artifact(gc, surface, loc, ArtifactKind::Other);
} else { } else {
push_stack(gc, loc, StackEntryKind::TableCell(cell.clone()))?; push_stack(gc, loc, StackEntryKind::TableCell(cell.clone()))?;
} }
@ -141,9 +145,10 @@ fn push_stack(
Ok(()) Ok(())
} }
pub(crate) fn handle_end(gc: &mut GlobalContext, loc: Location) { pub(crate) fn handle_end(gc: &mut GlobalContext, surface: &mut Surface, loc: Location) {
if let Some((l, _)) = gc.tags.in_artifact { if let Some((l, _)) = gc.tags.in_artifact {
if l == loc { if l == loc {
surface.end_tagged();
gc.tags.in_artifact = None; gc.tags.in_artifact = None;
} }
return; return;
@ -202,6 +207,20 @@ pub(crate) fn handle_end(gc: &mut GlobalContext, loc: Location) {
gc.tags.push(node); gc.tags.push(node);
} }
pub(crate) fn page_start(gc: &mut GlobalContext, surface: &mut Surface) {
if let Some((_, kind)) = gc.tags.in_artifact {
let ty = artifact_type(kind);
let id = surface.start_tagged(ContentTag::Artifact(ty));
gc.tags.push(TagNode::Leaf(id));
}
}
pub(crate) fn page_end(gc: &mut GlobalContext, surface: &mut Surface) {
if gc.tags.in_artifact.is_some() {
surface.end_tagged();
}
}
/// Add all annotations that were found in the page frame. /// Add all annotations that were found in the page frame.
pub(crate) fn add_annotations( pub(crate) fn add_annotations(
gc: &mut GlobalContext, gc: &mut GlobalContext,
@ -397,11 +416,16 @@ pub(crate) struct Placeholder(usize);
/// Automatically calls [`Surface::end_tagged`] when dropped. /// Automatically calls [`Surface::end_tagged`] when dropped.
pub(crate) struct TagHandle<'a, 'b> { pub(crate) struct TagHandle<'a, 'b> {
surface: &'b mut Surface<'a>, surface: &'b mut Surface<'a>,
/// Whether this tag handle started the marked content sequence, and should
/// thus end it when it is dropped.
started: bool,
} }
impl Drop for TagHandle<'_, '_> { impl Drop for TagHandle<'_, '_> {
fn drop(&mut self) { fn drop(&mut self) {
self.surface.end_tagged(); if self.started {
self.surface.end_tagged();
}
} }
} }
@ -435,9 +459,8 @@ fn start_content<'a, 'b>(
surface: &'b mut Surface<'a>, surface: &'b mut Surface<'a>,
content: ContentTag, content: ContentTag,
) -> TagHandle<'a, 'b> { ) -> TagHandle<'a, 'b> {
let content = if let Some((_, kind)) = gc.tags.in_artifact { let content = if gc.tags.in_artifact.is_some() {
let ty = artifact_type(kind); return TagHandle { surface, started: false };
ContentTag::Artifact(ty)
} else if let Some(StackEntryKind::Table(_)) = gc.tags.stack.last().map(|e| &e.kind) { } else if let Some(StackEntryKind::Table(_)) = gc.tags.stack.last().map(|e| &e.kind) {
// Mark any direct child of a table as an aritfact. Any real content // Mark any direct child of a table as an aritfact. Any real content
// will be wrapped inside a `TableCell`. // will be wrapped inside a `TableCell`.
@ -447,10 +470,18 @@ fn start_content<'a, 'b>(
}; };
let id = surface.start_tagged(content); let id = surface.start_tagged(content);
gc.tags.push(TagNode::Leaf(id)); gc.tags.push(TagNode::Leaf(id));
TagHandle { surface } TagHandle { surface, started: true }
} }
fn start_artifact(gc: &mut GlobalContext, loc: Location, kind: ArtifactKind) { fn start_artifact(
gc: &mut GlobalContext,
surface: &mut Surface,
loc: Location,
kind: ArtifactKind,
) {
let ty = artifact_type(kind);
let id = surface.start_tagged(ContentTag::Artifact(ty));
gc.tags.push(TagNode::Leaf(id));
gc.tags.in_artifact = Some((loc, kind)); gc.tags.in_artifact = Some((loc, kind));
} }