Skip to content

Commit 75cfa62

Browse files
committed
finish
1 parent 33740b1 commit 75cfa62

File tree

6 files changed

+192
-18
lines changed

6 files changed

+192
-18
lines changed

src/document.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::object::outline::Outline;
12
use crate::object::page::PageLabel;
23
use crate::serialize::{SerializeSettings, SerializerContext};
34
use crate::surface::PageBuilder;
@@ -26,6 +27,10 @@ impl Document {
2627
PageBuilder::new_with(&mut self.serializer_context, size, page_label)
2728
}
2829

30+
pub fn set_outline(&mut self, outline: Outline) {
31+
self.serializer_context.set_outline(outline);
32+
}
33+
2934
pub fn finish(self) -> Vec<u8> {
3035
self.serializer_context.finish().finish()
3136
}

src/object/ext_g_state.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,6 @@ impl Object for ExtGState {
119119
ext_st.blend_mode(bm);
120120
}
121121

122-
123122
if let Some(mask_ref) = mask_ref {
124123
ext_st.pair(Name(b"SMask"), mask_ref);
125124
}

src/object/outline.rs

Lines changed: 164 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
use pdf_writer::{Chunk, Ref};
2-
use tiny_skia_path::Point;
31
use crate::serialize::{Object, SerializerContext};
2+
use pdf_writer::{Chunk, Finish, Ref, TextStr};
3+
use tiny_skia_path::{Point, Transform};
44

5-
#[derive(Debug)]
5+
#[derive(Debug, Clone)]
66
pub struct Outline {
77
children: Vec<OutlineNode>,
88
}
@@ -17,8 +17,8 @@ impl Outline {
1717
}
1818
}
1919

20-
#[derive(Debug)]
21-
struct OutlineNode {
20+
#[derive(Debug, Clone)]
21+
pub struct OutlineNode {
2222
children: Vec<Box<OutlineNode>>,
2323
text: String,
2424
page_index: u32,
@@ -39,11 +39,168 @@ impl OutlineNode {
3939
self.children.push(Box::new(node))
4040
}
4141

42+
pub fn serialize_into(
43+
&self,
44+
sc: &mut SerializerContext,
45+
parent: Ref,
46+
root: Ref,
47+
next: Option<Ref>,
48+
prev: Option<Ref>,
49+
) -> Chunk {
50+
let mut chunk = Chunk::new();
4251

52+
let mut sub_chunks = vec![];
53+
54+
let mut outline_entry = chunk.outline_item(root);
55+
outline_entry.parent(parent);
56+
57+
if let Some(next) = next {
58+
outline_entry.next(next);
59+
}
60+
61+
if let Some(prev) = prev {
62+
outline_entry.prev(prev);
63+
}
64+
65+
if !self.children.is_empty() {
66+
let first = sc.new_ref();
67+
let mut last = first;
68+
69+
let mut prev = None;
70+
let mut cur = Some(first);
71+
72+
for i in 0..self.children.len() {
73+
let next = if i < self.children.len() - 1 {
74+
Some(sc.new_ref())
75+
} else {
76+
None
77+
};
78+
79+
last = cur.unwrap();
80+
81+
sub_chunks.push(self.children[i].serialize_into(sc, root, last, next, prev));
82+
83+
prev = cur;
84+
cur = next;
85+
}
86+
87+
outline_entry.first(first);
88+
outline_entry.last(last);
89+
outline_entry.count(-i32::try_from(self.children.len()).unwrap());
90+
}
91+
92+
if !self.text.is_empty() {
93+
outline_entry.title(TextStr(&self.text));
94+
}
95+
96+
let page_ref = sc.page_infos()[self.page_index as usize].ref_;
97+
let page_size = sc.page_infos()[self.page_index as usize].media_box.height();
98+
let mut mapped_point = self.pos;
99+
let invert_transform = Transform::from_row(1.0, 0.0, 0.0, -1.0, 0.0, page_size);
100+
invert_transform.map_point(&mut mapped_point);
101+
102+
outline_entry
103+
.dest()
104+
.page(page_ref)
105+
.xyz(mapped_point.x, mapped_point.y, None);
106+
107+
outline_entry.finish();
108+
109+
for sub_chunk in sub_chunks {
110+
chunk.extend(&sub_chunk);
111+
}
112+
113+
chunk
114+
}
43115
}
44116

45117
impl Object for Outline {
46118
fn serialize_into(self, sc: &mut SerializerContext, root_ref: Ref) -> Chunk {
47-
todo!()
119+
let mut chunk = Chunk::new();
120+
121+
let mut sub_chunks = vec![];
122+
123+
let mut outline = chunk.outline(root_ref);
124+
125+
if !self.children.is_empty() {
126+
let first = sc.new_ref();
127+
let mut last = first;
128+
129+
let mut prev = None;
130+
let mut cur = Some(first);
131+
132+
for i in 0..self.children.len() {
133+
let next = if i < self.children.len() - 1 {
134+
Some(sc.new_ref())
135+
} else {
136+
None
137+
};
138+
139+
last = cur.unwrap();
140+
141+
sub_chunks.push(self.children[i].serialize_into(sc, root_ref, last, next, prev));
142+
143+
prev = cur;
144+
cur = next;
145+
}
146+
147+
outline.first(first);
148+
outline.last(last);
149+
outline.count(i32::try_from(self.children.len()).unwrap());
150+
}
151+
152+
outline.finish();
153+
154+
for sub_chunk in sub_chunks {
155+
chunk.extend(&sub_chunk);
156+
}
157+
158+
chunk
159+
}
160+
}
161+
162+
#[cfg(test)]
163+
mod tests {
164+
use crate::document::Document;
165+
use crate::object::outline::{Outline, OutlineNode};
166+
use crate::rgb::Rgb;
167+
use crate::serialize::SerializeSettings;
168+
use crate::test_utils::check_snapshot;
169+
use crate::Fill;
170+
use tiny_skia_path::{PathBuilder, Point, Rect, Size};
171+
172+
#[test]
173+
fn simple() {
174+
let mut builder = PathBuilder::new();
175+
builder.push_rect(Rect::from_xywh(50.0, 50.0, 100.0, 100.0).unwrap());
176+
let path = builder.finish().unwrap();
177+
178+
let mut db = Document::new(SerializeSettings::default_test());
179+
let mut page = db.start_page(Size::from_wh(200.0, 200.0).unwrap());
180+
let mut surface = page.surface();
181+
surface.fill_path(&path, Fill::<Rgb>::default());
182+
surface.finish();
183+
page.finish();
184+
185+
db.start_page(Size::from_wh(200.0, 500.0).unwrap());
186+
db.start_page(Size::from_wh(250.0, 700.0).unwrap());
187+
188+
let mut outline = Outline::new();
189+
190+
let mut child1 = OutlineNode::new("Level 1".to_string(), 0, Point::from_xy(50.0, 50.0));
191+
child1.push_child(OutlineNode::new(
192+
"Level 2".to_string(),
193+
0,
194+
Point::from_xy(50.0, 150.0),
195+
));
196+
197+
let child2 = OutlineNode::new("Level 1 try 2".to_string(), 1, Point::from_xy(75.0, 150.0));
198+
199+
outline.push_child(child1);
200+
outline.push_child(child2);
201+
202+
db.set_outline(outline);
203+
204+
check_snapshot("outline/simple", &db.finish());
48205
}
49-
}
206+
}

src/object/page.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,6 @@ mod tests {
250250
check_snapshot("page/page_label", sc.finish().as_bytes());
251251
}
252252

253-
// TODO: Fix issues with not being able to create empty pages with just start_page_with.
254253
// TODO: Fix issue with two duplicate pages not showing up.
255254

256255
#[test]

src/serialize.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use crate::object::cid_font::CIDFont;
33
use crate::object::color_space::luma::SGray;
44
use crate::object::color_space::rgb::Srgb;
55
use crate::object::color_space::{DEVICE_GRAY, DEVICE_RGB};
6+
use crate::object::outline::Outline;
67
use crate::object::page::{PageLabel, PageLabelContainer};
78
use crate::object::type3_font::Type3Font;
89
use crate::resource::{ColorSpaceEnum, FontResource};
@@ -17,7 +18,6 @@ use std::collections::HashMap;
1718
use std::hash::Hash;
1819
use std::sync::Arc;
1920
use tiny_skia_path::Rect;
20-
use crate::object::outline::Outline;
2121

2222
#[derive(Copy, Clone, Debug)]
2323
pub struct SvgSettings {
@@ -83,6 +83,7 @@ pub struct SerializerContext {
8383
catalog_ref: Ref,
8484
page_tree_ref: Ref,
8585
page_labels_ref: Option<Ref>,
86+
outline_ref: Option<Ref>,
8687
page_infos: Vec<PageInfo>,
8788
outline: Option<Outline>,
8889
cached_mappings: HashMap<u128, Ref>,
@@ -126,6 +127,7 @@ impl SerializerContext {
126127
page_tree_ref,
127128
catalog_ref,
128129
outline: None,
130+
outline_ref: None,
129131
page_labels_ref: None,
130132
page_infos: vec![],
131133
chunks_len: 0,
@@ -134,6 +136,10 @@ impl SerializerContext {
134136
}
135137
}
136138

139+
pub fn page_infos(&self) -> &[PageInfo] {
140+
&self.page_infos
141+
}
142+
137143
pub fn set_outline(&mut self, outline: Outline) {
138144
self.outline = Some(outline);
139145
}
@@ -299,6 +305,13 @@ impl SerializerContext {
299305
self.page_labels_ref = Some(self.add(container));
300306
}
301307

308+
if let Some(outline) = self.outline.clone() {
309+
let outline_ref = self.new_ref();
310+
self.outline_ref = Some(outline_ref);
311+
let chunk = outline.serialize_into(&mut self, outline_ref);
312+
self.push_chunk(chunk);
313+
}
314+
302315
// Write fonts
303316
// TODO: Make more efficient
304317
let fonts = std::mem::take(&mut self.font_map);
@@ -341,6 +354,10 @@ impl SerializerContext {
341354
catalog.pair(Name(b"PageLabels"), plr);
342355
}
343356

357+
if let Some(olr) = self.outline_ref {
358+
catalog.outlines(olr);
359+
}
360+
344361
catalog.finish();
345362

346363
pdf.extend(&page_tree_chunk);

src/surface.rs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,10 @@ impl<'a> PageBuilder<'a> {
218218
}
219219
}
220220

221+
pub(crate) fn root_transform(&self) -> Transform {
222+
Transform::from_row(1.0, 0.0, 0.0, -1.0, 0.0, self.size.height())
223+
}
224+
221225
pub(crate) fn new_with(
222226
sc: &'a mut SerializerContext,
223227
size: Size,
@@ -234,14 +238,7 @@ impl<'a> PageBuilder<'a> {
234238
pub fn surface(&mut self) -> Surface {
235239
let mut root_builder = ContentBuilder::new();
236240
// Invert the y-axis.
237-
root_builder.concat_transform(&Transform::from_row(
238-
1.0,
239-
0.0,
240-
0.0,
241-
-1.0,
242-
0.0,
243-
self.size.height(),
244-
));
241+
root_builder.concat_transform(&self.root_transform());
245242

246243
let finish_fn = Box::new(|stream| self.page_stream = stream);
247244

0 commit comments

Comments
 (0)