diff --git a/crates/typst-pdf/src/convert.rs b/crates/typst-pdf/src/convert.rs index 3f24494bb..a8a7e88b2 100644 --- a/crates/typst-pdf/src/convert.rs +++ b/crates/typst-pdf/src/convert.rs @@ -171,14 +171,14 @@ impl State { /// Context needed for converting a single frame. pub(crate) struct FrameContext { states: Vec, - pub(crate) link_annotations: HashMap, + link_annotations: Vec, } impl FrameContext { pub(crate) fn new(size: Size) -> Self { Self { states: vec![State::new(size)], - link_annotations: HashMap::new(), + link_annotations: Vec::new(), } } @@ -197,6 +197,20 @@ impl FrameContext { pub(crate) fn state_mut(&mut self) -> &mut State { self.states.last_mut().unwrap() } + + pub(crate) fn get_link_annotation( + &mut self, + link_id: tags::LinkId, + ) -> Option<&mut LinkAnnotation> { + self.link_annotations + .iter_mut() + .rev() + .find(|annot| annot.id == link_id) + } + + pub(crate) fn push_link_annotation(&mut self, annotation: LinkAnnotation) { + self.link_annotations.push(annotation); + } } /// Globally needed context for converting a typst document. diff --git a/crates/typst-pdf/src/link.rs b/crates/typst-pdf/src/link.rs index e0df6a58d..32949068b 100644 --- a/crates/typst-pdf/src/link.rs +++ b/crates/typst-pdf/src/link.rs @@ -1,18 +1,18 @@ -use std::collections::hash_map::Entry; - use ecow::EcoString; use krilla::action::{Action, LinkAction}; use krilla::annotation::Target; +use krilla::configure::Validator; use krilla::destination::XyzDestination; use krilla::geom as kg; use typst_library::layout::{Abs, Point, Position, Size}; use typst_library::model::Destination; use crate::convert::{FrameContext, GlobalContext}; -use crate::tags::{Placeholder, StackEntryKind, TagNode}; +use crate::tags::{self, Placeholder, StackEntryKind, TagNode}; use crate::util::{AbsExt, PointExt}; pub(crate) struct LinkAnnotation { + pub(crate) id: tags::LinkId, pub(crate) placeholder: Placeholder, pub(crate) alt: Option, pub(crate) rect: kg::Rect, @@ -50,7 +50,7 @@ pub(crate) fn handle_link( }; let entry = gc.tags.stack.last_mut().expect("a link parent"); - let StackEntryKind::Link(link_id, link) = &entry.kind else { + let StackEntryKind::Link(link_id, ref link) = entry.kind else { unreachable!("expected a link parent") }; let alt = link.alt.as_ref().map(EcoString::to_string); @@ -58,18 +58,21 @@ pub(crate) fn handle_link( let rect = to_rect(fc, size); let quadpoints = quadpoints(rect); - match fc.link_annotations.entry(*link_id) { - Entry::Occupied(occupied) => { - // Update the bounding box and add the quadpoints of an existing link annotation. - let annotation = occupied.into_mut(); + // Unfortunately quadpoints still aren't well supported by most PDF readers, + // even by acrobat. Which is understandable since they were only introduced + // in PDF 1.6 (2005) /s + let should_use_quadpoints = gc.options.standards.config.validator() == Validator::UA1; + match fc.get_link_annotation(link_id) { + Some(annotation) if should_use_quadpoints => { + // Update the bounding box and add the quadpoints to an existing link annotation. annotation.rect = bounding_rect(annotation.rect, rect); annotation.quad_points.extend_from_slice(&quadpoints); } - Entry::Vacant(vacant) => { + _ => { let placeholder = gc.tags.reserve_placeholder(); gc.tags.push(TagNode::Placeholder(placeholder)); - - vacant.insert(LinkAnnotation { + fc.push_link_annotation(LinkAnnotation { + id: link_id, placeholder, rect, quad_points: quadpoints.to_vec(), diff --git a/crates/typst-pdf/src/tags.rs b/crates/typst-pdf/src/tags.rs index 942335ab8..d65e898c8 100644 --- a/crates/typst-pdf/src/tags.rs +++ b/crates/typst-pdf/src/tags.rs @@ -1,5 +1,4 @@ use std::cell::OnceCell; -use std::collections::HashMap; use ecow::EcoString; use krilla::page::Page; @@ -335,10 +334,11 @@ fn start_content<'a, 'b>( pub(crate) fn add_annotations( gc: &mut GlobalContext, page: &mut Page, - annotations: HashMap, + annotations: Vec, ) { - for annotation in annotations.into_values() { - let LinkAnnotation { placeholder, alt, rect, quad_points, target } = annotation; + for annotation in annotations.into_iter() { + let LinkAnnotation { id: _, placeholder, alt, rect, quad_points, target } = + annotation; let annot = krilla::annotation::Annotation::new_link( krilla::annotation::LinkAnnotation::new(rect, Some(quad_points), target), alt,