Skip to content

Commit d507d79

Browse files
authored
Update to usvg 0.35 + additional improvements (#37)
1 parent e873c3e commit d507d79

1,136 files changed

Lines changed: 9482 additions & 1015 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Cargo.lock

Lines changed: 209 additions & 349 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,11 @@ required-features = ["cli"]
3737
[dependencies]
3838
miniz_oxide = "0.7"
3939
pdf-writer = "0.8"
40-
usvg = { version = "0.32", default-features = false }
40+
usvg = { version = "0.35", default-features = false }
4141
image = { version = "0.24", features = ["jpeg", "png", "gif"], optional = true }
4242
termcolor = { version = "1", optional = true }
43-
clap = { version = "3", features = ["derive"], optional = true }
44-
fontdb = { version = "0.13", optional= true }
43+
clap = { version = "4.4.2", features = ["derive"], optional = true }
44+
fontdb = { version = "0.14", optional= true }
4545

4646
[dev-dependencies]
47-
usvg = { version = "0.32" }
47+
usvg = { version = "0.35.0" }

NOTICE

Lines changed: 436 additions & 0 deletions
Large diffs are not rendered by default.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ features like:
5959
Among the unsupported features are currently:
6060
- The `spreadMethod` attribute of gradients
6161
- Filters
62-
- Blend modes
62+
- Text will be converted into shapes before converting to PDF
6363
- Raster images are not color managed but use PDF's DeviceRGB color space
6464
- A number of features that were added in SVG2
6565

src/lib.rs

Lines changed: 58 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -39,20 +39,20 @@ In general, a large part of the SVG specification is supported, including featur
3939
Among the unsupported features are currently:
4040
- The `spreadMethod` attribute of gradients
4141
- Filters
42-
- Blend modes
4342
- Raster images are not color managed but use PDF's DeviceRGB color space
4443
- A number of features that were added in SVG2
45-
*/
44+
*/
4645

4746
mod render;
4847
mod util;
4948

5049
use pdf_writer::{Content, Filter, Finish, PdfWriter, Rect, Ref, TextStr};
5150
use usvg::utils::view_box_to_transform;
52-
use usvg::{Align, AspectRatio, Size, Transform, Tree, TreeParsing};
51+
use usvg::{Align, AspectRatio, NonZeroRect, Size, Transform, Tree, TreeParsing};
5352

53+
use crate::render::tree_to_stream;
5454
use crate::util::context::Context;
55-
use crate::util::helper::{dpi_ratio, NameExt, RectExt};
55+
use crate::util::helper::dpi_ratio;
5656

5757
/// Set size and scaling preferences for the conversion.
5858
#[derive(Copy, Clone)]
@@ -118,7 +118,7 @@ impl Default for Options {
118118
///
119119
/// Does not load any fonts and consequently cannot convert `text` elements. To
120120
/// convert text, you should convert your source string to a
121-
/// [`usvg` tree](usvg::Tree) manually,
121+
/// [`usvg` tree](Tree) manually,
122122
/// [convert text with usvg](usvg::TreeTextToPath::convert_text) and then use
123123
/// [`convert_tree`].
124124
///
@@ -132,7 +132,7 @@ pub fn convert_str(src: &str, options: Options) -> Result<Vec<u8>, usvg::Error>
132132
Ok(convert_tree(&tree, options))
133133
}
134134

135-
/// Convert a [`usvg` tree](usvg::Tree) into a standalone PDF buffer.
135+
/// Convert a [`usvg` tree](Tree) into a standalone PDF buffer.
136136
///
137137
/// ## Example
138138
/// The example below reads an SVG file, processes text within it, then converts
@@ -159,9 +159,8 @@ pub fn convert_str(src: &str, options: Options) -> Result<Vec<u8>, usvg::Error>
159159
/// # Ok(()) }
160160
/// ```
161161
pub fn convert_tree(tree: &Tree, options: Options) -> Vec<u8> {
162-
let page_size = options.viewport.unwrap_or(tree.size);
163-
let mut ctx =
164-
Context::new(tree, options, initial_transform(&options, tree, page_size), None);
162+
let pdf_size = pdf_size(tree, options);
163+
let mut ctx = Context::new(tree, options, None);
165164
let mut writer = PdfWriter::new();
166165

167166
let catalog_ref = ctx.alloc_ref();
@@ -174,31 +173,35 @@ pub fn convert_tree(tree: &Tree, options: Options) -> Vec<u8> {
174173

175174
// Generate main content
176175
ctx.deferrer.push();
177-
let tree_x_object = render::tree_to_x_object(tree, &mut writer, &mut ctx);
178176
let mut content = Content::new();
179-
content.x_object(tree_x_object.as_name());
180-
177+
tree_to_stream(
178+
tree,
179+
&mut writer,
180+
&mut content,
181+
&mut ctx,
182+
initial_transform(options.aspect, tree, pdf_size),
183+
);
181184
let content_stream = ctx.finish_content(content);
182185
let mut stream = writer.stream(content_ref, &content_stream);
183186

184187
if ctx.options.compress {
185188
stream.filter(Filter::FlateDecode);
186189
}
187-
188190
stream.finish();
189191

190192
let mut page = writer.page(page_ref);
191193
let mut page_resources = page.resources();
192194
ctx.deferrer.pop(&mut page_resources);
193195
page_resources.finish();
194196

195-
page.media_box(Rect::new(
196-
0.0,
197-
0.0,
198-
dpi_ratio(options.dpi) * page_size.width() as f32,
199-
dpi_ratio(options.dpi) * page_size.height() as f32,
200-
));
197+
page.media_box(Rect::new(0.0, 0.0, pdf_size.width(), pdf_size.height()));
201198
page.parent(page_tree_ref);
199+
page.group()
200+
.transparency()
201+
.isolated(true)
202+
.knockout(false)
203+
.color_space()
204+
.srgb();
202205
page.contents(content_ref);
203206
page.finish();
204207

@@ -208,7 +211,7 @@ pub fn convert_tree(tree: &Tree, options: Options) -> Vec<u8> {
208211
writer.finish()
209212
}
210213

211-
/// Convert a [`usvg` tree](usvg::Tree) into a Form XObject that can be used as
214+
/// Convert a [`usvg` tree](Tree) into a Form XObject that can be used as
212215
/// part of a larger document.
213216
///
214217
/// This method is intended for use in an existing [`PdfWriter`] workflow. It
@@ -304,30 +307,29 @@ pub fn convert_tree_into(
304307
writer: &mut PdfWriter,
305308
start_ref: Ref,
306309
) -> Ref {
307-
let mut ctx = Context::new(
308-
tree,
309-
options,
310-
initial_transform(&options, tree, tree.size),
311-
Some(start_ref.get()),
312-
);
310+
let pdf_size = pdf_size(tree, options);
311+
let mut ctx = Context::new(tree, options, Some(start_ref.get()));
313312

314313
let x_ref = ctx.alloc_ref();
315314
ctx.deferrer.push();
316315

317-
let tree_x_object = render::tree_to_x_object(tree, writer, &mut ctx);
318316
let mut content = Content::new();
319-
content.x_object(tree_x_object.as_name());
320-
317+
tree_to_stream(
318+
tree,
319+
writer,
320+
&mut content,
321+
&mut ctx,
322+
initial_transform(options.aspect, tree, pdf_size),
323+
);
321324
let content_stream = ctx.finish_content(content);
322325

323326
let mut x_object = writer.form_xobject(x_ref, &content_stream);
324-
x_object.bbox(ctx.get_rect().as_pdf_rect());
325-
// Revert the PDF transformation so that the resulting XObject is 1x1 in size.
327+
x_object.bbox(Rect::new(0.0, 0.0, pdf_size.width(), pdf_size.height()));
326328
x_object.matrix([
327-
1.0 / ctx.get_rect().width() as f32,
329+
1.0 / pdf_size.width(),
328330
0.0,
329331
0.0,
330-
1.0 / ctx.get_rect().height() as f32,
332+
1.0 / pdf_size.height(),
331333
0.0,
332334
0.0,
333335
]);
@@ -342,33 +344,35 @@ pub fn convert_tree_into(
342344
ctx.alloc_ref()
343345
}
344346

345-
/// Return the initial transform that is necessary for the conversion between SVG coordinates
346-
/// and the final PDF page (including DPI and a custom viewport).
347-
fn initial_transform(options: &Options, tree: &Tree, actual_size: Size) -> Transform {
348-
// Account for DPI.
349-
let dpi_transform = Transform::new_scale(
350-
dpi_ratio(options.dpi) as f64,
351-
dpi_ratio(options.dpi) as f64,
352-
);
347+
/// Return the dimensions of the PDF page
348+
fn pdf_size(tree: &Tree, options: Options) -> Size {
349+
// If no custom viewport is defined, we use the size of the tree.
350+
let viewport_size = options.viewport.unwrap_or(tree.size);
351+
Size::from_wh(
352+
viewport_size.width() * dpi_ratio(options.dpi),
353+
viewport_size.height() * dpi_ratio(options.dpi),
354+
)
355+
.unwrap()
356+
}
353357

358+
/// Return the initial transform that is necessary for the conversion between SVG coordinates
359+
/// and the final PDF page.
360+
fn initial_transform(
361+
aspect: Option<AspectRatio>,
362+
tree: &Tree,
363+
pdf_size: Size,
364+
) -> Transform {
354365
// Account for the custom viewport that has been passed in the Options struct. If nothing has
355-
// been passed, actual_size will be same as tree.size, so the transform will just be the
366+
// been passed, pdf_size should be the same as tree.size, so the transform will just be the
356367
// default transform.
357368
let custom_viewport_transform = view_box_to_transform(
358-
usvg::Rect::new(0.0, 0.0, tree.size.width(), tree.size.height()).unwrap(),
359-
options.aspect.unwrap_or(AspectRatio {
360-
defer: false,
361-
align: Align::None,
362-
slice: false,
363-
}),
364-
actual_size,
369+
NonZeroRect::from_xywh(0.0, 0.0, tree.size.width(), tree.size.height()).unwrap(),
370+
aspect.unwrap_or(AspectRatio { defer: false, align: Align::None, slice: false }),
371+
pdf_size,
365372
);
366373

367374
// Account for the direction of the y axis and the shift of the origin in the coordinate system.
368-
let pdf_transform = Transform::new(1.0, 0.0, 0.0, -1.0, 0.0, actual_size.height());
375+
let pdf_transform = Transform::from_row(1.0, 0.0, 0.0, -1.0, 0.0, pdf_size.height());
369376

370-
let mut base_transform = dpi_transform;
371-
base_transform.append(&pdf_transform);
372-
base_transform.append(&custom_viewport_transform);
373-
base_transform
377+
pdf_transform.pre_concat(custom_viewport_transform)
374378
}

0 commit comments

Comments
 (0)