qrcode_rs/
lib.rs

1//! QRCode encoder
2//!
3//! This crate provides a QR code and Micro QR code encoder for binary data.
4//!
5#![cfg_attr(feature = "image", doc = "```rust")]
6#![cfg_attr(not(feature = "image"), doc = "```ignore")]
7//! use qrcode_rs::QrCode;
8//! use image::Luma;
9//!
10//! // Encode some data into bits.
11//! let code = QrCode::new(b"01234567").unwrap();
12//!
13//! // Render the bits into an image.
14//! let image = code.render::<Luma<u8>>().build();
15//!
16//! // Save the image.
17//! # if cfg!(unix) {
18//! image.save("/tmp/qrcode.png").unwrap();
19//! # }
20//!
21//! // You can also render it into a string.
22//! let string = code.render()
23//!     .light_color(' ')
24//!     .dark_color('#')
25//!     .build();
26//! println!("{}", string);
27//! ```
28
29#![cfg_attr(feature = "bench", feature(test))] // Unstable libraries
30#![deny(clippy::uninlined_format_args, clippy::manual_range_contains, clippy::semicolon_if_nothing_returned)]
31#![allow(
32    clippy::must_use_candidate, // This is just annoying.
33)]
34#![cfg_attr(feature = "bench", doc = include_str!("../README.md"))]
35// ^ make sure we can test our README.md.
36
37pub mod bits;
38pub mod canvas;
39mod cast;
40pub mod ec;
41pub mod optimize;
42pub mod render;
43pub mod types;
44pub use crate::types::{Color, EcLevel, QrResult, Version};
45
46use crate::cast::As;
47use crate::render::{Pixel, Renderer};
48use std::ops::Index;
49
50/// The encoded QR code symbol.
51#[derive(Clone)]
52pub struct QrCode {
53    content: Vec<Color>,
54    version: Version,
55    ec_level: EcLevel,
56    width: usize,
57}
58
59impl QrCode {
60    /// Constructs a new QR code which automatically encodes the given data.
61    ///
62    /// This method uses the "medium" error correction level and automatically
63    /// chooses the smallest QR code.
64    ///
65    ///     use qrcode_rs::QrCode;
66    ///
67    ///     let code = QrCode::new(b"Some data").unwrap();
68    ///
69    /// # Errors
70    ///
71    /// Returns error if the QR code cannot be constructed, e.g. when the data
72    /// is too long.
73    pub fn new<D: AsRef<[u8]>>(data: D) -> QrResult<Self> {
74        Self::with_error_correction_level(data, EcLevel::M)
75    }
76
77    /// Constructs a new QR code which automatically encodes the given data at a
78    /// specific error correction level.
79    ///
80    /// This method automatically chooses the smallest QR code.
81    ///
82    ///     use qrcode_rs::{QrCode, EcLevel};
83    ///
84    ///     let code = QrCode::with_error_correction_level(b"Some data", EcLevel::H).unwrap();
85    ///
86    /// # Errors
87    ///
88    /// Returns error if the QR code cannot be constructed, e.g. when the data
89    /// is too long.
90    pub fn with_error_correction_level<D: AsRef<[u8]>>(data: D, ec_level: EcLevel) -> QrResult<Self> {
91        let bits = bits::encode_auto(data.as_ref(), ec_level)?;
92        Self::with_bits(bits, ec_level)
93    }
94
95    /// Constructs a new QR code for the given version and error correction
96    /// level.
97    ///
98    ///     use qrcode_rs::{QrCode, Version, EcLevel};
99    ///
100    ///     let code = QrCode::with_version(b"Some data", Version::Normal(5), EcLevel::M).unwrap();
101    ///
102    /// This method can also be used to generate Micro QR code.
103    ///
104    ///     use qrcode_rs::{QrCode, Version, EcLevel};
105    ///
106    ///     let micro_code = QrCode::with_version(b"123", Version::Micro(1), EcLevel::L).unwrap();
107    ///
108    /// # Errors
109    ///
110    /// Returns error if the QR code cannot be constructed, e.g. when the data
111    /// is too long, or when the version and error correction level are
112    /// incompatible.
113    pub fn with_version<D: AsRef<[u8]>>(data: D, version: Version, ec_level: EcLevel) -> QrResult<Self> {
114        let mut bits = bits::Bits::new(version);
115        bits.push_optimal_data(data.as_ref())?;
116        bits.push_terminator(ec_level)?;
117        Self::with_bits(bits, ec_level)
118    }
119
120    /// Constructs a new QR code with encoded bits.
121    ///
122    /// Use this method only if there are very special need to manipulate the
123    /// raw bits before encoding. Some examples are:
124    ///
125    /// * Encode data using specific character set with ECI
126    /// * Use the FNC1 modes
127    /// * Avoid the optimal segmentation algorithm
128    ///
129    /// See the `Bits` structure for detail.
130    ///
131    ///     #![allow(unused_must_use)]
132    ///
133    ///     use qrcode_rs::{QrCode, Version, EcLevel};
134    ///     use qrcode_rs::bits::Bits;
135    ///
136    ///     let mut bits = Bits::new(Version::Normal(1));
137    ///     bits.push_eci_designator(9);
138    ///     bits.push_byte_data(b"\xca\xfe\xe4\xe9\xea\xe1\xf2 QR");
139    ///     bits.push_terminator(EcLevel::L);
140    ///     let qrcode = QrCode::with_bits(bits, EcLevel::L);
141    ///
142    /// # Errors
143    ///
144    /// Returns error if the QR code cannot be constructed, e.g. when the bits
145    /// are too long, or when the version and error correction level are
146    /// incompatible.
147    pub fn with_bits(bits: bits::Bits, ec_level: EcLevel) -> QrResult<Self> {
148        let version = bits.version();
149        let data = bits.into_bytes();
150        let (encoded_data, ec_data) = ec::construct_codewords(&data, version, ec_level)?;
151        let mut canvas = canvas::Canvas::new(version, ec_level);
152        canvas.draw_all_functional_patterns();
153        canvas.draw_data(&encoded_data, &ec_data);
154        let canvas = canvas.apply_best_mask();
155        Ok(Self { content: canvas.into_colors(), version, ec_level, width: version.width().as_usize() })
156    }
157
158    /// Gets the version of this QR code.
159    pub fn version(&self) -> Version {
160        self.version
161    }
162
163    /// Gets the error correction level of this QR code.
164    pub fn error_correction_level(&self) -> EcLevel {
165        self.ec_level
166    }
167
168    /// Gets the number of modules per side, i.e. the width of this QR code.
169    ///
170    /// The width here does not contain the quiet zone paddings.
171    pub fn width(&self) -> usize {
172        self.width
173    }
174
175    /// Gets the maximum number of allowed erratic modules can be introduced
176    /// before the data becomes corrupted. Note that errors should not be
177    /// introduced to functional modules.
178    pub fn max_allowed_errors(&self) -> usize {
179        ec::max_allowed_errors(self.version, self.ec_level).expect("invalid version or ec_level")
180    }
181
182    /// Checks whether a module at coordinate (x, y) is a functional module or
183    /// not.
184    pub fn is_functional(&self, x: usize, y: usize) -> bool {
185        let x = x.try_into().expect("coordinate is too large for QR code");
186        let y = y.try_into().expect("coordinate is too large for QR code");
187        canvas::is_functional(self.version, self.version.width(), x, y)
188    }
189
190    /// Converts the QR code into a human-readable string. This is mainly for
191    /// debugging only.
192    pub fn to_debug_str(&self, on_char: char, off_char: char) -> String {
193        self.render().quiet_zone(false).dark_color(on_char).light_color(off_char).build()
194    }
195
196    /// Converts the QR code to a vector of booleans. Each entry represents the
197    /// color of the module, with "true" means dark and "false" means light.
198    #[deprecated(since = "0.4.0", note = "use `to_colors()` instead")]
199    pub fn to_vec(&self) -> Vec<bool> {
200        self.content.iter().map(|c| *c != Color::Light).collect()
201    }
202
203    /// Converts the QR code to a vector of booleans. Each entry represents the
204    /// color of the module, with "true" means dark and "false" means light.
205    #[deprecated(since = "0.4.0", note = "use `into_colors()` instead")]
206    pub fn into_vec(self) -> Vec<bool> {
207        self.content.into_iter().map(|c| c != Color::Light).collect()
208    }
209
210    /// Converts the QR code to a vector of colors.
211    pub fn to_colors(&self) -> Vec<Color> {
212        self.content.clone()
213    }
214
215    /// Converts the QR code to a vector of colors.
216    pub fn into_colors(self) -> Vec<Color> {
217        self.content
218    }
219
220    /// Renders the QR code into an image. The result is an image builder, which
221    /// you may do some additional configuration before copying it into a
222    /// concrete image.
223    ///  Note: the`image` crate itself also provides method to rotate the image,
224    /// or overlay a logo on top of the QR code.
225    /// # Examples
226    ///
227    #[cfg_attr(feature = "image", doc = " ```rust")]
228    #[cfg_attr(not(feature = "image"), doc = " ```ignore")]
229    /// # use qrcode_rs::QrCode;
230    /// # use image::Rgb;
231    ///
232    /// let image = QrCode::new(b"hello").unwrap()
233    ///                     .render()
234    ///                     .dark_color(Rgb([0, 0, 128]))
235    ///                     .light_color(Rgb([224, 224, 224])) // adjust colors
236    ///                     .quiet_zone(false)          // disable quiet zone (white border)
237    ///                     .min_dimensions(300, 300)   // sets minimum image size
238    ///                     .build();
239    /// ```
240    ///
241    pub fn render<P: Pixel>(&self) -> Renderer<P> {
242        let quiet_zone = if self.version.is_micro() { 2 } else { 4 };
243        Renderer::new(&self.content, self.width, quiet_zone)
244    }
245}
246
247impl Index<(usize, usize)> for QrCode {
248    type Output = Color;
249
250    fn index(&self, (x, y): (usize, usize)) -> &Color {
251        let index = y * self.width + x;
252        &self.content[index]
253    }
254}
255
256#[cfg(test)]
257mod tests {
258    use crate::{EcLevel, QrCode, Version};
259
260    #[test]
261    fn test_annex_i_qr() {
262        // This uses the ISO Annex I as test vector.
263        let code = QrCode::with_version(b"01234567", Version::Normal(1), EcLevel::M).unwrap();
264        assert_eq!(
265            &*code.to_debug_str('#', '.'),
266            "\
267             #######..#.##.#######\n\
268             #.....#..####.#.....#\n\
269             #.###.#.#.....#.###.#\n\
270             #.###.#.##....#.###.#\n\
271             #.###.#.#.###.#.###.#\n\
272             #.....#.#...#.#.....#\n\
273             #######.#.#.#.#######\n\
274             ........#..##........\n\
275             #.#####..#..#.#####..\n\
276             ...#.#.##.#.#..#.##..\n\
277             ..#...##.#.#.#..#####\n\
278             ....#....#.....####..\n\
279             ...######..#.#..#....\n\
280             ........#.#####..##..\n\
281             #######..##.#.##.....\n\
282             #.....#.#.#####...#.#\n\
283             #.###.#.#...#..#.##..\n\
284             #.###.#.##..#..#.....\n\
285             #.###.#.#.##.#..#.#..\n\
286             #.....#........##.##.\n\
287             #######.####.#..#.#.."
288        );
289    }
290
291    #[test]
292    fn test_annex_i_micro_qr() {
293        let code = QrCode::with_version(b"01234567", Version::Micro(2), EcLevel::L).unwrap();
294        assert_eq!(
295            &*code.to_debug_str('#', '.'),
296            "\
297             #######.#.#.#\n\
298             #.....#.###.#\n\
299             #.###.#..##.#\n\
300             #.###.#..####\n\
301             #.###.#.###..\n\
302             #.....#.#...#\n\
303             #######..####\n\
304             .........##..\n\
305             ##.#....#...#\n\
306             .##.#.#.#.#.#\n\
307             ###..#######.\n\
308             ...#.#....##.\n\
309             ###.#..##.###"
310        );
311    }
312}
313
314#[cfg(all(test, feature = "image"))]
315mod image_tests {
316    use crate::{EcLevel, QrCode, Version};
317    use image::{load_from_memory, Luma, Rgb};
318
319    #[test]
320    fn test_annex_i_qr_as_image() {
321        let code = QrCode::new(b"01234567").unwrap();
322        let image = code.render::<Luma<u8>>().build();
323        let expected =
324            load_from_memory(include_bytes!("../docs/images/test_annex_i_qr_as_image.png")).unwrap().to_luma8();
325        assert_eq!(image.dimensions(), expected.dimensions());
326        assert_eq!(image.into_raw(), expected.into_raw());
327    }
328
329    #[test]
330    fn test_annex_i_micro_qr_as_image() {
331        let code = QrCode::with_version(b"01234567", Version::Micro(2), EcLevel::L).unwrap();
332        let image = code
333            .render()
334            .min_dimensions(200, 200)
335            .dark_color(Rgb([128, 0, 0]))
336            .light_color(Rgb([255, 255, 128]))
337            .build();
338        let expected =
339            load_from_memory(include_bytes!("../docs/images/test_annex_i_micro_qr_as_image.png")).unwrap().to_rgb8();
340        assert_eq!(image.dimensions(), expected.dimensions());
341        assert_eq!(image.into_raw(), expected.into_raw());
342    }
343}
344
345#[cfg(all(test, feature = "svg"))]
346mod svg_tests {
347    use crate::render::svg::Color as SvgColor;
348    use crate::{EcLevel, QrCode, Version};
349
350    #[test]
351    fn test_annex_i_qr_as_svg() {
352        let code = QrCode::new(b"01234567").unwrap();
353        let image = code.render::<SvgColor>().build();
354        let expected = include_str!("../docs/images/test_annex_i_qr_as_svg.svg");
355        assert_eq!(&image, expected);
356    }
357
358    #[test]
359    fn test_annex_i_micro_qr_as_svg() {
360        let code = QrCode::with_version(b"01234567", Version::Micro(2), EcLevel::L).unwrap();
361        let image = code
362            .render()
363            .min_dimensions(200, 200)
364            .dark_color(SvgColor("#800000"))
365            .light_color(SvgColor("#ffff80"))
366            .build();
367        let expected = include_str!("../docs/images/test_annex_i_micro_qr_as_svg.svg");
368        assert_eq!(&image, expected);
369    }
370}