From 6494743b6e7a3598b323a781d5d5da6f35431acc Mon Sep 17 00:00:00 2001 From: NetterSilver <133535268+Silvertree2010@users.noreply.github.com> Date: Fri, 23 Oct 2026 22:55:35 +0000 Subject: [PATCH] Fix duplicate SVG ids when a layer name matches a generated id Found while testing crates/svg/src/lib.rs:549 (unique_id) with :162 (id) and :387 Co-Authored-By: Claude Haiku 5.5 Co-Authored-By: Claude Sonnet 5.5 --- crates/svg/src/lib.rs | 9 +++++++-- crates/svg/src/tests.rs | 11 +++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/crates/svg/src/lib.rs b/crates/svg/src/lib.rs index 55176aaf..16942934 100644 --- a/crates/svg/src/lib.rs +++ b/crates/svg/src/lib.rs @@ -160,8 +160,13 @@ fn hex(c: Color) -> String { impl W<'_> { fn id(&mut self, prefix: &str) -> String { - self.next += 1; - format!("{prefix}{}", self.next) + loop { + self.next += 1; + let id = format!("{prefix}{}", self.next); + if self.used_ids.insert(id.clone()) { + return id; + } + } } /// `fill="..." [fill-opacity]` for a paint on a w×h box; `None` when it paints nothing. diff --git a/crates/svg/src/tests.rs b/crates/svg/src/tests.rs index 8dece77f..6706899f 100644 --- a/crates/svg/src/tests.rs +++ b/crates/svg/src/tests.rs @@ -159,3 +159,14 @@ fn point_joins_equal_to_the_layer_join_keep_a_plain_stroke() { assert!(svg_with(StrokeJoin::Miter).contains("stroke-width=\"2\""), "equal joins stay a stroke"); assert!(!svg_with(StrokeJoin::Round).contains("stroke-width="), "a differing join is drawn as an outline"); } + +#[test] +fn layer_ids_never_clash_with_generated_ids() { + let mut d = Document::new("t"); + let p = d.first_page().unwrap(); + let f = d.create(p, NodeKind::Frame, "clip1", 0.0, 0.0, 100.0, 100.0).unwrap(); + d.get_mut(f).unwrap().clip = true; + d.create(f, NodeKind::Rectangle, "R", 0.0, 0.0, 50.0, 50.0).unwrap(); + let svg = export_svg(&d, &[f], &SvgOptions { include_ids: true, ..SvgOptions::default() }).unwrap(); + assert_eq!(svg.matches("id=\"clip1\"").count(), 1, "{svg}"); +}