From ecc131fc869253161fc827a7be58a33a30938fe2 Mon Sep 17 00:00:00 2001 From: webdevtodayjason <115739170+webdevtodayjason@users.noreply.github.com> Date: Mon, 12 Oct 2026 17:45:45 +0000 Subject: [PATCH] SVG: percentages in the root resolve against its own width and height --- crates/svg/src/lib.rs | 4 ++++ crates/svg/src/tests.rs | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/crates/svg/src/lib.rs b/crates/svg/src/lib.rs index 75985fbd..9e24a5b8 100644 --- a/crates/svg/src/lib.rs +++ b/crates/svg/src/lib.rs @@ -1147,6 +1147,10 @@ pub fn parse(bytes: &[u8]) -> Result { } None => (pct_or("width", 'x', 300.0), pct_or("height", 'y', 150.0)), }; + // Percentages use the root viewport's user space, even without a viewBox. A size that is not + // a finite positive number keeps the 300x150 default, so it cannot produce NaN geometry. + let usable = |v: f64, d: f64| if v.is_finite() && v > 0.0 { v } else { d }; + (p.vw, p.vh) = vb.map_or((usable(dw, 300.0), usable(dh, 150.0)), |(_, w, h)| (w, h)); let mut rst = st.clone(); let (opacity, _) = p.cascade(root, &mut rst); let mut children = vec![]; diff --git a/crates/svg/src/tests.rs b/crates/svg/src/tests.rs index 3553205d..e06537e3 100644 --- a/crates/svg/src/tests.rs +++ b/crates/svg/src/tests.rs @@ -86,6 +86,28 @@ fn paths_gradients_css_use() { assert!(c[0] > 0.9 && c[3] > 0.9, "{c:?}"); } +#[test] +fn root_svg_percentages_use_the_document_viewport() { + for (attrs, width, height) in [ + ("width=\"100\" height=\"80\"", 100.0, 80.0), + ("width=\"50%\" height=\"200%\"", 150.0, 300.0), + ("", 300.0, 150.0), + ("width=\"100\" height=\"80\" viewBox=\"0 0 200 160\"", 200.0, 160.0), + ("width=\"NaN%\" height=\"1e400\"", 300.0, 150.0), + ] { + let svg = format!(r#""#); + let d = parse(svg.as_bytes()).unwrap(); + let shapes = d.flatten(); + assert_eq!(shapes.len(), 1); + assert!( + matches!(shapes[0].2.geom, Geom::Rect { x, y, w, h, .. } + if x == width / 4.0 && y == height / 4.0 && w == width / 2.0 && h == height / 2.0), + "{attrs}: {:?}", + shapes[0].2.geom + ); + } +} + #[test] fn nested_svg_percentages_use_the_nearest_viewport() { let d = parse(