Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update to resvg 0.42 #72

Merged
merged 19 commits into from
Jun 1, 2024
954 changes: 683 additions & 271 deletions Cargo.lock

Large diffs are not rendered by default.

14 changes: 7 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,24 @@ license = "MIT OR Apache-2.0"

[workspace.dependencies]
log = "0.4.20"
svg2pdf = { path = ".", default-features = false, version = "0.9.1" }
svg2pdf = { path = ".", default-features = false }
clap = { version = "4.4.2", features = ["derive"] }
clap_complete = "4.4.3"
clap_mangen = "0.2.14"
fontdb = "0.16.1"
image = { version = "0.24", default-features = false, features = ["jpeg", "png", "gif"] }
fontdb = "0.18.0"
image = { version = "0.25.1", default-features = false, features = ["jpeg", "png", "gif"] }
miniz_oxide = "0.7"
once_cell = "1.18.0"
oxipng = { version = "9", default-features = false, features = ["filetime", "parallel", "zopfli"] }
pdf-writer = "0.9"
pdf-writer = "0.10"
pdfium-render = "0.8.6"
termcolor = "1.2"
usvg = { version = "0.41", default-features = false }
usvg = { git = "https://github.com/RazrFalcon/resvg", default-features = false }
tiny-skia = "0.11.4"
unicode-properties = "0.1.1"
resvg = { version = "0.41", default-features = false }
resvg = { git = "https://github.com/RazrFalcon/resvg", default-features = false }
subsetter = "0.1.1"
ttf-parser = { version = "0.20.0" }
ttf-parser = { version = "0.21.1" }
siphasher = { version = "1.0.1"}

[package]
Expand Down
30 changes: 13 additions & 17 deletions cli/src/convert.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use std::path::{Path, PathBuf};
#[cfg(feature = "text")]
use std::sync::Arc;
use svg2pdf::{ConversionOptions, PageOptions};

pub fn convert_(
Expand All @@ -11,7 +13,6 @@ pub fn convert_(
log::set_max_level(log::LevelFilter::Warn);
}

// Prepare the font database.
let mut fontdb = fontdb::Database::new();
fontdb.load_system_fonts();

Expand All @@ -21,29 +22,24 @@ pub fn convert_(
fontdb.set_fantasy_family("Impact");
fontdb.set_monospace_family("Courier New");

#[cfg(feature = "text")]
let options = usvg::Options {
fontdb: Arc::new(fontdb),
..usvg::Options::default()
};

#[cfg(not(feature = "text"))]
let options = usvg::Options::default();

// Convert the file.
let name = Path::new(input.file_name().ok_or("Input path does not point to a file")?);
let output = output.unwrap_or_else(|| name.with_extension("pdf"));

let svg = std::fs::read_to_string(input).map_err(|_| "Failed to load SVG file")?;

let options = usvg::Options::default();
let tree = usvg::Tree::from_str(&svg, &options).map_err(|err| err.to_string())?;

let tree = usvg::Tree::from_str(
&svg,
&options,
#[cfg(feature = "text")]
&fontdb,
)
.map_err(|err| err.to_string())?;

let pdf = svg2pdf::to_pdf(
&tree,
conversion_options,
page_options,
#[cfg(feature = "text")]
&fontdb,
);
let pdf = svg2pdf::to_pdf(&tree, conversion_options, page_options);

std::fs::write(output, pdf).map_err(|_| "Failed to write PDF file")?;

Expand Down
56 changes: 25 additions & 31 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,21 @@ This example reads an SVG file and writes the corresponding PDF back to the disk
# fn main() -> Result<(), Box<dyn std::error::Error>> {
use svg2pdf::usvg::fontdb;
use svg2pdf::{ConversionOptions, PageOptions};
use std::sync::Arc;

let input = "tests/svg/custom/integration/matplotlib/stairs.svg";
let output = "target/stairs.pdf";

let svg = std::fs::read_to_string(input)?;
let options = svg2pdf::usvg::Options::default();
let mut db = fontdb::Database::new();
db.load_system_fonts();
let tree = svg2pdf::usvg::Tree::from_str(&svg, &options, &db)?;
let options = svg2pdf::usvg::Options {
LaurenzV marked this conversation as resolved.
Show resolved Hide resolved
fontdb: Arc::new(db),
..svg2pdf::usvg::Options::default()
};
let tree = svg2pdf::usvg::Tree::from_str(&svg, &options)?;

let pdf = svg2pdf::to_pdf(&tree, ConversionOptions::default(), PageOptions::default(), &db);
let pdf = svg2pdf::to_pdf(&tree, ConversionOptions::default(), PageOptions::default());
std::fs::write(output, pdf)?;
# Ok(()) }
```
Expand Down Expand Up @@ -60,8 +64,6 @@ pub use usvg;

use once_cell::sync::Lazy;
use pdf_writer::{Chunk, Content, Filter, Finish, Pdf, Ref, TextStr};
#[cfg(feature = "text")]
use usvg::fontdb;
use usvg::{Size, Transform, Tree};

use crate::render::{tree_to_stream, tree_to_xobject};
Expand Down Expand Up @@ -120,7 +122,7 @@ pub struct ConversionOptions {
impl Default for ConversionOptions {
fn default() -> Self {
Self {
compress: false,
LaurenzV marked this conversation as resolved.
Show resolved Hide resolved
compress: true,
raster_scale: 1.5,
embed_text: true,
}
Expand All @@ -140,34 +142,31 @@ impl Default for ConversionOptions {
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// use svg2pdf::usvg::fontdb;
/// use svg2pdf::{ConversionOptions, PageOptions};
/// use std::sync::Arc;
///
/// let input = "tests/svg/custom/integration/matplotlib/stairs.svg";
/// let output = "target/stairs.pdf";
///
/// let svg = std::fs::read_to_string(input)?;
/// let options = svg2pdf::usvg::Options::default();
/// let mut db = fontdb::Database::new();
/// db.load_system_fonts();
/// let mut tree = svg2pdf::usvg::Tree::from_str(&svg, &options, &db)?;
/// let options = svg2pdf::usvg::Options {
LaurenzV marked this conversation as resolved.
Show resolved Hide resolved
/// fontdb: Arc::new(db),
/// ..svg2pdf::usvg::Options::default()
/// };
/// let mut tree = svg2pdf::usvg::Tree::from_str(&svg, &options)?;
///
///
/// let pdf = svg2pdf::to_pdf(&tree, ConversionOptions::default(), PageOptions::default(), &db);
/// let pdf = svg2pdf::to_pdf(&tree, ConversionOptions::default(), PageOptions::default());
/// std::fs::write(output, pdf)?;
/// # Ok(()) }
/// ```
pub fn to_pdf(
tree: &Tree,
conversion_options: ConversionOptions,
page_options: PageOptions,
#[cfg(feature = "text")] fontdb: &fontdb::Database,
) -> Vec<u8> {
let mut ctx = Context::new(
#[cfg(feature = "text")]
tree,
conversion_options,
#[cfg(feature = "text")]
fontdb,
);
let mut ctx = Context::new(tree, conversion_options);
let mut pdf = Pdf::new();

let dpi_ratio = 72.0 / page_options.dpi;
Expand Down Expand Up @@ -245,6 +244,7 @@ pub fn to_pdf(
/// ```
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// use std::collections::HashMap;
/// use std::sync::Arc;
LaurenzV marked this conversation as resolved.
Show resolved Hide resolved
/// use svg2pdf;
/// use pdf_writer::{Content, Finish, Name, Pdf, Rect, Ref, Str};
/// use svg2pdf::usvg::fontdb;
Expand All @@ -264,8 +264,12 @@ pub fn to_pdf(
/// let svg = std::fs::read_to_string(path)?;
/// let mut db = fontdb::Database::new();
/// db.load_system_fonts();
/// let tree = svg2pdf::usvg::Tree::from_str(&svg, &svg2pdf::usvg::Options::default(), &db)?;
/// let (mut svg_chunk, svg_id) = svg2pdf::to_chunk(&tree, svg2pdf::ConversionOptions::default(), &db);
/// let options = svg2pdf::usvg::Options {
/// fontdb: Arc::new(db),
/// ..svg2pdf::usvg::Options::default()
/// };
/// let tree = svg2pdf::usvg::Tree::from_str(&svg, &options)?;
/// let (mut svg_chunk, svg_id) = svg2pdf::to_chunk(&tree, svg2pdf::ConversionOptions::default());
///
/// // Renumber the chunk so that we can embed it into our existing workflow, and also make sure
/// // to update `svg_id`.
Expand Down Expand Up @@ -320,20 +324,10 @@ pub fn to_pdf(
/// std::fs::write("target/embedded.pdf", pdf.finish())?;
/// # Ok(()) }
/// ```
pub fn to_chunk(
tree: &Tree,
conversion_options: ConversionOptions,
#[cfg(feature = "text")] fontdb: &fontdb::Database,
) -> (Chunk, Ref) {
pub fn to_chunk(tree: &Tree, conversion_options: ConversionOptions) -> (Chunk, Ref) {
let mut chunk = Chunk::new();

let mut ctx = Context::new(
#[cfg(feature = "text")]
tree,
conversion_options,
#[cfg(feature = "text")]
fontdb,
);
let mut ctx = Context::new(tree, conversion_options);
let x_ref = tree_to_xobject(tree, &mut chunk, &mut ctx);
ctx.write_global_objects(&mut chunk);
(chunk, x_ref)
Expand Down
4 changes: 2 additions & 2 deletions src/render/filter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::util::resources::ResourceContainer;
use pdf_writer::{Chunk, Content};
use std::sync::Arc;
use tiny_skia::{Size, Transform};
use usvg::{AspectRatio, Group, ImageKind, Node, ViewBox, Visibility};
use usvg::{Group, ImageKind, Node, Visibility};

/// Render a group with filters as an image.
pub fn render(
Expand Down Expand Up @@ -48,7 +48,7 @@ pub fn render(
image::render(
Visibility::Visible,
&ImageKind::PNG(Arc::new(encoded_image)),
ViewBox { rect: layer_bbox, aspect: AspectRatio::default() },
Some(layer_bbox.to_rect()),
chunk,
content,
ctx,
Expand Down
26 changes: 11 additions & 15 deletions src/render/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,18 @@ use std::rc::Rc;
use image::{ColorType, DynamicImage, ImageFormat, Luma, Rgb, Rgba};
use miniz_oxide::deflate::{compress_to_vec_zlib, CompressionLevel};
use pdf_writer::{Chunk, Content, Filter, Finish};
use usvg::{ImageKind, Size, Transform, Tree, ViewBox, Visibility};
use usvg::{ImageKind, Rect, Size, Transform, Tree, Visibility};

use crate::render::tree_to_xobject;
use crate::util::context::Context;
use crate::util::helper;
use crate::util::helper::{image_rect, NameExt, TransformExt};
use crate::util::helper::{NameExt, TransformExt};
use crate::util::resources::ResourceContainer;

/// Render an image into a content stream.
pub fn render(
visibility: Visibility,
kind: &ImageKind,
view_box: ViewBox,
view_box: Option<Rect>,
chunk: &mut Chunk,
content: &mut Content,
ctx: &mut Context,
Expand Down Expand Up @@ -79,29 +78,26 @@ pub fn render(
ImageKind::SVG(tree) => create_svg_image(tree, chunk, ctx, rc),
};

// Get the dimensions of the actual rect that is needed to scale the image into the image view
// box. If the keepAspectRatio is slice, this rect will exceed the actual image view box, but
// it will be clipped further below so that it always stays within the bounds of the actual image
// rect.
let image_rect = image_rect(&view_box, image_size);
let view_box = view_box.unwrap_or(
Rect::from_xywh(0.0, 0.0, image_size.width(), image_size.height()).unwrap(),
);

content.save_state();
// Clip the image so just the part inside of the view box is actually visible.
helper::clip_to_rect(view_box.rect, content);

// Account for the x/y of the viewbox.
content.transform(
Transform::from_translate(image_rect.x(), image_rect.y()).to_pdf_transform(),
Transform::from_translate(view_box.x(), view_box.y()).to_pdf_transform(),
);

// Scale the image from 1x1 to the actual dimensions.
content.transform(
Transform::from_row(
image_rect.width(),
view_box.width(),
0.0,
0.0,
-image_rect.height(),
-view_box.height(),
0.0,
image_rect.height(),
view_box.height(),
)
.to_pdf_transform(),
);
Expand Down
8 changes: 3 additions & 5 deletions src/render/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,9 @@ pub fn tree_to_stream(
) {
content.save_state();

// From PDF coordinate system to SVG coordinate system
let initial_transform =
// From PDF coordinate system to SVG coordinate system
Transform::from_row(1.0, 0.0, 0.0, -1.0, 0.0, tree.size().height())
// Account for view box of tree.
.pre_concat(tree.view_box().to_transform(tree.size()));
Transform::from_row(1.0, 0.0, 0.0, -1.0, 0.0, tree.size().height());

content.transform(initial_transform.to_pdf_transform());

Expand Down Expand Up @@ -100,7 +98,7 @@ impl Render for Node {
Node::Image(ref image) => image::render(
image.visibility(),
image.kind(),
image.view_box(),
None,
chunk,
content,
ctx,
Expand Down
13 changes: 1 addition & 12 deletions src/render/pattern.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::sync::Arc;

use pdf_writer::types::{PaintType, TilingType};
use pdf_writer::{Chunk, Content, Filter, Ref};
use usvg::{Opacity, Pattern, Size, Transform};
use usvg::{Opacity, Pattern, Transform};

use super::group;
use crate::util::context::Context;
Expand All @@ -27,15 +27,6 @@ pub fn create(
);

let mut content = Content::new();
content.save_state();

if let Some(view_box) = pattern.view_box() {
let view_box_transform = view_box.to_transform(
Size::from_wh(pattern_rect.width(), pattern_rect.height()).unwrap(),
);
content.transform(view_box_transform.to_pdf_transform());
}

group::render(
pattern.root(),
chunk,
Expand All @@ -46,8 +37,6 @@ pub fn create(
&mut rc,
);

content.restore_state();

let content_stream = ctx.finish_content(content);

let mut tiling_pattern = chunk.tiling_pattern(pattern_ref, &content_stream);
Expand Down
Loading