qrcode/render/
unicode.rs

1//! UTF-8 rendering, with 2 pixels per symbol.
2
3use crate::render::{Canvas as RenderCanvas, Color, Pixel};
4
5const CODEPAGE: [&str; 4] = [" ", "\u{2584}", "\u{2580}", "\u{2588}"];
6
7#[derive(Copy, Clone, PartialEq)]
8pub enum Dense1x2 {
9    Dark,
10    Light,
11}
12
13impl Pixel for Dense1x2 {
14    type Image = String;
15    type Canvas = Canvas1x2;
16    fn default_color(color: Color) -> Dense1x2 {
17        color.select(Dense1x2::Dark, Dense1x2::Light)
18    }
19    fn default_unit_size() -> (u32, u32) {
20        (1, 1)
21    }
22}
23
24impl Dense1x2 {
25    fn value(self) -> u8 {
26        match self {
27            Dense1x2::Dark => 1,
28            Dense1x2::Light => 0,
29        }
30    }
31    fn parse_2_bits(sym: u8) -> &'static str {
32        CODEPAGE[usize::from(sym)]
33    }
34}
35
36pub struct Canvas1x2 {
37    canvas: Vec<u8>,
38    width: u32,
39    dark_pixel: u8,
40}
41
42impl RenderCanvas for Canvas1x2 {
43    type Pixel = Dense1x2;
44    type Image = String;
45
46    fn new(width: u32, height: u32, dark_pixel: Dense1x2, light_pixel: Dense1x2) -> Self {
47        let a = vec![light_pixel.value(); (width * height) as usize];
48        Canvas1x2 { width, canvas: a, dark_pixel: dark_pixel.value() }
49    }
50
51    fn draw_dark_pixel(&mut self, x: u32, y: u32) {
52        self.canvas[(x + y * self.width) as usize] = self.dark_pixel;
53    }
54
55    fn into_image(self) -> String {
56        self.canvas
57            // Chopping array into 1-line sized fragments
58            .chunks_exact(self.width as usize)
59            .collect::<Vec<&[u8]>>()
60            // And then glueing every 2 lines.
61            .chunks(2)
62            .map(|rows| {
63                {
64                    // Then zipping those 2 lines together into a single 2-bit number list.
65                    if rows.len() == 2 {
66                        rows[0].iter().zip(rows[1]).map(|(top, bot)| (top * 2 + bot)).collect::<Vec<u8>>()
67                    } else {
68                        rows[0].iter().map(|top| (top * 2)).collect::<Vec<u8>>()
69                    }
70                }
71                .into_iter()
72                // Mapping those 2-bit numbers to corresponding pixels.
73                .map(Dense1x2::parse_2_bits)
74                .collect::<Vec<&str>>()
75                .concat()
76            })
77            .collect::<Vec<String>>()
78            .join("\n")
79    }
80}
81
82#[test]
83fn test_render_to_utf8_string() {
84    use crate::render::Renderer;
85    let colors = &[Color::Dark, Color::Light, Color::Light, Color::Dark];
86    let image: String = Renderer::<Dense1x2>::new(colors, 2, 1).build();
87
88    assert_eq!(&image, " ▄  \n  ▀ ");
89
90    let image2 = Renderer::<Dense1x2>::new(colors, 2, 1).module_dimensions(2, 2).build();
91
92    assert_eq!(&image2, "        \n  ██    \n    ██  \n        ");
93}
94
95#[test]
96fn integration_render_utf8_1x2() {
97    use crate::render::unicode::Dense1x2;
98    use crate::{EcLevel, QrCode, Version};
99
100    let code = QrCode::with_version(b"09876542", Version::Micro(2), EcLevel::L).unwrap();
101    let image = code.render::<Dense1x2>().module_dimensions(1, 1).build();
102    assert_eq!(
103        image,
104        String::new()
105            + "                 \n"
106            + "  █▀▀▀▀▀█ ▀ █ ▀  \n"
107            + "  █ ███ █  ▀ █   \n"
108            + "  █ ▀▀▀ █  ▀█ █  \n"
109            + "  ▀▀▀▀▀▀▀ ▄▀▀ █  \n"
110            + "  ▀█ ▀▀▀▀▀██▀▀▄  \n"
111            + "  ▀███▄ ▀▀ █ ██  \n"
112            + "  ▀▀▀ ▀ ▀▀ ▀  ▀  \n"
113            + "                 "
114    )
115}
116
117#[test]
118fn integration_render_utf8_1x2_inverted() {
119    use crate::render::unicode::Dense1x2;
120    use crate::{EcLevel, QrCode, Version};
121
122    let code = QrCode::with_version(b"12345678", Version::Micro(2), EcLevel::L).unwrap();
123    let image = code
124        .render::<Dense1x2>()
125        .dark_color(Dense1x2::Light)
126        .light_color(Dense1x2::Dark)
127        .module_dimensions(1, 1)
128        .build();
129    assert_eq!(
130        image,
131        "█████████████████\n\
132         ██ ▄▄▄▄▄ █▄▀▄█▄██\n\
133         ██ █   █ █   █ ██\n\
134         ██ █▄▄▄█ █▄▄██▀██\n\
135         ██▄▄▄▄▄▄▄█▄▄▄▀ ██\n\
136         ██▄ ▀ ▀ ▀▄▄  ████\n\
137         ██▄▄▀▄█ ▀▀▀ ▀▄▄██\n\
138         ██▄▄▄█▄▄█▄██▄█▄██\n\
139         ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀"
140    );
141}