Skip to main content

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        // These AId have an initial value of none
286        let is_possible_none = matches!(
287            aid,
288            AId::Mask
289                | AId::MarkerStart
290                | AId::MarkerMid
291                | AId::MarkerEnd
292                | AId::ClipPath
293                | AId::Filter
294                | AId::FontSizeAdjust
295                | AId::TextDecoration
296                | AId::Stroke
297                | AId::StrokeDasharray
298        );
299        if is_possible_none && value == "none" {
300            return None;
301        }
302        match T::parse(*self, aid, value) {
303            Some(v) => Some(v),
304            None => {
305                // TODO: show position in XML
306                log::warn!("Failed to parse {} value: '{}'.", aid, value);
307                None
308            }
309        }
310    }
311
312    /// Returns an attribute value.
313    ///
314    /// Same as `SvgNode::attribute`, but doesn't show a warning.
315    pub fn try_attribute<T: FromValue<'a, 'input>>(&self, aid: AId) -> Option<T> {
316        let value = self
317            .attributes()
318            .iter()
319            .find(|a| a.name == aid)
320            .map(|a| a.value.as_str())?;
321        T::parse(*self, aid, value)
322    }
323
324    #[inline]
325    fn node_attribute(&self, aid: AId) -> Option<SvgNode<'a, 'input>> {
326        let value = self.attribute(aid)?;
327        let id = if aid == AId::Href {
328            svgtypes::IRI::from_str(value).ok().map(|v| v.0)
329        } else {
330            svgtypes::FuncIRI::from_str(value).ok().map(|v| v.0)
331        }?;
332
333        self.document().element_by_id(id)
334    }
335
336    /// Checks if an attribute is present.
337    #[inline]
338    pub fn has_attribute(&self, aid: AId) -> bool {
339        self.attributes().iter().any(|a| a.name == aid)
340    }
341
342    /// Returns a list of all element's attributes.
343    #[inline]
344    pub fn attributes(&self) -> &'a [Attribute<'input>] {
345        match self.d.kind {
346            NodeKind::Element { ref attributes, .. } => &self.doc.attrs[attributes.to_urange()],
347            _ => &[],
348        }
349    }
350
351    #[inline]
352    fn attribute_id(&self, aid: AId) -> Option<usize> {
353        match self.d.kind {
354            NodeKind::Element { ref attributes, .. } => {
355                let idx = self.attributes().iter().position(|attr| attr.name == aid)?;
356                Some(attributes.start as usize + idx)
357            }
358            _ => None,
359        }
360    }
361
362    /// Finds a [`Node`] that contains the required attribute.
363    ///
364    /// For inheritable attributes walks over ancestors until a node with
365    /// the specified attribute is found.
366    ///
367    /// For non-inheritable attributes checks only the current node and the parent one.
368    /// As per SVG spec.
369    pub fn find_attribute<T: FromValue<'a, 'input>>(&self, aid: AId) -> Option<T> {
370        self.find_attribute_impl(aid)?.attribute(aid)
371    }
372
373    fn find_attribute_impl(&self, aid: AId) -> Option<SvgNode<'a, 'input>> {
374        if aid.is_inheritable() {
375            for n in self.ancestors() {
376                if n.has_attribute(aid) {
377                    return Some(n);
378                }
379            }
380
381            None
382        } else {
383            if self.has_attribute(aid) {
384                Some(*self)
385            } else {
386                // Non-inheritable attributes can inherit a value only from a direct parent.
387                let n = self.parent_element()?;
388                if n.has_attribute(aid) { Some(n) } else { None }
389            }
390        }
391    }
392
393    /// Returns node's text data.
394    ///
395    /// For text nodes returns its content. For elements returns the first child node text.
396    #[inline]
397    pub fn text(&self) -> &'a str {
398        match self.d.kind {
399            NodeKind::Element { .. } => match self.first_child() {
400                Some(child) if child.is_text() => match self.doc.nodes[child.id.get_usize()].kind {
401                    NodeKind::Text(ref text) => text,
402                    _ => "",
403                },
404                _ => "",
405            },
406            NodeKind::Text(ref text) => text,
407            _ => "",
408        }
409    }
410
411    /// Returns a parent node.
412    #[inline]
413    pub fn parent(&self) -> Option<Self> {
414        self.d.parent.map(|id| self.doc.get(id))
415    }
416
417    /// Returns the parent element.
418    #[inline]
419    pub fn parent_element(&self) -> Option<Self> {
420        self.ancestors().skip(1).find(|n| n.is_element())
421    }
422
423    /// Returns the next sibling.
424    #[inline]
425    pub fn next_sibling(&self) -> Option<Self> {
426        self.d.next_sibling.map(|id| self.doc.get(id))
427    }
428
429    /// Returns the first child.
430    #[inline]
431    pub fn first_child(&self) -> Option<Self> {
432        self.d.children.map(|(id, _)| self.doc.get(id))
433    }
434
435    /// Returns the first child element.
436    #[inline]
437    pub fn first_element_child(&self) -> Option<Self> {
438        self.children().find(|n| n.is_element())
439    }
440
441    /// Returns the last child.
442    #[inline]
443    pub fn last_child(&self) -> Option<Self> {
444        self.d.children.map(|(_, id)| self.doc.get(id))
445    }
446
447    /// Checks if the node has child nodes.
448    #[inline]
449    pub fn has_children(&self) -> bool {
450        self.d.children.is_some()
451    }
452
453    /// Returns an iterator over ancestor nodes starting at this node.
454    #[inline]
455    pub fn ancestors(&self) -> Ancestors<'a, 'input> {
456        Ancestors(Some(*self))
457    }
458
459    /// Returns an iterator over children nodes.
460    #[inline]
461    pub fn children(&self) -> Children<'a, 'input> {
462        Children {
463            front: self.first_child(),
464            back: self.last_child(),
465        }
466    }
467
468    /// Returns an iterator which traverses the subtree starting at this node.
469    #[inline]
470    fn traverse(&self) -> Traverse<'a, 'input> {
471        Traverse {
472            root: *self,
473            edge: None,
474        }
475    }
476
477    /// Returns an iterator over this node and its descendants.
478    #[inline]
479    pub fn descendants(&self) -> Descendants<'a, 'input> {
480        Descendants(self.traverse())
481    }
482
483    /// Returns an iterator over elements linked via `xlink:href`.
484    #[inline]
485    pub fn href_iter(&self) -> HrefIter<'a, 'input> {
486        HrefIter {
487            doc: self.document(),
488            origin: self.id(),
489            curr: self.id(),
490            is_first: true,
491            is_finished: false,
492        }
493    }
494}
495
496impl std::fmt::Debug for SvgNode<'_, '_> {
497    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
498        match self.d.kind {
499            NodeKind::Root => write!(f, "Root"),
500            NodeKind::Element { .. } => {
501                write!(
502                    f,
503                    "Element {{ tag_name: {:?}, attributes: {:?} }}",
504                    self.tag_name(),
505                    self.attributes()
506                )
507            }
508            NodeKind::Text(ref text) => write!(f, "Text({:?})", text),
509        }
510    }
511}
512
513/// An iterator over ancestor nodes.
514#[derive(Clone, Debug)]
515pub struct Ancestors<'a, 'input: 'a>(Option<SvgNode<'a, 'input>>);
516
517impl<'a, 'input: 'a> Iterator for Ancestors<'a, 'input> {
518    type Item = SvgNode<'a, 'input>;
519
520    #[inline]
521    fn next(&mut self) -> Option<Self::Item> {
522        let node = self.0.take();
523        self.0 = node.as_ref().and_then(SvgNode::parent);
524        node
525    }
526}
527
528/// An iterator over children nodes.
529#[derive(Clone, Debug)]
530pub struct Children<'a, 'input: 'a> {
531    front: Option<SvgNode<'a, 'input>>,
532    back: Option<SvgNode<'a, 'input>>,
533}
534
535impl<'a, 'input: 'a> Iterator for Children<'a, 'input> {
536    type Item = SvgNode<'a, 'input>;
537
538    fn next(&mut self) -> Option<Self::Item> {
539        let node = self.front.take();
540        if self.front == self.back {
541            self.back = None;
542        } else {
543            self.front = node.as_ref().and_then(SvgNode::next_sibling);
544        }
545        node
546    }
547}
548
549#[derive(Clone, Copy, PartialEq, Debug)]
550enum Edge<'a, 'input: 'a> {
551    Open(SvgNode<'a, 'input>),
552    Close(SvgNode<'a, 'input>),
553}
554
555#[derive(Clone, Debug)]
556struct Traverse<'a, 'input: 'a> {
557    root: SvgNode<'a, 'input>,
558    edge: Option<Edge<'a, 'input>>,
559}
560
561impl<'a, 'input: 'a> Iterator for Traverse<'a, 'input> {
562    type Item = Edge<'a, 'input>;
563
564    fn next(&mut self) -> Option<Self::Item> {
565        match self.edge {
566            Some(Edge::Open(node)) => {
567                self.edge = Some(match node.first_child() {
568                    Some(first_child) => Edge::Open(first_child),
569                    None => Edge::Close(node),
570                });
571            }
572            Some(Edge::Close(node)) => {
573                if node == self.root {
574                    self.edge = None;
575                } else if let Some(next_sibling) = node.next_sibling() {
576                    self.edge = Some(Edge::Open(next_sibling));
577                } else {
578                    self.edge = node.parent().map(Edge::Close);
579                }
580            }
581            None => {
582                self.edge = Some(Edge::Open(self.root));
583            }
584        }
585
586        self.edge
587    }
588}
589
590/// A descendants iterator.
591#[derive(Clone, Debug)]
592pub struct Descendants<'a, 'input: 'a>(Traverse<'a, 'input>);
593
594impl<'a, 'input: 'a> Iterator for Descendants<'a, 'input> {
595    type Item = SvgNode<'a, 'input>;
596
597    #[inline]
598    fn next(&mut self) -> Option<Self::Item> {
599        for edge in &mut self.0 {
600            if let Edge::Open(node) = edge {
601                return Some(node);
602            }
603        }
604
605        None
606    }
607}
608
609/// An iterator over `xlink:href` references.
610#[derive(Clone, Debug)]
611pub struct HrefIter<'a, 'input: 'a> {
612    doc: &'a Document<'input>,
613    origin: NodeId,
614    curr: NodeId,
615    is_first: bool,
616    is_finished: bool,
617}
618
619impl<'a, 'input: 'a> Iterator for HrefIter<'a, 'input> {
620    type Item = SvgNode<'a, 'input>;
621
622    fn next(&mut self) -> Option<Self::Item> {
623        if self.is_finished {
624            return None;
625        }
626
627        if self.is_first {
628            self.is_first = false;
629            return Some(self.doc.get(self.curr));
630        }
631
632        if let Some(link) = self.doc.get(self.curr).node_attribute(AId::Href) {
633            if link.id() == self.curr || link.id() == self.origin {
634                log::warn!(
635                    "Element '#{}' cannot reference itself via 'xlink:href'.",
636                    self.doc.get(self.origin).element_id()
637                );
638                self.is_finished = true;
639                return None;
640            }
641
642            self.curr = link.id();
643            Some(self.doc.get(self.curr))
644        } else {
645            None
646        }
647    }
648}
649
650impl EId {
651    /// Checks if this is a
652    /// [graphics element](https://www.w3.org/TR/SVG11/intro.html#TermGraphicsElement).
653    pub fn is_graphic(&self) -> bool {
654        matches!(
655            self,
656            EId::Circle
657                | EId::Ellipse
658                | EId::Image
659                | EId::Line
660                | EId::Path
661                | EId::Polygon
662                | EId::Polyline
663                | EId::Rect
664                | EId::Text
665                | EId::Use
666        )
667    }
668
669    /// Checks if this is a
670    /// [gradient element](https://www.w3.org/TR/SVG11/intro.html#TermGradientElement).
671    pub fn is_gradient(&self) -> bool {
672        matches!(self, EId::LinearGradient | EId::RadialGradient)
673    }
674
675    /// Checks if this is a
676    /// [paint server element](https://www.w3.org/TR/SVG11/intro.html#TermPaint).
677    pub fn is_paint_server(&self) -> bool {
678        matches!(
679            self,
680            EId::LinearGradient | EId::RadialGradient | EId::Pattern
681        )
682    }
683}
684
685impl AId {
686    fn is_presentation(&self) -> bool {
687        matches!(
688            self,
689            AId::AlignmentBaseline
690                | AId::BaselineShift
691                | AId::BackgroundColor // non-standard SVG attribute
692                | AId::ClipPath
693                | AId::ClipRule
694                | AId::Color
695                | AId::ColorInterpolation
696                | AId::ColorInterpolationFilters
697                | AId::ColorRendering
698                | AId::Direction
699                | AId::Display
700                | AId::DominantBaseline
701                | AId::Fill
702                | AId::FillOpacity
703                | AId::FillRule
704                | AId::Filter
705                | AId::FloodColor
706                | AId::FloodOpacity
707                | AId::FontFamily
708                | AId::FontKerning // technically not presentation
709                | AId::FontOpticalSizing // technically not presentation
710                | AId::FontSize
711                | AId::FontSizeAdjust
712                | AId::FontStretch
713                | AId::FontStyle
714                | AId::FontVariant
715                | AId::FontWeight
716                | AId::FontVariationSettings
717                | AId::GlyphOrientationHorizontal
718                | AId::GlyphOrientationVertical
719                | AId::ImageRendering
720                | AId::Isolation // technically not presentation
721                | AId::LetterSpacing
722                | AId::LightingColor
723                | AId::MarkerEnd
724                | AId::MarkerMid
725                | AId::MarkerStart
726                | AId::Mask
727                | AId::MaskType
728                | AId::MixBlendMode // technically not presentation
729                | AId::Opacity
730                | AId::Overflow
731                | AId::PaintOrder
732                | AId::ShapeRendering
733                | AId::StopColor
734                | AId::StopOpacity
735                | AId::Stroke
736                | AId::StrokeDasharray
737                | AId::StrokeDashoffset
738                | AId::StrokeLinecap
739                | AId::StrokeLinejoin
740                | AId::StrokeMiterlimit
741                | AId::StrokeOpacity
742                | AId::StrokeWidth
743                | AId::TextAnchor
744                | AId::TextDecoration
745                | AId::TextOverflow
746                | AId::TextRendering
747                | AId::Transform
748                | AId::TransformOrigin
749                | AId::UnicodeBidi
750                | AId::VectorEffect
751                | AId::Visibility
752                | AId::WhiteSpace
753                | AId::WordSpacing
754                | AId::WritingMode
755        )
756    }
757
758    /// Checks if the current attribute is inheritable.
759    fn is_inheritable(&self) -> bool {
760        if self.is_presentation() {
761            !is_non_inheritable(*self)
762        } else {
763            false
764        }
765    }
766
767    fn allows_inherit_value(&self) -> bool {
768        matches!(
769            self,
770            AId::AlignmentBaseline
771                | AId::BaselineShift
772                | AId::ClipPath
773                | AId::ClipRule
774                | AId::Color
775                | AId::ColorInterpolationFilters
776                | AId::Direction
777                | AId::Display
778                | AId::DominantBaseline
779                | AId::Fill
780                | AId::FillOpacity
781                | AId::FillRule
782                | AId::Filter
783                | AId::FloodColor
784                | AId::FloodOpacity
785                | AId::FontFamily
786                | AId::FontKerning
787                | AId::FontOpticalSizing
788                | AId::FontSize
789                | AId::FontStretch
790                | AId::FontStyle
791                | AId::FontVariant
792                | AId::FontWeight
793                | AId::ImageRendering
794                | AId::Kerning
795                | AId::LetterSpacing
796                | AId::MarkerEnd
797                | AId::MarkerMid
798                | AId::MarkerStart
799                | AId::Mask
800                | AId::Opacity
801                | AId::Overflow
802                | AId::ShapeRendering
803                | AId::StopColor
804                | AId::StopOpacity
805                | AId::Stroke
806                | AId::StrokeDasharray
807                | AId::StrokeDashoffset
808                | AId::StrokeLinecap
809                | AId::StrokeLinejoin
810                | AId::StrokeMiterlimit
811                | AId::StrokeOpacity
812                | AId::StrokeWidth
813                | AId::TextAnchor
814                | AId::TextDecoration
815                | AId::TextRendering
816                | AId::Visibility
817                | AId::WordSpacing
818                | AId::WritingMode
819        )
820    }
821}
822
823fn is_non_inheritable(id: AId) -> bool {
824    matches!(
825        id,
826        AId::AlignmentBaseline
827            | AId::BaselineShift
828            | AId::ClipPath
829            | AId::Display
830            | AId::DominantBaseline
831            | AId::Filter
832            | AId::FloodColor
833            | AId::FloodOpacity
834            | AId::Mask
835            | AId::Opacity
836            | AId::Overflow
837            | AId::LightingColor
838            | AId::StopColor
839            | AId::StopOpacity
840            | AId::TextDecoration
841            | AId::Transform
842            | AId::TransformOrigin
843    )
844}
845
846// TODO: is there a way yo make it less ugly? Too many lifetimes.
847/// A trait for parsing attribute values.
848pub trait FromValue<'a, 'input: 'a>: Sized {
849    /// Parses an attribute value.
850    ///
851    /// When `None` is returned, the attribute value will be logged as a parsing failure.
852    fn parse(node: SvgNode<'a, 'input>, aid: AId, value: &'a str) -> Option<Self>;
853}
854
855impl<'a, 'input: 'a> FromValue<'a, 'input> for &'a str {
856    fn parse(_: SvgNode<'a, 'input>, _: AId, value: &'a str) -> Option<Self> {
857        Some(value)
858    }
859}
860
861impl<'a, 'input: 'a> FromValue<'a, 'input> for f32 {
862    fn parse(_: SvgNode, _: AId, value: &str) -> Option<Self> {
863        svgtypes::Number::from_str(value).ok().map(|v| v.0 as f32)
864    }
865}
866
867impl<'a, 'input: 'a> FromValue<'a, 'input> for svgtypes::Length {
868    fn parse(_: SvgNode, _: AId, value: &str) -> Option<Self> {
869        svgtypes::Length::from_str(value).ok()
870    }
871}
872
873// TODO: to svgtypes?
874impl<'a, 'input: 'a> FromValue<'a, 'input> for Opacity {
875    fn parse(_: SvgNode, _: AId, value: &str) -> Option<Self> {
876        let length = svgtypes::Length::from_str(value).ok()?;
877        if length.unit == svgtypes::LengthUnit::Percent {
878            Some(Opacity::new_clamped(length.number as f32 / 100.0))
879        } else if length.unit == svgtypes::LengthUnit::None {
880            Some(Opacity::new_clamped(length.number as f32))
881        } else {
882            None
883        }
884    }
885}
886
887impl<'a, 'input: 'a> FromValue<'a, 'input> for Transform {
888    fn parse(_: SvgNode, _: AId, value: &str) -> Option<Self> {
889        let ts = match svgtypes::Transform::from_str(value) {
890            Ok(v) => v,
891            Err(_) => return None,
892        };
893
894        let ts = Transform::from_row(
895            ts.a as f32,
896            ts.b as f32,
897            ts.c as f32,
898            ts.d as f32,
899            ts.e as f32,
900            ts.f as f32,
901        );
902
903        if ts.is_valid() {
904            Some(ts)
905        } else {
906            Some(Transform::default())
907        }
908    }
909}
910
911impl<'a, 'input: 'a> FromValue<'a, 'input> for svgtypes::TransformOrigin {
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::ViewBox {
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 Units {
924    fn parse(_: SvgNode, _: AId, value: &str) -> Option<Self> {
925        match value {
926            "userSpaceOnUse" => Some(Units::UserSpaceOnUse),
927            "objectBoundingBox" => Some(Units::ObjectBoundingBox),
928            _ => None,
929        }
930    }
931}
932
933impl<'a, 'input: 'a> FromValue<'a, 'input> for svgtypes::AspectRatio {
934    fn parse(_: SvgNode, _: AId, value: &str) -> Option<Self> {
935        Self::from_str(value).ok()
936    }
937}
938
939impl<'a, 'input: 'a> FromValue<'a, 'input> for svgtypes::PaintOrder {
940    fn parse(_: SvgNode, _: AId, value: &str) -> Option<Self> {
941        Self::from_str(value).ok()
942    }
943}
944
945impl<'a, 'input: 'a> FromValue<'a, 'input> for svgtypes::Color {
946    fn parse(_: SvgNode, _: AId, value: &str) -> Option<Self> {
947        Self::from_str(value).ok()
948    }
949}
950
951impl<'a, 'input: 'a> FromValue<'a, 'input> for svgtypes::Angle {
952    fn parse(_: SvgNode, _: AId, value: &str) -> Option<Self> {
953        Self::from_str(value).ok()
954    }
955}
956
957impl<'a, 'input: 'a> FromValue<'a, 'input> for svgtypes::EnableBackground {
958    fn parse(_: SvgNode, _: AId, value: &str) -> Option<Self> {
959        Self::from_str(value).ok()
960    }
961}
962
963impl<'a, 'input: 'a> FromValue<'a, 'input> for svgtypes::Paint<'a> {
964    fn parse(_: SvgNode, _: AId, value: &'a str) -> Option<Self> {
965        Self::from_str(value).ok()
966    }
967}
968
969impl<'a, 'input: 'a> FromValue<'a, 'input> for Vec<f32> {
970    fn parse(_: SvgNode, _: AId, value: &str) -> Option<Self> {
971        let mut list = Vec::new();
972        for n in svgtypes::NumberListParser::from(value) {
973            list.push(n.ok()? as f32);
974        }
975
976        Some(list)
977    }
978}
979
980impl<'a, 'input: 'a> FromValue<'a, 'input> for Vec<svgtypes::Length> {
981    fn parse(_: SvgNode, _: AId, value: &str) -> Option<Self> {
982        let mut list = Vec::new();
983        for n in svgtypes::LengthListParser::from(value) {
984            list.push(n.ok()?);
985        }
986
987        Some(list)
988    }
989}
990
991impl<'a, 'input: 'a> FromValue<'a, 'input> for Visibility {
992    fn parse(_: SvgNode, _: AId, value: &str) -> Option<Self> {
993        match value {
994            "visible" => Some(Visibility::Visible),
995            "hidden" => Some(Visibility::Hidden),
996            "collapse" => Some(Visibility::Collapse),
997            _ => None,
998        }
999    }
1000}
1001
1002impl<'a, 'input: 'a> FromValue<'a, 'input> for SpreadMethod {
1003    fn parse(_: SvgNode, _: AId, value: &str) -> Option<Self> {
1004        match value {
1005            "pad" => Some(SpreadMethod::Pad),
1006            "reflect" => Some(SpreadMethod::Reflect),
1007            "repeat" => Some(SpreadMethod::Repeat),
1008            _ => None,
1009        }
1010    }
1011}
1012
1013impl<'a, 'input: 'a> FromValue<'a, 'input> for ShapeRendering {
1014    fn parse(_: SvgNode, _: AId, value: &str) -> Option<Self> {
1015        match value {
1016            "optimizeSpeed" => Some(ShapeRendering::OptimizeSpeed),
1017            "crispEdges" => Some(ShapeRendering::CrispEdges),
1018            "auto" | "geometricPrecision" => Some(ShapeRendering::GeometricPrecision),
1019            _ => None,
1020        }
1021    }
1022}
1023
1024impl<'a, 'input: 'a> FromValue<'a, 'input> for TextRendering {
1025    fn parse(_: SvgNode, _: AId, value: &str) -> Option<Self> {
1026        match value {
1027            "optimizeSpeed" => Some(TextRendering::OptimizeSpeed),
1028            "auto" | "optimizeLegibility" => Some(TextRendering::OptimizeLegibility),
1029            "geometricPrecision" => Some(TextRendering::GeometricPrecision),
1030            _ => None,
1031        }
1032    }
1033}
1034
1035impl<'a, 'input: 'a> FromValue<'a, 'input> for ImageRendering {
1036    fn parse(_: SvgNode, _: AId, value: &str) -> Option<Self> {
1037        match value {
1038            "auto" | "optimizeQuality" => Some(ImageRendering::OptimizeQuality),
1039            "optimizeSpeed" => Some(ImageRendering::OptimizeSpeed),
1040            "smooth" => Some(ImageRendering::Smooth),
1041            "high-quality" => Some(ImageRendering::HighQuality),
1042            "crisp-edges" => Some(ImageRendering::CrispEdges),
1043            "pixelated" => Some(ImageRendering::Pixelated),
1044            _ => None,
1045        }
1046    }
1047}
1048
1049impl<'a, 'input: 'a> FromValue<'a, 'input> for BlendMode {
1050    fn parse(_: SvgNode, _: AId, value: &str) -> Option<Self> {
1051        match value {
1052            "normal" => Some(BlendMode::Normal),
1053            "multiply" => Some(BlendMode::Multiply),
1054            "screen" => Some(BlendMode::Screen),
1055            "overlay" => Some(BlendMode::Overlay),
1056            "darken" => Some(BlendMode::Darken),
1057            "lighten" => Some(BlendMode::Lighten),
1058            "color-dodge" => Some(BlendMode::ColorDodge),
1059            "color-burn" => Some(BlendMode::ColorBurn),
1060            "hard-light" => Some(BlendMode::HardLight),
1061            "soft-light" => Some(BlendMode::SoftLight),
1062            "difference" => Some(BlendMode::Difference),
1063            "exclusion" => Some(BlendMode::Exclusion),
1064            "hue" => Some(BlendMode::Hue),
1065            "saturation" => Some(BlendMode::Saturation),
1066            "color" => Some(BlendMode::Color),
1067            "luminosity" => Some(BlendMode::Luminosity),
1068            _ => None,
1069        }
1070    }
1071}
1072
1073impl<'a, 'input: 'a> FromValue<'a, 'input> for SvgNode<'a, 'input> {
1074    fn parse(node: SvgNode<'a, 'input>, aid: AId, value: &str) -> Option<Self> {
1075        let id = if aid == AId::Href {
1076            svgtypes::IRI::from_str(value).ok().map(|v| v.0)
1077        } else {
1078            svgtypes::FuncIRI::from_str(value).ok().map(|v| v.0)
1079        }?;
1080
1081        node.document().element_by_id(id)
1082    }
1083}