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