qrcode_rs/render/
unicode.rs

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