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, &mut self,
link_id: tags::LinkId, link_id: tags::LinkId,
) -> Option<&mut LinkAnnotation> { ) -> Option<&mut LinkAnnotation> {
self.link_annotations self.link_annotations.iter_mut().rfind(|a| a.id == link_id)
.iter_mut()
.rev()
.find(|annot| annot.id == link_id)
} }
pub(crate) fn push_link_annotation(&mut self, annotation: LinkAnnotation) { 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 typst_syntax::Span;
use crate::convert::{FrameContext, GlobalContext}; use crate::convert::{FrameContext, GlobalContext};
use crate::tags::{self, Placeholder, TagNode}; use crate::tags::{LinkId, Placeholder, TagNode};
use crate::util::{AbsExt, PointExt}; use crate::util::{AbsExt, PointExt};
pub(crate) struct LinkAnnotation { pub(crate) struct LinkAnnotation {
pub(crate) id: tags::LinkId, pub(crate) id: LinkId,
pub(crate) placeholder: Placeholder, pub(crate) placeholder: Placeholder,
pub(crate) alt: Option<String>, pub(crate) alt: Option<String>,
pub(crate) quad_points: Vec<kg::Quadrilateral>, pub(crate) quad_points: Vec<kg::Quadrilateral>,
@ -60,12 +60,19 @@ pub(crate) fn handle_link(
}; };
let quad = to_quadrilateral(fc, size); let quad = to_quadrilateral(fc, size);
// Unfortunately quadpoints still aren't well supported by most PDF readers, // Unfortunately quadpoints still aren't well supported by most PDF readers.
// even by acrobat. Which is understandable since they were only introduced // So only add multiple quadpoint entries to one annotation when targeting
// in PDF 1.6 (2005) /s // PDF/UA. Otherwise generate multiple annotations, to avoid pdf readers
let should_use_quadpoints = gc.options.standards.config.validator() == Validator::UA1; // 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) { 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 placeholder = gc.tags.placeholders.reserve();
let (alt, span) = if let Some((link, nodes)) = tagging_ctx { 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, page: &mut Page,
annotations: Vec<LinkAnnotation>, annotations: Vec<LinkAnnotation>,
) { ) {
for annotation in annotations.into_iter() { for a in annotations.into_iter() {
let LinkAnnotation { id: _, placeholder, alt, quad_points, target, span } = let annotation = krilla::annotation::Annotation::new_link(
annotation; krilla::annotation::LinkAnnotation::new_with_quad_points(
let annot = krilla::annotation::Annotation::new_link( a.quad_points,
krilla::annotation::LinkAnnotation::new_with_quad_points(quad_points, target), a.target,
alt, ),
a.alt,
) )
.with_location(Some(span.into_raw().get())); .with_location(Some(a.span.into_raw()));
if gc.options.disable_tags { if gc.options.disable_tags {
page.add_annotation(annot); page.add_annotation(annotation);
} else { } else {
let annot_id = page.add_tagged_annotation(annot); let annot_id = page.add_tagged_annotation(annotation);
gc.tags.placeholders.init(placeholder, Node::Leaf(annot_id)); gc.tags.placeholders.init(a.placeholder, Node::Leaf(annot_id));
} }
} }
} }