From 283c6de2b160b9168f7509546a238dcb77201259 Mon Sep 17 00:00:00 2001 From: NetterSilver <133535268+Silvertree2010@users.noreply.github.com> Date: Sun, 25 Oct 2026 06:07:08 +0000 Subject: [PATCH] Keep significant digits in SVG matrix scale coefficients Found while testing crates/svg/src/lib.rs:97 (num, used by matrix at :129 for the image pattern's patternTransform at :243-254) Co-Authored-By: Claude Haiku 5.5 Co-Authored-By: Claude Sonnet 5.5 --- crates/svg/src/lib.rs | 17 ++++++++++++++++- crates/svg/src/tests.rs | 13 +++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/crates/svg/src/lib.rs b/crates/svg/src/lib.rs index 656307d95..449426d22 100644 --- a/crates/svg/src/lib.rs +++ b/crates/svg/src/lib.rs @@ -126,9 +126,24 @@ fn path_d(p: &BezPath) -> String { s } +/// A matrix scale/shear coefficient to 6 significant digits (unlike [`num`], small values survive). +fn coeff(v: f64) -> String { + if !v.is_finite() || v == 0.0 { + return "0".into(); + } + let digits = 5 - v.abs().log10().floor() as i32; + let p = 10f64.powi(digits); + let r = (v * p).round() / p; + format!("{r}") +} + fn matrix(a: Affine) -> String { let c = a.as_coeffs(); - format!("matrix({} {} {} {} {} {})", num(c[0]), num(c[1]), num(c[2]), num(c[3]), num(c[4]), num(c[5])) + // Snap float noise (e.g. 6e-17 from a 90 degree rotation) to zero, relative to the largest term. + let eps = c[..4].iter().fold(0.0f64, |m, v| m.max(v.abs())) * 1e-12; + let eps = eps.max(1e-12); + let k = |v: f64| if v.abs() <= eps { "0".to_string() } else { coeff(v) }; + format!("matrix({} {} {} {} {} {})", k(c[0]), k(c[1]), k(c[2]), k(c[3]), num(c[4]), num(c[5])) } /// A view of page area `b` at `k` device pixels per unit (fewer when that makes it larger than diff --git a/crates/svg/src/tests.rs b/crates/svg/src/tests.rs index dedeca3bd..bfce1cf46 100644 --- a/crates/svg/src/tests.rs +++ b/crates/svg/src/tests.rs @@ -264,3 +264,16 @@ fn a_filled_open_curve_is_closed_for_its_fill() { }; assert_eq!(d.nodes().map(loops).sum::(), 1); } + +#[test] +fn matrix_keeps_small_scale_precision() { + assert_eq!(matrix(Affine::scale(0.0375)), "matrix(0.0375 0 0 0.0375 0 0)"); + assert_eq!(matrix(Affine::scale(0.0001)), "matrix(0.0001 0 0 0.0001 0 0)"); +} + +#[test] +fn matrix_snaps_rotation_noise_and_tiny_values() { + let r = Affine::rotate(std::f64::consts::FRAC_PI_2); + assert_eq!(matrix(r), "matrix(0 1 -1 0 0 0)"); + assert_eq!(matrix(Affine::scale(1e-320)), "matrix(0 0 0 0 0 0)"); +}