1use std::sync::Arc;
6
7use svgtypes::{Length, LengthUnit};
8
9use super::svgtree::{AId, EId, SvgNode};
10use super::{converter, style};
11use crate::tree::ContextElement;
12use crate::{Group, IsValidLength, Node, NonZeroRect, Path, Size, Transform, ViewBox};
13
14pub(crate) fn convert(
15 node: SvgNode,
16 state: &converter::State,
17 cache: &mut converter::Cache,
18 parent: &mut Group,
19) {
20 let child = match node.first_child() {
21 Some(v) => v,
22 None => return,
23 };
24
25 if state.parent_clip_path.is_some() && child.tag_name() == Some(EId::Symbol) {
26 return;
30 }
31
32 let mut use_state = state.clone();
33 use_state.context_element = Some((
34 style::resolve_fill(node, true, state, cache).map(|mut f| {
35 f.context_element = Some(ContextElement::UseNode);
36 f
37 }),
38 style::resolve_stroke(node, true, state, cache).map(|mut s| {
39 s.context_element = Some(ContextElement::UseNode);
40 s
41 }),
42 ));
43
44 let mut orig_ts = node.resolve_transform(AId::Transform, state);
46 let mut new_ts = Transform::default();
47
48 {
49 let x = node.convert_user_length(AId::X, &use_state, Length::zero());
50 let y = node.convert_user_length(AId::Y, &use_state, Length::zero());
51 new_ts = new_ts.pre_translate(x, y);
52 }
53
54 let linked_to_symbol = child.tag_name() == Some(EId::Symbol);
55
56 if linked_to_symbol {
57 if let Some(ts) = viewbox_transform(node, child, &use_state) {
58 new_ts = new_ts.pre_concat(ts);
59 }
60
61 if let Some(clip_rect) = get_clip_rect(node, child, &use_state) {
62 let mut g = clip_element(node, clip_rect, orig_ts, &use_state, cache);
63 g.abs_transform = parent.abs_transform;
64
65 if let Some(mut g2) =
67 converter::convert_group(node, &use_state, true, cache, &mut g, &|cache, g2| {
68 convert_children(child, new_ts, &use_state, cache, false, g2);
69 })
70 {
71 g.is_context_element = true;
74 g2.id = String::new(); g2.transform = Transform::default();
76 g.children.push(Node::Group(Box::new(g2)));
77 }
78
79 if g.children.is_empty() {
80 return;
81 }
82
83 g.calculate_bounding_boxes();
84 parent.children.push(Node::Group(Box::new(g)));
85 return;
86 }
87 }
88
89 orig_ts = orig_ts.pre_concat(new_ts);
90
91 if linked_to_symbol {
92 if let Some(mut g) =
94 converter::convert_group(node, &use_state, false, cache, parent, &|cache, g| {
95 convert_children(child, orig_ts, &use_state, cache, false, g);
96 })
97 {
98 g.is_context_element = true;
99 g.transform = Transform::default();
100 parent.children.push(Node::Group(Box::new(g)));
101 }
102 } else {
103 let linked_to_svg = child.tag_name() == Some(EId::Svg);
104 if linked_to_svg {
105 let def = Length::new(100.0, LengthUnit::Percent);
110 use_state.use_size = (None, None);
122
123 if node.has_attribute(AId::Width) {
125 use_state.use_size.0 = Some(node.convert_user_length(AId::Width, &use_state, def));
126 }
127 if node.has_attribute(AId::Height) {
128 use_state.use_size.1 = Some(node.convert_user_length(AId::Height, &use_state, def));
129 }
130
131 convert_children(node, orig_ts, &use_state, cache, true, parent);
132 } else {
133 convert_children(node, orig_ts, &use_state, cache, true, parent);
134 }
135 }
136}
137
138pub(crate) fn convert_svg(
139 node: SvgNode,
140 state: &converter::State,
141 cache: &mut converter::Cache,
142 parent: &mut Group,
143) {
144 let mut orig_ts = node.resolve_transform(AId::Transform, state);
146 let mut new_ts = Transform::default();
147
148 let x = node.convert_user_length(AId::X, state, Length::zero());
149 let y = node.convert_user_length(AId::Y, state, Length::zero());
150 new_ts = new_ts.pre_translate(x, y);
151
152 if let Some(ts) = viewbox_transform(node, node, state) {
153 new_ts = new_ts.pre_concat(ts);
154 }
155
156 let mut new_state = state.clone();
159 new_state.view_box = {
160 if let Some(vb) = node.parse_viewbox() {
161 vb
162 } else {
163 let (mut w, mut h) = use_node_size(node, &state);
165
166 w = state.use_size.0.unwrap_or(w);
170 h = state.use_size.1.unwrap_or(h);
171
172 NonZeroRect::from_xywh(x, y, w, h).unwrap_or(state.view_box)
173 }
174 };
175
176 if let Some(clip_rect) = get_clip_rect(node, node, state) {
177 let mut g = clip_element(node, clip_rect, orig_ts, state, cache);
178 g.abs_transform = parent.abs_transform;
179 convert_children(node, new_ts, &new_state, cache, false, &mut g);
180 g.calculate_bounding_boxes();
181 parent.children.push(Node::Group(Box::new(g)));
182 } else {
183 orig_ts = orig_ts.pre_concat(new_ts);
184 convert_children(node, orig_ts, &new_state, cache, false, parent);
185 }
186}
187
188fn clip_element(
189 node: SvgNode,
190 clip_rect: NonZeroRect,
191 transform: Transform,
192 state: &converter::State,
193 cache: &mut converter::Cache,
194) -> Group {
195 let mut clip_path = crate::ClipPath::empty(cache.gen_clip_path_id());
216
217 let mut path = Path::new_simple(Arc::new(tiny_skia_path::PathBuilder::from_rect(
218 clip_rect.to_rect(),
219 )))
220 .unwrap();
221 path.fill = Some(crate::Fill::default());
222 clip_path.root.children.push(Node::Path(Box::new(path)));
223
224 let id = if state.parent_markers.is_empty() {
226 node.element_id().to_string()
227 } else {
228 String::new()
229 };
230
231 Group {
232 id,
233 transform,
234 clip_path: Some(Arc::new(clip_path)),
235 ..Group::empty()
236 }
237}
238
239fn convert_children(
240 node: SvgNode,
241 transform: Transform,
242 state: &converter::State,
243 cache: &mut converter::Cache,
244 is_context_element: bool,
245 parent: &mut Group,
246) {
247 let old_abs_transform = parent.abs_transform;
249 parent.abs_transform = parent.abs_transform.pre_concat(transform);
250
251 let required = !transform.is_identity();
252 if let Some(mut g) =
253 converter::convert_group(node, state, required, cache, parent, &|cache, g| {
254 if state.parent_clip_path.is_some() {
255 converter::convert_clip_path_elements(node, state, cache, g);
256 } else {
257 converter::convert_children(node, state, cache, g);
258 }
259 })
260 {
261 g.is_context_element = is_context_element;
262 g.transform = transform;
263 parent.children.push(Node::Group(Box::new(g)));
264 }
265
266 parent.abs_transform = old_abs_transform;
267}
268
269fn get_clip_rect(
270 use_node: SvgNode,
271 symbol_node: SvgNode,
272 state: &converter::State,
273) -> Option<NonZeroRect> {
274 if matches!(
276 symbol_node.attribute(AId::Overflow),
277 Some("visible") | Some("auto")
278 ) {
279 return None;
280 }
281
282 if use_node.tag_name() == Some(EId::Svg) {
285 if state.use_size.0.is_none() && state.use_size.1.is_none() {
287 if !(use_node.has_attribute(AId::Width) && use_node.has_attribute(AId::Height)) {
288 return None;
289 }
290 }
291 }
292
293 let (x, y, mut w, mut h) = {
294 let x = use_node.convert_user_length(AId::X, state, Length::zero());
295 let y = use_node.convert_user_length(AId::Y, state, Length::zero());
296 let (w, h) = use_node_size(use_node, state);
297 (x, y, w, h)
298 };
299
300 if use_node.tag_name() == Some(EId::Svg) {
301 w = state.use_size.0.unwrap_or(w);
305 h = state.use_size.1.unwrap_or(h);
306 }
307
308 if !w.is_valid_length() || !h.is_valid_length() {
309 return None;
310 }
311
312 NonZeroRect::from_xywh(x, y, w, h)
313}
314
315fn use_node_size(node: SvgNode, state: &converter::State) -> (f32, f32) {
316 let def = Length::new(100.0, LengthUnit::Percent);
317 let w = node.convert_user_length(AId::Width, state, def);
318 let h = node.convert_user_length(AId::Height, state, def);
319 (w, h)
320}
321
322fn viewbox_transform(
323 node: SvgNode,
324 linked: SvgNode,
325 state: &converter::State,
326) -> Option<Transform> {
327 let (mut w, mut h) = use_node_size(node, state);
328
329 if node.tag_name() == Some(EId::Svg) {
330 w = state.use_size.0.unwrap_or(w);
334 h = state.use_size.1.unwrap_or(h);
335 }
336
337 let size = Size::from_wh(w, h)?;
338 let rect = linked.parse_viewbox()?;
339 let aspect = linked
340 .attribute(AId::PreserveAspectRatio)
341 .unwrap_or_default();
342 let view_box = ViewBox { rect, aspect };
343
344 Some(view_box.to_transform(size))
345}