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