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;
25pub(crate) use svgtree::{AId, EId};
26
27#[derive(Debug)]
29pub enum Error {
30 NotAnUtf8Str,
32
33 MalformedGZip,
35
36 ElementsLimitReached,
38
39 InvalidSize,
45
46 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 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 pub fn from_data_nested(data: &[u8], opt: &Options) -> Result<Self, Error> {
113 let nested_opt = Options {
114 resources_dir: None,
115 dpi: opt.dpi,
116 font_size: opt.font_size,
117 languages: opt.languages.clone(),
118 shape_rendering: opt.shape_rendering,
119 text_rendering: opt.text_rendering,
120 image_rendering: opt.image_rendering,
121 default_size: opt.default_size,
122 image_href_resolver: ImageHrefResolver {
123 resolve_data: Box::new(|a, b, c| (opt.image_href_resolver.resolve_data)(a, b, c)),
124 resolve_string: Box::new(|_, _| None),
126 },
127 #[cfg(feature = "text")]
130 fontdb: opt.fontdb.clone(),
131 #[cfg(feature = "text")]
133 font_resolver: crate::FontResolver {
134 select_font: Box::new(|font, db| (opt.font_resolver.select_font)(font, db)),
135 select_fallback: Box::new(|c, used_fonts, db| {
136 (opt.font_resolver.select_fallback)(c, used_fonts, db)
137 }),
138 },
139 ..Options::default()
140 };
141
142 Self::from_data(data, &nested_opt)
143 }
144
145 pub fn from_str(text: &str, opt: &Options) -> Result<Self, Error> {
147 let xml_opt = roxmltree::ParsingOptions {
148 allow_dtd: true,
149 ..Default::default()
150 };
151
152 let doc =
153 roxmltree::Document::parse_with_options(text, xml_opt).map_err(Error::ParsingFailed)?;
154
155 Self::from_xmltree(&doc, opt)
156 }
157
158 pub fn from_xmltree(doc: &roxmltree::Document, opt: &Options) -> Result<Self, Error> {
160 let doc = svgtree::Document::parse_tree(doc, opt.style_sheet.as_deref())?;
161 self::converter::convert_doc(&doc, opt)
162 }
163}
164
165pub fn decompress_svgz(data: &[u8]) -> Result<Vec<u8>, Error> {
167 use std::io::Read;
168
169 let mut decoder = flate2::read::GzDecoder::new(data);
170 let mut decoded = Vec::with_capacity(data.len() * 2);
171 decoder
172 .read_to_end(&mut decoded)
173 .map_err(|_| Error::MalformedGZip)?;
174 Ok(decoded)
175}
176
177#[inline]
178pub(crate) fn f32_bound(min: f32, val: f32, max: f32) -> f32 {
179 debug_assert!(min.is_finite());
180 debug_assert!(val.is_finite());
181 debug_assert!(max.is_finite());
182
183 if val > max {
184 max
185 } else if val < min {
186 min
187 } else {
188 val
189 }
190}