diff --git a/crates/svg/src/lib.rs b/crates/svg/src/lib.rs index 6bef8d63..75985fbd 100644 --- a/crates/svg/src/lib.rs +++ b/crates/svg/src/lib.rs @@ -794,10 +794,10 @@ impl<'a, 'i> Parser<'a, 'i> { let vb = viewbox_transform(n, w, h); let mut children = vec![]; let saved = (self.vw, self.vh); - if let Some((_, vw, vh)) = viewbox(n) { - self.vw = vw; - self.vh = vh; - } + // Percentages use this viewport's user space, even without a viewBox. + let (vw, vh) = viewbox(n).map_or((w, h), |(_, vw, vh)| (vw, vh)); + self.vw = vw; + self.vh = vh; for c in n.children() { self.node(c, &st, &mut children, depth + 1); } diff --git a/crates/svg/src/tests.rs b/crates/svg/src/tests.rs index a7a9bef3..3553205d 100644 --- a/crates/svg/src/tests.rs +++ b/crates/svg/src/tests.rs @@ -86,6 +86,39 @@ fn paths_gradients_css_use() { assert!(c[0] > 0.9 && c[3] > 0.9, "{c:?}"); } +#[test] +fn nested_svg_percentages_use_the_nearest_viewport() { + let d = parse( + br#" + + + + + + + + + + + "#, + ) + .unwrap(); + let shapes = d.flatten(); + assert_eq!(shapes.len(), 4); + for ((_, _, shape), (name, width, height)) in + shapes.iter().zip([("inner", 20.0, 10.0), ("deep", 10.0, 5.0), ("outer", 50.0, 10.0), ("viewbox", 100.0, 50.0)]) + { + assert_eq!(shape.name, name); + assert!(matches!(shape.geom, Geom::Rect { w, h, .. } if w == width && h == height), "{name}: {:?}", shape.geom); + } + let img = rasterize(&d, 100, 80, 1.0); + assert_eq!(px(&img, 15, 15), [1.0, 0.0, 0.0, 1.0]); + assert_eq!(px(&img, 35, 12), [0.0, 0.0, 1.0, 1.0]); + assert_eq!(px(&img, 35, 17)[3], 0.0); + assert_eq!(px(&img, 45, 65)[3], 1.0); + assert_eq!(px(&img, 55, 65)[3], 0.0); +} + #[test] fn malformed_input() { assert!(parse(b"").is_err());