usvg/parser/svgtree/
mod.rs

1// Copyright 2021 the Resvg Authors
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3
4use 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
20/// An SVG tree container.
21///
22/// Contains only element and text nodes.
23/// Text nodes are present only inside the `text` element.
24pub struct Document<'input> {
25    nodes: Vec<NodeData>,
26    attrs: Vec<Attribute<'input>>,
27    links: HashMap<String, NodeId>,
28}
29
30impl<'input> Document<'input> {
31    /// Returns the root node.
32    #[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    /// Returns the root element.
42    #[inline]
43    pub fn root_element<'a>(&'a self) -> SvgNode<'a, 'input> {
44        // `unwrap` is safe, because `Document` is guarantee to have at least one element.
45        self.root().first_element_child().unwrap()
46    }
47
48    /// Returns an iterator over document's descendant nodes.
49    ///
50    /// Shorthand for `doc.root().descendants()`.
51    #[inline]
52    pub fn descendants<'a>(&'a self) -> Descendants<'a, 'input> {
53        self.root().descendants()
54    }
55
56    /// Returns an element by ID.
57    ///
58    /// Unlike the [`Descendants`] iterator, this is just a HashMap lookup.
59    /// Meaning it's way faster.
60    #[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        // We are using `NonZeroU32` to reduce overhead of `Option<NodeId>`.
161        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        // We already checked that `id` is limited by u32::MAX.
179        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/// An attribute.
201#[derive(Clone)]
202pub struct Attribute<'input> {
203    /// Attribute's name.
204    pub name: AId,
205    /// Attribute's value.
206    pub value: roxmltree::StringStorage<'input>,
207    /// Attribute's importance
208    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/// An SVG node.
222#[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    /// Checks if the current node is an element.
245    #[inline]
246    pub fn is_element(&self) -> bool {
247        matches!(self.d.kind, NodeKind::Element { .. })
248    }
249
250    /// Checks if the current node is a text.
251    #[inline]
252    pub fn is_text(&self) -> bool {
253        matches!(self.d.kind, NodeKind::Text(_))
254    }
255
256    /// Returns node's document.
257    #[inline]
258    pub fn document(&self) -> &'a Document<'input> {
259        self.doc
260    }
261
262    /// Returns element's tag name, unless the current node is text.
263    #[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    /// Returns element's `id` attribute value.
271    ///
272    /// Returns an empty string otherwise.
273    #[inline]
274    pub fn element_id(&self) -> &'a str {
275        self.attribute(AId::Id).unwrap_or("")
276    }
277
278    /// Returns an attribute value.
279    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                // TODO: show position in XML
289                log::warn!("Failed to parse {} value: '{}'.", aid, value);
290                None
291            }
292        }
293    }
294
295    /// Returns an attribute value.
296    ///
297    /// Same as `SvgNode::attribute`, but doesn't show a warning.
298    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    /// Checks if an attribute is present.
320    #[inline]
321    pub fn has_attribute(&self, aid: AId) -> bool {
322        self.attributes().iter().any(|a| a.name == aid)
323    }
324
325    /// Returns a list of all element's attributes.
326    #[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    /// Finds a [`Node`] that contains the required attribute.
346    ///
347    /// For inheritable attributes walks over ancestors until a node with
348    /// the specified attribute is found.
349    ///
350    /// For non-inheritable attributes checks only the current node and the parent one.
351    /// As per SVG spec.
352    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                // Non-inheritable attributes can inherit a value only from a direct parent.
370                let n = self.parent_element()?;
371                if n.has_attribute(aid) {
372                    Some(n)
373                } else {
374                    None
375                }
376            }
377        }
378    }
379
380    /// Returns node's text data.
381    ///
382    /// For text nodes returns its content. For elements returns the first child node text.
383    #[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    /// Returns a parent node.
399    #[inline]
400    pub fn parent(&self) -> Option<Self> {
401        self.d.parent.map(|id| self.doc.get(id))
402    }
403
404    /// Returns the parent element.
405    #[inline]
406    pub fn parent_element(&self) -> Option<Self> {
407        self.ancestors().skip(1).find(|n| n.is_element())
408    }
409
410    /// Returns the next sibling.
411    #[inline]
412    pub fn next_sibling(&self) -> Option<Self> {
413        self.d.next_sibling.map(|id| self.doc.get(id))
414    }
415
416    /// Returns the first child.
417    #[inline]
418    pub fn first_child(&self) -> Option<Self> {
419        self.d.children.map(|(id, _)| self.doc.get(id))
420    }
421
422    /// Returns the first child element.
423    #[inline]
424    pub fn first_element_child(&self) -> Option<Self> {
425        self.children().find(|n| n.is_element())
426    }
427
428    /// Returns the last child.
429    #[inline]
430    pub fn last_child(&self) -> Option<Self> {
431        self.d.children.map(|(_, id)| self.doc.get(id))
432    }
433
434    /// Checks if the node has child nodes.
435    #[inline]
436    pub fn has_children(&self) -> bool {
437        self.d.children.is_some()
438    }
439
440    /// Returns an iterator over ancestor nodes starting at this node.
441    #[inline]
442    pub fn ancestors(&self) -> Ancestors<'a, 'input> {
443        Ancestors(Some(*self))
444    }
445
446    /// Returns an iterator over children nodes.
447    #[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    /// Returns an iterator which traverses the subtree starting at this node.
456    #[inline]
457    fn traverse(&self) -> Traverse<'a, 'input> {
458        Traverse {
459            root: *self,
460            edge: None,
461        }
462    }
463
464    /// Returns an iterator over this node and its descendants.
465    #[inline]
466    pub fn descendants(&self) -> Descendants<'a, 'input> {
467        Descendants(self.traverse())
468    }
469
470    /// Returns an iterator over elements linked via `xlink:href`.
471    #[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/// An iterator over ancestor nodes.
501#[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/// An iterator over children nodes.
516#[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/// A descendants iterator.
578#[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/// An iterator over `xlink:href` references.
597#[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    /// Checks if this is a
639    /// [graphics element](https://www.w3.org/TR/SVG11/intro.html#TermGraphicsElement).
640    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    /// Checks if this is a
657    /// [gradient element](https://www.w3.org/TR/SVG11/intro.html#TermGradientElement).
658    pub fn is_gradient(&self) -> bool {
659        matches!(self, EId::LinearGradient | EId::RadialGradient)
660    }
661
662    /// Checks if this is a
663    /// [paint server element](https://www.w3.org/TR/SVG11/intro.html#TermPaint).
664    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 // non-standard SVG attribute
679                | 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 // technically not presentation
696                | 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 // technically not presentation
706                | AId::LetterSpacing
707                | AId::LightingColor
708                | AId::MarkerEnd
709                | AId::MarkerMid
710                | AId::MarkerStart
711                | AId::Mask
712                | AId::MaskType
713                | AId::MixBlendMode // technically not presentation
714                | 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    /// Checks if the current attribute is inheritable.
744    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
830// TODO: is there a way yo make it less ugly? Too many lifetimes.
831/// A trait for parsing attribute values.
832pub trait FromValue<'a, 'input: 'a>: Sized {
833    /// Parses an attribute value.
834    ///
835    /// When `None` is returned, the attribute value will be logged as a parsing failure.
836    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
857// TODO: to svgtypes?
858impl<'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}