summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTobias Schmitz <tobiasschmitz2001@gmail.com>2025-06-26 17:38:25 +0200
committerTobias Schmitz <tobiasschmitz2001@gmail.com>2025-07-03 18:43:02 +0200
commit76d09b567345c0eb8f51622064a998e4bcdde416 (patch)
tree574cb8c235931d40a9ad9c16f50ce4bc7fda0fcc
parentd6307831dd78b4ac5c2d6498b2874387331ec36f (diff)
fix: only use link annotation quadpoints when exporting a PDF/UA-1 document
-rw-r--r--crates/typst-pdf/src/convert.rs18
-rw-r--r--crates/typst-pdf/src/link.rs25
-rw-r--r--crates/typst-pdf/src/tags.rs8
3 files changed, 34 insertions, 17 deletions
diff --git a/crates/typst-pdf/src/convert.rs b/crates/typst-pdf/src/convert.rs
index 3f24494b..a8a7e88b 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<State>,
- pub(crate) link_annotations: HashMap<tags::LinkId, LinkAnnotation>,
+ link_annotations: Vec<LinkAnnotation>,
}
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 e0df6a58..32949068 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<String>,
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 942335ab..d65e898c 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<LinkId, LinkAnnotation>,
+ annotations: Vec<LinkAnnotation>,
) {
- 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,