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