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#[cfg(feature = "text")]
22pub(crate) use converter::Cache;
23pub use image::{ImageHrefDataResolverFn, ImageHrefResolver, ImageHrefStringResolverFn};
24pub use options::Options;
25#[cfg(feature = "writer")]
26pub(crate) use svgtree::{AId, EId};
27
28#[derive(Debug)]
30pub enum Error {
31 NotAnUtf8Str,
33
34 SvgzFeatureNotEnabled,
36
37 MalformedGZip,
39
40 ElementsLimitReached,
42
43 InvalidSize,
49
50 ParsingFailed(roxmltree::Error),
52}
53
54impl From<roxmltree::Error> for Error {
55 fn from(e: roxmltree::Error) -> Self {
56 Error::ParsingFailed(e)
57 }
58}
59
60impl std::fmt::Display for Error {
61 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
62 match *self {
63 Error::NotAnUtf8Str => {
64 write!(f, "provided data has not an UTF-8 encoding")
65 }
66 Self::SvgzFeatureNotEnabled => {
67 write!(f, "enable svgz cargo feature to decode SVGZ data")
68 }
69 Error::MalformedGZip => {
70 write!(f, "provided data has a malformed GZip content")
71 }
72 Error::ElementsLimitReached => {
73 write!(f, "the maximum number of SVG elements has been reached")
74 }
75 Error::InvalidSize => {
76 write!(f, "SVG has an invalid size")
77 }
78 Error::ParsingFailed(ref e) => {
79 write!(f, "SVG data parsing failed cause {}", e)
80 }
81 }
82 }
83}
84
85impl std::error::Error for Error {}
86
87pub(crate) trait OptionLog {
88 fn log_none<F: FnOnce()>(self, f: F) -> Self;
89}
90
91impl<T> OptionLog for Option<T> {
92 #[inline]
93 fn log_none<F: FnOnce()>(self, f: F) -> Self {
94 self.or_else(|| {
95 f();
96 None
97 })
98 }
99}
100
101impl crate::Tree {
102 pub fn from_data(data: &[u8], opt: &Options) -> Result<Self, Error> {
106 if data.starts_with(&[0x1f, 0x8b]) {
107 #[cfg(feature = "svgz")]
108 {
109 let data = decompress_svgz(data)?;
110 let text = std::str::from_utf8(&data).map_err(|_| Error::NotAnUtf8Str)?;
111 Self::from_str(text, opt)
112 }
113
114 #[cfg(not(feature = "svgz"))]
115 Err(Error::SvgzFeatureNotEnabled)
116 } else {
117 let text = std::str::from_utf8(data).map_err(|_| Error::NotAnUtf8Str)?;
118 Self::from_str(text, opt)
119 }
120 }
121
122 pub fn from_data_nested(data: &[u8], opt: &Options) -> Result<Self, Error> {
126 let nested_opt = Options {
127 resources_dir: None,
128 dpi: opt.dpi,
129 font_size: opt.font_size,
130 languages: opt.languages.clone(),
131 shape_rendering: opt.shape_rendering,
132 text_rendering: opt.text_rendering,
133 image_rendering: opt.image_rendering,
134 default_size: opt.default_size,
135 image_href_resolver: ImageHrefResolver {
136 resolve_data: Box::new(|a, b, c| (opt.image_href_resolver.resolve_data)(a, b, c)),
137 resolve_string: Box::new(|_, _| None),
139 },
140 #[cfg(feature = "text")]
143 fontdb: opt.fontdb.clone(),
144 #[cfg(feature = "text")]
146 font_resolver: crate::FontResolver {
147 select_font: Box::new(|font, db| (opt.font_resolver.select_font)(font, db)),
148 select_fallback: Box::new(|c, used_fonts, db| {
149 (opt.font_resolver.select_fallback)(c, used_fonts, db)
150 }),
151 },
152 ..Options::default()
153 };
154
155 Self::from_data(data, &nested_opt)
156 }
157
158 pub fn from_str(text: &str, opt: &Options) -> Result<Self, Error> {
160 let xml_opt = roxmltree::ParsingOptions {
161 allow_dtd: true,
162 ..Default::default()
163 };
164
165 let doc =
166 roxmltree::Document::parse_with_options(text, xml_opt).map_err(Error::ParsingFailed)?;
167
168 Self::from_xmltree(&doc, opt)
169 }
170
171 pub fn from_xmltree(doc: &roxmltree::Document, opt: &Options) -> Result<Self, Error> {
173 let doc = svgtree::Document::parse_tree(doc, opt.style_sheet.as_deref())?;
174 self::converter::convert_doc(&doc, opt)
175 }
176}
177
178#[cfg(feature = "svgz")]
180pub fn decompress_svgz(data: &[u8]) -> Result<Vec<u8>, Error> {
181 use std::io::Read;
182
183 let mut decoder = flate2::read::GzDecoder::new(data);
184 let mut decoded = Vec::with_capacity(data.len() * 2);
185 decoder
186 .read_to_end(&mut decoded)
187 .map_err(|_| Error::MalformedGZip)?;
188 Ok(decoded)
189}
190
191#[inline]
192pub(crate) fn f32_bound(min: f32, val: f32, max: f32) -> f32 {
193 debug_assert!(min.is_finite());
194 debug_assert!(max.is_finite());
195
196 if val > max {
197 max
198 } else if val >= min {
199 val
200 } else {
201 min
203 }
204}