1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
|
use {
crate::{
errors::SvgError,
},
image::{
DynamicImage,
RgbaImage,
},
std::path::PathBuf,
usvg::ScreenSize,
usvg_text_layout::{fontdb, TreeTextToPath},
};
fn compute_zoom(width:u32, height:u32, max_width:u32, max_height:u32) -> Result<f32, SvgError> {
let w: f32 = width as f32;
let h: f32 = height as f32;
let mw: f32 = max_width.max(2) as f32;
let mh: f32 = max_height.max(2) as f32;
let zoom = 1.0f32
.min(mw / w)
.min(mh / h);
if zoom > 0.0f32 {
Ok(zoom)
} else {
Err(SvgError::Internal { message: "invalid SVG dimensions" })
}
}
/// Generate a bitmap at the natural dimensions of the SVG unless it's too big
/// in which case a smaller one is generated to fit into (max_width x max_height).
pub fn render<P: Into<PathBuf>>(
path: P,
max_width: u32,
max_height: u32,
) -> Result<DynamicImage, SvgError> {
let path: PathBuf = path.into();
let mut opt = usvg::Options::default();
opt.resources_dir = Some(path.clone());
let mut fontdb = fontdb::Database::new();
fontdb.load_system_fonts();
let svg_data = std::fs::read(path)?;
let mut tree = usvg::Tree::from_data(&svg_data, &opt)?;
debug!("SVG natural size: {} x {}", tree.size.width(), tree.size.height());
let px_size = tree.size.to_screen_size();
let zoom = compute_zoom(px_size.width(), px_size.height(), max_width, max_height)?;
debug!("svg rendering zoom: {zoom}");
let Some(px_size) = ScreenSize::new(
(px_size.width() as f32 * zoom) as u32,
(px_size.height() as f32 * zoom) as u32,
) else {
return Err(SvgError::Internal { message: "invalid SVG dimensions" });
};
debug!("px_size: {px_size:?}");
tree.convert_text(&fontdb, opt.keep_named_groups);
let mut pixmap = tiny_skia::Pixmap::new(
px_size.width(),
px_size.height(),
).ok_or(SvgError::Internal { message: "unable to create pixmap buffer" })?;
resvg::render(
&tree,
usvg::FitTo::Zoom(zoom),
tiny_skia::Transform::default(),
pixmap.as_mut(),
).ok_or(SvgError::Internal { message: "resvg doesn't look happy (not sure)" })?;
let image_buffer = RgbaImage::from_vec(
pixmap.width(),
pixmap.height(),
pixmap.take(),
).ok_or(SvgError::Internal { message: "wrong image buffer size" })?;
Ok(DynamicImage::ImageRgba8(image_buffer))
}
|