diff options
| author | Laurenz <laurmaedje@gmail.com> | 2022-11-28 12:40:16 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2022-11-28 12:40:16 +0100 |
| commit | 989d170dc7318ca3cbaa5b76760eb14f4e6a8605 (patch) | |
| tree | 0a486ddb4d339b8a43313f7c6e18b9595b8fd955 /src | |
| parent | 7caf98fe42797eab59a39ef71071030c9790245a (diff) | |
Fragments
Diffstat (limited to 'src')
| -rw-r--r-- | src/doc.rs | 108 | ||||
| -rw-r--r-- | src/export/pdf/mod.rs | 2 | ||||
| -rw-r--r-- | src/export/pdf/outline.rs | 33 | ||||
| -rw-r--r-- | src/export/pdf/page.rs | 23 |
4 files changed, 89 insertions, 77 deletions
@@ -14,7 +14,7 @@ use crate::model::{dict, Dict, Value}; use crate::util::EcoString; /// A finished document with metadata and page frames. -#[derive(Debug, Clone, Eq, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub struct Document { /// The document's metadata. pub metadata: Metadata, @@ -31,6 +31,73 @@ pub struct Metadata { pub author: Option<EcoString>, } +/// A partial layout result. +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct Fragment(Vec<Frame>); + +impl Fragment { + /// Create a fragment from a single frame. + pub fn frame(frame: Frame) -> Self { + Self(vec![frame]) + } + + /// Create a fragment from multiple frames. + pub fn frames(frames: Vec<Frame>) -> Self { + Self(frames) + } + + /// The number of frames in the fragment. + pub fn len(&self) -> usize { + self.0.len() + } + + /// Extract the first and only frame. + /// + /// Panics if there are multiple frames. + #[track_caller] + pub fn into_frame(self) -> Frame { + assert_eq!(self.0.len(), 1, "expected exactly one frame"); + self.0.into_iter().next().unwrap() + } + + /// Iterate over the contained frames. + pub fn iter(&self) -> std::slice::Iter<Frame> { + self.0.iter() + } + + /// Iterate over the contained frames. + pub fn iter_mut(&mut self) -> std::slice::IterMut<Frame> { + self.0.iter_mut() + } +} + +impl IntoIterator for Fragment { + type Item = Frame; + type IntoIter = std::vec::IntoIter<Frame>; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl<'a> IntoIterator for &'a Fragment { + type Item = &'a Frame; + type IntoIter = std::slice::Iter<'a, Frame>; + + fn into_iter(self) -> Self::IntoIter { + self.0.iter() + } +} + +impl<'a> IntoIterator for &'a mut Fragment { + type Item = &'a mut Frame; + type IntoIter = std::slice::IterMut<'a, Frame>; + + fn into_iter(self) -> Self::IntoIter { + self.0.iter_mut() + } +} + /// A finished layout with elements at fixed positions. #[derive(Default, Clone, Eq, PartialEq)] pub struct Frame { @@ -39,8 +106,6 @@ pub struct Frame { /// The baseline of the frame measured from the top. If this is `None`, the /// frame's implicit baseline is at the bottom. baseline: Option<Abs>, - /// The semantic role of the frame. - role: Option<Role>, /// The elements composing this layout. elements: Arc<Vec<(Point, Element)>>, } @@ -53,12 +118,7 @@ impl Frame { #[track_caller] pub fn new(size: Size) -> Self { assert!(size.is_finite()); - Self { - size, - baseline: None, - role: None, - elements: Arc::new(vec![]), - } + Self { size, baseline: None, elements: Arc::new(vec![]) } } /// The size of the frame. @@ -96,11 +156,6 @@ impl Frame { self.baseline = Some(baseline); } - /// The role of the frame. - pub fn role(&self) -> Option<Role> { - self.role - } - /// An iterator over the elements inside this frame alongside their /// positions relative to the top-left of the frame. pub fn elements(&self) -> std::slice::Iter<'_, (Point, Element)> { @@ -125,7 +180,7 @@ impl Frame { } } -/// Inserting elements and subframes. +/// Insert elements and subframes. impl Frame { /// The layer the next item will be added on. This corresponds to the number /// of elements in the frame. @@ -141,7 +196,7 @@ impl Frame { /// Add a frame at a position in the foreground. /// /// Automatically decides whether to inline the frame or to include it as a - /// group based on the number of elements in and the role of the frame. + /// group based on the number of elements in it. pub fn push_frame(&mut self, pos: Point, frame: Frame) { if self.should_inline(&frame) { self.inline(self.layer(), pos, frame); @@ -185,8 +240,7 @@ impl Frame { /// Whether the given frame should be inlined. fn should_inline(&self, frame: &Frame) -> bool { - (self.elements.is_empty() || frame.elements.len() <= 5) - && frame.role().map_or(true, |role| role.is_weak()) + self.elements.is_empty() || frame.elements.len() <= 5 } /// Inline a frame at the given layer. @@ -294,10 +348,6 @@ impl Frame { impl Debug for Frame { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - if let Some(role) = self.role { - write!(f, "{role:?} ")?; - } - f.debug_list() .entries(self.elements.iter().map(|(_, element)| element)) .finish() @@ -503,8 +553,8 @@ impl Location { } } -/// A semantic role of a frame. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +/// Standard semantic roles. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum Role { /// A paragraph. Paragraph, @@ -542,13 +592,3 @@ pub enum Role { /// A page foreground. Foreground, } - -impl Role { - /// Whether the role describes a generic element and is not very - /// descriptive. - pub fn is_weak(self) -> bool { - // In Typst, all text is in a paragraph, so paragraph isn't very - // descriptive. - matches!(self, Self::Paragraph | Self::GenericBlock | Self::GenericInline) - } -} diff --git a/src/export/pdf/mod.rs b/src/export/pdf/mod.rs index 7e5a3c06..8f9d9637 100644 --- a/src/export/pdf/mod.rs +++ b/src/export/pdf/mod.rs @@ -12,7 +12,7 @@ use std::hash::Hash; use pdf_writer::types::Direction; use pdf_writer::{Finish, Name, PdfWriter, Ref, TextStr}; -use self::outline::{Heading, HeadingNode}; +use self::outline::HeadingNode; use self::page::Page; use crate::doc::{Document, Lang, Metadata}; use crate::font::Font; diff --git a/src/export/pdf/outline.rs b/src/export/pdf/outline.rs index add167b4..e7a356c1 100644 --- a/src/export/pdf/outline.rs +++ b/src/export/pdf/outline.rs @@ -4,43 +4,34 @@ use super::{AbsExt, PdfContext, RefExt}; use crate::geom::{Abs, Point}; use crate::util::EcoString; -/// A heading that can later be linked in the outline panel. +/// A heading in the outline panel. #[derive(Debug, Clone)] -pub struct Heading { +pub struct HeadingNode { pub content: EcoString, pub level: usize, pub position: Point, pub page: Ref, -} - -/// A node in the outline tree. -#[derive(Debug, Clone)] -pub struct HeadingNode { - pub heading: Heading, pub children: Vec<HeadingNode>, } impl HeadingNode { - pub fn leaf(heading: Heading) -> Self { - HeadingNode { heading, children: Vec::new() } - } - pub fn len(&self) -> usize { 1 + self.children.iter().map(Self::len).sum::<usize>() } - pub fn insert(&mut self, other: Heading, level: usize) -> bool { - if level >= other.level { + #[allow(unused)] + pub fn try_insert(&mut self, child: Self, level: usize) -> bool { + if level >= child.level { return false; } - if let Some(child) = self.children.last_mut() { - if child.insert(other.clone(), level + 1) { + if let Some(last) = self.children.last_mut() { + if last.try_insert(child.clone(), level + 1) { return true; } } - self.children.push(Self::leaf(other)); + self.children.push(child); true } } @@ -74,10 +65,10 @@ pub fn write_outline_item( outline.count(-(node.children.len() as i32)); } - outline.title(TextStr(&node.heading.content)); - outline.dest_direct().page(node.heading.page).xyz( - node.heading.position.x.to_f32(), - (node.heading.position.y + Abs::pt(3.0)).to_f32(), + outline.title(TextStr(&node.content)); + outline.dest_direct().page(node.page).xyz( + node.position.x.to_f32(), + (node.position.y + Abs::pt(3.0)).to_f32(), None, ); diff --git a/src/export/pdf/page.rs b/src/export/pdf/page.rs index 7c479425..fc714e7a 100644 --- a/src/export/pdf/page.rs +++ b/src/export/pdf/page.rs @@ -2,10 +2,8 @@ use pdf_writer::types::{ActionType, AnnotationType, ColorSpaceOperand}; use pdf_writer::writers::ColorSpace; use pdf_writer::{Content, Filter, Finish, Name, Rect, Ref, Str}; -use super::{ - deflate, AbsExt, EmExt, Heading, HeadingNode, PdfContext, RefExt, D65_GRAY, SRGB, -}; -use crate::doc::{Destination, Element, Frame, Group, Role, Text}; +use super::{deflate, AbsExt, EmExt, PdfContext, RefExt, D65_GRAY, SRGB}; +use crate::doc::{Destination, Element, Frame, Group, Text}; use crate::font::Font; use crate::geom::{ self, Abs, Color, Em, Geometry, Numeric, Paint, Point, Ratio, Shape, Size, Stroke, @@ -281,23 +279,6 @@ impl PageContext<'_, '_> { /// Encode a frame into the content stream. fn write_frame(ctx: &mut PageContext, frame: &Frame) { - if let Some(Role::Heading { level, outlined: true }) = frame.role() { - let heading = Heading { - position: Point::new(ctx.state.transform.tx, ctx.state.transform.ty), - content: frame.text(), - page: ctx.page_ref, - level: level.get(), - }; - - if let Some(last) = ctx.parent.heading_tree.last_mut() { - if !last.insert(heading.clone(), 1) { - ctx.parent.heading_tree.push(HeadingNode::leaf(heading)) - } - } else { - ctx.parent.heading_tree.push(HeadingNode::leaf(heading)) - } - } - for &(pos, ref element) in frame.elements() { let x = pos.x.to_f32(); let y = pos.y.to_f32(); |
