Skip to main content

cursive_image/image/compatibility/
svg.rs

1use super::{
2    super::{format::*, image::Image, stream::*},
3    zlib::*,
4};
5
6use {
7    resvg::*,
8    std::{
9        fs::*,
10        io,
11        path::{Path, *},
12    },
13    tiny_skia::*,
14    usvg::*,
15};
16
17impl Image {
18    /// Constructor.
19    ///
20    /// The implementation supports both [PNG](ImageFormat::PNG) and [RGBA](ImageFormat::RGBA).
21    pub fn new_owned_from_svg_file<PathT>(
22        path: PathT,
23        format: ImageFormat,
24        scale: f64,
25        compress: bool,
26    ) -> io::Result<Self>
27    where
28        PathT: AsRef<Path>,
29    {
30        let tree = read_svg(path)?;
31        let size = size(&tree, scale)?;
32        let data = render_svg(tree, format, scale)?;
33        let data = if compress { zlib(&data)? } else { data };
34        Ok(Self::new_owned(data, compress, format, (size.width(), size.height())))
35    }
36
37    /// Constructor.
38    ///
39    /// The implementation supports both [PNG](ImageFormat::PNG) and [RGBA](ImageFormat::RGBA).
40    pub fn new_stream_from_svg_file<PathT>(path: PathT, format: ImageFormat, scale: f64) -> io::Result<Self>
41    where
42        PathT: AsRef<Path>,
43    {
44        let path = path.as_ref();
45        let tree = read_svg(path)?;
46        let size = size(&tree, scale)?;
47        let stream = SvgImageStream::new(path.into(), format, scale);
48        Ok(Self::new_stream(stream, format, (size.width(), size.height())))
49    }
50}
51
52//
53// SvgImageStream
54//
55
56struct SvgImageStream {
57    path: PathBuf,
58    format: ImageFormat,
59    scale: f64,
60}
61
62impl SvgImageStream {
63    fn new(path: PathBuf, format: ImageFormat, scale: f64) -> Self {
64        Self { path, format, scale }
65    }
66}
67
68impl ImageStream for SvgImageStream {
69    fn open(&self) -> io::Result<(Box<dyn io::Read>, bool)> {
70        // We must load the entire file into memory to render it
71        let tree = read_svg(&self.path)?;
72        let data = render_svg(tree, self.format, self.scale)?;
73        Ok((Box::new(io::Cursor::new(data)), false))
74    }
75}
76
77// Utils
78
79fn read_svg<PathT>(path: PathT) -> io::Result<Tree>
80where
81    PathT: AsRef<Path>,
82{
83    let data = read(path.as_ref())?;
84
85    let mut options = Options::default();
86    options.fontdb_mut().load_system_fonts();
87
88    // TODO: this is not safe...
89    // if let Some(parent) = path.as_ref().parent() {
90    //     options.resources_dir = Some(parent.into());
91    // }
92
93    Ok(Tree::from_data(&data, &options).map_err(io::Error::other)?)
94}
95
96fn render_svg(tree: Tree, format: ImageFormat, scale: f64) -> io::Result<Vec<u8>> {
97    let size = size(&tree, scale)?;
98    let mut pixmap =
99        Pixmap::new(size.width(), size.height()).ok_or_else(|| io::Error::other("cannot create Pixmap"))?;
100    let scale = scale as f32;
101    render(&tree, Transform::from_scale(scale, scale), &mut pixmap.as_mut());
102
103    Ok(match format {
104        ImageFormat::PNG => pixmap.encode_png()?,
105        ImageFormat::RGBA => pixmap.take(),
106        _ => return Err(io::Error::other("unsupported format")),
107    })
108}
109
110fn size(tree: &Tree, scale: f64) -> io::Result<IntSize> {
111    Ok(tree.size().scale_by(scale as f32).ok_or_else(|| io::Error::other("scaling error"))?.to_int_size())
112}