pulldown_cmark_mdcat/resources/
svg.rs1use std::io::Result;
10
11pub fn render_svg_to_png(svg: &[u8]) -> Result<Vec<u8>> {
13 implementation::render_svg_to_png(svg)
14}
15
16#[cfg(feature = "svg")]
17mod implementation {
18 use std::fmt::Display;
19 use std::sync::{Arc, OnceLock};
20 use std::{error::Error, io::ErrorKind};
21
22 use resvg::tiny_skia::{IntSize, Pixmap, Transform};
23 use resvg::usvg::{self, Tree};
24 use usvg::fontdb;
25
26 #[derive(Debug)]
27 pub enum RenderSvgError {
28 ParseError(usvg::Error),
29 FailedToCreatePixmap(IntSize),
30 EncodePngError(Box<dyn Error + Send + Sync>),
31 }
32
33 impl Display for RenderSvgError {
34 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35 match self {
36 RenderSvgError::ParseError(error) => write!(f, "Failed to parse SVG: {error}"),
37 RenderSvgError::FailedToCreatePixmap(int_size) => {
38 write!(f, "Failed to create pixmap of size {int_size:?}")
39 }
40 RenderSvgError::EncodePngError(error) => {
41 write!(f, "Failed to encode pixmap to PNG image: {error}")
42 }
43 }
44 }
45 }
46
47 impl std::error::Error for RenderSvgError {
48 fn source(&self) -> Option<&(dyn Error + 'static)> {
49 match self {
50 RenderSvgError::ParseError(error) => Some(error),
51 RenderSvgError::FailedToCreatePixmap(_) => None,
52 RenderSvgError::EncodePngError(error) => Some(error.as_ref()),
53 }
54 }
55 }
56
57 impl From<RenderSvgError> for std::io::Error {
58 fn from(value: RenderSvgError) -> Self {
59 std::io::Error::new(ErrorKind::Other, value)
60 }
61 }
62
63 impl From<usvg::Error> for RenderSvgError {
64 fn from(value: usvg::Error) -> Self {
65 Self::ParseError(value)
66 }
67 }
68
69 static FONTS: OnceLock<Arc<fontdb::Database>> = OnceLock::new();
70
71 fn parse_svg(svg: &[u8]) -> Result<Tree, RenderSvgError> {
72 let fonts = FONTS.get_or_init(|| {
73 let mut fontdb = fontdb::Database::new();
74 fontdb.load_system_fonts();
75 Arc::new(fontdb)
76 });
77 let options = usvg::Options {
78 fontdb: fonts.clone(),
79 ..Default::default()
80 };
81 Ok(usvg::Tree::from_data(svg, &options)?)
82 }
83
84 fn render_svg_to_png_with_resvg(svg: &[u8]) -> Result<Vec<u8>, RenderSvgError> {
85 let tree = parse_svg(svg)?;
86 let size = tree.size().to_int_size();
87 let mut pixmap = Pixmap::new(size.width(), size.height())
88 .ok_or(RenderSvgError::FailedToCreatePixmap(size))?;
89 resvg::render(&tree, Transform::default(), &mut pixmap.as_mut());
93 pixmap
94 .encode_png()
95 .map_err(|err| RenderSvgError::EncodePngError(Box::new(err)))
96 }
97
98 pub fn render_svg_to_png(svg: &[u8]) -> std::io::Result<Vec<u8>> {
99 render_svg_to_png_with_resvg(svg).map_err(Into::into)
100 }
101}
102
103#[cfg(not(feature = "svg"))]
104mod implementation {
105 use std::io::{Error, ErrorKind, Result};
106 pub fn render_svg_to_png(_svg: &[u8]) -> Result<Vec<u8>> {
107 Err(Error::new(
108 ErrorKind::Unsupported,
109 "SVG rendering not enabled in this build",
110 ))
111 }
112}