summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-11-28 12:40:16 +0100
committerLaurenz <laurmaedje@gmail.com>2022-11-28 12:40:16 +0100
commit989d170dc7318ca3cbaa5b76760eb14f4e6a8605 (patch)
tree0a486ddb4d339b8a43313f7c6e18b9595b8fd955 /src
parent7caf98fe42797eab59a39ef71071030c9790245a (diff)
Fragments
Diffstat (limited to 'src')
-rw-r--r--src/doc.rs108
-rw-r--r--src/export/pdf/mod.rs2
-rw-r--r--src/export/pdf/outline.rs33
-rw-r--r--src/export/pdf/page.rs23
4 files changed, 89 insertions, 77 deletions
diff --git a/src/doc.rs b/src/doc.rs
index f65d5ae6..5d395be4 100644
--- a/src/doc.rs
+++ b/src/doc.rs
@@ -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();