WIP [no ci]

This commit is contained in:
Tobias Schmitz 2025-06-16 18:28:42 +02:00
parent 1552cdf0f8
commit 529ae78561
No known key found for this signature in database
3 changed files with 50 additions and 62 deletions

View File

@ -9,11 +9,11 @@ use crate::introspection::Locatable;
// TODO: docs // TODO: docs
#[elem(Locatable, Show)] #[elem(Locatable, Show)]
pub struct PdfTagElem { pub struct PdfTagElem {
#[default(PdfStructElem::NonStruct)] #[default(PdfTagKind::NonStruct)]
pub kind: PdfStructElem, pub kind: PdfTagKind,
/// An alternate description /// An alternate description.
pub alt_desc: Option<EcoString>, pub alt: Option<EcoString>,
/// Exact replacement for this structure element and its children. /// Exact replacement for this structure element and its children.
pub actual_text: Option<EcoString>, pub actual_text: Option<EcoString>,
/// The expanded form of an abbreviation/acronym. /// The expanded form of an abbreviation/acronym.
@ -34,7 +34,7 @@ impl Show for Packed<PdfTagElem> {
// TODO: docs // TODO: docs
/// PDF structure elements /// PDF structure elements
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum PdfStructElem { pub enum PdfTagKind {
// grouping elements // grouping elements
/// (Part) /// (Part)
Part, Part,
@ -146,9 +146,9 @@ pub enum PdfStructElem {
} }
cast! { cast! {
PdfStructElem, PdfTagKind,
self => match self { self => match self {
PdfStructElem::Part => "part".into_value(), PdfTagKind::Part => "part".into_value(),
_ => todo!(), _ => todo!(),
}, },
"part" => Self::Part, "part" => Self::Part,

View File

@ -2,6 +2,7 @@ use krilla::action::{Action, LinkAction};
use krilla::annotation::{Annotation, LinkAnnotation, Target}; use krilla::annotation::{Annotation, LinkAnnotation, Target};
use krilla::destination::XyzDestination; use krilla::destination::XyzDestination;
use krilla::geom::Rect; use krilla::geom::Rect;
use krilla::tagging::Tag;
use typst_library::layout::{Abs, Point, Position, Size}; use typst_library::layout::{Abs, Point, Position, Size};
use typst_library::model::Destination; use typst_library::model::Destination;
@ -45,6 +46,8 @@ pub(crate) fn handle_link(
let rect = Rect::from_ltrb(x1, y1, x2, y2).unwrap(); let rect = Rect::from_ltrb(x1, y1, x2, y2).unwrap();
// TODO: Support quad points. // TODO: Support quad points.
// > Beginning with PDF 1.7, use of the Link structure element to enclose
// > multiple link annotations is deprecated.
let target = match dest { let target = match dest {
Destination::Url(u) => { Destination::Url(u) => {
@ -70,7 +73,13 @@ pub(crate) fn handle_link(
}; };
let placeholder = gc.tags.reserve_placeholder(); let placeholder = gc.tags.reserve_placeholder();
gc.tags.push(TagNode::Placeholder(placeholder)); let mut node = TagNode::Placeholder(placeholder);
let (parent_tag, _) = gc.tags.parent_nodes();
if !matches!(parent_tag, Some(Tag::Link | Tag::Reference)) {
node = TagNode::Group(Link, )
}
// Prepend so the link annotation is the first child.
gc.tags.prepend();
fc.push_annotation( fc.push_annotation(
placeholder, placeholder,

View File

@ -8,8 +8,11 @@ use krilla::tagging::{
}; };
use typst_library::foundations::{Content, StyleChain}; use typst_library::foundations::{Content, StyleChain};
use typst_library::introspection::Location; use typst_library::introspection::Location;
use typst_library::model::{HeadingElem, OutlineElem, OutlineEntry}; use typst_library::model::{
use typst_library::pdf::{ArtifactElem, ArtifactKind, PdfStructElem, PdfTagElem}; CiteElem, FigureCaption, FigureElem, HeadingElem, LinkElem, OutlineElem,
OutlineEntry, RefElem,
};
use typst_library::pdf::{ArtifactElem, ArtifactKind, PdfTagElem, PdfTagKind};
use crate::convert::GlobalContext; use crate::convert::GlobalContext;
@ -72,6 +75,14 @@ impl Tags {
} }
} }
pub(crate) fn prepend(&mut self, node: TagNode) {
if let Some((_, _, nodes)) = self.stack.last_mut() {
nodes.insert(0, node);
} else {
self.tree.insert(0, node);
}
}
pub(crate) fn build_tree(&mut self) -> TagTree { pub(crate) fn build_tree(&mut self) -> TagTree {
let mut tree = TagTree::new(); let mut tree = TagTree::new();
let nodes = std::mem::take(&mut self.tree); let nodes = std::mem::take(&mut self.tree);
@ -99,60 +110,17 @@ impl Tags {
} }
/// Returns the current parent's list of children and whether it is the tree root. /// Returns the current parent's list of children and whether it is the tree root.
fn parent_nodes(&mut self) -> (bool, &mut Vec<TagNode>) { pub(crate) fn parent_nodes(&mut self) -> (Option<&mut Tag>, &mut Vec<TagNode>) {
if let Some((_, _, parent_nodes)) = self.stack.last_mut() { if let Some((_, tag, parent_nodes)) = self.stack.last_mut() {
(false, parent_nodes) (Some(tag), parent_nodes)
} else { } else {
(true, &mut self.tree) (None, &mut self.tree)
} }
} }
fn context_supports(&self, tag: &Tag) -> bool { fn context_supports(&self, _tag: &Tag) -> bool {
let Some((_, parent, _)) = self.stack.last() else { return true }; // TODO: generate using: https://pdfa.org/resource/iso-ts-32005-hierarchical-inclusion-rules/
true
use Tag::*;
match parent {
Part => true,
Article => !matches!(tag, Article),
Section => true,
BlockQuote => todo!(),
Caption => todo!(),
TOC => matches!(tag, TOC | TOCI),
// TODO: NonStruct is allowed to but (currently?) not supported by krilla
TOCI => matches!(tag, TOC | Lbl | Reference | P),
Index => todo!(),
P => todo!(),
H1(_) => todo!(),
H2(_) => todo!(),
H3(_) => todo!(),
H4(_) => todo!(),
H5(_) => todo!(),
H6(_) => todo!(),
L(_list_numbering) => todo!(),
LI => todo!(),
Lbl => todo!(),
LBody => todo!(),
Table => todo!(),
TR => todo!(),
TH(_table_header_scope) => todo!(),
TD => todo!(),
THead => todo!(),
TBody => todo!(),
TFoot => todo!(),
InlineQuote => todo!(),
Note => todo!(),
Reference => todo!(),
BibEntry => todo!(),
Code => todo!(),
Link => todo!(),
Annot => todo!(),
Figure(_) => todo!(),
Formula(_) => todo!(),
Datetime => todo!(),
Terms => todo!(),
Title => todo!(),
}
} }
} }
@ -212,7 +180,7 @@ pub(crate) fn handle_start(
let tag = if let Some(pdf_tag) = elem.to_packed::<PdfTagElem>() { let tag = if let Some(pdf_tag) = elem.to_packed::<PdfTagElem>() {
let kind = pdf_tag.kind(StyleChain::default()); let kind = pdf_tag.kind(StyleChain::default());
match kind { match kind {
PdfStructElem::Part => Tag::Part, PdfTagKind::Part => Tag::Part,
_ => todo!(), _ => todo!(),
} }
} else if let Some(heading) = elem.to_packed::<HeadingElem>() { } else if let Some(heading) = elem.to_packed::<HeadingElem>() {
@ -229,8 +197,19 @@ pub(crate) fn handle_start(
} }
} else if let Some(_) = elem.to_packed::<OutlineElem>() { } else if let Some(_) = elem.to_packed::<OutlineElem>() {
Tag::TOC Tag::TOC
} else if let Some(_outline_entry) = elem.to_packed::<OutlineEntry>() { } else if let Some(_) = elem.to_packed::<OutlineEntry>() {
Tag::TOCI Tag::TOCI
} else if let Some(_) = elem.to_packed::<FigureElem>() {
let alt = None; // TODO
Tag::Figure(alt)
} else if let Some(_) = elem.to_packed::<FigureCaption>() {
Tag::Caption
} else if let Some(_) = elem.to_packed::<LinkElem>() {
Tag::Link
} else if let Some(_) = elem.to_packed::<RefElem>() {
Tag::Reference
} else if let Some(_) = elem.to_packed::<CiteElem>() {
Tag::Reference
} else { } else {
return; return;
}; };