usvg/parser/
mod.rs

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