|
1 | | -//! Convert SVG files to PDFs. |
| 1 | +/*! Convert SVG files to PDFs. |
| 2 | +
|
| 3 | +This crate allows to convert static (i.e. non-interactive) SVG files to |
| 4 | +either standalone PDF files or Form XObjects that can be embedded in another |
| 5 | +PDF file and used just like images. |
| 6 | +
|
| 7 | +The conversion will translate the SVG content to PDF without rasterizing it, |
| 8 | +so no quality is lost. |
| 9 | +
|
| 10 | +## Example |
| 11 | +This example reads an SVG file and writes the corresponding PDF back to the disk. |
| 12 | +
|
| 13 | +```rust |
| 14 | +let svg = std::fs::read_to_string("tests/example.svg").unwrap(); |
| 15 | +
|
| 16 | +// This can only fail if the SVG is malformed. This one is not. |
| 17 | +let pdf = svg2pdf::convert_str(&svg, svg2pdf::Options::default()).unwrap(); |
| 18 | +
|
| 19 | +// ... and now you have a Vec<u8> which you could write to a file or |
| 20 | +// transmit over the network! |
| 21 | +std::fs::write("target/example.pdf", pdf).unwrap(); |
| 22 | +``` |
| 23 | +
|
| 24 | +## Supported features |
| 25 | +- Path drawing with fills and strokes |
| 26 | +- Gradients |
| 27 | +- Patterns |
| 28 | +- Clip paths |
| 29 | +- Masks |
| 30 | +- Transformation matrices |
| 31 | +- Respecting the `keepAspectRatio` attribute |
| 32 | +- Raster images and nested SVGs |
| 33 | +
|
| 34 | +Filters are not currently supported and embedded raster images are not color |
| 35 | +managed. Instead, they use PDF's `DeviceRGB` color space. |
| 36 | +*/ |
2 | 37 |
|
3 | 38 | use std::collections::HashMap; |
4 | 39 |
|
@@ -258,12 +293,82 @@ pub fn convert_tree(tree: &Tree, options: Options) -> Vec<u8> { |
258 | 293 | /// |
259 | 294 | /// The resulting object can be used by registering a name and the `id` with a |
260 | 295 | /// page's [`/XObject`](pdf_writer::writers::Resources::x_objects) resources |
261 | | -/// dictionary and then invoking the [`/Do`](pdf_writer::Content::x_object) |
| 296 | +/// dictionary and then invoking the [`Do`](pdf_writer::Content::x_object) |
262 | 297 | /// operator with the name in the page's content stream. |
263 | 298 | /// |
264 | 299 | /// As the conversion process may need to create multiple indirect objects in |
265 | 300 | /// the PDF, this function allocates consecutive IDs starting at `id` for its |
266 | 301 | /// objects and returns the next available ID for your future writing. |
| 302 | +/// |
| 303 | +/// ## Example |
| 304 | +/// Write a PDF file with some text and an SVG graphic. |
| 305 | +/// |
| 306 | +/// ```rust |
| 307 | +/// use svg2pdf; |
| 308 | +/// use pdf_writer::{Content, Finish, Name, PdfWriter, Rect, Ref, Str}; |
| 309 | +/// |
| 310 | +/// // Allocate the indirect reference IDs and names. |
| 311 | +/// let catalog_id = Ref::new(1); |
| 312 | +/// let page_tree_id = Ref::new(2); |
| 313 | +/// let page_id = Ref::new(3); |
| 314 | +/// let font_id = Ref::new(4); |
| 315 | +/// let content_id = Ref::new(5); |
| 316 | +/// let svg_id = Ref::new(6); |
| 317 | +/// let font_name = Name(b"F1"); |
| 318 | +/// let svg_name = Name(b"S1"); |
| 319 | +/// |
| 320 | +/// // Start writing a PDF. |
| 321 | +/// let mut writer = PdfWriter::new(); |
| 322 | +/// writer.catalog(catalog_id).pages(page_tree_id); |
| 323 | +/// writer.pages(page_tree_id).kids([page_id]).count(1); |
| 324 | +/// |
| 325 | +/// // Set up a simple A4 page. |
| 326 | +/// let mut page = writer.page(page_id); |
| 327 | +/// page.media_box(Rect::new(0.0, 0.0, 595.0, 842.0)); |
| 328 | +/// page.parent(page_tree_id); |
| 329 | +/// page.contents(content_id); |
| 330 | +/// |
| 331 | +/// // Add the font and, more importantly, the SVG to the resource dictionary |
| 332 | +/// // so that it can be referenced in the content stream. |
| 333 | +/// let mut resources = page.resources(); |
| 334 | +/// resources.x_objects().pair(svg_name, svg_id); |
| 335 | +/// resources.fonts().pair(font_name, font_id); |
| 336 | +/// resources.finish(); |
| 337 | +/// page.finish(); |
| 338 | +/// |
| 339 | +/// // Set a predefined font, so we do not have to load anything extra. |
| 340 | +/// writer.type1_font(font_id).base_font(Name(b"Helvetica")); |
| 341 | +/// |
| 342 | +/// // Let's add an SVG graphic to this file. |
| 343 | +/// // We need to load its source first and manually parse it into a usvg Tree. |
| 344 | +/// let svg = std::fs::read_to_string("tests/example.svg").unwrap(); |
| 345 | +/// let tree = usvg::Tree::from_str(&svg, &usvg::Options::default().to_ref()).unwrap(); |
| 346 | +/// |
| 347 | +/// // Then, we will write it to the page as the 6th indirect object. |
| 348 | +/// // |
| 349 | +/// // This call allocates some indirect object reference IDs for itself. If we |
| 350 | +/// // wanted to write some more indirect objects afterwards, we could use the |
| 351 | +/// // return value as the next unused reference ID. |
| 352 | +/// svg2pdf::convert_tree_into(&tree, svg2pdf::Options::default(), &mut writer, svg_id); |
| 353 | +/// |
| 354 | +/// // Write a content stream with some text and our SVG. |
| 355 | +/// let mut content = Content::new(); |
| 356 | +/// content |
| 357 | +/// .begin_text() |
| 358 | +/// .set_font(font_name, 16.0) |
| 359 | +/// .next_line(108.0, 734.0) |
| 360 | +/// .show(Str(b"Look at my wonderful vector graphic!")) |
| 361 | +/// .end_text(); |
| 362 | +/// |
| 363 | +/// // Add our graphic. |
| 364 | +/// content |
| 365 | +/// .transform([300.0, 0.0, 0.0, 300.0, 147.5, 385.0]) |
| 366 | +/// .x_object(svg_name); |
| 367 | +/// |
| 368 | +/// // Write the file to the disk. |
| 369 | +/// writer.stream(content_id, &content.finish()); |
| 370 | +/// std::fs::write("target/embedded.pdf", writer.finish()).unwrap(); |
| 371 | +/// ``` |
267 | 372 | pub fn convert_tree_into( |
268 | 373 | tree: &Tree, |
269 | 374 | options: Options, |
|
0 commit comments