1#[derive(Debug, Clone, PartialEq, Eq)]
2pub struct SvgTransform {
3 pub value: String,
4}
5
6impl SvgTransform {
7 #[must_use]
8 pub fn new(value: impl Into<String>) -> Self {
9 Self {
10 value: value.into(),
11 }
12 }
13}
14
15#[must_use]
16pub fn extract_transform_values(input: &str) -> Vec<SvgTransform> {
17 crate::attribute::extract_attribute_values(input, "transform")
18 .into_iter()
19 .map(SvgTransform::new)
20 .collect()
21}
22
23#[must_use]
24pub fn normalize_transform(value: &str) -> String {
25 let mut normalized = String::with_capacity(value.len());
26 let mut pending_space = false;
27
28 for ch in value.trim().chars() {
29 if ch.is_whitespace() {
30 pending_space = true;
31 continue;
32 }
33
34 match ch {
35 '(' => {
36 if normalized.ends_with(' ') {
37 normalized.pop();
38 }
39 normalized.push('(');
40 pending_space = false;
41 }
42 ')' => {
43 if normalized.ends_with(' ') {
44 normalized.pop();
45 }
46 normalized.push(')');
47 pending_space = true;
48 }
49 ',' => {
50 if normalized.ends_with(' ') {
51 normalized.pop();
52 }
53 normalized.push(',');
54 pending_space = false;
55 }
56 _ => {
57 if pending_space
58 && !normalized.is_empty()
59 && !normalized.ends_with('(')
60 && !normalized.ends_with(',')
61 {
62 normalized.push(' ');
63 }
64 normalized.push(ch);
65 pending_space = false;
66 }
67 }
68 }
69
70 normalized.trim().to_string()
71}