refactor: clean up link annotation code

This commit is contained in:
Tobias Schmitz 2025-07-26 15:09:28 +02:00
parent 6d0c4c620d
commit 5ebe5490b5
No known key found for this signature in database
3 changed files with 26 additions and 21 deletions

View File

@ -213,10 +213,7 @@ impl FrameContext {
&mut self,
link_id: tags::LinkId,
) -> Option<&mut LinkAnnotation> {
self.link_annotations
.iter_mut()
.rev()
.find(|annot| annot.id == link_id)
self.link_annotations.iter_mut().rfind(|a| a.id == link_id)
}
pub(crate) fn push_link_annotation(&mut self, annotation: LinkAnnotation) {

View File

@ -9,11 +9,11 @@ use typst_library::model::Destination;
use typst_syntax::Span;
use crate::convert::{FrameContext, GlobalContext};
use crate::tags::{self, Placeholder, TagNode};
use crate::tags::{LinkId, Placeholder, TagNode};
use crate::util::{AbsExt, PointExt};
pub(crate) struct LinkAnnotation {
pub(crate) id: tags::LinkId,
pub(crate) id: LinkId,
pub(crate) placeholder: Placeholder,
pub(crate) alt: Option<String>,
pub(crate) quad_points: Vec<kg::Quadrilateral>,
@ -60,12 +60,19 @@ pub(crate) fn handle_link(
};
let quad = to_quadrilateral(fc, size);
// 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;
// Unfortunately quadpoints still aren't well supported by most PDF readers.
// So only add multiple quadpoint entries to one annotation when targeting
// PDF/UA. Otherwise generate multiple annotations, to avoid pdf readers
// falling back to the bounding box rectangle, which can span parts unrelated
// to the link. For example if there is a linebreak:
// ```
// Imagine this is a paragraph containing a link. It starts here https://github.com/
// typst/typst and then ends on another line.
// ```
// The bounding box would span the entire paragraph, which is undesirable.
let join_annotations = gc.options.standards.config.validator() == Validator::UA1;
match fc.get_link_annotation(link_id) {
Some(annotation) if should_use_quadpoints => annotation.quad_points.push(quad),
Some(annotation) if join_annotations => annotation.quad_points.push(quad),
_ => {
let placeholder = gc.tags.placeholders.reserve();
let (alt, span) = if let Some((link, nodes)) = tagging_ctx {

View File

@ -434,20 +434,21 @@ pub(crate) fn add_link_annotations(
page: &mut Page,
annotations: Vec<LinkAnnotation>,
) {
for annotation in annotations.into_iter() {
let LinkAnnotation { id: _, placeholder, alt, quad_points, target, span } =
annotation;
let annot = krilla::annotation::Annotation::new_link(
krilla::annotation::LinkAnnotation::new_with_quad_points(quad_points, target),
alt,
for a in annotations.into_iter() {
let annotation = krilla::annotation::Annotation::new_link(
krilla::annotation::LinkAnnotation::new_with_quad_points(
a.quad_points,
a.target,
),
a.alt,
)
.with_location(Some(span.into_raw().get()));
.with_location(Some(a.span.into_raw()));
if gc.options.disable_tags {
page.add_annotation(annot);
page.add_annotation(annotation);
} else {
let annot_id = page.add_tagged_annotation(annot);
gc.tags.placeholders.init(placeholder, Node::Leaf(annot_id));
let annot_id = page.add_tagged_annotation(annotation);
gc.tags.placeholders.init(a.placeholder, Node::Leaf(annot_id));
}
}
}