1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
use super::prelude::*;
pub fn ungroup_groups(doc: &mut Document, svg: &Node, opt: &Options) {
let mut groups = Vec::with_capacity(16);
loop {
_ungroup_groups(svg, opt, &mut groups);
if groups.is_empty() {
break;
}
while let Some(mut g) = groups.pop() {
ungroup_group(&mut g);
doc.remove_node(g);
}
}
}
fn _ungroup_groups(parent: &Node, opt: &Options, groups: &mut Vec<Node>) {
for node in parent.children() {
if node.has_children() {
_ungroup_groups(&node, opt, groups);
}
if node.is_tag_name(EId::G) {
if !node.has_children() {
groups.push(node.clone());
continue;
}
if opt.keep_named_groups && node.has_id() {
continue;
}
// Do not ungroup groups inside `clipPath`.
// They will be removed during conversion.
if node.ancestors().skip(1).any(|n| n.is_tag_name(EId::ClipPath)) {
// Groups that was created from 'use' can be ungroupped.
if !node.has_attribute("usvg-group") {
continue;
}
}
// Groups with a `clip-path` attribute can't be ungroupped.
if let Some(&AValue::FuncLink(_)) = node.attributes().get_type(AId::ClipPath) {
continue;
}
// Groups with a `mask` attribute can't be ungroupped.
if let Some(&AValue::FuncLink(_)) = node.attributes().get_type(AId::Mask) {
continue;
}
// We can ungroup group with opacity only when it has only one child.
if node.has_attribute(AId::Opacity) {
if node.children().count() != 1 {
continue;
}
}
groups.push(node.clone());
}
}
}
fn ungroup_group(g: &mut Node) {
for (aid, attr) in g.attributes().iter().svg() {
for (_, mut child) in g.children().svg() {
// Not all attributes can be copied directly.
if prepare_attribute(g, &mut child, aid) {
continue;
}
child.set_attribute_if_none((aid, attr.value.clone()));
}
}
let is_single_child = g.children().count() == 1;
while g.has_children() {
let mut child = g.last_child().unwrap();
child.detach();
g.insert_after(child.clone());
// Transfer the group ID to the child.
if is_single_child && !child.has_id() {
child.set_id(g.id().clone());
}
}
}
fn prepare_attribute(g_node: &Node, child_node: &mut Node, aid: AId) -> bool {
if aid == AId::Opacity {
if child_node.has_attribute(aid) {
// We can't just replace 'opacity' attribute,
// we should multiply it.
let op1 = g_node.attributes().get_number_or(aid, 1.0);
let op2 = child_node.attributes().get_number_or(aid, 1.0);
child_node.set_attribute((aid, op1 * op2));
return true;
}
}
if aid == AId::Transform {
if child_node.has_attribute(aid) {
// We should multiply transform matrices.
let ts = g_node.attributes().get_transform(aid).unwrap_or_default();
child_node.prepend_transform(ts);
return true;
}
}
if aid == AId::Display {
// Display attribute has a priority during rendering, so we must
// copy it even if a child has it already.
g_node.copy_attribute_to(aid, child_node);
return true;
}
false
}