Bump resvg + svg2pdf and add support for filters (#3254)

This commit is contained in:
Laurenz Stampfl 2024-01-25 16:42:04 +01:00 committed by GitHub
parent cd71741532
commit afc28264e8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 76 additions and 69 deletions

31
Cargo.lock generated
View File

@ -1735,12 +1735,6 @@ dependencies = [
"crossbeam-utils",
]
[[package]]
name = "rctree"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b42e27ef78c35d3998403c1d26f3efd9e135d3e5121b0a4845cc5cc27547f4f"
[[package]]
name = "redox_syscall"
version = "0.4.1"
@ -1798,9 +1792,9 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "resvg"
version = "0.37.0"
version = "0.38.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cadccb3d99a9efb8e5e00c16fbb732cbe400db2ec7fc004697ee7d97d86cf1f4"
checksum = "5c34501046959e06470ba62a2dc7f31c15f94ac250d842a45f9e012f4ee40c1e"
dependencies = [
"gif",
"jpeg-decoder",
@ -2170,12 +2164,14 @@ checksum = "09eab8a83bff89ba2200bd4c59be45c7c787f988431b936099a5a266c957f2f9"
[[package]]
name = "svg2pdf"
version = "0.9.1"
source = "git+https://github.com/typst/svg2pdf?rev=11f34c7#11f34c7f407d504c49db19e4e2b521fce4c8622f"
source = "git+https://github.com/typst/svg2pdf?rev=49891ef#49891ef48eee1a03f2ed090541d88fd0193bf2c9"
dependencies = [
"image",
"miniz_oxide",
"once_cell",
"pdf-writer",
"resvg",
"tiny-skia",
"usvg",
]
@ -2859,9 +2855,9 @@ dependencies = [
[[package]]
name = "usvg"
version = "0.37.0"
version = "0.38.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38b0a51b72ab80ca511d126b77feeeb4fb1e972764653e61feac30adc161a756"
checksum = "377f62b4a3c173de8654c1aa80ab1dac1154e6f13a779a9943e53780120d1625"
dependencies = [
"base64",
"log",
@ -2874,9 +2870,9 @@ dependencies = [
[[package]]
name = "usvg-parser"
version = "0.37.0"
version = "0.38.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bd4e3c291f45d152929a31f0f6c819245e2921bfd01e7bd91201a9af39a2bdc"
checksum = "351a05e6f2023d6b4e946f734240a3927aefdcf930d7d42587a2c8a8869814b0"
dependencies = [
"data-url",
"flate2",
@ -2892,9 +2888,9 @@ dependencies = [
[[package]]
name = "usvg-text-layout"
version = "0.37.0"
version = "0.38.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d383a3965de199d7f96d4e11a44dd859f46e86de7f3dca9a39bf82605da0a37c"
checksum = "8c41888b9d5cf431fe852eaf9d047bbde83251b98f1749c2f08b1071e6db46e2"
dependencies = [
"fontdb",
"kurbo",
@ -2908,11 +2904,10 @@ dependencies = [
[[package]]
name = "usvg-tree"
version = "0.37.0"
version = "0.38.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ee3d202ebdb97a6215604b8f5b4d6ef9024efd623cf2e373a6416ba976ec7d3"
checksum = "18863e0404ed153d6e56362c5b1146db9f4f262a3244e3cf2dbe7d8a85909f05"
dependencies = [
"rctree",
"strict-num",
"svgtypes",
"tiny-skia-path",

View File

@ -76,7 +76,7 @@ pulldown-cmark = "0.9"
quote = "1"
rayon = "1.7.0"
regex = "1"
resvg = { version = "0.37.0", default-features = false, features = ["raster-images"] }
resvg = { version = "0.38.0", default-features = false, features = ["raster-images"] }
roxmltree = "0.19"
rustls = "0.21" # in sync with ureq
rustls-pemfile = "1" # in sync with rustls
@ -91,7 +91,7 @@ siphasher = "1"
smallvec = { version = "1.11.1", features = ["union", "const_generics", "const_new"] }
stacker = "0.1.15"
subsetter = "0.1.1"
svg2pdf = { git = "https://github.com/typst/svg2pdf", rev = "11f34c7" }
svg2pdf = { git = "https://github.com/typst/svg2pdf", rev = "49891ef" }
syn = { version = "2", features = ["full", "extra-traits"] }
syntect = { version = "5", default-features = false, features = ["parsing", "regex-fancy", "plist-load", "yaml-load"] }
tar = "0.4"
@ -111,7 +111,7 @@ unicode-script = "0.5"
unicode-segmentation = "1"
unscanny = "0.1"
ureq = "2"
usvg = { version = "0.37", default-features = false, features = ["text"] }
usvg = { version = "0.38.0", default-features = false, features = ["text"] }
walkdir = "2"
wasmi = "0.31.0"
xmlparser = "0.13.5"

View File

@ -19,7 +19,7 @@ use typst::visualize::{
Color, DashPattern, FixedStroke, Geometry, Gradient, Image, ImageKind, LineCap,
LineJoin, Paint, Path, PathItem, Pattern, RasterFormat, RelativeTo, Shape,
};
use usvg::{NodeExt, TreeParsing};
use usvg::TreeParsing;
/// Export a frame into a raster image.
///
@ -272,8 +272,8 @@ fn render_svg_glyph(
// Parse SVG.
let opts = usvg::Options::default();
let usvg_tree = usvg::Tree::from_xmltree(&document, &opts).ok()?;
let tree = resvg::Tree::from_usvg(&usvg_tree);
let mut tree = usvg::Tree::from_xmltree(&document, &opts).ok()?;
tree.calculate_bounding_boxes();
let view_box = tree.view_box.rect;
// If there's no viewbox defined, use the em square for our scale
@ -298,10 +298,8 @@ fn render_svg_glyph(
// See https://github.com/RazrFalcon/resvg/issues/602 for why
// using the svg size is problematic here.
let mut bbox = usvg::BBox::default();
for node in usvg_tree.root.descendants() {
if let Some(rect) = node.calculate_bbox() {
bbox = bbox.expand(rect);
}
if let Some(tree_bbox) = tree.root.bounding_box {
bbox = bbox.expand(tree_bbox);
}
// Compute the bbox after the transform is applied.
@ -320,7 +318,7 @@ fn render_svg_glyph(
// We offset our transform so that the pixmap starts at the edge of the bbox.
let ts = ts.post_translate(-bbox.left() as f32, -bbox.top() as f32);
tree.render(ts, &mut pixmap.as_mut());
resvg::render(&tree, ts, &mut pixmap.as_mut());
canvas.draw_pixmap(
bbox.left(),
@ -757,12 +755,11 @@ fn scaled_texture(image: &Image, w: u32, h: u32) -> Option<Arc<sk::Pixmap>> {
// of `with`.
ImageKind::Svg(svg) => unsafe {
svg.with(|tree| {
let tree = resvg::Tree::from_usvg(tree);
let ts = tiny_skia::Transform::from_scale(
w as f32 / tree.size.width(),
h as f32 / tree.size.height(),
);
tree.render(ts, &mut pixmap.as_mut())
resvg::render(tree, ts, &mut pixmap.as_mut())
});
},
}

View File

@ -5,7 +5,7 @@ use std::sync::Arc;
use comemo::Tracked;
use ecow::EcoString;
use siphasher::sip128::Hasher128;
use usvg::{NodeExt, TreeParsing, TreeTextToPath};
use usvg::{Node, PostProcessingSteps, TreeParsing, TreePostProc};
use crate::diag::{format_xml_like_error, StrResult};
use crate::foundations::Bytes;
@ -56,10 +56,11 @@ impl SvgImage {
let mut tree = usvg::Tree::from_data(&data, &opts).map_err(format_usvg_error)?;
let mut font_hash = 0;
if tree.has_text_nodes() {
let (fontdb, hash) = load_svg_fonts(world, &tree, families);
tree.convert_text(&fontdb);
let (fontdb, hash) = load_svg_fonts(world, &mut tree, families);
tree.postprocess(PostProcessingSteps::default(), &fontdb);
font_hash = hash;
}
tree.calculate_bounding_boxes();
Ok(Self(Arc::new(Repr {
data,
size: tree_size(&tree),
@ -128,7 +129,7 @@ impl Hash for Repr {
/// Discover and load the fonts referenced by an SVG.
fn load_svg_fonts(
world: Tracked<dyn World + '_>,
tree: &usvg::Tree,
tree: &mut usvg::Tree,
families: &[String],
) -> (fontdb::Database, u128) {
let book = world.book();
@ -153,56 +154,70 @@ fn load_svg_fonts(
};
// Determine the best font for each text node.
traverse_svg(&tree.root, &mut |node| {
let usvg::NodeKind::Text(text) = &mut *node.borrow_mut() else { return };
for chunk in &mut text.chunks {
'spans: for span in &mut chunk.spans {
let Some(text) = chunk.text.get(span.start..span.end) else { continue };
let variant = FontVariant {
style: span.font.style.into(),
weight: FontWeight::from_number(span.font.weight),
stretch: span.font.stretch.into(),
};
// Find a font that covers the whole text among the span's fonts
// and the current document font families.
let mut like = None;
for family in span.font.families.iter().chain(families) {
let Some(id) = book.select(&family.to_lowercase(), variant) else {
for child in &mut tree.root.children {
traverse_svg(child, &mut |node| {
let usvg::Node::Text(ref mut text) = node else { return };
for chunk in &mut text.chunks {
'spans: for span in &mut chunk.spans {
let Some(text) = chunk.text.get(span.start..span.end) else {
continue;
};
let Some(info) = book.info(id) else { continue };
like.get_or_insert(info);
let variant = FontVariant {
style: span.font.style.into(),
weight: FontWeight::from_number(span.font.weight),
stretch: span.font.stretch.into(),
};
if text.chars().all(|c| info.coverage.contains(c as u32)) {
// Find a font that covers the whole text among the span's fonts
// and the current document font families.
let mut like = None;
for family in span.font.families.iter().chain(families) {
let Some(id) = book.select(&family.to_lowercase(), variant)
else {
continue;
};
let Some(info) = book.info(id) else { continue };
like.get_or_insert(info);
if text.chars().all(|c| info.coverage.contains(c as u32)) {
if let Some(usvg_family) = load_into_db(id) {
span.font.families = vec![usvg_family];
continue 'spans;
}
}
}
// If we didn't find a match, select a fallback font.
if let Some(id) = book.select_fallback(like, variant, text) {
if let Some(usvg_family) = load_into_db(id) {
span.font.families = vec![usvg_family];
continue 'spans;
}
}
}
// If we didn't find a match, select a fallback font.
if let Some(id) = book.select_fallback(like, variant, text) {
if let Some(usvg_family) = load_into_db(id) {
span.font.families = vec![usvg_family];
}
}
}
}
});
});
}
(fontdb, hasher.finish128().as_u128())
}
/// Search for all font families referenced by an SVG.
fn traverse_svg<F>(node: &usvg::Node, f: &mut F)
fn traverse_svg<F>(node: &mut usvg::Node, f: &mut F)
where
F: FnMut(&usvg::Node),
F: FnMut(&mut usvg::Node),
{
for descendant in node.descendants() {
f(&descendant);
descendant.subroots(|subroot| traverse_svg(&subroot, f))
f(node);
node.subroots_mut(|subroot| {
for child in &mut subroot.children {
traverse_svg(child, f);
}
});
if let Node::Group(ref mut group) = node {
for child in &mut group.children {
traverse_svg(child, f);
}
}
}