skia_safe/modules/svg/
dom.rs

1use std::{
2    error::Error,
3    fmt,
4    io::{self},
5};
6
7use crate::{
8    interop::{MemoryStream, NativeStreamBase, RustStream},
9    prelude::*,
10    resources::NativeResourceProvider,
11    Canvas, Size,
12};
13use skia_bindings::{self as sb, SkRefCntBase};
14
15use super::Svg;
16
17pub type Dom = RCHandle<sb::SkSVGDOM>;
18require_base_type!(sb::SkSVGDOM, sb::SkRefCnt);
19
20impl NativeRefCountedBase for sb::SkSVGDOM {
21    type Base = SkRefCntBase;
22}
23
24impl fmt::Debug for Dom {
25    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26        f.debug_struct("Dom").finish()
27    }
28}
29
30/// This type represents an SVG as a node-based data structure.
31///
32/// To convert an SVG to a `Dom`, a [`NativeResourceProvider`] is required.
33///
34/// ### Creating a Resource Provider
35///
36/// To create a resource provider, a [`crate::FontMgr`] is required at a minimum.
37///
38/// - If you don't need font support, pass [`crate::FontMgr::new_empty()`] as the resource provider.
39/// - To use the installed fonts on your system, pass [`crate::FontMgr::default()`] as the resource provider.
40///
41/// When you pass a [`crate::FontMgr`] as the resource provider, a
42/// [`crate::resources::LocalResourceProvider`] is created behind the scenes. This provider, in
43/// addition to supporting typefaces, also adds support for `data:` URLs.
44///
45/// ### Supporting External Resources
46///
47/// To support `http://` or `https://` external resources, enable the `ureq` feature and create a
48/// [`crate::resources::UReqResourceProvider`].
49///
50/// ### Custom Resource Providers
51///
52/// If you need more customization, you can implement the trait [`crate::resources::ResourceProvider`].
53impl Dom {
54    pub fn read<R: io::Read>(
55        mut reader: R,
56        resource_provider: impl Into<NativeResourceProvider>,
57    ) -> Result<Self, LoadError> {
58        let mut reader = RustStream::new(&mut reader);
59        let stream = reader.stream_mut();
60        let resource_provider = resource_provider.into();
61
62        let out = unsafe { sb::C_SkSVGDOM_MakeFromStream(stream, resource_provider.into_ptr()) };
63
64        Self::from_ptr(out).ok_or(LoadError)
65    }
66
67    pub fn from_str(
68        svg: impl AsRef<str>,
69        resource_provider: impl Into<NativeResourceProvider>,
70    ) -> Result<Self, LoadError> {
71        Self::from_bytes(svg.as_ref().as_bytes(), resource_provider)
72    }
73
74    pub fn from_bytes(
75        svg: &[u8],
76        resource_provider: impl Into<NativeResourceProvider>,
77    ) -> Result<Self, LoadError> {
78        let mut ms = MemoryStream::from_bytes(svg);
79        let resource_provider = resource_provider.into();
80
81        let out = unsafe {
82            sb::C_SkSVGDOM_MakeFromStream(
83                ms.native_mut().as_stream_mut(),
84                resource_provider.into_ptr(),
85            )
86        };
87        Self::from_ptr(out).ok_or(LoadError)
88    }
89
90    pub fn root(&self) -> Svg {
91        unsafe {
92            Svg::from_unshared_ptr(sb::C_SkSVGDOM_getRoot(self.native()) as *mut _)
93                .unwrap_unchecked()
94        }
95    }
96
97    pub fn render(&self, canvas: &Canvas) {
98        // TODO: may be we should init ICU whenever we expose a Canvas?
99        #[cfg(all(feature = "embed-icudtl", feature = "textlayout"))]
100        crate::icu::init();
101
102        unsafe { sb::SkSVGDOM::render(self.native() as &_, canvas.native_mut()) }
103    }
104
105    pub fn set_container_size(&mut self, size: impl Into<Size>) {
106        let size = size.into();
107        unsafe { sb::C_SkSVGDOM_setContainerSize(self.native_mut(), size.native()) }
108    }
109}
110
111/// Error when something goes wrong when loading an SVG file. Sadly, Skia doesn't give further
112/// details so we can't return a more expressive error type, but we still use this instead of
113/// `Option` to express the intent and allow for `Try`.
114#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
115pub struct LoadError;
116
117impl fmt::Display for LoadError {
118    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
119        write!(f, "Failed to load svg (reason unknown)")
120    }
121}
122
123impl Error for LoadError {
124    fn description(&self) -> &str {
125        "Failed to load svg (reason unknown)"
126    }
127}
128
129impl From<LoadError> for io::Error {
130    fn from(other: LoadError) -> Self {
131        io::Error::other(other)
132    }
133}