1use std::collections::{HashMap, HashSet};
5use std::hash::{Hash, Hasher};
6use std::str::FromStr;
7use std::sync::Arc;
8
9#[cfg(feature = "text")]
10use fontdb::Database;
11use svgtypes::{Length, LengthUnit as Unit, PaintOrderKind, TransformOrigin};
12use tiny_skia_path::PathBuilder;
13
14use super::svgtree::{self, AId, EId, FromValue, SvgNode};
15use super::units::{self, convert_length};
16use super::{marker, Error, Options};
17use crate::parser::paint_server::process_paint;
18use crate::*;
19
20#[derive(Clone)]
21pub struct State<'a> {
22 pub(crate) parent_clip_path: Option<SvgNode<'a, 'a>>,
23 pub(crate) parent_markers: Vec<SvgNode<'a, 'a>>,
24 pub(crate) context_element: Option<(Option<Fill>, Option<Stroke>)>,
27 pub(crate) fe_image_link: bool,
28 pub(crate) view_box: NonZeroRect,
30 pub(crate) use_size: (Option<f32>, Option<f32>),
34 pub(crate) opt: &'a Options<'a>,
35}
36
37#[derive(Clone)]
38pub struct Cache {
39 #[cfg(feature = "text")]
42 pub fontdb: Arc<Database>,
43
44 pub clip_paths: HashMap<String, Arc<ClipPath>>,
45 pub masks: HashMap<String, Arc<Mask>>,
46 pub filters: HashMap<String, Arc<filter::Filter>>,
47 pub paint: HashMap<String, Paint>,
48
49 all_ids: HashSet<u64>,
51 linear_gradient_index: usize,
52 radial_gradient_index: usize,
53 pattern_index: usize,
54 clip_path_index: usize,
55 mask_index: usize,
56 filter_index: usize,
57 image_index: usize,
58}
59
60impl Cache {
61 pub(crate) fn new(#[cfg(feature = "text")] fontdb: Arc<Database>) -> Self {
62 Self {
63 #[cfg(feature = "text")]
64 fontdb,
65
66 clip_paths: HashMap::new(),
67 masks: HashMap::new(),
68 filters: HashMap::new(),
69 paint: HashMap::new(),
70
71 all_ids: HashSet::new(),
72 linear_gradient_index: 0,
73 radial_gradient_index: 0,
74 pattern_index: 0,
75 clip_path_index: 0,
76 mask_index: 0,
77 filter_index: 0,
78 image_index: 0,
79 }
80 }
81
82 pub(crate) fn gen_linear_gradient_id(&mut self) -> NonEmptyString {
84 loop {
85 self.linear_gradient_index += 1;
86 let new_id = format!("linearGradient{}", self.linear_gradient_index);
87 let new_hash = string_hash(&new_id);
88 if !self.all_ids.contains(&new_hash) {
89 return NonEmptyString::new(new_id).unwrap();
90 }
91 }
92 }
93
94 pub(crate) fn gen_radial_gradient_id(&mut self) -> NonEmptyString {
95 loop {
96 self.radial_gradient_index += 1;
97 let new_id = format!("radialGradient{}", self.radial_gradient_index);
98 let new_hash = string_hash(&new_id);
99 if !self.all_ids.contains(&new_hash) {
100 return NonEmptyString::new(new_id).unwrap();
101 }
102 }
103 }
104
105 pub(crate) fn gen_pattern_id(&mut self) -> NonEmptyString {
106 loop {
107 self.pattern_index += 1;
108 let new_id = format!("pattern{}", self.pattern_index);
109 let new_hash = string_hash(&new_id);
110 if !self.all_ids.contains(&new_hash) {
111 return NonEmptyString::new(new_id).unwrap();
112 }
113 }
114 }
115
116 pub(crate) fn gen_clip_path_id(&mut self) -> NonEmptyString {
117 loop {
118 self.clip_path_index += 1;
119 let new_id = format!("clipPath{}", self.clip_path_index);
120 let new_hash = string_hash(&new_id);
121 if !self.all_ids.contains(&new_hash) {
122 return NonEmptyString::new(new_id).unwrap();
123 }
124 }
125 }
126
127 pub(crate) fn gen_mask_id(&mut self) -> NonEmptyString {
128 loop {
129 self.mask_index += 1;
130 let new_id = format!("mask{}", self.mask_index);
131 let new_hash = string_hash(&new_id);
132 if !self.all_ids.contains(&new_hash) {
133 return NonEmptyString::new(new_id).unwrap();
134 }
135 }
136 }
137
138 pub(crate) fn gen_filter_id(&mut self) -> NonEmptyString {
139 loop {
140 self.filter_index += 1;
141 let new_id = format!("filter{}", self.filter_index);
142 let new_hash = string_hash(&new_id);
143 if !self.all_ids.contains(&new_hash) {
144 return NonEmptyString::new(new_id).unwrap();
145 }
146 }
147 }
148
149 pub(crate) fn gen_image_id(&mut self) -> NonEmptyString {
150 loop {
151 self.image_index += 1;
152 let new_id = format!("image{}", self.image_index);
153 let new_hash = string_hash(&new_id);
154 if !self.all_ids.contains(&new_hash) {
155 return NonEmptyString::new(new_id).unwrap();
156 }
157 }
158 }
159}
160
161fn string_hash(s: &str) -> u64 {
163 let mut h = std::collections::hash_map::DefaultHasher::new();
164 s.hash(&mut h);
165 h.finish()
166}
167
168impl<'a, 'input: 'a> SvgNode<'a, 'input> {
169 pub(crate) fn convert_length(
170 &self,
171 aid: AId,
172 object_units: Units,
173 state: &State,
174 def: Length,
175 ) -> f32 {
176 units::convert_length(
177 self.attribute(aid).unwrap_or(def),
178 *self,
179 aid,
180 object_units,
181 state,
182 )
183 }
184
185 pub fn convert_user_length(&self, aid: AId, state: &State, def: Length) -> f32 {
186 self.convert_length(aid, Units::UserSpaceOnUse, state, def)
187 }
188
189 pub fn parse_viewbox(&self) -> Option<NonZeroRect> {
190 let vb: svgtypes::ViewBox = self.attribute(AId::ViewBox)?;
191 NonZeroRect::from_xywh(vb.x as f32, vb.y as f32, vb.w as f32, vb.h as f32)
192 }
193
194 pub fn resolve_length(&self, aid: AId, state: &State, def: f32) -> f32 {
195 debug_assert!(
196 !matches!(aid, AId::BaselineShift | AId::FontSize),
197 "{} cannot be resolved via this function",
198 aid
199 );
200
201 if let Some(n) = self.ancestors().find(|n| n.has_attribute(aid)) {
202 if let Some(length) = n.attribute(aid) {
203 return units::convert_user_length(length, n, aid, state);
204 }
205 }
206
207 def
208 }
209
210 pub fn resolve_valid_length(
211 &self,
212 aid: AId,
213 state: &State,
214 def: f32,
215 ) -> Option<NonZeroPositiveF32> {
216 let n = self.resolve_length(aid, state, def);
217 NonZeroPositiveF32::new(n)
218 }
219
220 pub(crate) fn try_convert_length(
221 &self,
222 aid: AId,
223 object_units: Units,
224 state: &State,
225 ) -> Option<f32> {
226 Some(units::convert_length(
227 self.attribute(aid)?,
228 *self,
229 aid,
230 object_units,
231 state,
232 ))
233 }
234
235 pub fn has_valid_transform(&self, aid: AId) -> bool {
236 let attr = match self.attribute(aid) {
240 Some(attr) => attr,
241 None => return true,
242 };
243
244 let ts = match svgtypes::Transform::from_str(attr) {
245 Ok(v) => v,
246 Err(_) => return true,
247 };
248
249 let ts = Transform::from_row(
250 ts.a as f32,
251 ts.b as f32,
252 ts.c as f32,
253 ts.d as f32,
254 ts.e as f32,
255 ts.f as f32,
256 );
257 ts.is_valid()
258 }
259
260 pub fn is_visible_element(&self, opt: &crate::Options) -> bool {
261 self.attribute(AId::Display) != Some("none")
262 && self.has_valid_transform(AId::Transform)
263 && super::switch::is_condition_passed(*self, opt)
264 }
265}
266
267pub trait SvgColorExt {
268 fn split_alpha(self) -> (Color, Opacity);
269}
270
271impl SvgColorExt for svgtypes::Color {
272 fn split_alpha(self) -> (Color, Opacity) {
273 (
274 Color::new_rgb(self.red, self.green, self.blue),
275 Opacity::new_u8(self.alpha),
276 )
277 }
278}
279
280pub(crate) fn convert_doc(svg_doc: &svgtree::Document, opt: &Options) -> Result<Tree, Error> {
287 let svg = svg_doc.root_element();
288 let (size, restore_viewbox) = resolve_svg_size(&svg, opt);
289 let size = size?;
290 let view_box = ViewBox {
291 rect: svg
292 .parse_viewbox()
293 .unwrap_or_else(|| size.to_non_zero_rect(0.0, 0.0)),
294 aspect: svg.attribute(AId::PreserveAspectRatio).unwrap_or_default(),
295 };
296
297 let background_color = svg
298 .attribute::<&str>(AId::BackgroundColor)
299 .and_then(|s| svgtypes::Paint::from_str(s).ok())
300 .and_then(|paint| match paint {
301 svgtypes::Paint::Color(c) => Some(c),
302 _ => None,
303 });
304
305 let mut tree = Tree {
306 size,
307 root: Group::empty(),
308 linear_gradients: Vec::new(),
309 radial_gradients: Vec::new(),
310 patterns: Vec::new(),
311 clip_paths: Vec::new(),
312 masks: Vec::new(),
313 filters: Vec::new(),
314 #[cfg(feature = "text")]
315 fontdb: opt.fontdb.clone(),
316 };
317
318 if !svg.is_visible_element(opt) {
319 return Ok(tree);
320 }
321
322 let state = State {
323 parent_clip_path: None,
324 context_element: None,
325 parent_markers: Vec::new(),
326 fe_image_link: false,
327 view_box: view_box.rect,
328 use_size: (None, None),
329 opt,
330 };
331
332 let mut cache = Cache::new(
333 #[cfg(feature = "text")]
334 opt.fontdb.clone(),
335 );
336
337 for node in svg_doc.descendants() {
338 if let Some(tag) = node.tag_name() {
339 if matches!(
340 tag,
341 EId::ClipPath
342 | EId::Filter
343 | EId::LinearGradient
344 | EId::Mask
345 | EId::Pattern
346 | EId::RadialGradient
347 | EId::Image
348 ) {
349 if !node.element_id().is_empty() {
350 cache.all_ids.insert(string_hash(node.element_id()));
351 }
352 }
353 }
354 }
355
356 let root_ts = view_box.to_transform(tree.size());
357 if root_ts.is_identity() && background_color.is_none() {
358 convert_children(svg_doc.root(), &state, &mut cache, &mut tree.root);
359 } else {
360 let mut g = Group::empty();
361
362 if let Some(background_color) = background_color {
363 if let Some(path) = background_path(background_color, view_box.rect.to_rect()) {
364 g.children.push(Node::Path(Box::new(path)));
365 }
366 }
367
368 g.transform = root_ts;
369 g.abs_transform = root_ts;
370 convert_children(svg_doc.root(), &state, &mut cache, &mut g);
371 g.calculate_bounding_boxes();
372 tree.root.children.push(Node::Group(Box::new(g)));
373 }
374
375 cache.clip_paths.clear();
377 cache.masks.clear();
378 cache.filters.clear();
379 cache.paint.clear();
380
381 super::paint_server::update_paint_servers(
382 &mut tree.root,
383 Transform::default(),
384 None,
385 None,
386 &mut cache,
387 );
388 tree.collect_paint_servers();
389 tree.root.collect_clip_paths(&mut tree.clip_paths);
390 tree.root.collect_masks(&mut tree.masks);
391 tree.root.collect_filters(&mut tree.filters);
392 tree.root.calculate_bounding_boxes();
393
394 #[cfg(feature = "text")]
397 {
398 tree.fontdb = cache.fontdb;
399 }
400
401 if restore_viewbox {
402 calculate_svg_bbox(&mut tree);
403 }
404
405 Ok(tree)
406}
407
408fn background_path(background_color: svgtypes::Color, area: Rect) -> Option<Path> {
409 let path = PathBuilder::from_rect(area);
410
411 let fill = Fill {
412 paint: Paint::Color(Color::new_rgb(
413 background_color.red,
414 background_color.green,
415 background_color.blue,
416 )),
417 opacity: NormalizedF32::new(background_color.alpha as f32 / 255.0)?,
418 ..Default::default()
419 };
420
421 let mut path = Path::new_simple(Arc::new(path))?;
422 path.fill = Some(fill);
423
424 Some(path)
425}
426
427fn resolve_svg_size(svg: &SvgNode, opt: &Options) -> (Result<Size, Error>, bool) {
428 let mut state = State {
429 parent_clip_path: None,
430 context_element: None,
431 parent_markers: Vec::new(),
432 fe_image_link: false,
433 view_box: NonZeroRect::from_xywh(0.0, 0.0, 100.0, 100.0).unwrap(),
434 use_size: (None, None),
435 opt,
436 };
437
438 let def = Length::new(100.0, Unit::Percent);
439 let mut width: Length = svg.attribute(AId::Width).unwrap_or(def);
440 let mut height: Length = svg.attribute(AId::Height).unwrap_or(def);
441
442 let view_box = svg.parse_viewbox();
443
444 let restore_viewbox =
445 if (width.unit == Unit::Percent || height.unit == Unit::Percent) && view_box.is_none() {
446 if width.unit == Unit::Percent {
448 width = Length::new(
449 (width.number / 100.0) * state.opt.default_size.width() as f64,
450 Unit::None,
451 );
452 }
453
454 if height.unit == Unit::Percent {
455 height = Length::new(
456 (height.number / 100.0) * state.opt.default_size.height() as f64,
457 Unit::None,
458 );
459 }
460
461 true
462 } else {
463 false
464 };
465
466 let size = if let Some(vbox) = view_box {
467 state.view_box = vbox;
468
469 let w = if width.unit == Unit::Percent {
470 vbox.width() * (width.number as f32 / 100.0)
471 } else {
472 svg.convert_user_length(AId::Width, &state, def)
473 };
474
475 let h = if height.unit == Unit::Percent {
476 vbox.height() * (height.number as f32 / 100.0)
477 } else {
478 svg.convert_user_length(AId::Height, &state, def)
479 };
480
481 Size::from_wh(w, h)
482 } else {
483 Size::from_wh(
484 svg.convert_user_length(AId::Width, &state, def),
485 svg.convert_user_length(AId::Height, &state, def),
486 )
487 };
488
489 (size.ok_or(Error::InvalidSize), restore_viewbox)
490}
491
492fn calculate_svg_bbox(tree: &mut Tree) {
496 let bbox = tree.root.abs_bounding_box();
497 if let Some(size) = Size::from_wh(bbox.right(), bbox.bottom()) {
498 tree.size = size;
499 }
500}
501
502#[inline(never)]
503pub(crate) fn convert_children(
504 parent_node: SvgNode,
505 state: &State,
506 cache: &mut Cache,
507 parent: &mut Group,
508) {
509 for node in parent_node.children() {
510 convert_element(node, state, cache, parent);
511 }
512}
513
514#[inline(never)]
515pub(crate) fn convert_element(node: SvgNode, state: &State, cache: &mut Cache, parent: &mut Group) {
516 let tag_name = match node.tag_name() {
517 Some(v) => v,
518 None => return,
519 };
520
521 if !tag_name.is_graphic() && !matches!(tag_name, EId::G | EId::Switch | EId::Svg) {
522 return;
523 }
524
525 if !node.is_visible_element(state.opt) {
526 return;
527 }
528
529 if tag_name == EId::Use {
530 super::use_node::convert(node, state, cache, parent);
531 return;
532 }
533
534 if tag_name == EId::Switch {
535 super::switch::convert(node, state, cache, parent);
536 return;
537 }
538
539 if let Some(g) = convert_group(node, state, false, cache, parent, &|cache, g| {
540 convert_element_impl(tag_name, node, state, cache, g);
541 }) {
542 parent.children.push(Node::Group(Box::new(g)));
543 }
544}
545
546#[inline(never)]
547fn convert_element_impl(
548 tag_name: EId,
549 node: SvgNode,
550 state: &State,
551 cache: &mut Cache,
552 parent: &mut Group,
553) {
554 match tag_name {
555 EId::Rect
556 | EId::Circle
557 | EId::Ellipse
558 | EId::Line
559 | EId::Polyline
560 | EId::Polygon
561 | EId::Path => {
562 if let Some(path) = super::shapes::convert(node, state) {
563 convert_path(node, path, state, cache, parent);
564 }
565 }
566 EId::Image => {
567 super::image::convert(node, state, cache, parent);
568 }
569 EId::Text => {
570 #[cfg(feature = "text")]
571 {
572 super::text::convert(node, state, cache, parent);
573 }
574 }
575 EId::Svg => {
576 if node.parent_element().is_some() {
577 super::use_node::convert_svg(node, state, cache, parent);
578 } else {
579 convert_children(node, state, cache, parent);
581 }
582 }
583 EId::G => {
584 convert_children(node, state, cache, parent);
585 }
586 _ => {}
587 }
588}
589
590#[inline(never)]
595pub(crate) fn convert_clip_path_elements(
596 clip_node: SvgNode,
597 state: &State,
598 cache: &mut Cache,
599 parent: &mut Group,
600) {
601 for node in clip_node.children() {
602 let tag_name = match node.tag_name() {
603 Some(v) => v,
604 None => continue,
605 };
606
607 if !tag_name.is_graphic() {
608 continue;
609 }
610
611 if !node.is_visible_element(state.opt) {
612 continue;
613 }
614
615 if tag_name == EId::Use {
616 super::use_node::convert(node, state, cache, parent);
617 continue;
618 }
619
620 if let Some(g) = convert_group(node, state, false, cache, parent, &|cache, g| {
621 convert_clip_path_elements_impl(tag_name, node, state, cache, g);
622 }) {
623 parent.children.push(Node::Group(Box::new(g)));
624 }
625 }
626}
627
628#[inline(never)]
629fn convert_clip_path_elements_impl(
630 tag_name: EId,
631 node: SvgNode,
632 state: &State,
633 cache: &mut Cache,
634 parent: &mut Group,
635) {
636 match tag_name {
637 EId::Rect | EId::Circle | EId::Ellipse | EId::Polyline | EId::Polygon | EId::Path => {
638 if let Some(path) = super::shapes::convert(node, state) {
639 convert_path(node, path, state, cache, parent);
640 }
641 }
642 EId::Text => {
643 #[cfg(feature = "text")]
644 {
645 super::text::convert(node, state, cache, parent);
646 }
647 }
648 _ => {
649 log::warn!("'{}' is no a valid 'clip-path' child.", tag_name);
650 }
651 }
652}
653
654#[derive(Clone, Copy, PartialEq, Debug)]
655enum Isolation {
656 Auto,
657 Isolate,
658}
659
660impl Default for Isolation {
661 fn default() -> Self {
662 Self::Auto
663 }
664}
665
666impl<'a, 'input: 'a> FromValue<'a, 'input> for Isolation {
667 fn parse(_: SvgNode, _: AId, value: &str) -> Option<Self> {
668 match value {
669 "auto" => Some(Isolation::Auto),
670 "isolate" => Some(Isolation::Isolate),
671 _ => None,
672 }
673 }
674}
675
676pub(crate) fn convert_group(
678 node: SvgNode,
679 state: &State,
680 force: bool,
681 cache: &mut Cache,
682 parent: &mut Group,
683 collect_children: &dyn Fn(&mut Cache, &mut Group),
684) -> Option<Group> {
685 let opacity = if state.parent_clip_path.is_none() {
687 node.attribute::<Opacity>(AId::Opacity)
688 .unwrap_or(Opacity::ONE)
689 } else {
690 Opacity::ONE
691 };
692
693 let transform = node.resolve_transform(AId::Transform, state);
694 let blend_mode: BlendMode = node.attribute(AId::MixBlendMode).unwrap_or_default();
695 let isolation: Isolation = node.attribute(AId::Isolation).unwrap_or_default();
696 let isolate = isolation == Isolation::Isolate;
697
698 let is_g_or_use = matches!(node.tag_name(), Some(EId::G) | Some(EId::Use));
700 let id = if is_g_or_use && state.parent_markers.is_empty() {
701 node.element_id().to_string()
702 } else {
703 String::new()
704 };
705
706 let abs_transform = parent.abs_transform.pre_concat(transform);
707 let dummy = Rect::from_xywh(0.0, 0.0, 0.0, 0.0).unwrap();
708 let mut g = Group {
709 id,
710 transform,
711 abs_transform,
712 opacity,
713 blend_mode,
714 isolate,
715 clip_path: None,
716 mask: None,
717 filters: Vec::new(),
718 is_context_element: false,
719 bounding_box: dummy,
720 abs_bounding_box: dummy,
721 stroke_bounding_box: dummy,
722 abs_stroke_bounding_box: dummy,
723 layer_bounding_box: NonZeroRect::from_xywh(0.0, 0.0, 1.0, 1.0).unwrap(),
724 abs_layer_bounding_box: NonZeroRect::from_xywh(0.0, 0.0, 1.0, 1.0).unwrap(),
725 children: Vec::new(),
726 };
727 collect_children(cache, &mut g);
728
729 let object_bbox = g.calculate_object_bbox();
732
733 let mut clip_path = None;
737 if let Some(link) = node.attribute::<SvgNode>(AId::ClipPath) {
738 clip_path = super::clippath::convert(link, state, object_bbox, cache);
739 if clip_path.is_none() {
740 return None;
741 }
742 }
743
744 let mut mask = None;
745 if state.parent_clip_path.is_none() {
746 if let Some(link) = node.attribute::<SvgNode>(AId::Mask) {
747 mask = super::mask::convert(link, state, object_bbox, cache);
748 if mask.is_none() {
749 return None;
750 }
751 }
752 }
753
754 let filters = {
755 let mut filters = Vec::new();
756 if state.parent_clip_path.is_none() {
757 if node.attribute(AId::Filter) == Some("none") {
758 } else if node.has_attribute(AId::Filter) {
760 if let Ok(f) = super::filter::convert(node, state, object_bbox, cache) {
761 filters = f;
762 } else {
763 return None;
776 }
777 }
778 }
779
780 filters
781 };
782
783 let required = opacity.get().approx_ne_ulps(&1.0, 4)
784 || clip_path.is_some()
785 || mask.is_some()
786 || !filters.is_empty()
787 || !transform.is_identity()
788 || blend_mode != BlendMode::Normal
789 || isolate
790 || is_g_or_use
791 || force;
792
793 if !required {
794 parent.children.append(&mut g.children);
795 return None;
796 }
797
798 g.clip_path = clip_path;
799 g.mask = mask;
800 g.filters = filters;
801
802 g.calculate_bounding_boxes();
804
805 Some(g)
806}
807
808fn convert_path(
809 node: SvgNode,
810 tiny_skia_path: Arc<tiny_skia_path::Path>,
811 state: &State,
812 cache: &mut Cache,
813 parent: &mut Group,
814) {
815 debug_assert!(tiny_skia_path.len() >= 2);
816 if tiny_skia_path.len() < 2 {
817 return;
818 }
819
820 let has_bbox = tiny_skia_path.bounds().width() > 0.0 && tiny_skia_path.bounds().height() > 0.0;
821 let mut fill = super::style::resolve_fill(node, has_bbox, state, cache);
822 let mut stroke = super::style::resolve_stroke(node, has_bbox, state, cache);
823 let visibility: Visibility = node.find_attribute(AId::Visibility).unwrap_or_default();
824 let mut visible = visibility == Visibility::Visible;
825 let rendering_mode: ShapeRendering = node
826 .find_attribute(AId::ShapeRendering)
827 .unwrap_or(state.opt.shape_rendering);
828
829 let raw_paint_order: svgtypes::PaintOrder =
831 node.find_attribute(AId::PaintOrder).unwrap_or_default();
832 let paint_order = svg_paint_order_to_usvg(raw_paint_order);
833 let path_transform = parent.abs_transform;
834
835 if fill.is_none() && stroke.is_none() {
838 visible = false;
839 }
840
841 if let Some(fill) = fill.as_mut() {
842 if let Some(ContextElement::PathNode(context_transform, context_bbox)) =
843 fill.context_element
844 {
845 process_paint(
846 &mut fill.paint,
847 true,
848 context_transform,
849 context_bbox.map(|r| r.to_rect()),
850 path_transform,
851 tiny_skia_path.bounds(),
852 cache,
853 );
854 fill.context_element = None;
855 }
856 }
857
858 if let Some(stroke) = stroke.as_mut() {
859 if let Some(ContextElement::PathNode(context_transform, context_bbox)) =
860 stroke.context_element
861 {
862 process_paint(
863 &mut stroke.paint,
864 true,
865 context_transform,
866 context_bbox.map(|r| r.to_rect()),
867 path_transform,
868 tiny_skia_path.bounds(),
869 cache,
870 );
871 stroke.context_element = None;
872 }
873 }
874
875 let mut marker = None;
876 if marker::is_valid(node) && visibility == Visibility::Visible {
877 let mut marker_group = Group {
878 abs_transform: parent.abs_transform,
879 ..Group::empty()
880 };
881
882 let mut marker_state = state.clone();
883
884 let bbox = tiny_skia_path
885 .compute_tight_bounds()
886 .and_then(|r| r.to_non_zero_rect());
887
888 let fill = fill.clone().map(|mut f| {
889 f.context_element = Some(ContextElement::PathNode(path_transform, bbox));
890 f
891 });
892
893 let stroke = stroke.clone().map(|mut s| {
894 s.context_element = Some(ContextElement::PathNode(path_transform, bbox));
895 s
896 });
897
898 marker_state.context_element = Some((fill, stroke));
899
900 marker::convert(
901 node,
902 &tiny_skia_path,
903 &marker_state,
904 cache,
905 &mut marker_group,
906 );
907 marker_group.calculate_bounding_boxes();
908 marker = Some(marker_group);
909 }
910
911 let id = if state.parent_markers.is_empty() {
913 node.element_id().to_string()
914 } else {
915 String::new()
916 };
917
918 let path = Path::new(
919 id,
920 visible,
921 fill,
922 stroke,
923 paint_order,
924 rendering_mode,
925 tiny_skia_path,
926 path_transform,
927 );
928
929 let path = match path {
930 Some(v) => v,
931 None => return,
932 };
933
934 match (raw_paint_order.order, marker) {
935 ([PaintOrderKind::Markers, _, _], Some(markers_node)) => {
936 parent.children.push(Node::Group(Box::new(markers_node)));
937 parent.children.push(Node::Path(Box::new(path.clone())));
938 }
939 ([first, PaintOrderKind::Markers, last], Some(markers_node)) => {
940 append_single_paint_path(first, &path, parent);
941 parent.children.push(Node::Group(Box::new(markers_node)));
942 append_single_paint_path(last, &path, parent);
943 }
944 ([_, _, PaintOrderKind::Markers], Some(markers_node)) => {
945 parent.children.push(Node::Path(Box::new(path.clone())));
946 parent.children.push(Node::Group(Box::new(markers_node)));
947 }
948 _ => parent.children.push(Node::Path(Box::new(path.clone()))),
949 }
950}
951
952fn append_single_paint_path(paint_order_kind: PaintOrderKind, path: &Path, parent: &mut Group) {
953 match paint_order_kind {
954 PaintOrderKind::Fill => {
955 if path.fill.is_some() {
956 let mut fill_path = path.clone();
957 fill_path.stroke = None;
958 fill_path.id = String::new();
959 parent.children.push(Node::Path(Box::new(fill_path)));
960 }
961 }
962 PaintOrderKind::Stroke => {
963 if path.stroke.is_some() {
964 let mut stroke_path = path.clone();
965 stroke_path.fill = None;
966 stroke_path.id = String::new();
967 parent.children.push(Node::Path(Box::new(stroke_path)));
968 }
969 }
970 _ => {}
971 }
972}
973
974pub fn svg_paint_order_to_usvg(order: svgtypes::PaintOrder) -> PaintOrder {
975 match (order.order[0], order.order[1]) {
976 (svgtypes::PaintOrderKind::Stroke, _) => PaintOrder::StrokeAndFill,
977 (svgtypes::PaintOrderKind::Markers, svgtypes::PaintOrderKind::Stroke) => {
978 PaintOrder::StrokeAndFill
979 }
980 _ => PaintOrder::FillAndStroke,
981 }
982}
983
984impl SvgNode<'_, '_> {
985 pub(crate) fn resolve_transform(&self, transform_aid: AId, state: &State) -> Transform {
986 let mut transform: Transform = self.attribute(transform_aid).unwrap_or_default();
987 let transform_origin: Option<TransformOrigin> = self.attribute(AId::TransformOrigin);
988
989 if let Some(transform_origin) = transform_origin {
990 let dx = convert_length(
991 transform_origin.x_offset,
992 *self,
993 AId::Width,
994 Units::UserSpaceOnUse,
995 state,
996 );
997 let dy = convert_length(
998 transform_origin.y_offset,
999 *self,
1000 AId::Height,
1001 Units::UserSpaceOnUse,
1002 state,
1003 );
1004 transform = Transform::default()
1005 .pre_translate(dx, dy)
1006 .pre_concat(transform)
1007 .pre_translate(-dx, -dy);
1008 }
1009
1010 transform
1011 }
1012}