qr_code_styling/rendering/
raster_renderer.rs1use crate::error::{QRError, Result};
4use crate::types::OutputFormat;
5use image::{DynamicImage, ImageFormat, RgbaImage};
6use resvg::tiny_skia::Pixmap;
7use resvg::usvg::{Options, Transform, Tree};
8use std::io::Cursor;
9
10pub struct RasterRenderer;
12
13impl RasterRenderer {
14 pub fn render(svg: &str, width: u32, height: u32, format: OutputFormat) -> Result<Vec<u8>> {
16 let image = Self::svg_to_image(svg, width, height)?;
18
19 Self::encode_image(&image, format)
21 }
22
23 fn svg_to_image(svg: &str, width: u32, height: u32) -> Result<DynamicImage> {
25 let tree = Tree::from_str(svg, &Options::default())
27 .map_err(|e| QRError::SvgError(e.to_string()))?;
28
29 let svg_size = tree.size();
31
32 let mut pixmap = Pixmap::new(width, height)
34 .ok_or_else(|| QRError::SvgError("Failed to create pixmap".to_string()))?;
35
36 pixmap.fill(resvg::tiny_skia::Color::WHITE);
38
39 let scale_x = width as f32 / svg_size.width();
41 let scale_y = height as f32 / svg_size.height();
42 let scale = scale_x.min(scale_y);
43
44 let offset_x = (width as f32 - svg_size.width() * scale) / 2.0;
46 let offset_y = (height as f32 - svg_size.height() * scale) / 2.0;
47
48 let transform = Transform::from_scale(scale, scale).post_translate(offset_x, offset_y);
50
51 resvg::render(&tree, transform, &mut pixmap.as_mut());
53
54 let img = RgbaImage::from_raw(width, height, pixmap.data().to_vec())
56 .ok_or_else(|| QRError::SvgError("Failed to create image from pixmap".to_string()))?;
57
58 Ok(DynamicImage::ImageRgba8(img))
59 }
60
61 fn encode_image(image: &DynamicImage, format: OutputFormat) -> Result<Vec<u8>> {
63 let mut buffer = Cursor::new(Vec::new());
64
65 let image_format = match format {
66 OutputFormat::Png => ImageFormat::Png,
67 OutputFormat::Jpeg => ImageFormat::Jpeg,
68 OutputFormat::WebP => ImageFormat::WebP,
69 OutputFormat::Svg => {
70 return Err(QRError::ImageEncodeError(
71 "SVG format should use SVG renderer".to_string(),
72 ));
73 }
74 OutputFormat::Pdf => {
75 return Err(QRError::ImageEncodeError(
76 "PDF format should use PDF renderer".to_string(),
77 ));
78 }
79 };
80
81 image
82 .write_to(&mut buffer, image_format)
83 .map_err(|e| QRError::ImageEncodeError(e.to_string()))?;
84
85 Ok(buffer.into_inner())
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92
93 #[test]
94 fn test_encode_png() {
95 let img = DynamicImage::ImageRgba8(RgbaImage::new(100, 100));
96 let result = RasterRenderer::encode_image(&img, OutputFormat::Png);
97 assert!(result.is_ok());
98 let bytes = result.unwrap();
99 assert!(!bytes.is_empty());
100 assert_eq!(&bytes[0..4], &[0x89, 0x50, 0x4E, 0x47]);
102 }
103
104 #[test]
105 fn test_encode_jpeg() {
106 let img = DynamicImage::ImageRgba8(RgbaImage::new(100, 100));
107 let result = RasterRenderer::encode_image(&img, OutputFormat::Jpeg);
108 assert!(result.is_ok());
109 let bytes = result.unwrap();
110 assert!(!bytes.is_empty());
111 assert_eq!(&bytes[0..2], &[0xFF, 0xD8]);
113 }
114
115 #[test]
116 fn test_svg_to_image() {
117 let svg = r#"<?xml version="1.0" encoding="UTF-8"?>
119 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100">
120 <rect x="0" y="0" width="100" height="100" fill="white"/>
121 <rect x="25" y="25" width="50" height="50" fill="black"/>
122 </svg>"#;
123
124 let result = RasterRenderer::svg_to_image(svg, 100, 100);
125 assert!(result.is_ok());
126 }
127
128 #[test]
129 fn test_full_render() {
130 let svg = r#"<?xml version="1.0" encoding="UTF-8"?>
131 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100">
132 <rect x="0" y="0" width="100" height="100" fill="white"/>
133 <rect x="25" y="25" width="50" height="50" fill="black"/>
134 </svg>"#;
135
136 let result = RasterRenderer::render(svg, 100, 100, OutputFormat::Png);
137 assert!(result.is_ok());
138 let bytes = result.unwrap();
139 assert_eq!(&bytes[0..4], &[0x89, 0x50, 0x4E, 0x47]);
141 }
142}