@@ -4,19 +4,25 @@ This crate allows to convert static (i.e. non-interactive) SVG files to
4
4
either standalone PDF files or Form XObjects that can be embedded in another
5
5
PDF file and used just like images.
6
6
7
- The conversion will translate the SVG content to PDF without rasterizing them,
8
- so no quality is lost.
7
+ The conversion will translate the SVG content to PDF without rasterizing them
8
+ (the only exception being objects with filters on them, but in this case only
9
+ this single group will be rasterized, while the remaining contents of the SVG
10
+ will still be turned into a vector graphic), so no quality is lost.
9
11
10
12
## Example
11
13
This example reads an SVG file and writes the corresponding PDF back to the disk.
12
14
13
15
```
14
16
# fn main() -> Result<(), Box<dyn std::error::Error>> {
17
+ use svg2pdf::usvg::fontdb;
18
+
15
19
let path = "tests/svg/custom/integration/matplotlib/time_series.svg";
16
20
let svg = std::fs::read_to_string(path)?;
21
+ let mut db = fontdb::Database::new();
22
+ db.load_system_fonts();
17
23
18
24
// This can only fail if the SVG is malformed. This one is not.
19
- let pdf = svg2pdf::convert_str(&svg, svg2pdf::Options::default())?;
25
+ let pdf = svg2pdf::convert_str(&svg, svg2pdf::Options::default(), &db )?;
20
26
21
27
// ... and now you have a Vec<u8> which you could write to a file or
22
28
// transmit over the network!
@@ -25,22 +31,26 @@ std::fs::write("target/time_series.pdf", pdf)?;
25
31
```
26
32
27
33
## Supported features
28
- In general, a large part of the SVG specification is supported, including features like:
29
- - Path drawing with fills and strokes
34
+ In general, a very large part of the SVG specification is supported, including
35
+ but not limited to:
36
+ - Paths with simple and complex fills
30
37
- Gradients
31
38
- Patterns
32
39
- Clip paths
33
40
- Masks
34
- - Transformation matrices
35
- - Respecting the `keepAspectRatio` attribute
41
+ - Transformations
42
+ - Viewbox
43
+ - Text (although it will be converted into paths)
36
44
- Raster images and nested SVGs
37
45
38
46
## Unsupported features
39
47
Among the unsupported features are currently:
40
48
- The `spreadMethod` attribute of gradients
41
49
- Filters
42
50
- Raster images are not color managed but use PDF's DeviceRGB color space
43
- - A number of features that were added in SVG2
51
+ - A number of features that were added in SVG2, See
52
+ [here](https://github.com/RazrFalcon/resvg/blob/master/docs/svg2-changelog.md) for a more
53
+ comprehensive list.
44
54
*/
45
55
46
56
mod render;
@@ -50,8 +60,7 @@ pub use usvg;
50
60
51
61
use once_cell:: sync:: Lazy ;
52
62
use pdf_writer:: { Chunk , Content , Filter , Finish , Pdf , Rect , Ref , TextStr } ;
53
- use usvg:: utils:: view_box_to_transform;
54
- use usvg:: { Align , AspectRatio , NonZeroRect , Size , Transform , Tree , TreeParsing } ;
63
+ use usvg:: { fontdb, Align , AspectRatio , NonZeroRect , Size , Transform , Tree , ViewBox } ;
55
64
56
65
use crate :: render:: tree_to_stream;
57
66
use crate :: util:: context:: Context ;
@@ -134,19 +143,17 @@ impl Default for Options {
134
143
135
144
/// Convert an SVG source string to a standalone PDF buffer.
136
145
///
137
- /// Does not load any fonts and consequently cannot convert `text` elements. To
138
- /// convert text, you should convert your source string to a
139
- /// [`usvg` tree](Tree) manually,
140
- /// [convert text with usvg](usvg::TreePostProc::postprocess) and then use
141
- /// [`convert_tree`].
142
- ///
143
146
/// Returns an error if the SVG string is malformed.
144
- pub fn convert_str ( src : & str , options : Options ) -> Result < Vec < u8 > , usvg:: Error > {
147
+ pub fn convert_str (
148
+ src : & str ,
149
+ options : Options ,
150
+ fontdb : & fontdb:: Database ,
151
+ ) -> Result < Vec < u8 > , usvg:: Error > {
145
152
let mut usvg_options = usvg:: Options :: default ( ) ;
146
153
if let Some ( size) = options. viewport {
147
154
usvg_options. default_size = size;
148
155
}
149
- let tree = Tree :: from_str ( src, & usvg_options) ?;
156
+ let tree = Tree :: from_str ( src, & usvg_options, fontdb ) ?;
150
157
Ok ( convert_tree ( & tree, options) )
151
158
}
152
159
@@ -158,19 +165,17 @@ pub fn convert_str(src: &str, options: Options) -> Result<Vec<u8>, usvg::Error>
158
165
///
159
166
/// ```
160
167
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
161
- /// use svg2pdf::usvg::{ fontdb, PostProcessingSteps, TreeParsing, TreePostProc} ;
168
+ /// use svg2pdf::usvg::fontdb;
162
169
/// use svg2pdf::Options;
163
170
///
164
171
/// let input = "tests/svg/custom/integration/matplotlib/step.svg";
165
172
/// let output = "target/step.pdf";
166
173
///
167
174
/// let svg = std::fs::read_to_string(input)?;
168
175
/// let options = svg2pdf::usvg::Options::default();
169
- /// let mut tree = svg2pdf::usvg::Tree::from_str(&svg, &options)?;
170
- ///
171
176
/// let mut db = fontdb::Database::new();
172
177
/// db.load_system_fonts();
173
- /// tree.postprocess(PostProcessingSteps::default() , &db);
178
+ /// let mut tree = svg2pdf::usvg::Tree::from_str(&svg , &options, & db)? ;
174
179
///
175
180
///
176
181
/// let pdf = svg2pdf::convert_tree(&tree, Options::default());
@@ -256,7 +261,7 @@ pub fn convert_tree(tree: &Tree, options: Options) -> Vec<u8> {
256
261
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
257
262
/// use svg2pdf;
258
263
/// use pdf_writer::{Content, Finish, Name, Pdf, Rect, Ref, Str};
259
- /// use svg2pdf::usvg::TreeParsing ;
264
+ /// use svg2pdf::usvg::fontdb ;
260
265
///
261
266
/// // Allocate the indirect reference IDs and names.
262
267
/// let catalog_id = Ref::new(1);
@@ -294,7 +299,9 @@ pub fn convert_tree(tree: &Tree, options: Options) -> Vec<u8> {
294
299
/// // We need to load its source first and manually parse it into a usvg Tree.
295
300
/// let path = "tests/svg/custom/integration/matplotlib/step.svg";
296
301
/// let svg = std::fs::read_to_string(path)?;
297
- /// let tree = svg2pdf::usvg::Tree::from_str(&svg, &svg2pdf::usvg::Options::default())?;
302
+ /// let mut db = fontdb::Database::new();
303
+ /// db.load_system_fonts();
304
+ /// let tree = svg2pdf::usvg::Tree::from_str(&svg, &svg2pdf::usvg::Options::default(), &db)?;
298
305
///
299
306
/// // Then, we will write it to the page as the 6th indirect object.
300
307
/// //
@@ -391,7 +398,7 @@ fn write_color_spaces(ctx: &mut Context, chunk: &mut Chunk) {
391
398
/// Return the dimensions of the PDF page
392
399
fn pdf_size ( tree : & Tree , options : Options ) -> Size {
393
400
// If no custom viewport is defined, we use the size of the tree.
394
- let viewport_size = options. viewport . unwrap_or ( tree. size ) ;
401
+ let viewport_size = options. viewport . unwrap_or ( tree. size ( ) ) ;
395
402
Size :: from_wh (
396
403
// dpi_ratio is in dot per user unit so dividing by it gave user unit
397
404
viewport_size. width ( ) / dpi_ratio ( options. dpi ) ,
@@ -410,11 +417,16 @@ fn initial_transform(
410
417
// Account for the custom viewport that has been passed in the Options struct. If nothing has
411
418
// been passed, pdf_size should be the same as tree.size, so the transform will just be the
412
419
// default transform.
413
- let custom_viewport_transform = view_box_to_transform (
414
- NonZeroRect :: from_xywh ( 0.0 , 0.0 , tree. size . width ( ) , tree. size . height ( ) ) . unwrap ( ) ,
415
- aspect. unwrap_or ( AspectRatio { defer : false , align : Align :: None , slice : false } ) ,
416
- pdf_size,
417
- ) ;
420
+ let view_box = ViewBox {
421
+ rect : NonZeroRect :: from_xywh ( 0.0 , 0.0 , tree. size ( ) . width ( ) , tree. size ( ) . height ( ) )
422
+ . unwrap ( ) ,
423
+ aspect : aspect. unwrap_or ( AspectRatio {
424
+ defer : false ,
425
+ align : Align :: None ,
426
+ slice : false ,
427
+ } ) ,
428
+ } ;
429
+ let custom_viewport_transform = view_box. to_transform ( pdf_size) ;
418
430
419
431
// Account for the direction of the y axis and the shift of the origin in the coordinate system.
420
432
let pdf_transform = Transform :: from_row ( 1.0 , 0.0 , 0.0 , -1.0 , 0.0 , pdf_size. height ( ) ) ;
0 commit comments