skia_safe/svg/
canvas.rs

1use std::{fmt, ops::Deref, pin::Pin, ptr};
2
3use skia_bindings::{self as sb, SkCanvas};
4
5use crate::{interop::DynamicMemoryWStream, prelude::*, Data, Rect};
6
7pub struct Canvas {
8    canvas: *mut SkCanvas,
9    stream: Pin<Box<DynamicMemoryWStream>>,
10}
11
12impl Drop for Canvas {
13    fn drop(&mut self) {
14        unsafe {
15            sb::C_SkCanvas_delete(self.canvas);
16        }
17    }
18}
19
20impl Deref for Canvas {
21    type Target = crate::Canvas;
22
23    fn deref(&self) -> &Self::Target {
24        crate::Canvas::borrow_from_native(unsafe { &*self.canvas })
25    }
26}
27
28bitflags! {
29    #[derive(Default, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
30    pub struct Flags : u32 {
31        const CONVERT_TEXT_TO_PATHS = sb::SkSVGCanvas_kConvertTextToPaths_Flag as _;
32        const NO_PRETTY_XML = sb::SkSVGCanvas_kNoPrettyXML_Flag as _;
33        const RELATIVE_PATH_ENCODING = sb::SkSVGCanvas_kRelativePathEncoding_Flag as _;
34    }
35}
36
37impl fmt::Debug for Canvas {
38    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39        f.debug_struct("Canvas")
40            .field(
41                "canvas",
42                crate::Canvas::borrow_from_native(unsafe { &*self.canvas }),
43            )
44            .field("stream", &self.stream)
45            .finish()
46    }
47}
48
49impl Canvas {
50    /// Creates a new SVG canvas.
51    pub fn new(bounds: impl AsRef<Rect>, flags: impl Into<Option<Flags>>) -> Canvas {
52        let bounds = bounds.as_ref();
53        let flags = flags.into().unwrap_or_default();
54        let mut stream = Box::pin(DynamicMemoryWStream::new());
55        let canvas = unsafe {
56            sb::C_SkSVGCanvas_Make(
57                bounds.native(),
58                &mut stream.native_mut()._base,
59                flags.bits(),
60            )
61        };
62        Canvas { canvas, stream }
63    }
64
65    /// Ends the Canvas drawing and returns the resulting SVG.
66    /// TODO: rename to into_svg() or into_svg_data()?
67    pub fn end(mut self) -> Data {
68        // note: flushing canvas + XMLStreamWriter does not seem to work,
69        // we have to delete the canvas and destruct the stream writer
70        // to get all data out _and_ keep the referential integrity.
71        unsafe {
72            sb::C_SkCanvas_delete(self.canvas);
73        }
74        self.canvas = ptr::null_mut();
75        self.stream.detach_as_data()
76    }
77}
78
79#[cfg(test)]
80mod tests {
81    use super::Canvas;
82    use crate::Rect;
83
84    #[test]
85    fn test_svg() {
86        use crate::Paint;
87
88        let canvas = Canvas::new(Rect::from_size((20, 20)), None);
89        let paint = Paint::default();
90        canvas.draw_circle((10, 10), 10.0, &paint);
91        let data = canvas.end();
92        let contents = String::from_utf8_lossy(data.as_bytes());
93        dbg!(&contents);
94        assert!(contents.contains(r#"<ellipse cx="10" cy="10" rx="10" ry="10"/>"#));
95        assert!(contents.contains(r#"</svg>"#));
96    }
97
98    #[test]
99    fn test_svg_without_ending() {
100        use crate::Paint;
101        let canvas = Canvas::new(Rect::from_size((20, 20)), None);
102        let paint = Paint::default();
103        canvas.draw_circle((10, 10), 10.0, &paint);
104    }
105}