1mod 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#[derive(Debug)]
28pub enum Error {
29 NotAnUtf8Str,
31
32 MalformedGZip,
34
35 ElementsLimitReached,
37
38 InvalidSize,
44
45 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 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 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 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
128pub 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}