From 85a269b609981326e0a0360bd00f0c1007f4a818 Mon Sep 17 00:00:00 2001 From: webdevtodayjason <115739170+webdevtodayjason@users.noreply.github.com> Date: Mon, 12 Oct 2026 18:03:15 +0000 Subject: [PATCH] SVG: non-finite viewBox and nested sizes no longer panic the importer --- crates/svg/src/lib.rs | 8 ++++---- crates/svg/src/tests.rs | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/crates/svg/src/lib.rs b/crates/svg/src/lib.rs index 75985fbd1..2497294ed 100644 --- a/crates/svg/src/lib.rs +++ b/crates/svg/src/lib.rs @@ -789,8 +789,8 @@ impl<'a, 'i> Parser<'a, 'i> { // Nested viewport. let x = self.attr_len(n, "x", 'x', &st); let y = self.attr_len(n, "y", 'y', &st); - let w = n.attribute("width").and_then(|v| self.length(v, 'x', st.font_size)).unwrap_or(self.vw); - let h = n.attribute("height").and_then(|v| self.length(v, 'y', st.font_size)).unwrap_or(self.vh); + let w = n.attribute("width").and_then(|v| self.length(v, 'x', st.font_size)).filter(|v| v.is_finite()).unwrap_or(self.vw); + let h = n.attribute("height").and_then(|v| self.length(v, 'y', st.font_size)).filter(|v| v.is_finite()).unwrap_or(self.vh); let vb = viewbox_transform(n, w, h); let mut children = vec![]; let saved = (self.vw, self.vh); @@ -926,7 +926,7 @@ impl<'a, 'i> Parser<'a, 'i> { } "rect" => { let (w, h) = (l("width", 'x'), l("height", 'y')); - if w <= 0.0 || h <= 0.0 { + if !(w > 0.0 && h > 0.0) { return None; } let rx_a = n.attribute("rx").and_then(|v| self.length(v, 'x', st.font_size)); @@ -981,7 +981,7 @@ impl<'a, 'i> Parser<'a, 'i> { fn viewbox(n: roxmltree::Node) -> Option<([f64; 2], f64, f64)> { let v = pathdata::numbers(n.attribute("viewBox")?); - (v.len() == 4 && v[2] > 0.0 && v[3] > 0.0).then(|| ([v[0], v[1]], v[2], v[3])) + (v.len() == 4 && v.iter().all(|x| x.is_finite()) && v[2] > 0.0 && v[3] > 0.0).then(|| ([v[0], v[1]], v[2], v[3])) } /// The viewBox → viewport transform (`preserveAspectRatio`, default `xMidYMid meet`). diff --git a/crates/svg/src/tests.rs b/crates/svg/src/tests.rs index 3553205d8..243b87ff0 100644 --- a/crates/svg/src/tests.rs +++ b/crates/svg/src/tests.rs @@ -119,6 +119,45 @@ fn nested_svg_percentages_use_the_nearest_viewport() { assert_eq!(px(&img, 55, 65)[3], 0.0); } +#[test] +fn non_finite_viewboxes_do_not_panic_or_produce_nan_geometry() { + for attrs in ["viewBox=\"0 0 1e400 100\"", "viewBox=\"0 0 100 1e400\"", "viewBox=\"-1e400 0 100 100\""] { + for shape in ["", ""] { + let svg = format!(r#"{shape}"#); + let d = parse(svg.as_bytes()).unwrap(); + assert_eq!((d.width, d.height), (300.0, 150.0)); + let shapes = d.flatten(); + assert_eq!(shapes.len(), 1); + assert!(matches!(shapes[0].2.geom, Geom::Rect { x, y, w, h, rx, ry } + if [x, y, w, h, rx, ry].iter().all(|v| v.is_finite()) && w == 150.0 && h == 75.0)); + assert!(shapes[0].0.as_coeffs().iter().all(|v| v.is_finite())); + } + } +} + +#[test] +fn non_finite_nested_viewports_do_not_panic_or_produce_nan_geometry() { + for attrs in ["width=\"1e400\" height=\"10\"", "width=\"10\" height=\"1e400\""] { + for shape in ["", ""] { + let svg = format!(r#"{shape}"#); + 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, rx, ry } + if [x, y, w, h, rx, ry].iter().all(|v| v.is_finite()) && w > 0.0 && h > 0.0)); + assert!(shapes[0].0.as_coeffs().iter().all(|v| v.is_finite())); + } + } +} + +#[test] +fn zero_size_nested_viewport_draws_nothing() { + for attrs in ["width=\"0\" height=\"10\"", "width=\"10\" height=\"0\""] { + let svg = format!(r#""#); + assert!(parse(svg.as_bytes()).unwrap().flatten().is_empty(), "{attrs}"); + } +} + #[test] fn malformed_input() { assert!(parse(b"").is_err());