usvg/parser/
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
5mod clippath;
6mod converter;
7mod filter;
8mod image;
9mod marker;
10mod mask;
11mod options;
12mod paint_server;
13mod shapes;
14mod style;
15mod svgtree;
16mod switch;
17mod units;
18mod use_node;
19
20#[cfg(feature = "text")]
21mod text;
22
23pub use image::{ImageHrefDataResolverFn, ImageHrefResolver, ImageHrefStringResolverFn};
24pub use options::Options;
25pub(crate) use svgtree::{AId, EId};
26
27/// List of all errors.
28#[derive(Debug)]
29pub enum Error {
30    /// Only UTF-8 content are supported.
31    NotAnUtf8Str,
32
33    /// Compressed SVG must use the GZip algorithm.
34    MalformedGZip,
35
36    /// We do not allow SVG with more than 1_000_000 elements for security reasons.
37    ElementsLimitReached,
38
39    /// SVG doesn't have a valid size.
40    ///
41    /// Occurs when width and/or height are <= 0.
42    ///
43    /// Also occurs if width, height and viewBox are not set.
44    InvalidSize,
45
46    /// Failed to parse an SVG data.
47    ParsingFailed(roxmltree::Error),
48}
49
50impl From<roxmltree::Error> for Error {
51    fn from(e: roxmltree::Error) -> Self {
52        Error::ParsingFailed(e)
53    }
54}
55
56impl std::fmt::Display for Error {
57    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
58        match *self {
59            Error::NotAnUtf8Str => {
60                write!(f, "provided data has not an UTF-8 encoding")
61            }
62            Error::MalformedGZip => {
63                write!(f, "provided data has a malformed GZip content")
64            }
65            Error::ElementsLimitReached => {
66                write!(f, "the maximum number of SVG elements has been reached")
67            }
68            Error::InvalidSize => {
69                write!(f, "SVG has an invalid size")
70            }
71            Error::ParsingFailed(ref e) => {
72                write!(f, "SVG data parsing failed cause {}", e)
73            }
74        }
75    }
76}
77
78impl std::error::Error for Error {}
79
80pub(crate) trait OptionLog {
81    fn log_none<F: FnOnce()>(self, f: F) -> Self;
82}
83
84impl<T> OptionLog for Option<T> {
85    #[inline]
86    fn log_none<F: FnOnce()>(self, f: F) -> Self {
87        self.or_else(|| {
88            f();
89            None
90        })
91    }
92}
93
94impl crate::Tree {
95    /// Parses `Tree` from an SVG data.
96    ///
97    /// Can contain an SVG string or a gzip compressed data.
98    pub fn from_data(data: &[u8], opt: &Options) -> Result<Self, Error> {
99        if data.starts_with(&[0x1f, 0x8b]) {
100            let data = decompress_svgz(data)?;
101            let text = std::str::from_utf8(&data).map_err(|_| Error::NotAnUtf8Str)?;
102            Self::from_str(text, opt)
103        } else {
104            let text = std::str::from_utf8(data).map_err(|_| Error::NotAnUtf8Str)?;
105            Self::from_str(text, opt)
106        }
107    }
108
109    /// Parses `Tree` from an SVG string.
110    pub fn from_str(text: &str, opt: &Options) -> Result<Self, Error> {
111        let xml_opt = roxmltree::ParsingOptions {
112            allow_dtd: true,
113            ..Default::default()
114        };
115
116        let doc =
117            roxmltree::Document::parse_with_options(text, xml_opt).map_err(Error::ParsingFailed)?;
118
119        Self::from_xmltree(&doc, opt)
120    }
121
122    /// Parses `Tree` from `roxmltree::Document`.
123    pub fn from_xmltree(doc: &roxmltree::Document, opt: &Options) -> Result<Self, Error> {
124        let doc = svgtree::Document::parse_tree(doc)?;
125        self::converter::convert_doc(&doc, opt)
126    }
127}
128
129/// Decompresses an SVGZ file.
130pub fn decompress_svgz(data: &[u8]) -> Result<Vec<u8>, Error> {
131    use std::io::Read;
132
133    let mut decoder = flate2::read::GzDecoder::new(data);
134    let mut decoded = Vec::with_capacity(data.len() * 2);
135    decoder
136        .read_to_end(&mut decoded)
137        .map_err(|_| Error::MalformedGZip)?;
138    Ok(decoded)
139}
140
141#[inline]
142pub(crate) fn f32_bound(min: f32, val: f32, max: f32) -> f32 {
143    debug_assert!(min.is_finite());
144    debug_assert!(val.is_finite());
145    debug_assert!(max.is_finite());
146
147    if val > max {
148        max
149    } else if val < min {
150        min
151    } else {
152        val
153    }
154}