diff --git a/crates/typst-pdf/src/convert.rs b/crates/typst-pdf/src/convert.rs index 06f076f9a..09f3c2c5f 100644 --- a/crates/typst-pdf/src/convert.rs +++ b/crates/typst-pdf/src/convert.rs @@ -107,6 +107,8 @@ fn convert_pages(gc: &mut GlobalContext, document: &mut Document) -> SourceResul let mut surface = page.surface(); let mut fc = FrameContext::new(typst_page.frame.size()); + tags::page_start(gc, &mut surface); + handle_frame( &mut fc, &typst_page.frame, @@ -115,6 +117,8 @@ fn convert_pages(gc: &mut GlobalContext, document: &mut Document) -> SourceResul gc, )?; + tags::page_end(gc, &mut surface); + surface.finish(); 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)? } FrameItem::Link(dest, size) => handle_link(fc, gc, dest, *size), - FrameItem::Tag(Tag::Start(elem)) => tags::handle_start(gc, elem)?, - FrameItem::Tag(Tag::End(loc, _)) => tags::handle_end(gc, *loc), + FrameItem::Tag(Tag::Start(elem)) => tags::handle_start(gc, surface, elem)?, + FrameItem::Tag(Tag::End(loc, _)) => tags::handle_end(gc, surface, *loc), } fc.pop(); diff --git a/crates/typst-pdf/src/tags/mod.rs b/crates/typst-pdf/src/tags/mod.rs index 59768ee07..1e82ee8b1 100644 --- a/crates/typst-pdf/src/tags/mod.rs +++ b/crates/typst-pdf/src/tags/mod.rs @@ -31,7 +31,11 @@ use crate::tags::table::TableCtx; mod outline; 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() { // Don't nest artifacts return Ok(()); @@ -41,10 +45,10 @@ pub(crate) fn handle_start(gc: &mut GlobalContext, elem: &Content) -> SourceResu if let Some(artifact) = elem.to_packed::() { let kind = artifact.kind.get(StyleChain::default()); - start_artifact(gc, loc, kind); + start_artifact(gc, surface, loc, kind); return Ok(()); } else if let Some(_) = elem.to_packed::() { - start_artifact(gc, loc, ArtifactKind::Other); + start_artifact(gc, surface, loc, ArtifactKind::Other); 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 // would require more changes in the layouting code, or a pre-pass // 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 { push_stack(gc, loc, StackEntryKind::TableCell(cell.clone()))?; } @@ -141,9 +145,10 @@ fn push_stack( 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 l == loc { + surface.end_tagged(); gc.tags.in_artifact = None; } return; @@ -202,6 +207,20 @@ pub(crate) fn handle_end(gc: &mut GlobalContext, loc: Location) { 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. pub(crate) fn add_annotations( gc: &mut GlobalContext, @@ -397,11 +416,16 @@ pub(crate) struct Placeholder(usize); /// Automatically calls [`Surface::end_tagged`] when dropped. pub(crate) struct TagHandle<'a, 'b> { 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<'_, '_> { 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>, content: ContentTag, ) -> TagHandle<'a, 'b> { - let content = if let Some((_, kind)) = gc.tags.in_artifact { - let ty = artifact_type(kind); - ContentTag::Artifact(ty) + let content = if gc.tags.in_artifact.is_some() { + return TagHandle { surface, started: false }; } 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 // will be wrapped inside a `TableCell`. @@ -447,10 +470,18 @@ fn start_content<'a, 'b>( }; let id = surface.start_tagged(content); 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)); }