From 85b6c2a5eb4fb01484d7d3134df638d46427915d Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Mon, 2 Nov 2026 02:59:51 -0800 Subject: [PATCH] SVG: ids made up on import never repeat an id in the file The import gives an element an id when it needs to find it again in usvg's tree (links, symbol uses, events, data attributes, and now circles and ellipses). A made-up id could repeat one the file already had, such as vectorcraft-id-0, so a or paint reference to that element showed the other one. Made-up ids now skip every id in the file. Circles and ellipses in a file whose style sheet has an [id selector get no made-up id and keep usvg's curves, so the selector matches the same elements as before. --- crates/svg/src/import/prepass.rs | 22 ++++++++++++++++++---- crates/svg/tests/it/basic_shapes.rs | 20 ++++++++++++++++++++ 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/crates/svg/src/import/prepass.rs b/crates/svg/src/import/prepass.rs index 2afa651a5..8ed825948 100644 --- a/crates/svg/src/import/prepass.rs +++ b/crates/svg/src/import/prepass.rs @@ -40,6 +40,8 @@ pub(crate) struct Edits { list: Vec<(std::ops::Range, String)>, /// The ids made up so far, by element. ids: HashMap, + /// The ids in the file and those made up, read when the first id is made up. + taken: Option>, /// Declarations added to elements' `style`s, by element: (where they go, whether the element /// has a `style` attribute, the declarations, XML-escaped). styles: HashMap, @@ -50,7 +52,8 @@ impl Edits { self.list.push((at..at, text)); } - /// The id of element `n` (in `svg`), made up and written in when it has none. + /// The id of element `n` (in `svg`), made up and written in when it has none. A made-up id is + /// one no element of the file has. pub(super) fn id(&mut self, svg: &str, n: XNode) -> String { if let Some(id) = n.attribute("id").filter(|id| !id.is_empty()) { return id.to_string(); @@ -58,7 +61,16 @@ impl Edits { if let Some(id) = self.ids.get(&n.id()) { return id.clone(); } - let id = format!("{MADE_UP_ID}{}", self.ids.len()); + let taken = self.taken.get_or_insert_with(|| n.document().descendants().filter_map(|e| e.attribute("id")).map(str::to_string).collect()); + let mut i = self.ids.len(); + let id = loop { + let id = format!("{MADE_UP_ID}{i}"); + if !taken.contains(&id) { + break id; + } + i += 1; + }; + taken.insert(id.clone()); self.set(svg, n, "id", &id); self.ids.insert(n.id(), id.clone()); id @@ -119,7 +131,8 @@ impl Edits { /// ([`user_events`]), and the `