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