WIP [no ci]

This commit is contained in:
Tobias Schmitz 2025-06-18 18:02:49 +02:00
parent 4a64a717e8
commit 616c626acb
No known key found for this signature in database
9 changed files with 60 additions and 33 deletions

View File

@ -16,12 +16,12 @@ use crate::diag::{SourceResult, StrResult};
use crate::engine::Engine;
use crate::foundations::{
elem, func, scope, ty, Context, Dict, Element, Fields, IntoValue, Label,
NativeElement, Recipe, RecipeIndex, Repr, Selector, Str, Style, StyleChain, Styles,
Value,
NativeElement, Recipe, RecipeIndex, Repr, Selector, Show, Str, Style, StyleChain,
Styles, Value,
};
use crate::introspection::Location;
use crate::introspection::{Locatable, Location};
use crate::layout::{AlignElem, Alignment, Axes, Length, MoveElem, PadElem, Rel, Sides};
use crate::model::{Destination, EmphElem, LinkElem, StrongElem};
use crate::model::{Destination, EmphElem, StrongElem};
use crate::pdf::{ArtifactElem, ArtifactKind};
use crate::text::UnderlineElem;
@ -504,9 +504,9 @@ impl Content {
}
/// Link the content somewhere.
pub fn linked(self, alt: Option<EcoString>, dest: Destination) -> Self {
self.styled(LinkElem::set_alt(alt))
.styled(LinkElem::set_current(Some(dest)))
pub fn linked(self, dest: Destination, alt: Option<EcoString>) -> Self {
let span = self.span();
LinkMarker::new(self, dest, alt).pack().spanned(span)
}
/// Set alignments for this content.
@ -988,6 +988,26 @@ pub trait PlainText {
fn plain_text(&self, text: &mut EcoString);
}
// TODO: remove `FrameItem::Link` and `FrameModifiers::dest`, then handle links by looking at tags
/// An element that associates the body of a link with the destination.
#[elem(Show, Locatable)]
pub struct LinkMarker {
/// The content.
#[required]
pub body: Content,
#[required]
pub dest: Destination,
#[required]
pub alt: Option<EcoString>,
}
impl Show for Packed<LinkMarker> {
fn show(&self, _: &mut Engine, _: StyleChain) -> SourceResult<Content> {
Ok(self.body.clone())
}
}
/// An error arising when trying to access a field of content.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum FieldAccessError {

View File

@ -523,6 +523,8 @@ pub struct StyleChain<'a> {
}
impl<'a> StyleChain<'a> {
pub const EMPTY: Self = Self { head: &[], tail: None };
/// Start a new style chain with root styles.
pub fn new(root: &'a Styles) -> Self {
Self { head: &root.0, tail: None }

View File

@ -874,7 +874,7 @@ impl<'a> Generator<'a> {
if let Some(location) = first_occurrences.get(item.key.as_str()) {
let dest = Destination::Location(*location);
// TODO: accept user supplied alt text
content = content.linked(None, dest);
content = content.linked(dest, None);
}
StrResult::Ok(content)
})
@ -1010,7 +1010,7 @@ impl ElemRenderer<'_> {
if let Some(location) = (self.link)(i) {
let dest = Destination::Location(location);
// TODO: accept user supplied alt text
content = content.linked(None, dest);
content = content.linked(dest, None);
}
}

View File

@ -148,7 +148,7 @@ impl Show for Packed<FootnoteElem> {
let loc = loc.variant(1);
// Add zero-width weak spacing to make the footnote "sticky".
// TODO: accept user supplied alt text
Ok(HElem::hole().pack() + sup.linked(None, Destination::Location(loc)))
Ok(HElem::hole().pack() + sup.linked(Destination::Location(loc), None))
}
}
@ -298,7 +298,7 @@ impl Show for Packed<FootnoteEntry> {
.pack()
.spanned(span)
// TODO: accept user supplied alt text
.linked(None, Destination::Location(loc))
.linked(Destination::Location(loc), None)
.located(loc.variant(1));
Ok(Content::sequence([

View File

@ -91,11 +91,6 @@ pub struct LinkElem {
_ => args.expect("body")?,
})]
pub body: Content,
/// A destination style that should be applied to elements.
#[internal]
#[ghost]
pub current: Option<Destination>,
}
impl LinkElem {
@ -128,11 +123,11 @@ impl Show for Packed<LinkElem> {
} else {
let alt = self.alt(styles);
match &self.dest {
LinkTarget::Dest(dest) => body.linked(alt, dest.clone()),
LinkTarget::Dest(dest) => body.linked(dest.clone(), alt),
LinkTarget::Label(label) => {
let elem = engine.introspector.query_label(*label).at(self.span())?;
let dest = Destination::Location(elem.location().unwrap());
body.clone().linked(alt, dest)
body.clone().linked(dest, alt)
}
}
})

View File

@ -446,7 +446,7 @@ impl Show for Packed<OutlineEntry> {
};
let loc = self.element_location().at(span)?;
Ok(block.linked(Some(alt), Destination::Location(loc)))
Ok(block.linked(Destination::Location(loc), Some(alt)))
}
}

View File

@ -310,7 +310,7 @@ fn show_reference(
}
// TODO: accept user supplied alt text
Ok(content.linked(None, Destination::Location(loc)))
Ok(content.linked(Destination::Location(loc), None))
}
/// Turn a reference into a citation.

View File

@ -30,10 +30,6 @@ pub(crate) fn handle_image(
let interpolate = image.scaling() == Smart::Custom(ImageScaling::Smooth);
if let Some(alt) = image.alt() {
surface.start_alt_text(alt);
}
gc.image_spans.insert(span);
match image.kind() {
@ -62,10 +58,6 @@ pub(crate) fn handle_image(
}
}
if image.alt().is_some() {
surface.end_alt_text();
}
surface.pop();
surface.reset_location();

View File

@ -9,10 +9,11 @@ use krilla::tagging::{
use typst_library::foundations::{Content, StyleChain};
use typst_library::introspection::Location;
use typst_library::model::{
CiteElem, FigureCaption, FigureElem, HeadingElem, LinkElem, OutlineElem,
CiteElem, FigureCaption, FigureElem, HeadingElem, LinkElem, Outlinable, OutlineElem,
OutlineEntry, RefElem,
};
use typst_library::pdf::{ArtifactElem, ArtifactKind, PdfTagElem, PdfTagKind};
use typst_library::visualize::ImageElem;
use crate::convert::GlobalContext;
@ -177,7 +178,7 @@ pub(crate) fn handle_start(
_ => todo!(),
}
} else if let Some(heading) = elem.to_packed::<HeadingElem>() {
let level = heading.resolve_level(StyleChain::default());
let level = heading.level();
let name = heading.body.plain_text().to_string();
match level.get() {
1 => Tag::H1(Some(name)),
@ -195,6 +196,25 @@ pub(crate) fn handle_start(
} else if let Some(_) = elem.to_packed::<FigureElem>() {
let alt = None; // TODO
Tag::Figure(alt)
} else if let Some(image) = elem.to_packed::<ImageElem>() {
let alt = image.alt(StyleChain::default()).map(|s| s.to_string());
end_open(gc, surface);
let id = surface.start_tagged(ContentTag::Other);
let mut node = TagNode::Leaf(id);
if let Some(Tag::Figure(alt_text)) = gc.tags.parent().0 {
// HACK: set alt text of outer figure tag, if the contained image
// has alt text specified
if alt_text.is_none() {
*alt_text = alt;
}
} else {
node = TagNode::Group(Tag::Figure(alt), vec![node]);
}
gc.tags.push(node);
return;
} else if let Some(_) = elem.to_packed::<FigureCaption>() {
Tag::Caption
} else if let Some(_) = elem.to_packed::<LinkElem>() {
@ -212,9 +232,7 @@ pub(crate) fn handle_start(
}
// close previous marked-content and open a nested tag.
if !gc.tags.stack.is_empty() {
surface.end_tagged();
}
end_open(gc, surface);
let id = surface.start_tagged(krilla::tagging::ContentTag::Other);
gc.tags.stack.push((loc, tag, vec![TagNode::Leaf(id)]));
}