diff --git a/crates/typst-pdf/src/krilla.rs b/crates/typst-pdf/src/krilla.rs index a0795e238..80898281c 100644 --- a/crates/typst-pdf/src/krilla.rs +++ b/crates/typst-pdf/src/krilla.rs @@ -157,15 +157,19 @@ impl krilla::font::Glyph for PdfGlyph { } pub struct GlobalContext<'a> { + /// Cache the conversion between krilla and Typst fonts (forward and backward). fonts_forward: HashMap, fonts_backward: HashMap, // Note: In theory, the same image can have multiple spans // if it appears in the document multiple times. We just store the // first appearance, though. + /// Mapping between images and their span. image_spans: HashMap, pub(crate) document: &'a PagedDocument, pub(crate) options: &'a PdfOptions<'a>, + /// Mapping between locations in the document and named destinations. loc_to_named: HashMap, + /// The languages used throughout the document. languages: BTreeMap, } diff --git a/crates/typst-pdf/src/outline.rs b/crates/typst-pdf/src/outline.rs index 34d25ddd9..ad4f7ee14 100644 --- a/crates/typst-pdf/src/outline.rs +++ b/crates/typst-pdf/src/outline.rs @@ -1,13 +1,14 @@ use std::num::NonZeroUsize; + use krilla::destination::XyzDestination; use krilla::outline::{Outline, OutlineNode}; use typst_library::foundations::{NativeElement, Packed, StyleChain}; use typst_library::layout::Abs; use typst_library::model::HeadingElem; + use crate::krilla::GlobalContext; use crate::util::AbsExt; -/// A heading in the outline panel. #[derive(Debug)] struct HeadingNode<'a> { element: &'a Packed, @@ -28,6 +29,30 @@ impl<'a> HeadingNode<'a> { children: Vec::new(), } } + + fn to_krilla(&self, gc: &GlobalContext) -> Option { + let loc = self.element.location().unwrap(); + let title = self.element.body().plain_text().to_string(); + let pos = gc.document.introspector.position(loc); + let page_index = pos.page.get() - 1; + + if !gc.page_excluded(page_index) { + let y = (pos.point.y - Abs::pt(10.0)).max(Abs::zero()); + let dest = XyzDestination::new( + page_index, + krilla::geom::Point::from_xy(pos.point.x.to_f32(), y.to_f32()), + ); + + let mut outline_node = OutlineNode::new(title, dest); + for child in convert_nodes(&self.children, gc) { + outline_node.push_child(child); + } + + return Some(outline_node); + } + + None + } } pub(crate) fn build_outline(gc: &GlobalContext) -> Outline { @@ -115,35 +140,13 @@ pub(crate) fn build_outline(gc: &GlobalContext) -> Outline { let mut outline = Outline::new(); - for child in convert_heading_nodes(&tree, gc) { + for child in convert_nodes(&tree, gc) { outline.push_child(child); } outline } -fn convert_heading_nodes(nodes: &[HeadingNode], gc: &GlobalContext) -> Vec { - nodes.iter().flat_map(|node| { - let loc = node.element.location().unwrap(); - let title = node.element.body().plain_text().to_string(); - let pos = gc.document.introspector.position(loc); - let page_index = pos.page.get() - 1; - - if !gc.page_excluded(page_index) { - let y = (pos.point.y - Abs::pt(10.0)).max(Abs::zero()); - let dest = XyzDestination::new( - page_index, - krilla::geom::Point::from_xy(pos.point.x.to_f32(), y.to_f32()), - ); - - let mut outline_node = OutlineNode::new(title, dest); - for child in convert_heading_nodes(&node.children, gc) { - outline_node.push_child(child); - } - - return Some(outline_node); - } - - None - }).collect() +fn convert_nodes(nodes: &[HeadingNode], gc: &GlobalContext) -> Vec { + nodes.iter().flat_map(|node| node.to_krilla(gc)).collect() }