1use pdf_writer::{Chunk, Content, Filter, Finish, Ref};
2use usvg::{Node, Transform, Tree};
3
4use crate::util::context::Context;
5use crate::util::helper::{ContentExt, RectExt, TransformExt};
6use crate::util::resources::ResourceContainer;
7use crate::Result;
8
9pub mod clip_path;
10#[cfg(feature = "filters")]
11pub mod filter;
12pub mod gradient;
13pub mod group;
14#[cfg(feature = "image")]
15pub mod image;
16pub mod mask;
17pub mod path;
18pub mod pattern;
19#[cfg(feature = "text")]
20pub mod text;
21
22pub fn tree_to_stream(
25 tree: &Tree,
26 chunk: &mut Chunk,
27 content: &mut Content,
28 ctx: &mut Context,
29 rc: &mut ResourceContainer,
30) -> Result<()> {
31 content.save_state_checked()?;
32
33 let initial_transform =
35 Transform::from_row(1.0, 0.0, 0.0, -1.0, 0.0, tree.size().height());
36
37 content.transform(initial_transform.to_pdf_transform());
38
39 group::render(tree.root(), chunk, content, ctx, initial_transform, None, rc)?;
40 content.restore_state();
41
42 Ok(())
43}
44
45pub fn tree_to_xobject(tree: &Tree, chunk: &mut Chunk, ctx: &mut Context) -> Result<Ref> {
47 let bbox = tree.size().to_non_zero_rect(0.0, 0.0);
48 let x_ref = ctx.alloc_ref();
49
50 let mut rc = ResourceContainer::new();
51
52 let mut content = Content::new();
53 tree_to_stream(tree, chunk, &mut content, ctx, &mut rc)?;
54 let stream = ctx.finish_content(content);
55
56 let mut x_object = chunk.form_xobject(x_ref, &stream);
57 x_object.bbox(bbox.to_pdf_rect());
58 x_object.matrix([1.0 / bbox.width(), 0.0, 0.0, 1.0 / bbox.height(), 0.0, 0.0]);
59
60 if ctx.options.compress {
61 x_object.filter(Filter::FlateDecode);
62 }
63
64 let mut resources = x_object.resources();
65 rc.finish(&mut resources);
66
67 resources.finish();
68 x_object.finish();
69
70 Ok(x_ref)
71}
72
73trait Render {
74 fn render(
75 &self,
76 chunk: &mut Chunk,
77 content: &mut Content,
78 ctx: &mut Context,
79 accumulated_transform: Transform,
80 rc: &mut ResourceContainer,
81 ) -> Result<()>;
82}
83
84impl Render for Node {
85 fn render(
86 &self,
87 chunk: &mut Chunk,
88 content: &mut Content,
89 ctx: &mut Context,
90 accumulated_transform: Transform,
91 rc: &mut ResourceContainer,
92 ) -> Result<()> {
93 match self {
94 Node::Path(ref path) => {
95 path::render(path, chunk, content, ctx, rc, accumulated_transform)
96 }
97 Node::Group(ref group) => {
98 group::render(group, chunk, content, ctx, accumulated_transform, None, rc)
99 }
100 #[cfg(feature = "image")]
101 Node::Image(ref image) => image::render(
102 image.is_visible(),
103 image.kind(),
104 None,
105 chunk,
106 content,
107 ctx,
108 rc,
109 ),
110 #[cfg(not(feature = "image"))]
111 Node::Image(_) => {
112 log::warn!("Failed convert image because the image feature was disabled. Skipping.");
113 Ok(())
114 }
115 #[cfg(feature = "text")]
116 Node::Text(ref text) => {
117 if ctx.options.embed_text {
118 text::render(text, chunk, content, ctx, rc, accumulated_transform)
119 } else {
120 group::render(
121 text.flattened(),
122 chunk,
123 content,
124 ctx,
125 accumulated_transform,
126 None,
127 rc,
128 )
129 }
130 }
131 #[cfg(not(feature = "text"))]
132 Node::Text(_) => {
133 log::warn!("Failed convert text because the text feature was disabled. Skipping.");
134 Ok(())
135 }
136 }
137 }
138}