@@ -4,19 +4,25 @@ This crate allows to convert static (i.e. non-interactive) SVG files to
44either standalone PDF files or Form XObjects that can be embedded in another
55PDF file and used just like images.
66
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.
911
1012## Example
1113This example reads an SVG file and writes the corresponding PDF back to the disk.
1214
1315```
1416# fn main() -> Result<(), Box<dyn std::error::Error>> {
17+ use svg2pdf::usvg::fontdb;
18+
1519let path = "tests/svg/custom/integration/matplotlib/time_series.svg";
1620let svg = std::fs::read_to_string(path)?;
21+ let mut db = fontdb::Database::new();
22+ db.load_system_fonts();
1723
1824// 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 )?;
2026
2127// ... and now you have a Vec<u8> which you could write to a file or
2228// transmit over the network!
@@ -25,22 +31,26 @@ std::fs::write("target/time_series.pdf", pdf)?;
2531```
2632
2733## 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
3037- Gradients
3138- Patterns
3239- Clip paths
3340- Masks
34- - Transformation matrices
35- - Respecting the `keepAspectRatio` attribute
41+ - Transformations
42+ - Viewbox
43+ - Text (although it will be converted into paths)
3644- Raster images and nested SVGs
3745
3846## Unsupported features
3947Among the unsupported features are currently:
4048- The `spreadMethod` attribute of gradients
4149- Filters
4250- 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.
4454 */
4555
4656mod render;
@@ -50,8 +60,7 @@ pub use usvg;
5060
5161use once_cell:: sync:: Lazy ;
5262use 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 } ;
5564
5665use crate :: render:: tree_to_stream;
5766use crate :: util:: context:: Context ;
@@ -134,19 +143,17 @@ impl Default for Options {
134143
135144/// Convert an SVG source string to a standalone PDF buffer.
136145///
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- ///
143146/// 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 > {
145152 let mut usvg_options = usvg:: Options :: default ( ) ;
146153 if let Some ( size) = options. viewport {
147154 usvg_options. default_size = size;
148155 }
149- let tree = Tree :: from_str ( src, & usvg_options) ?;
156+ let tree = Tree :: from_str ( src, & usvg_options, fontdb ) ?;
150157 Ok ( convert_tree ( & tree, options) )
151158}
152159
@@ -158,19 +165,17 @@ pub fn convert_str(src: &str, options: Options) -> Result<Vec<u8>, usvg::Error>
158165///
159166/// ```
160167/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
161- /// use svg2pdf::usvg::{ fontdb, PostProcessingSteps, TreeParsing, TreePostProc} ;
168+ /// use svg2pdf::usvg::fontdb;
162169/// use svg2pdf::Options;
163170///
164171/// let input = "tests/svg/custom/integration/matplotlib/step.svg";
165172/// let output = "target/step.pdf";
166173///
167174/// let svg = std::fs::read_to_string(input)?;
168175/// let options = svg2pdf::usvg::Options::default();
169- /// let mut tree = svg2pdf::usvg::Tree::from_str(&svg, &options)?;
170- ///
171176/// let mut db = fontdb::Database::new();
172177/// db.load_system_fonts();
173- /// tree.postprocess(PostProcessingSteps::default() , &db);
178+ /// let mut tree = svg2pdf::usvg::Tree::from_str(&svg , &options, & db)? ;
174179///
175180///
176181/// let pdf = svg2pdf::convert_tree(&tree, Options::default());
@@ -256,7 +261,7 @@ pub fn convert_tree(tree: &Tree, options: Options) -> Vec<u8> {
256261/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
257262/// use svg2pdf;
258263/// use pdf_writer::{Content, Finish, Name, Pdf, Rect, Ref, Str};
259- /// use svg2pdf::usvg::TreeParsing ;
264+ /// use svg2pdf::usvg::fontdb ;
260265///
261266/// // Allocate the indirect reference IDs and names.
262267/// let catalog_id = Ref::new(1);
@@ -294,7 +299,9 @@ pub fn convert_tree(tree: &Tree, options: Options) -> Vec<u8> {
294299/// // We need to load its source first and manually parse it into a usvg Tree.
295300/// let path = "tests/svg/custom/integration/matplotlib/step.svg";
296301/// 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)?;
298305///
299306/// // Then, we will write it to the page as the 6th indirect object.
300307/// //
@@ -391,7 +398,7 @@ fn write_color_spaces(ctx: &mut Context, chunk: &mut Chunk) {
391398/// Return the dimensions of the PDF page
392399fn pdf_size ( tree : & Tree , options : Options ) -> Size {
393400 // 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 ( ) ) ;
395402 Size :: from_wh (
396403 // dpi_ratio is in dot per user unit so dividing by it gave user unit
397404 viewport_size. width ( ) / dpi_ratio ( options. dpi ) ,
@@ -410,11 +417,16 @@ fn initial_transform(
410417 // Account for the custom viewport that has been passed in the Options struct. If nothing has
411418 // been passed, pdf_size should be the same as tree.size, so the transform will just be the
412419 // 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) ;
418430
419431 // Account for the direction of the y axis and the shift of the origin in the coordinate system.
420432 let pdf_transform = Transform :: from_row ( 1.0 , 0.0 , 0.0 , -1.0 , 0.0 , pdf_size. height ( ) ) ;
0 commit comments