skia_safe/utils/
parse_path.rs

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
use crate::{
    interop,
    prelude::{IfBoolSome, NativeAccess},
    Path,
};
use skia_bindings as sb;
use std::ffi::CString;

pub fn from_svg(svg: impl AsRef<str>) -> Option<Path> {
    let str = CString::new(svg.as_ref()).unwrap();
    let mut path = Path::default();
    unsafe { sb::SkParsePath_FromSVGString(str.as_ptr(), path.native_mut()) }.if_true_some(path)
}

pub use skia_bindings::SkParsePath_PathEncoding as PathEncoding;
variant_name!(PathEncoding::Absolute);

impl Path {
    pub fn from_svg(svg: impl AsRef<str>) -> Option<Path> {
        from_svg(svg)
    }

    pub fn to_svg(&self) -> String {
        to_svg(self)
    }

    pub fn to_svg_with_encoding(&self, encoding: PathEncoding) -> String {
        to_svg_with_encoding(self, encoding)
    }
}

pub fn to_svg(path: &Path) -> String {
    to_svg_with_encoding(path, PathEncoding::Absolute)
}

pub fn to_svg_with_encoding(path: &Path, encoding: PathEncoding) -> String {
    interop::String::construct(|svg| {
        unsafe { sb::C_SkParsePath_ToSVGString(path.native(), svg, encoding) };
    })
    .as_str()
    .into()
}

#[cfg(test)]
mod tests {
    use super::Path;

    #[test]
    fn simple_path_to_svg_and_back() {
        let mut path = Path::default();
        path.move_to((0, 0));
        path.line_to((100, 100));
        path.line_to((0, 100));
        path.close();

        let svg = Path::to_svg(&path);
        assert_eq!(svg, "M0 0L100 100L0 100L0 0Z");
        // And back. Someone may find why they are not equal.
        let _recreated = Path::from_svg(svg).expect("Failed to parse SVG path");
    }
}