SVG classes: split on ASCII whitespace only, keep class names that are not XML names

This commit is contained in:
vangel76
2026-10-21 20:50:41 +02:00
parent fce9b70e0f
commit 7aaba5c47b
3 changed files with 11 additions and 3 deletions
+1 -1
View File
@@ -637,7 +637,7 @@ impl Writer<'_> {
.map_or(&[][..], |a| a.classes.as_slice())
.iter()
.map(String::as_str)
.filter(|c| !c.is_empty() && c.chars().all(xml_name_char))
.filter(|c| !c.is_empty() && !c.chars().any(|ch| ch.is_control() || matches!(ch, '\u{fffe}' | '\u{ffff}')))
.collect();
if !classes.is_empty() && !self.tiny() {
out.push_str(&format!(" class=\"{}\"", xml_attr_escape(&classes.join(" "))));
+3 -2
View File
@@ -718,11 +718,12 @@ type Extras = (Vec<(String, String)>, Vec<String>);
const MAX_CLASSES: usize = 64;
/// The CSS classes of element `e`, in order and without repeats. Classes our own export
/// generated (`cls-1`, with or without a prefix) aren't kept: export makes them again.
/// generated (`cls-1`, with or without a prefix) aren't kept: export makes them again. A class list
/// is split on SVG's five ASCII whitespace characters only, so a no-break space stays inside a name.
fn user_classes(e: XNode) -> Vec<String> {
let generated = |c: &str| c.rsplit_once("cls-").is_some_and(|(_, n)| !n.is_empty() && n.bytes().all(|b| b.is_ascii_digit()));
let mut out: Vec<String> = Vec::new();
for c in e.attribute("class").into_iter().flat_map(str::split_whitespace) {
for c in e.attribute("class").into_iter().flat_map(|v| v.split(['\u{20}', '\t', '\n', '\u{c}', '\r']).filter(|c| !c.is_empty())) {
if out.len() < MAX_CLASSES && !generated(c) && !out.iter().any(|k| k == c) {
out.push(c.to_string());
}
+7
View File
@@ -29,3 +29,10 @@ fn imported_classes_survive_every_styling_with_generated_ones() {
assert_eq!(class_of(&again, "r"), Some(rect), "{styling:?}");
}
}
#[test]
fn class_names_with_colons_slashes_and_no_break_spaces_are_kept() {
let src = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"10\" height=\"10\"><rect id=\"r\" class=\"hover:bg-blue w-1/2 a\u{a0}b\" width=\"5\" height=\"5\" fill=\"red\"/></svg>";
let out = export(&import(src).unwrap(), &ExportOptions::default());
assert_eq!(class_of(&out, "r").as_deref(), Some("hover:bg-blue w-1/2 a\u{a0}b"));
}