From 7a3e2dbcbee9ac00961ac0f3c2d0755714e0fad1 Mon Sep 17 00:00:00 2001 From: Tobias Schmitz Date: Fri, 25 Jul 2025 13:22:57 +0200 Subject: [PATCH] feat: better error reporting for missing link alt text this can only really happen using a position as a link target --- crates/typst-layout/src/rules.rs | 8 +++++++- crates/typst-library/src/foundations/content/mod.rs | 5 +---- crates/typst-pdf/src/convert.rs | 2 +- crates/typst-pdf/src/link.rs | 2 +- crates/typst-pdf/src/tags/mod.rs | 4 ++-- 5 files changed, 12 insertions(+), 9 deletions(-) diff --git a/crates/typst-layout/src/rules.rs b/crates/typst-layout/src/rules.rs index 456c73b71..0f8eb115a 100644 --- a/crates/typst-layout/src/rules.rs +++ b/crates/typst-layout/src/rules.rs @@ -216,13 +216,19 @@ const TERMS_RULE: ShowFn = |elem, _, styles| { const LINK_MARKER_RULE: ShowFn = |elem, _, _| Ok(elem.body.clone()); const LINK_RULE: ShowFn = |elem, engine, styles| { + let span = elem.span(); let body = elem.body.clone(); let dest = elem.dest.resolve(engine.introspector).at(elem.span())?; let alt = match elem.alt.get_cloned(styles) { Some(alt) => Some(alt), None => dest.alt_text(engine, styles)?, }; - Ok(body.linked(dest, alt)) + // Manually construct link marker that spans the whole link elem, not just + // the body. + Ok(LinkMarker::new(body, dest.clone(), alt) + .pack() + .spanned(span) + .set(LinkElem::current, Some(dest))) }; const HEADING_RULE: ShowFn = |elem, engine, styles| { diff --git a/crates/typst-library/src/foundations/content/mod.rs b/crates/typst-library/src/foundations/content/mod.rs index 5d469774f..d8aba293f 100644 --- a/crates/typst-library/src/foundations/content/mod.rs +++ b/crates/typst-library/src/foundations/content/mod.rs @@ -479,7 +479,7 @@ impl Content { /// Link the content somewhere. pub fn linked(self, dest: Destination, alt: Option) -> Self { let span = self.span(); - LinkMarker::new(self, dest.clone(), alt, span) + LinkMarker::new(self, dest.clone(), alt) .pack() .spanned(span) .set(LinkElem::current, Some(dest)) @@ -797,9 +797,6 @@ pub struct LinkMarker { #[internal] #[required] pub alt: Option, - #[internal] - #[required] - pub span: Span, } impl Construct for LinkMarker { diff --git a/crates/typst-pdf/src/convert.rs b/crates/typst-pdf/src/convert.rs index ca55eacb7..c1c1b1d74 100644 --- a/crates/typst-pdf/src/convert.rs +++ b/crates/typst-pdf/src/convert.rs @@ -617,7 +617,7 @@ fn convert_error( let span = to_span(*loc); error!( span, "{prefix} missing annotation alt text"; - hint: "please report this as a bug" + hint: "manually add alt text to your links" ) } ValidationError::MissingAltText(loc) => { diff --git a/crates/typst-pdf/src/link.rs b/crates/typst-pdf/src/link.rs index b8e5e98a3..2d9595c01 100644 --- a/crates/typst-pdf/src/link.rs +++ b/crates/typst-pdf/src/link.rs @@ -71,7 +71,7 @@ pub(crate) fn handle_link( let (alt, span) = if let Some((link, nodes)) = tagging_ctx { nodes.push(TagNode::Placeholder(placeholder)); let alt = link.alt.as_ref().map(EcoString::to_string); - (alt, link.span) + (alt, link.span()) } else { (None, Span::detached()) }; diff --git a/crates/typst-pdf/src/tags/mod.rs b/crates/typst-pdf/src/tags/mod.rs index 426530834..b67cbd5e0 100644 --- a/crates/typst-pdf/src/tags/mod.rs +++ b/crates/typst-pdf/src/tags/mod.rs @@ -535,10 +535,10 @@ impl TagStack { pub(crate) fn find_parent_link( &mut self, - ) -> Option<(LinkId, &LinkMarker, &mut Vec)> { + ) -> Option<(LinkId, &Packed, &mut Vec)> { self.0.iter_mut().rev().find_map(|e| { let (link_id, link) = e.kind.as_link()?; - Some((link_id, link.as_ref(), &mut e.nodes)) + Some((link_id, link, &mut e.nodes)) }) }