use roxmltree::{Attribute, Document, ExpandedName};
pub fn collect_editor_prefixes(doc: &Document, editor_uris: &[&str]) -> Vec<String> {
let svg = match doc.root().first_element_child() {
Some(el) => el,
None => return Vec::new(),
};
let mut prefixes = Vec::new();
for ns in svg.namespaces() {
if let Some(prefix) = ns.name()
&& editor_uris.contains(&ns.uri())
{
prefixes.push(prefix.to_string());
}
}
prefixes
}
pub fn is_editor_element(tag: ExpandedName, editor_prefixes: &[String]) -> bool {
let ns = match tag.namespace() {
Some(ns) => ns,
None => return false,
};
if ns == "http://www.w3.org/2000/svg" || ns == "http://www.w3.org/1999/xlink" {
return false;
}
let _ = editor_prefixes; true
}
pub fn is_collapsible_container(tag_name: &str) -> bool {
matches!(
tag_name,
"g" | "defs" | "symbol" | "marker" | "clipPath" | "mask" | "pattern"
)
}
pub fn has_no_meaningful_attributes(node: &roxmltree::Node, editor_prefixes: &[String]) -> bool {
for attr in node.attributes() {
if is_editor_attribute(&attr, editor_prefixes) {
continue;
}
if attr.value().is_empty() {
continue;
}
return false;
}
true
}
pub fn is_editor_attribute(attr: &Attribute, editor_prefixes: &[String]) -> bool {
if let Some(ns) = attr.namespace()
&& ns != "http://www.w3.org/2000/svg"
&& ns != "http://www.w3.org/1999/xlink"
&& ns != "http://www.w3.org/XML/1998/namespace"
{
return true;
}
if let Some(prefix) = attr.name().split(':').next()
&& attr.name().contains(':')
&& editor_prefixes.contains(&prefix.to_string())
{
return true;
}
false
}
pub fn minify_text(text: &str) -> String {
let trimmed = text.trim();
if trimmed.is_empty() {
return String::new();
}
let mut result = String::with_capacity(trimmed.len());
let mut prev_was_space = false;
for ch in trimmed.chars() {
if ch.is_whitespace() {
if !prev_was_space {
result.push(' ');
prev_was_space = true;
}
} else {
result.push(ch);
prev_was_space = false;
}
}
result
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_collect_editor_prefixes_finds_inkscape() {
let svg = r#"<svg xmlns="http://www.w3.org/2000/svg" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"><rect/></svg>"#;
let doc = Document::parse(svg).unwrap();
let uris = &["http://www.inkscape.org/namespaces/inkscape"];
let prefixes = collect_editor_prefixes(&doc, uris);
assert_eq!(prefixes, vec!["inkscape"]);
}
#[test]
fn test_collect_editor_prefixes_ignores_svg_namespace() {
let svg = r#"<svg xmlns="http://www.w3.org/2000/svg"><rect/></svg>"#;
let doc = Document::parse(svg).unwrap();
let uris = &["http://www.inkscape.org/namespaces/inkscape"];
let prefixes = collect_editor_prefixes(&doc, uris);
assert!(prefixes.is_empty());
}
#[test]
fn test_collapsible_containers() {
assert!(is_collapsible_container("g"));
assert!(is_collapsible_container("defs"));
assert!(is_collapsible_container("symbol"));
assert!(!is_collapsible_container("rect"));
assert!(!is_collapsible_container("svg"));
assert!(!is_collapsible_container("text"));
}
#[test]
fn test_minify_text_collapses_whitespace() {
assert_eq!(minify_text(" hello world "), "hello world");
assert_eq!(minify_text("\n \t "), "");
assert_eq!(minify_text("no change"), "no change");
assert_eq!(minify_text(" a b c "), "a b c");
}
#[test]
fn test_minify_text_empty_string() {
assert_eq!(minify_text(""), "");
assert_eq!(minify_text(" "), "");
}
}