usvg_parser/svgtree/
mod.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
5use 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
15/// An SVG tree container.
16///
17/// Contains only element and text nodes.
18/// Text nodes are present only inside the `text` element.
19pub struct Document<'input> {
20    nodes: Vec<NodeData>,
21    attrs: Vec<Attribute<'input>>,
22    links: HashMap<String, NodeId>,
23}
24
25impl<'input> Document<'input> {
26    /// Returns the root node.
27    #[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    /// Returns the root element.
37    #[inline]
38    pub fn root_element<'a>(&'a self) -> SvgNode<'a, 'input> {
39        // `unwrap` is safe, because `Document` is guarantee to have at least one element.
40        self.root().first_element_child().unwrap()
41    }
42
43    /// Returns an iterator over document's descendant nodes.
44    ///
45    /// Shorthand for `doc.root().descendants()`.
46    #[inline]
47    pub fn descendants<'a>(&'a self) -> Descendants<'a, 'input> {
48        self.root().descendants()
49    }
50
51    /// Returns an element by ID.
52    ///
53    /// Unlike the [`Descendants`] iterator, this is just a HashMap lookup.
54    /// Meaning it's way faster.
55    #[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        // We are using `NonZeroU32` to reduce overhead of `Option<NodeId>`.
156        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        // We already checked that `id` is limited by u32::MAX.
174        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/// An attribute.
196#[derive(Clone)]
197pub struct Attribute<'input> {
198    /// Attribute's name.
199    pub name: AId,
200    /// Attribute's value.
201    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/// An SVG node.
215#[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    /// Checks if the current node is an element.
238    #[inline]
239    pub fn is_element(&self) -> bool {
240        matches!(self.d.kind, NodeKind::Element { .. })
241    }
242
243    /// Checks if the current node is a text.
244    #[inline]
245    pub fn is_text(&self) -> bool {
246        matches!(self.d.kind, NodeKind::Text(_))
247    }
248
249    /// Returns node's document.
250    #[inline]
251    pub fn document(&self) -> &'a Document<'input> {
252        self.doc
253    }
254
255    /// Returns element's tag name, unless the current node is text.
256    #[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    /// Returns element's `id` attribute value.
264    ///
265    /// Returns an empty string otherwise.
266    #[inline]
267    pub fn element_id(&self) -> &'a str {
268        self.attribute(AId::Id).unwrap_or("")
269    }
270
271    /// Returns an attribute value.
272    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                // TODO: show position in XML
282                log::warn!("Failed to parse {} value: '{}'.", aid, value);
283                None
284            }
285        }
286    }
287
288    /// Returns an attribute value.
289    ///
290    /// Same as `SvgNode::attribute`, but doesn't show a warning.
291    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    /// Checks if an attribute is present.
313    #[inline]
314    pub fn has_attribute(&self, aid: AId) -> bool {
315        self.attributes().iter().any(|a| a.name == aid)
316    }
317
318    /// Returns a list of all element's attributes.
319    #[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    /// Finds a [`Node`] that contains the required attribute.
339    ///
340    /// For inheritable attributes walks over ancestors until a node with
341    /// the specified attribute is found.
342    ///
343    /// For non-inheritable attributes checks only the current node and the parent one.
344    /// As per SVG spec.
345    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                // Non-inheritable attributes can inherit a value only from a direct parent.
363                let n = self.parent_element()?;
364                if n.has_attribute(aid) {
365                    Some(n)
366                } else {
367                    None
368                }
369            }
370        }
371    }
372
373    /// Returns node's text data.
374    ///
375    /// For text nodes returns its content. For elements returns the first child node text.
376    #[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    /// Returns a parent node.
392    #[inline]
393    pub fn parent(&self) -> Option<Self> {
394        self.d.parent.map(|id| self.doc.get(id))
395    }
396
397    /// Returns the parent element.
398    #[inline]
399    pub fn parent_element(&self) -> Option<Self> {
400        self.ancestors().skip(1).find(|n| n.is_element())
401    }
402
403    /// Returns the next sibling.
404    #[inline]
405    pub fn next_sibling(&self) -> Option<Self> {
406        self.d.next_sibling.map(|id| self.doc.get(id))
407    }
408
409    /// Returns the first child.
410    #[inline]
411    pub fn first_child(&self) -> Option<Self> {
412        self.d.children.map(|(id, _)| self.doc.get(id))
413    }
414
415    /// Returns the first child element.
416    #[inline]
417    pub fn first_element_child(&self) -> Option<Self> {
418        self.children().find(|n| n.is_element())
419    }
420
421    /// Returns the last child.
422    #[inline]
423    pub fn last_child(&self) -> Option<Self> {
424        self.d.children.map(|(_, id)| self.doc.get(id))
425    }
426
427    /// Checks if the node has child nodes.
428    #[inline]
429    pub fn has_children(&self) -> bool {
430        self.d.children.is_some()
431    }
432
433    /// Returns an iterator over ancestor nodes starting at this node.
434    #[inline]
435    pub fn ancestors(&self) -> Ancestors<'a, 'input> {
436        Ancestors(Some(*self))
437    }
438
439    /// Returns an iterator over children nodes.
440    #[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    /// Returns an iterator which traverses the subtree starting at this node.
449    #[inline]
450    fn traverse(&self) -> Traverse<'a, 'input> {
451        Traverse {
452            root: *self,
453            edge: None,
454        }
455    }
456
457    /// Returns an iterator over this node and its descendants.
458    #[inline]
459    pub fn descendants(&self) -> Descendants<'a, 'input> {
460        Descendants(self.traverse())
461    }
462
463    /// Returns an iterator over elements linked via `xlink:href`.
464    #[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/// An iterator over ancestor nodes.
494#[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/// An iterator over children nodes.
509#[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/// A descendants iterator.
571#[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/// An iterator over `xlink:href` references.
590#[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    /// Checks if this is a
632    /// [graphics element](https://www.w3.org/TR/SVG11/intro.html#TermGraphicsElement).
633    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    /// Checks if this is a
650    /// [gradient element](https://www.w3.org/TR/SVG11/intro.html#TermGradientElement).
651    pub fn is_gradient(&self) -> bool {
652        matches!(self, EId::LinearGradient | EId::RadialGradient)
653    }
654
655    /// Checks if this is a
656    /// [paint server element](https://www.w3.org/TR/SVG11/intro.html#TermPaint).
657    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 // technically not presentation
688                | 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 // technically not presentation
698                | AId::LetterSpacing
699                | AId::LightingColor
700                | AId::MarkerEnd
701                | AId::MarkerMid
702                | AId::MarkerStart
703                | AId::Mask
704                | AId::MaskType
705                | AId::MixBlendMode // technically not presentation
706                | 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    /// Checks if the current attribute is inheritable.
736    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
822// TODO: is there a way yo make it less ugly? Too many lifetimes.
823/// A trait for parsing attribute values.
824pub trait FromValue<'a, 'input: 'a>: Sized {
825    /// Parses an attribute value.
826    ///
827    /// When `None` is returned, the attribute value will be logged as a parsing failure.
828    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
849// TODO: to svgtypes?
850impl<'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}