Skip to main content

qrcode_render/
string.rs

1//! String rendering support.
2
3#[cfg(not(feature = "std"))]
4#[allow(unused_imports)]
5use alloc::{
6    borrow::ToOwned,
7    format,
8    string::{String, ToString},
9    vec,
10    vec::Vec,
11};
12
13use crate::{Canvas as RenderCanvas, Pixel};
14use qrcode_core::As;
15use qrcode_core::Color;
16
17/// A renderable character or string fragment used by the plain-text renderer.
18///
19/// Implemented for `char` and `&'static str` so a [`Renderer`](crate::Renderer)
20/// can be driven with either.
21pub trait Element: Copy {
22    /// Returns the default element for a dark or light module.
23    fn default_color(color: Color) -> Self;
24    /// The length (in bytes/chars) of this element when rendered.
25    fn strlen(self) -> usize;
26    /// Appends this element to `string`.
27    fn append_to_string(self, string: &mut String);
28}
29
30impl Element for char {
31    fn default_color(color: Color) -> Self {
32        color.select('\u{2588}', ' ')
33    }
34
35    fn strlen(self) -> usize {
36        self.len_utf8()
37    }
38
39    fn append_to_string(self, string: &mut String) {
40        string.push(self);
41    }
42}
43
44impl Element for &str {
45    fn default_color(color: Color) -> Self {
46        color.select("\u{2588}", " ")
47    }
48
49    fn strlen(self) -> usize {
50        self.len()
51    }
52
53    fn append_to_string(self, string: &mut String) {
54        string.push_str(self);
55    }
56}
57
58#[doc(hidden)]
59pub struct Canvas<P: Element> {
60    buffer: Vec<P>,
61    width: usize,
62    dark_pixel: P,
63    dark_cap_inc: isize,
64    capacity: isize,
65}
66
67impl<P: Element> Pixel for P {
68    type Image = String;
69    type Canvas = Canvas<Self>;
70
71    fn default_unit_size() -> (u32, u32) {
72        (1, 1)
73    }
74
75    fn default_color(color: Color) -> Self {
76        <Self as Element>::default_color(color)
77    }
78}
79
80impl<P: Element> RenderCanvas for Canvas<P> {
81    type Pixel = P;
82    type Image = String;
83
84    fn new(width: u32, height: u32, dark_pixel: P, light_pixel: P) -> Self {
85        let width = width.as_usize();
86        let height = height.as_isize();
87        let dark_cap = dark_pixel.strlen().as_isize();
88        let light_cap = light_pixel.strlen().as_isize();
89        Self {
90            buffer: vec![light_pixel; width * height.as_usize()],
91            width,
92            dark_pixel,
93            dark_cap_inc: dark_cap - light_cap,
94            capacity: light_cap * width.as_isize() * height + (height - 1),
95        }
96    }
97
98    fn draw_dark_pixel(&mut self, x: u32, y: u32) {
99        let x = x.as_usize();
100        let y = y.as_usize();
101        self.capacity += self.dark_cap_inc;
102        self.buffer[x + y * self.width] = self.dark_pixel;
103    }
104
105    fn into_image(self) -> String {
106        let mut result = String::with_capacity(self.capacity.as_usize());
107        for (i, pixel) in self.buffer.into_iter().enumerate() {
108            if i != 0 && i % self.width == 0 {
109                result.push('\n');
110            }
111            pixel.append_to_string(&mut result);
112        }
113        result
114    }
115}
116
117#[test]
118fn test_render_to_string() {
119    use crate::Renderer;
120
121    let colors = &[Color::Dark, Color::Light, Color::Light, Color::Dark];
122    let image: String = Renderer::<char>::new(colors, 2, 1).build();
123    assert_eq!(&image, "    \n \u{2588}  \n  \u{2588} \n    ");
124
125    let image2 = Renderer::new(colors, 2, 1).light_color("A").dark_color("!B!").module_dimensions(2, 2).build();
126
127    assert_eq!(
128        &image2,
129        "AAAAAAAA\n\
130         AAAAAAAA\n\
131         AA!B!!B!AAAA\n\
132         AA!B!!B!AAAA\n\
133         AAAA!B!!B!AA\n\
134         AAAA!B!!B!AA\n\
135         AAAAAAAA\n\
136         AAAAAAAA"
137    );
138}