svgcleaner/task/
regroup_gradient_stops.rs1use svgdom::{
20 Document,
21 ElementType,
22 Node,
23};
24
25use task::short::{EId, AId};
26
27pub fn regroup_gradient_stops(doc: &mut Document) {
28 let mut nodes: Vec<Node> = doc.descendants()
29 .filter(|n| n.is_gradient())
30 .filter(|n| n.has_children())
31 .filter(|n| !n.has_attribute(AId::XlinkHref))
32 .collect();
33
34 let mut is_changed = false;
35 let mut join_nodes = Vec::new();
36
37 let mut i1 = 0;
39 while i1 < nodes.len() {
40 let mut node1 = nodes[i1].clone();
41
42 let mut i2 = i1 + 1;
43 while i2 < nodes.len() {
44 let node2 = nodes[i2].clone();
45 i2 += 1;
46
47 if super::rm_dupl_defs::is_equal_stops(&node1, &node2) {
48 join_nodes.push(node2.clone());
49
50 nodes.remove(i2 - 1);
51 i2 -= 1;
52 }
53 }
54
55 if !join_nodes.is_empty() {
56 is_changed = true;
57
58 let mut new_lg = doc.create_element(EId::LinearGradient);
59 let new_id = gen_id(doc, "lg");
60 new_lg.set_id(new_id);
61
62 while node1.has_children() {
63 let mut c = node1.first_child().unwrap();
64 c.detach();
65 new_lg.append(&c);
66 }
67 node1.set_attribute((AId::XlinkHref, new_lg.clone()));
68
69 node1.insert_before(&new_lg);
70
71 for jn in &mut join_nodes {
72 while jn.has_children() {
73 let mut c = jn.first_child().unwrap();
74 c.remove();
75 }
76 jn.set_attribute((AId::XlinkHref, new_lg.clone()));
77 }
78
79 join_nodes.clear();
80 }
81
82 i1 += 1;
83 }
84
85 if is_changed {
86 super::resolve_linear_gradient_attributes(doc);
88 }
89}
90
91fn gen_id(doc: &Document, prefix: &str) -> String {
92 let mut n = 1;
93
94 let mut s = String::new();
95 loop {
96 s.clear();
97 s.push_str(prefix);
98 s.push_str(&n.to_string());
99
100 if !doc.descendants().any(|n| *n.id() == s) {
102 break;
103 }
104
105 n += 1;
106 }
107
108 s
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114 use svgdom::{Document, ToStringWithOptions};
115 use task;
116
117 macro_rules! test {
118 ($name:ident, $in_text:expr, $out_text:expr) => (
119 #[test]
120 fn $name() {
121 let mut doc = Document::from_str($in_text).unwrap();
122 task::resolve_linear_gradient_attributes(&doc);
123 task::resolve_radial_gradient_attributes(&doc);
124 task::resolve_stop_attributes(&doc).unwrap();
125 regroup_gradient_stops(&mut doc);
126 assert_eq_text!(doc.to_string_with_opt(&write_opt_for_tests!()), $out_text);
127 }
128 )
129 }
130
131 test!(rm_1,
132"<svg>
133 <linearGradient id='lg1' x1='50'>
134 <stop offset='0'/>
135 <stop offset='1'/>
136 </linearGradient>
137 <linearGradient id='lg2' x1='100'>
138 <stop offset='0'/>
139 <stop offset='1'/>
140 </linearGradient>
141</svg>",
142"<svg>
143 <linearGradient id='lg3'>
144 <stop offset='0'/>
145 <stop offset='1'/>
146 </linearGradient>
147 <linearGradient id='lg1' x1='50' xlink:href='#lg3'/>
148 <linearGradient id='lg2' x1='100' xlink:href='#lg3'/>
149</svg>
150");
151
152 test!(rm_2,
153"<svg>
154 <linearGradient id='lg1' x1='50'>
155 <stop offset='0'/>
156 <stop offset='1'/>
157 </linearGradient>
158 <linearGradient id='lg3' x1='50'>
159 <stop offset='0.5'/>
160 <stop offset='1'/>
161 </linearGradient>
162 <linearGradient id='lg2' x1='100'>
163 <stop offset='0'/>
164 <stop offset='1'/>
165 </linearGradient>
166 <linearGradient id='lg4' x1='100'>
167 <stop offset='0.5'/>
168 <stop offset='1'/>
169 </linearGradient>
170</svg>",
171"<svg>
172 <linearGradient id='lg5'>
173 <stop offset='0'/>
174 <stop offset='1'/>
175 </linearGradient>
176 <linearGradient id='lg1' x1='50' xlink:href='#lg5'/>
177 <linearGradient id='lg6'>
178 <stop offset='0.5'/>
179 <stop offset='1'/>
180 </linearGradient>
181 <linearGradient id='lg3' x1='50' xlink:href='#lg6'/>
182 <linearGradient id='lg2' x1='100' xlink:href='#lg5'/>
183 <linearGradient id='lg4' x1='100' xlink:href='#lg6'/>
184</svg>
185");
186
187}