1use std::collections::HashMap;
6use std::num::NonZeroU32;
7use std::str::FromStr;
8
9#[rustfmt::skip] mod names;
10mod parse;
11mod text;
12
13pub use names::{AId, EId};
14
15pub struct Document<'input> {
20 nodes: Vec<NodeData>,
21 attrs: Vec<Attribute<'input>>,
22 links: HashMap<String, NodeId>,
23}
24
25impl<'input> Document<'input> {
26 #[inline]
28 pub fn root<'a>(&'a self) -> SvgNode<'a, 'input> {
29 SvgNode {
30 id: NodeId::new(0),
31 d: &self.nodes[0],
32 doc: self,
33 }
34 }
35
36 #[inline]
38 pub fn root_element<'a>(&'a self) -> SvgNode<'a, 'input> {
39 self.root().first_element_child().unwrap()
41 }
42
43 #[inline]
47 pub fn descendants<'a>(&'a self) -> Descendants<'a, 'input> {
48 self.root().descendants()
49 }
50
51 #[inline]
56 pub fn element_by_id<'a>(&'a self, id: &str) -> Option<SvgNode<'a, 'input>> {
57 let node_id = self.links.get(id)?;
58 Some(self.get(*node_id))
59 }
60
61 #[inline]
62 fn get<'a>(&'a self, id: NodeId) -> SvgNode<'a, 'input> {
63 SvgNode {
64 id,
65 d: &self.nodes[id.get_usize()],
66 doc: self,
67 }
68 }
69}
70
71impl std::fmt::Debug for Document<'_> {
72 fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
73 if !self.root().has_children() {
74 return write!(f, "Document []");
75 }
76
77 macro_rules! writeln_indented {
78 ($depth:expr, $f:expr, $fmt:expr) => {
79 for _ in 0..$depth { write!($f, " ")?; }
80 writeln!($f, $fmt)?;
81 };
82 ($depth:expr, $f:expr, $fmt:expr, $($arg:tt)*) => {
83 for _ in 0..$depth { write!($f, " ")?; }
84 writeln!($f, $fmt, $($arg)*)?;
85 };
86 }
87
88 fn print_children(
89 parent: SvgNode,
90 depth: usize,
91 f: &mut std::fmt::Formatter,
92 ) -> Result<(), std::fmt::Error> {
93 for child in parent.children() {
94 if child.is_element() {
95 writeln_indented!(depth, f, "Element {{");
96 writeln_indented!(depth, f, " tag_name: {:?}", child.tag_name());
97
98 if !child.attributes().is_empty() {
99 writeln_indented!(depth + 1, f, "attributes: [");
100 for attr in child.attributes() {
101 writeln_indented!(depth + 2, f, "{:?}", attr);
102 }
103 writeln_indented!(depth + 1, f, "]");
104 }
105
106 if child.has_children() {
107 writeln_indented!(depth, f, " children: [");
108 print_children(child, depth + 2, f)?;
109 writeln_indented!(depth, f, " ]");
110 }
111
112 writeln_indented!(depth, f, "}}");
113 } else {
114 writeln_indented!(depth, f, "{:?}", child);
115 }
116 }
117
118 Ok(())
119 }
120
121 writeln!(f, "Document [")?;
122 print_children(self.root(), 1, f)?;
123 writeln!(f, "]")?;
124
125 Ok(())
126 }
127}
128
129#[derive(Clone, Copy, Debug)]
130pub(crate) struct ShortRange {
131 start: u32,
132 end: u32,
133}
134
135impl ShortRange {
136 #[inline]
137 fn new(start: u32, end: u32) -> Self {
138 ShortRange { start, end }
139 }
140
141 #[inline]
142 fn to_urange(self) -> std::ops::Range<usize> {
143 self.start as usize..self.end as usize
144 }
145}
146
147#[derive(Clone, Copy, PartialEq, Debug)]
148pub(crate) struct NodeId(NonZeroU32);
149
150impl NodeId {
151 #[inline]
152 fn new(id: u32) -> Self {
153 debug_assert!(id < core::u32::MAX);
154
155 NodeId(NonZeroU32::new(id + 1).unwrap())
157 }
158
159 #[inline]
160 fn get(self) -> u32 {
161 self.0.get() - 1
162 }
163
164 #[inline]
165 fn get_usize(self) -> usize {
166 self.get() as usize
167 }
168}
169
170impl From<usize> for NodeId {
171 #[inline]
172 fn from(id: usize) -> Self {
173 debug_assert!(id <= core::u32::MAX as usize);
175 NodeId::new(id as u32)
176 }
177}
178
179pub(crate) enum NodeKind {
180 Root,
181 Element {
182 tag_name: EId,
183 attributes: ShortRange,
184 },
185 Text(String),
186}
187
188struct NodeData {
189 parent: Option<NodeId>,
190 next_sibling: Option<NodeId>,
191 children: Option<(NodeId, NodeId)>,
192 kind: NodeKind,
193}
194
195#[derive(Clone)]
197pub struct Attribute<'input> {
198 pub name: AId,
200 pub value: roxmltree::StringStorage<'input>,
202}
203
204impl std::fmt::Debug for Attribute<'_> {
205 fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
206 write!(
207 f,
208 "Attribute {{ name: {:?}, value: {} }}",
209 self.name, self.value
210 )
211 }
212}
213
214#[derive(Clone, Copy)]
216pub struct SvgNode<'a, 'input: 'a> {
217 id: NodeId,
218 doc: &'a Document<'input>,
219 d: &'a NodeData,
220}
221
222impl Eq for SvgNode<'_, '_> {}
223
224impl PartialEq for SvgNode<'_, '_> {
225 #[inline]
226 fn eq(&self, other: &Self) -> bool {
227 self.id == other.id && std::ptr::eq(self.doc, other.doc) && std::ptr::eq(self.d, other.d)
228 }
229}
230
231impl<'a, 'input: 'a> SvgNode<'a, 'input> {
232 #[inline]
233 fn id(&self) -> NodeId {
234 self.id
235 }
236
237 #[inline]
239 pub fn is_element(&self) -> bool {
240 matches!(self.d.kind, NodeKind::Element { .. })
241 }
242
243 #[inline]
245 pub fn is_text(&self) -> bool {
246 matches!(self.d.kind, NodeKind::Text(_))
247 }
248
249 #[inline]
251 pub fn document(&self) -> &'a Document<'input> {
252 self.doc
253 }
254
255 #[inline]
257 pub fn tag_name(&self) -> Option<EId> {
258 match self.d.kind {
259 NodeKind::Element { tag_name, .. } => Some(tag_name),
260 _ => None,
261 }
262 }
263 #[inline]
267 pub fn element_id(&self) -> &'a str {
268 self.attribute(AId::Id).unwrap_or("")
269 }
270
271 pub fn attribute<T: FromValue<'a, 'input>>(&self, aid: AId) -> Option<T> {
273 let value = self
274 .attributes()
275 .iter()
276 .find(|a| a.name == aid)
277 .map(|a| a.value.as_str())?;
278 match T::parse(*self, aid, value) {
279 Some(v) => Some(v),
280 None => {
281 log::warn!("Failed to parse {} value: '{}'.", aid, value);
283 None
284 }
285 }
286 }
287
288 pub fn try_attribute<T: FromValue<'a, 'input>>(&self, aid: AId) -> Option<T> {
292 let value = self
293 .attributes()
294 .iter()
295 .find(|a| a.name == aid)
296 .map(|a| a.value.as_str())?;
297 T::parse(*self, aid, value)
298 }
299
300 #[inline]
301 fn node_attribute(&self, aid: AId) -> Option<SvgNode<'a, 'input>> {
302 let value = self.attribute(aid)?;
303 let id = if aid == AId::Href {
304 svgtypes::IRI::from_str(value).ok().map(|v| v.0)
305 } else {
306 svgtypes::FuncIRI::from_str(value).ok().map(|v| v.0)
307 }?;
308
309 self.document().element_by_id(id)
310 }
311
312 #[inline]
314 pub fn has_attribute(&self, aid: AId) -> bool {
315 self.attributes().iter().any(|a| a.name == aid)
316 }
317
318 #[inline]
320 pub fn attributes(&self) -> &'a [Attribute<'input>] {
321 match self.d.kind {
322 NodeKind::Element { ref attributes, .. } => &self.doc.attrs[attributes.to_urange()],
323 _ => &[],
324 }
325 }
326
327 #[inline]
328 fn attribute_id(&self, aid: AId) -> Option<usize> {
329 match self.d.kind {
330 NodeKind::Element { ref attributes, .. } => {
331 let idx = self.attributes().iter().position(|attr| attr.name == aid)?;
332 Some(attributes.start as usize + idx)
333 }
334 _ => None,
335 }
336 }
337
338 pub fn find_attribute<T: FromValue<'a, 'input>>(&self, aid: AId) -> Option<T> {
346 self.find_attribute_impl(aid)?.attribute(aid)
347 }
348
349 fn find_attribute_impl(&self, aid: AId) -> Option<SvgNode<'a, 'input>> {
350 if aid.is_inheritable() {
351 for n in self.ancestors() {
352 if n.has_attribute(aid) {
353 return Some(n);
354 }
355 }
356
357 None
358 } else {
359 if self.has_attribute(aid) {
360 Some(*self)
361 } else {
362 let n = self.parent_element()?;
364 if n.has_attribute(aid) {
365 Some(n)
366 } else {
367 None
368 }
369 }
370 }
371 }
372
373 #[inline]
377 pub fn text(&self) -> &'a str {
378 match self.d.kind {
379 NodeKind::Element { .. } => match self.first_child() {
380 Some(child) if child.is_text() => match self.doc.nodes[child.id.get_usize()].kind {
381 NodeKind::Text(ref text) => text,
382 _ => "",
383 },
384 _ => "",
385 },
386 NodeKind::Text(ref text) => text,
387 _ => "",
388 }
389 }
390
391 #[inline]
393 pub fn parent(&self) -> Option<Self> {
394 self.d.parent.map(|id| self.doc.get(id))
395 }
396
397 #[inline]
399 pub fn parent_element(&self) -> Option<Self> {
400 self.ancestors().skip(1).find(|n| n.is_element())
401 }
402
403 #[inline]
405 pub fn next_sibling(&self) -> Option<Self> {
406 self.d.next_sibling.map(|id| self.doc.get(id))
407 }
408
409 #[inline]
411 pub fn first_child(&self) -> Option<Self> {
412 self.d.children.map(|(id, _)| self.doc.get(id))
413 }
414
415 #[inline]
417 pub fn first_element_child(&self) -> Option<Self> {
418 self.children().find(|n| n.is_element())
419 }
420
421 #[inline]
423 pub fn last_child(&self) -> Option<Self> {
424 self.d.children.map(|(_, id)| self.doc.get(id))
425 }
426
427 #[inline]
429 pub fn has_children(&self) -> bool {
430 self.d.children.is_some()
431 }
432
433 #[inline]
435 pub fn ancestors(&self) -> Ancestors<'a, 'input> {
436 Ancestors(Some(*self))
437 }
438
439 #[inline]
441 pub fn children(&self) -> Children<'a, 'input> {
442 Children {
443 front: self.first_child(),
444 back: self.last_child(),
445 }
446 }
447
448 #[inline]
450 fn traverse(&self) -> Traverse<'a, 'input> {
451 Traverse {
452 root: *self,
453 edge: None,
454 }
455 }
456
457 #[inline]
459 pub fn descendants(&self) -> Descendants<'a, 'input> {
460 Descendants(self.traverse())
461 }
462
463 #[inline]
465 pub fn href_iter(&self) -> HrefIter<'a, 'input> {
466 HrefIter {
467 doc: self.document(),
468 origin: self.id(),
469 curr: self.id(),
470 is_first: true,
471 is_finished: false,
472 }
473 }
474}
475
476impl std::fmt::Debug for SvgNode<'_, '_> {
477 fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
478 match self.d.kind {
479 NodeKind::Root => write!(f, "Root"),
480 NodeKind::Element { .. } => {
481 write!(
482 f,
483 "Element {{ tag_name: {:?}, attributes: {:?} }}",
484 self.tag_name(),
485 self.attributes()
486 )
487 }
488 NodeKind::Text(ref text) => write!(f, "Text({:?})", text),
489 }
490 }
491}
492
493#[derive(Clone, Debug)]
495pub struct Ancestors<'a, 'input: 'a>(Option<SvgNode<'a, 'input>>);
496
497impl<'a, 'input: 'a> Iterator for Ancestors<'a, 'input> {
498 type Item = SvgNode<'a, 'input>;
499
500 #[inline]
501 fn next(&mut self) -> Option<Self::Item> {
502 let node = self.0.take();
503 self.0 = node.as_ref().and_then(SvgNode::parent);
504 node
505 }
506}
507
508#[derive(Clone, Debug)]
510pub struct Children<'a, 'input: 'a> {
511 front: Option<SvgNode<'a, 'input>>,
512 back: Option<SvgNode<'a, 'input>>,
513}
514
515impl<'a, 'input: 'a> Iterator for Children<'a, 'input> {
516 type Item = SvgNode<'a, 'input>;
517
518 fn next(&mut self) -> Option<Self::Item> {
519 let node = self.front.take();
520 if self.front == self.back {
521 self.back = None;
522 } else {
523 self.front = node.as_ref().and_then(SvgNode::next_sibling);
524 }
525 node
526 }
527}
528
529#[derive(Clone, Copy, PartialEq, Debug)]
530enum Edge<'a, 'input: 'a> {
531 Open(SvgNode<'a, 'input>),
532 Close(SvgNode<'a, 'input>),
533}
534
535#[derive(Clone, Debug)]
536struct Traverse<'a, 'input: 'a> {
537 root: SvgNode<'a, 'input>,
538 edge: Option<Edge<'a, 'input>>,
539}
540
541impl<'a, 'input: 'a> Iterator for Traverse<'a, 'input> {
542 type Item = Edge<'a, 'input>;
543
544 fn next(&mut self) -> Option<Self::Item> {
545 match self.edge {
546 Some(Edge::Open(node)) => {
547 self.edge = Some(match node.first_child() {
548 Some(first_child) => Edge::Open(first_child),
549 None => Edge::Close(node),
550 });
551 }
552 Some(Edge::Close(node)) => {
553 if node == self.root {
554 self.edge = None;
555 } else if let Some(next_sibling) = node.next_sibling() {
556 self.edge = Some(Edge::Open(next_sibling));
557 } else {
558 self.edge = node.parent().map(Edge::Close);
559 }
560 }
561 None => {
562 self.edge = Some(Edge::Open(self.root));
563 }
564 }
565
566 self.edge
567 }
568}
569
570#[derive(Clone, Debug)]
572pub struct Descendants<'a, 'input: 'a>(Traverse<'a, 'input>);
573
574impl<'a, 'input: 'a> Iterator for Descendants<'a, 'input> {
575 type Item = SvgNode<'a, 'input>;
576
577 #[inline]
578 fn next(&mut self) -> Option<Self::Item> {
579 for edge in &mut self.0 {
580 if let Edge::Open(node) = edge {
581 return Some(node);
582 }
583 }
584
585 None
586 }
587}
588
589#[derive(Clone, Debug)]
591pub struct HrefIter<'a, 'input: 'a> {
592 doc: &'a Document<'input>,
593 origin: NodeId,
594 curr: NodeId,
595 is_first: bool,
596 is_finished: bool,
597}
598
599impl<'a, 'input: 'a> Iterator for HrefIter<'a, 'input> {
600 type Item = SvgNode<'a, 'input>;
601
602 fn next(&mut self) -> Option<Self::Item> {
603 if self.is_finished {
604 return None;
605 }
606
607 if self.is_first {
608 self.is_first = false;
609 return Some(self.doc.get(self.curr));
610 }
611
612 if let Some(link) = self.doc.get(self.curr).node_attribute(AId::Href) {
613 if link.id() == self.curr || link.id() == self.origin {
614 log::warn!(
615 "Element '#{}' cannot reference itself via 'xlink:href'.",
616 self.doc.get(self.origin).element_id()
617 );
618 self.is_finished = true;
619 return None;
620 }
621
622 self.curr = link.id();
623 Some(self.doc.get(self.curr))
624 } else {
625 None
626 }
627 }
628}
629
630impl EId {
631 pub fn is_graphic(&self) -> bool {
634 matches!(
635 self,
636 EId::Circle
637 | EId::Ellipse
638 | EId::Image
639 | EId::Line
640 | EId::Path
641 | EId::Polygon
642 | EId::Polyline
643 | EId::Rect
644 | EId::Text
645 | EId::Use
646 )
647 }
648
649 pub fn is_gradient(&self) -> bool {
652 matches!(self, EId::LinearGradient | EId::RadialGradient)
653 }
654
655 pub fn is_paint_server(&self) -> bool {
658 matches!(
659 self,
660 EId::LinearGradient | EId::RadialGradient | EId::Pattern
661 )
662 }
663}
664
665impl AId {
666 fn is_presentation(&self) -> bool {
667 matches!(
668 self,
669 AId::AlignmentBaseline
670 | AId::BaselineShift
671 | AId::ClipPath
672 | AId::ClipRule
673 | AId::Color
674 | AId::ColorInterpolation
675 | AId::ColorInterpolationFilters
676 | AId::ColorRendering
677 | AId::Direction
678 | AId::Display
679 | AId::DominantBaseline
680 | AId::Fill
681 | AId::FillOpacity
682 | AId::FillRule
683 | AId::Filter
684 | AId::FloodColor
685 | AId::FloodOpacity
686 | AId::FontFamily
687 | AId::FontKerning | AId::FontSize
689 | AId::FontSizeAdjust
690 | AId::FontStretch
691 | AId::FontStyle
692 | AId::FontVariant
693 | AId::FontWeight
694 | AId::GlyphOrientationHorizontal
695 | AId::GlyphOrientationVertical
696 | AId::ImageRendering
697 | AId::Isolation | AId::LetterSpacing
699 | AId::LightingColor
700 | AId::MarkerEnd
701 | AId::MarkerMid
702 | AId::MarkerStart
703 | AId::Mask
704 | AId::MaskType
705 | AId::MixBlendMode | AId::Opacity
707 | AId::Overflow
708 | AId::PaintOrder
709 | AId::ShapeRendering
710 | AId::StopColor
711 | AId::StopOpacity
712 | AId::Stroke
713 | AId::StrokeDasharray
714 | AId::StrokeDashoffset
715 | AId::StrokeLinecap
716 | AId::StrokeLinejoin
717 | AId::StrokeMiterlimit
718 | AId::StrokeOpacity
719 | AId::StrokeWidth
720 | AId::TextAnchor
721 | AId::TextDecoration
722 | AId::TextOverflow
723 | AId::TextRendering
724 | AId::Transform
725 | AId::TransformOrigin
726 | AId::UnicodeBidi
727 | AId::VectorEffect
728 | AId::Visibility
729 | AId::WhiteSpace
730 | AId::WordSpacing
731 | AId::WritingMode
732 )
733 }
734
735 fn is_inheritable(&self) -> bool {
737 if self.is_presentation() {
738 !is_non_inheritable(*self)
739 } else {
740 false
741 }
742 }
743
744 fn allows_inherit_value(&self) -> bool {
745 matches!(
746 self,
747 AId::AlignmentBaseline
748 | AId::BaselineShift
749 | AId::ClipPath
750 | AId::ClipRule
751 | AId::Color
752 | AId::ColorInterpolationFilters
753 | AId::Direction
754 | AId::Display
755 | AId::DominantBaseline
756 | AId::Fill
757 | AId::FillOpacity
758 | AId::FillRule
759 | AId::Filter
760 | AId::FloodColor
761 | AId::FloodOpacity
762 | AId::FontFamily
763 | AId::FontKerning
764 | AId::FontSize
765 | AId::FontStretch
766 | AId::FontStyle
767 | AId::FontVariant
768 | AId::FontWeight
769 | AId::ImageRendering
770 | AId::Kerning
771 | AId::LetterSpacing
772 | AId::MarkerEnd
773 | AId::MarkerMid
774 | AId::MarkerStart
775 | AId::Mask
776 | AId::Opacity
777 | AId::Overflow
778 | AId::ShapeRendering
779 | AId::StopColor
780 | AId::StopOpacity
781 | AId::Stroke
782 | AId::StrokeDasharray
783 | AId::StrokeDashoffset
784 | AId::StrokeLinecap
785 | AId::StrokeLinejoin
786 | AId::StrokeMiterlimit
787 | AId::StrokeOpacity
788 | AId::StrokeWidth
789 | AId::TextAnchor
790 | AId::TextDecoration
791 | AId::TextRendering
792 | AId::Visibility
793 | AId::WordSpacing
794 | AId::WritingMode
795 )
796 }
797}
798
799fn is_non_inheritable(id: AId) -> bool {
800 matches!(
801 id,
802 AId::AlignmentBaseline
803 | AId::BaselineShift
804 | AId::ClipPath
805 | AId::Display
806 | AId::DominantBaseline
807 | AId::Filter
808 | AId::FloodColor
809 | AId::FloodOpacity
810 | AId::Mask
811 | AId::Opacity
812 | AId::Overflow
813 | AId::LightingColor
814 | AId::StopColor
815 | AId::StopOpacity
816 | AId::TextDecoration
817 | AId::Transform
818 | AId::TransformOrigin
819 )
820}
821
822pub trait FromValue<'a, 'input: 'a>: Sized {
825 fn parse(node: SvgNode<'a, 'input>, aid: AId, value: &'a str) -> Option<Self>;
829}
830
831impl<'a, 'input: 'a> FromValue<'a, 'input> for &'a str {
832 fn parse(_: SvgNode<'a, 'input>, _: AId, value: &'a str) -> Option<Self> {
833 Some(value)
834 }
835}
836
837impl<'a, 'input: 'a> FromValue<'a, 'input> for f32 {
838 fn parse(_: SvgNode, _: AId, value: &str) -> Option<Self> {
839 svgtypes::Number::from_str(value).ok().map(|v| v.0 as f32)
840 }
841}
842
843impl<'a, 'input: 'a> FromValue<'a, 'input> for svgtypes::Length {
844 fn parse(_: SvgNode, _: AId, value: &str) -> Option<Self> {
845 svgtypes::Length::from_str(value).ok()
846 }
847}
848
849impl<'a, 'input: 'a> FromValue<'a, 'input> for usvg_tree::Opacity {
851 fn parse(_: SvgNode, _: AId, value: &str) -> Option<Self> {
852 let length = svgtypes::Length::from_str(value).ok()?;
853 if length.unit == svgtypes::LengthUnit::Percent {
854 Some(usvg_tree::Opacity::new_clamped(
855 length.number as f32 / 100.0,
856 ))
857 } else if length.unit == svgtypes::LengthUnit::None {
858 Some(usvg_tree::Opacity::new_clamped(length.number as f32))
859 } else {
860 None
861 }
862 }
863}
864
865impl<'a, 'input: 'a> FromValue<'a, 'input> for usvg_tree::Transform {
866 fn parse(_: SvgNode, _: AId, value: &str) -> Option<Self> {
867 let ts = match svgtypes::Transform::from_str(value) {
868 Ok(v) => v,
869 Err(_) => return None,
870 };
871
872 let ts = usvg_tree::Transform::from_row(
873 ts.a as f32,
874 ts.b as f32,
875 ts.c as f32,
876 ts.d as f32,
877 ts.e as f32,
878 ts.f as f32,
879 );
880
881 if ts.is_valid() {
882 Some(ts)
883 } else {
884 Some(usvg_tree::Transform::default())
885 }
886 }
887}
888
889impl<'a, 'input: 'a> FromValue<'a, 'input> for svgtypes::TransformOrigin {
890 fn parse(_: SvgNode, _: AId, value: &str) -> Option<Self> {
891 Self::from_str(value).ok()
892 }
893}
894
895impl<'a, 'input: 'a> FromValue<'a, 'input> for svgtypes::ViewBox {
896 fn parse(_: SvgNode, _: AId, value: &str) -> Option<Self> {
897 Self::from_str(value).ok()
898 }
899}
900
901impl<'a, 'input: 'a> FromValue<'a, 'input> for usvg_tree::Units {
902 fn parse(_: SvgNode, _: AId, value: &str) -> Option<Self> {
903 match value {
904 "userSpaceOnUse" => Some(usvg_tree::Units::UserSpaceOnUse),
905 "objectBoundingBox" => Some(usvg_tree::Units::ObjectBoundingBox),
906 _ => None,
907 }
908 }
909}
910
911impl<'a, 'input: 'a> FromValue<'a, 'input> for svgtypes::AspectRatio {
912 fn parse(_: SvgNode, _: AId, value: &str) -> Option<Self> {
913 Self::from_str(value).ok()
914 }
915}
916
917impl<'a, 'input: 'a> FromValue<'a, 'input> for svgtypes::PaintOrder {
918 fn parse(_: SvgNode, _: AId, value: &str) -> Option<Self> {
919 Self::from_str(value).ok()
920 }
921}
922
923impl<'a, 'input: 'a> FromValue<'a, 'input> for svgtypes::Color {
924 fn parse(_: SvgNode, _: AId, value: &str) -> Option<Self> {
925 Self::from_str(value).ok()
926 }
927}
928
929impl<'a, 'input: 'a> FromValue<'a, 'input> for svgtypes::Angle {
930 fn parse(_: SvgNode, _: AId, value: &str) -> Option<Self> {
931 Self::from_str(value).ok()
932 }
933}
934
935impl<'a, 'input: 'a> FromValue<'a, 'input> for svgtypes::EnableBackground {
936 fn parse(_: SvgNode, _: AId, value: &str) -> Option<Self> {
937 Self::from_str(value).ok()
938 }
939}
940
941impl<'a, 'input: 'a> FromValue<'a, 'input> for svgtypes::Paint<'a> {
942 fn parse(_: SvgNode, _: AId, value: &'a str) -> Option<Self> {
943 Self::from_str(value).ok()
944 }
945}
946
947impl<'a, 'input: 'a> FromValue<'a, 'input> for Vec<f32> {
948 fn parse(_: SvgNode, _: AId, value: &str) -> Option<Self> {
949 let mut list = Vec::new();
950 for n in svgtypes::NumberListParser::from(value) {
951 list.push(n.ok()? as f32);
952 }
953
954 Some(list)
955 }
956}
957
958impl<'a, 'input: 'a> FromValue<'a, 'input> for Vec<svgtypes::Length> {
959 fn parse(_: SvgNode, _: AId, value: &str) -> Option<Self> {
960 let mut list = Vec::new();
961 for n in svgtypes::LengthListParser::from(value) {
962 list.push(n.ok()?);
963 }
964
965 Some(list)
966 }
967}
968
969impl<'a, 'input: 'a> FromValue<'a, 'input> for usvg_tree::Visibility {
970 fn parse(_: SvgNode, _: AId, value: &str) -> Option<Self> {
971 match value {
972 "visible" => Some(usvg_tree::Visibility::Visible),
973 "hidden" => Some(usvg_tree::Visibility::Hidden),
974 "collapse" => Some(usvg_tree::Visibility::Collapse),
975 _ => None,
976 }
977 }
978}
979
980impl<'a, 'input: 'a> FromValue<'a, 'input> for usvg_tree::SpreadMethod {
981 fn parse(_: SvgNode, _: AId, value: &str) -> Option<Self> {
982 match value {
983 "pad" => Some(usvg_tree::SpreadMethod::Pad),
984 "reflect" => Some(usvg_tree::SpreadMethod::Reflect),
985 "repeat" => Some(usvg_tree::SpreadMethod::Repeat),
986 _ => None,
987 }
988 }
989}
990
991impl<'a, 'input: 'a> FromValue<'a, 'input> for usvg_tree::ShapeRendering {
992 fn parse(_: SvgNode, _: AId, value: &str) -> Option<Self> {
993 match value {
994 "optimizeSpeed" => Some(usvg_tree::ShapeRendering::OptimizeSpeed),
995 "crispEdges" => Some(usvg_tree::ShapeRendering::CrispEdges),
996 "auto" | "geometricPrecision" => Some(usvg_tree::ShapeRendering::GeometricPrecision),
997 _ => None,
998 }
999 }
1000}
1001
1002impl<'a, 'input: 'a> FromValue<'a, 'input> for usvg_tree::TextRendering {
1003 fn parse(_: SvgNode, _: AId, value: &str) -> Option<Self> {
1004 match value {
1005 "optimizeSpeed" => Some(usvg_tree::TextRendering::OptimizeSpeed),
1006 "auto" | "optimizeLegibility" => Some(usvg_tree::TextRendering::OptimizeLegibility),
1007 "geometricPrecision" => Some(usvg_tree::TextRendering::GeometricPrecision),
1008 _ => None,
1009 }
1010 }
1011}
1012
1013impl<'a, 'input: 'a> FromValue<'a, 'input> for usvg_tree::ImageRendering {
1014 fn parse(_: SvgNode, _: AId, value: &str) -> Option<Self> {
1015 match value {
1016 "auto" | "optimizeQuality" => Some(usvg_tree::ImageRendering::OptimizeQuality),
1017 "optimizeSpeed" => Some(usvg_tree::ImageRendering::OptimizeSpeed),
1018 _ => None,
1019 }
1020 }
1021}
1022
1023impl<'a, 'input: 'a> FromValue<'a, 'input> for usvg_tree::BlendMode {
1024 fn parse(_: SvgNode, _: AId, value: &str) -> Option<Self> {
1025 match value {
1026 "normal" => Some(usvg_tree::BlendMode::Normal),
1027 "multiply" => Some(usvg_tree::BlendMode::Multiply),
1028 "screen" => Some(usvg_tree::BlendMode::Screen),
1029 "overlay" => Some(usvg_tree::BlendMode::Overlay),
1030 "darken" => Some(usvg_tree::BlendMode::Darken),
1031 "lighten" => Some(usvg_tree::BlendMode::Lighten),
1032 "color-dodge" => Some(usvg_tree::BlendMode::ColorDodge),
1033 "color-burn" => Some(usvg_tree::BlendMode::ColorBurn),
1034 "hard-light" => Some(usvg_tree::BlendMode::HardLight),
1035 "soft-light" => Some(usvg_tree::BlendMode::SoftLight),
1036 "difference" => Some(usvg_tree::BlendMode::Difference),
1037 "exclusion" => Some(usvg_tree::BlendMode::Exclusion),
1038 "hue" => Some(usvg_tree::BlendMode::Hue),
1039 "saturation" => Some(usvg_tree::BlendMode::Saturation),
1040 "color" => Some(usvg_tree::BlendMode::Color),
1041 "luminosity" => Some(usvg_tree::BlendMode::Luminosity),
1042 _ => None,
1043 }
1044 }
1045}
1046
1047impl<'a, 'input: 'a> FromValue<'a, 'input> for SvgNode<'a, 'input> {
1048 fn parse(node: SvgNode<'a, 'input>, aid: AId, value: &str) -> Option<Self> {
1049 let id = if aid == AId::Href {
1050 svgtypes::IRI::from_str(value).ok().map(|v| v.0)
1051 } else {
1052 svgtypes::FuncIRI::from_str(value).ok().map(|v| v.0)
1053 }?;
1054
1055 node.document().element_by_id(id)
1056 }
1057}