feat: best effort link alt text generation

This commit is contained in:
Tobias Schmitz 2025-07-18 12:01:54 +02:00
parent 79423f3033
commit 99815f449c
No known key found for this signature in database
3 changed files with 35 additions and 17 deletions

View File

@ -222,9 +222,10 @@ const LINK_MARKER_RULE: ShowFn<LinkMarker> = |elem, _, _| Ok(elem.body.clone());
const LINK_RULE: ShowFn<LinkElem> = |elem, engine, styles| {
let body = elem.body.clone();
let dest = elem.dest.resolve(engine.introspector).at(elem.span())?;
let url = || dest.as_url().map(|url| url.clone().into_inner());
// TODO(accessibility): remove custom alt field and generate alt text
let alt = elem.alt.get_cloned(styles).or_else(url);
let alt = match elem.alt.get_cloned(styles) {
Some(alt) => Some(alt),
None => dest.alt_text(engine, styles)?,
};
Ok(body.linked(dest, alt))
};

View File

@ -795,8 +795,8 @@ impl<'a> Generator<'a> {
renderer.display_elem_child(elem, &mut None, false)?;
if let Some(location) = first_occurrences.get(item.key.as_str()) {
let dest = Destination::Location(*location);
// TODO(accessibility): generate alt text
content = content.linked(dest, None);
let alt = content.plain_text();
content = content.linked(dest, Some(alt));
}
StrResult::Ok(content)
})
@ -931,8 +931,8 @@ impl ElemRenderer<'_> {
if let Some(hayagriva::ElemMeta::Entry(i)) = elem.meta {
if let Some(location) = (self.link)(i) {
let dest = Destination::Location(location);
// TODO(accessibility): generate alt text
content = content.linked(dest, None);
let alt = content.plain_text();
content = content.linked(dest, Some(alt));
}
}

View File

@ -1,15 +1,18 @@
use std::ops::Deref;
use std::str::FromStr;
use comemo::Tracked;
use ecow::{eco_format, EcoString};
use crate::diag::{bail, StrResult};
use crate::diag::{bail, SourceResult, StrResult};
use crate::engine::Engine;
use crate::foundations::{
cast, elem, Content, Label, Packed, Repr, ShowSet, Smart, StyleChain, Styles,
};
use crate::introspection::{Introspector, Locatable, Location};
use crate::layout::Position;
use crate::text::TextElem;
use crate::introspection::{Counter, CounterKey, Introspector, Locatable, Location};
use crate::layout::{PageElem, Position};
use crate::model::NumberingPattern;
use crate::text::{LocalName, TextElem};
/// Links to a URL or a location in the document.
///
@ -216,12 +219,26 @@ pub enum Destination {
}
impl Destination {
pub fn as_url(&self) -> Option<&Url> {
if let Self::Url(v) = self {
Some(v)
} else {
None
pub fn alt_text(
&self,
engine: &mut Engine,
styles: StyleChain,
) -> SourceResult<Option<EcoString>> {
let alt = match self {
Destination::Url(url) => Some(url.clone().into_inner()),
Destination::Position(_) => None,
&Destination::Location(loc) => {
let numbering = loc
.page_numbering(engine)
.unwrap_or_else(|| NumberingPattern::from_str("1").unwrap().into());
let content = Counter::new(CounterKey::Page)
.display_at_loc(engine, loc, styles, &numbering)?;
let page_nr = content.plain_text();
let page_str = PageElem::local_name_in(styles);
Some(eco_format!("{page_str} {page_nr}"))
}
};
Ok(alt)
}
}