Type: SVG glyphs leave out images of SVG documents

The importer expanded an image whose data is an SVG document into art; the SVG table asks for it to be ignored, and a glyph draws no documents inside it.
This commit is contained in:
Jürg Lehni
2026-10-24 06:51:20 +07:00
committed by bflatastic
parent 3aa9855010
commit 4d1b4a3392
2 changed files with 28 additions and 2 deletions
+9 -2
View File
@@ -17,7 +17,7 @@ use crate::ImportOptions;
const MAX_SVG_GLYPH_BYTES: u64 = 1 << 20;
/// Elements left out of a glyph, as the `SVG ` table rules them out: text (type inside type),
/// embedded content and scripts.
/// embedded content and scripts (and images of SVG documents, in [`glyph`]).
const LEFT_OUT: [&str; 3] = ["text", "foreignObject", "script"];
/// Glyph `gid`'s art from its SVG `document` (possibly gzipped), in a font of `upem` units per em:
@@ -98,7 +98,14 @@ fn glyph(svg: &str, id: &str, color: &str) -> Option<Glyph> {
let at = target.range();
// Edits: byte ranges to cut and what to put in their place, in order.
let mut edits: Vec<(Range<usize>, String)> = vec![];
for n in root.descendants().filter(|n| n.is_element() && LEFT_OUT.contains(&n.tag_name().name())) {
// Cut with the left-out elements: images of SVG documents, as the table asks (a glyph draws no
// documents inside it).
let svg_image = |n: &roxmltree::Node| {
n.tag_name().name() == "image"
&& n.attributes()
.any(|a| a.name() == "href" && a.value().trim_start().get(..14).is_some_and(|v| v.eq_ignore_ascii_case("data:image/svg")))
};
for n in root.descendants().filter(|n| n.is_element() && (LEFT_OUT.contains(&n.tag_name().name()) || svg_image(n))) {
edits.push((n.range(), String::new()));
}
for a in root.attributes().filter(|a| matches!(a.name(), "width" | "height" | "color")) {
+19
View File
@@ -211,3 +211,22 @@ fn images_are_in_the_art_with_their_pixels() {
let b = art.geometric_bounds().unwrap();
assert!(b.x0.abs() < 1e-6 && (b.width() - 500.0).abs() < 1e-6 && (b.y0 + 500.0).abs() < 1e-6, "in font units: {b:?}");
}
/// An image whose data is an SVG document is left out, as the `SVG ` table asks: a glyph draws no
/// documents inside it.
#[test]
fn svg_images_are_left_out() {
let inner = r##"<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10"><rect width="10" height="10" fill="#f00"/></svg>"##;
let doc = format!(
r##"<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g id="glyph1">
<path fill="#00f" d="M0 0h500v-500z"/>
<image width="500" height="500" xlink:href="data:image/svg+xml;base64,{}"/>
<image width="500" height="500" href=" DATA:image/SVG+xml;utf8,{}"/>
</g></svg>"##,
vectorcraft_svg::base64_encode(inner.as_bytes()),
vectorcraft_svg::xml_escape(&inner.replace('#', "%23"))
);
let fills = fills(&art(&doc, 1).unwrap());
assert!(fills.contains(&Paint::solid(Color::rgb8(0, 0, 255))), "the glyph's own art");
assert!(!fills.contains(&Paint::solid(Color::rgb8(255, 0, 0))), "not the images' documents: {fills:?}");
}