Skip to main content

qrcode_rs/render/
mod.rs

1//! Rendering pipeline for converting QR codes into visual output.
2//!
3//! This module provides the [`Pixel`] and [`Canvas`] traits that abstract over
4//! different output formats, and the [`Renderer`] builder that drives the
5//! conversion from QR code data to a final image.
6//!
7//! # Supported formats
8//!
9//! | Module  | Feature | Output type |
10//! |---------|---------|-------------|
11//! | `image` | `image` | PNG/JPEG via the `image` crate |
12//! | `svg`   | `svg`   | SVG XML string |
13//! | `eps`   | `eps`   | Encapsulated PostScript |
14//! | `html`  | `html`  | HTML table or CSS Grid |
15//! | `pic`   | `pic`   | PIC (troff) macros |
16//! | `string`| —       | Plain text with custom characters |
17//! | `unicode`| —      | Unicode block-element rendering |
18//!
19//! # Custom rendering
20//!
21//! Implement [`Pixel`] for your own type to render into a custom format.
22//! The [`Pixel`] trait defines how to create dark/light pixels and how to
23//! finalize a canvas into a concrete image.
24
25use crate::cast::As;
26use crate::types::Color;
27use std::cmp::max;
28
29pub mod ansi;
30pub mod colors;
31pub mod eps;
32pub mod html;
33pub mod image;
34pub mod pdf;
35pub mod pic;
36pub mod string;
37pub mod svg;
38pub mod unicode;
39
40//------------------------------------------------------------------------------
41//{{{ Pixel trait
42
43/// Abstraction of an image pixel.
44pub trait Pixel: Copy + Sized {
45    /// Type of the finalized image.
46    type Image: Sized + 'static;
47
48    /// The type that stores an intermediate buffer before finalizing to a
49    /// concrete image
50    type Canvas: Canvas<Pixel = Self, Image = Self::Image>;
51
52    /// Obtains the default module size. The result must be at least 1×1.
53    fn default_unit_size() -> (u32, u32) {
54        (8, 8)
55    }
56
57    /// Obtains the default pixel color when a module is dark or light.
58    fn default_color(color: Color) -> Self;
59}
60
61/// Rendering canvas of a QR code image.
62pub trait Canvas: Sized {
63    type Pixel: Sized;
64    type Image: Sized;
65
66    /// Constructs a new canvas of the given dimensions.
67    fn new(width: u32, height: u32, dark_pixel: Self::Pixel, light_pixel: Self::Pixel) -> Self;
68
69    /// Draws a single dark pixel at the (x, y) coordinate.
70    fn draw_dark_pixel(&mut self, x: u32, y: u32);
71
72    fn draw_dark_rect(&mut self, left: u32, top: u32, width: u32, height: u32) {
73        for y in top..(top + height) {
74            for x in left..(left + width) {
75                self.draw_dark_pixel(x, y);
76            }
77        }
78    }
79
80    /// Finalize the canvas to a real image.
81    fn into_image(self) -> Self::Image;
82}
83
84//}}}
85//------------------------------------------------------------------------------
86//{{{ Renderer
87
88/// A QR code renderer. This is a builder type which converts a bool-vector into
89/// an image.
90pub struct Renderer<'a, P: Pixel> {
91    content: &'a [Color],
92    modules_count: u32, // <- we call it `modules_count` here to avoid ambiguity of `width`.
93    quiet_zone: u32,
94    module_size: (u32, u32),
95
96    dark_color: P,
97    light_color: P,
98    has_quiet_zone: bool,
99}
100
101impl<'a, P: Pixel> Renderer<'a, P> {
102    /// Creates a new renderer.
103    ///
104    /// # Panics
105    /// panics if content is not `modules_count` squared big
106    pub fn new(content: &'a [Color], modules_count: usize, quiet_zone: u32) -> Renderer<'a, P> {
107        assert_eq!(modules_count * modules_count, content.len());
108        Renderer {
109            content,
110            modules_count: modules_count.as_u32(),
111            quiet_zone,
112            module_size: P::default_unit_size(),
113            dark_color: P::default_color(Color::Dark),
114            light_color: P::default_color(Color::Light),
115            has_quiet_zone: true,
116        }
117    }
118
119    /// Sets color of a dark module. Default is opaque black.
120    pub fn dark_color(&mut self, color: P) -> &mut Self {
121        self.dark_color = color;
122        self
123    }
124
125    /// Sets color of a light module. Default is opaque white.
126    pub fn light_color(&mut self, color: P) -> &mut Self {
127        self.light_color = color;
128        self
129    }
130
131    /// Whether to include the quiet zone in the generated image.
132    pub fn quiet_zone(&mut self, has_quiet_zone: bool) -> &mut Self {
133        self.has_quiet_zone = has_quiet_zone;
134        self
135    }
136
137    /// Sets the size of each module in pixels. Default is 8×8.
138    pub fn module_dimensions(&mut self, width: u32, height: u32) -> &mut Self {
139        self.module_size = (max(width, 1), max(height, 1));
140        self
141    }
142
143    /// Sets the minimum total image size in pixels, including the quiet zone if
144    /// applicable. The renderer will try to find the dimension as small as
145    /// possible, such that each module in the QR code has uniform size (no
146    /// distortion).
147    ///
148    /// For instance, a version 1 QR code has 19 modules across including the
149    /// quiet zone. If we request an image of size ≥200×200, we get that each
150    /// module's size should be 11×11, so the actual image size will be 209×209.
151    pub fn min_dimensions(&mut self, width: u32, height: u32) -> &mut Self {
152        let quiet_zone = if self.has_quiet_zone { 2 } else { 0 } * self.quiet_zone;
153        let width_in_modules = self.modules_count + quiet_zone;
154        let unit_width = width.div_ceil(width_in_modules);
155        let unit_height = height.div_ceil(width_in_modules);
156        self.module_dimensions(unit_width, unit_height)
157    }
158
159    /// Sets the maximum total image size in pixels, including the quiet zone if
160    /// applicable. The renderer will try to find the dimension as large as
161    /// possible, such that each module in the QR code has uniform size (no
162    /// distortion).
163    ///
164    /// For instance, a version 1 QR code has 19 modules across including the
165    /// quiet zone. If we request an image of size ≤200×200, we get that each
166    /// module's size should be 10×10, so the actual image size will be 190×190.
167    ///
168    /// The module size is at least 1×1, so if the restriction is too small, the
169    /// final image *can* be larger than the input.
170    pub fn max_dimensions(&mut self, width: u32, height: u32) -> &mut Self {
171        let quiet_zone = if self.has_quiet_zone { 2 } else { 0 } * self.quiet_zone;
172        let width_in_modules = self.modules_count + quiet_zone;
173        let unit_width = width / width_in_modules;
174        let unit_height = height / width_in_modules;
175        self.module_dimensions(unit_width, unit_height)
176    }
177
178    /// Sets dimensions suitable for web display (200×200 pixels minimum).
179    ///
180    /// This is a convenience preset for embedding QR codes in web pages.
181    /// The actual size may be slightly larger to maintain uniform module sizing.
182    pub fn for_web(&mut self) -> &mut Self {
183        self.min_dimensions(200, 200)
184    }
185
186    /// Sets dimensions suitable for printing at the specified DPI.
187    ///
188    /// Targets a 1-inch × 1-inch physical size. For example, at 300 DPI
189    /// the image will be at least 300×300 pixels.
190    ///
191    /// # Arguments
192    ///
193    /// * `dpi` - Dots per inch (common values: 150 for draft, 300 for standard, 600 for high quality)
194    pub fn for_print(&mut self, dpi: u32) -> &mut Self {
195        self.min_dimensions(dpi.max(72), dpi.max(72))
196    }
197
198    /// Sets dimensions suitable for social media platform sharing.
199    ///
200    /// Targets platform-recommended sizes:
201    ///
202    /// | Platform       | Size (px) |
203    /// |----------------|-----------|
204    /// | `"twitter"`    | 400×400   |
205    /// | `"facebook"`   | 600×600   |
206    /// | `"instagram"`  | 1080×1080 |
207    /// | `"wechat"`     | 600×600   |
208    /// | Any other      | 400×400   |
209    pub fn for_social(&mut self, platform: &str) -> &mut Self {
210        let size = match platform {
211            "twitter" | "x" => 400,
212            "facebook" | "fb" => 600,
213            "instagram" | "ig" => 1080,
214            "wechat" | "weixin" => 600,
215            _ => 400,
216        };
217        self.min_dimensions(size, size)
218    }
219
220    /// Renders the QR code into an image.
221    pub fn build(&self) -> P::Image {
222        let w = self.modules_count;
223        let qz = if self.has_quiet_zone { self.quiet_zone } else { 0 };
224        let width = w + 2 * qz;
225
226        let (mw, mh) = self.module_size;
227        let real_width = width * mw;
228        let real_height = width * mh;
229
230        let mut canvas = P::Canvas::new(real_width, real_height, self.dark_color, self.light_color);
231        let mut i = 0;
232        for y in 0..width {
233            for x in 0..width {
234                if qz <= x && x < w + qz && qz <= y && y < w + qz {
235                    if self.content[i] != Color::Light {
236                        canvas.draw_dark_rect(x * mw, y * mh, mw, mh);
237                    }
238                    i += 1;
239                }
240            }
241        }
242
243        canvas.into_image()
244    }
245}
246
247//}}}