//! QRCode encoder //! //! This crate provides a QR code and Micro QR code encoder for binary data. //! #![cfg_attr(feature = "image", doc = "```rust")] #![cfg_attr(not(feature = "image"), doc = "```ignore")] //! use qrcode_rs::QrCode; //! use image::Luma; //! //! // Encode some data into bits. //! let code = QrCode::new(b"01234567").unwrap(); //! //! // Render the bits into an image. //! let image = code.render::>().build(); //! //! // Save the image. //! # if cfg!(unix) { //! image.save("/tmp/qrcode.png").unwrap(); //! # } //! //! // You can also render it into a string. //! let string = code.render() //! .light_color(' ') //! .dark_color('#') //! .build(); //! println!("{}", string); //! ``` #![cfg_attr(feature = "bench", feature(test))] // Unstable libraries #![deny(clippy::uninlined_format_args, clippy::manual_range_contains, clippy::semicolon_if_nothing_returned)] #![allow( clippy::must_use_candidate, // This is just annoying. )] #![cfg_attr(feature = "bench", doc = include_str!("../README.md"))] // ^ make sure we can test our README.md. pub mod bits; pub mod canvas; mod cast; pub mod ec; pub mod optimize; pub mod render; pub mod types; pub use crate::types::{Color, EcLevel, QrResult, Version}; use crate::cast::As; use crate::render::{Pixel, Renderer}; use std::ops::Index; /// The encoded QR code symbol. #[derive(Clone)] pub struct QrCode { content: Vec, version: Version, ec_level: EcLevel, width: usize, } impl QrCode { /// Constructs a new QR code which automatically encodes the given data. /// /// This method uses the "medium" error correction level and automatically /// chooses the smallest QR code. /// /// use qrcode_rs::QrCode; /// /// let code = QrCode::new(b"Some data").unwrap(); /// /// # Errors /// /// Returns error if the QR code cannot be constructed, e.g. when the data /// is too long. pub fn new>(data: D) -> QrResult { Self::with_error_correction_level(data, EcLevel::M) } /// Constructs a new QR code which automatically encodes the given data at a /// specific error correction level. /// /// This method automatically chooses the smallest QR code. /// /// use qrcode_rs::{QrCode, EcLevel}; /// /// let code = QrCode::with_error_correction_level(b"Some data", EcLevel::H).unwrap(); /// /// # Errors /// /// Returns error if the QR code cannot be constructed, e.g. when the data /// is too long. pub fn with_error_correction_level>(data: D, ec_level: EcLevel) -> QrResult { let bits = bits::encode_auto(data.as_ref(), ec_level)?; Self::with_bits(bits, ec_level) } /// Constructs a new QR code for the given version and error correction /// level. /// /// use qrcode_rs::{QrCode, Version, EcLevel}; /// /// let code = QrCode::with_version(b"Some data", Version::Normal(5), EcLevel::M).unwrap(); /// /// This method can also be used to generate Micro QR code. /// /// use qrcode_rs::{QrCode, Version, EcLevel}; /// /// let micro_code = QrCode::with_version(b"123", Version::Micro(1), EcLevel::L).unwrap(); /// /// # Errors /// /// Returns error if the QR code cannot be constructed, e.g. when the data /// is too long, or when the version and error correction level are /// incompatible. pub fn with_version>(data: D, version: Version, ec_level: EcLevel) -> QrResult { let mut bits = bits::Bits::new(version); bits.push_optimal_data(data.as_ref())?; bits.push_terminator(ec_level)?; Self::with_bits(bits, ec_level) } /// Constructs a new QR code with encoded bits. /// /// Use this method only if there are very special need to manipulate the /// raw bits before encoding. Some examples are: /// /// * Encode data using specific character set with ECI /// * Use the FNC1 modes /// * Avoid the optimal segmentation algorithm /// /// See the `Bits` structure for detail. /// /// #![allow(unused_must_use)] /// /// use qrcode_rs::{QrCode, Version, EcLevel}; /// use qrcode_rs::bits::Bits; /// /// let mut bits = Bits::new(Version::Normal(1)); /// bits.push_eci_designator(9); /// bits.push_byte_data(b"\xca\xfe\xe4\xe9\xea\xe1\xf2 QR"); /// bits.push_terminator(EcLevel::L); /// let qrcode = QrCode::with_bits(bits, EcLevel::L); /// /// # Errors /// /// Returns error if the QR code cannot be constructed, e.g. when the bits /// are too long, or when the version and error correction level are /// incompatible. pub fn with_bits(bits: bits::Bits, ec_level: EcLevel) -> QrResult { let version = bits.version(); let data = bits.into_bytes(); let (encoded_data, ec_data) = ec::construct_codewords(&data, version, ec_level)?; let mut canvas = canvas::Canvas::new(version, ec_level); canvas.draw_all_functional_patterns(); canvas.draw_data(&encoded_data, &ec_data); let canvas = canvas.apply_best_mask(); Ok(Self { content: canvas.into_colors(), version, ec_level, width: version.width().as_usize() }) } /// Gets the version of this QR code. pub fn version(&self) -> Version { self.version } /// Gets the error correction level of this QR code. pub fn error_correction_level(&self) -> EcLevel { self.ec_level } /// Gets the number of modules per side, i.e. the width of this QR code. /// /// The width here does not contain the quiet zone paddings. pub fn width(&self) -> usize { self.width } /// Gets the maximum number of allowed erratic modules can be introduced /// before the data becomes corrupted. Note that errors should not be /// introduced to functional modules. pub fn max_allowed_errors(&self) -> usize { ec::max_allowed_errors(self.version, self.ec_level).expect("invalid version or ec_level") } /// Checks whether a module at coordinate (x, y) is a functional module or /// not. pub fn is_functional(&self, x: usize, y: usize) -> bool { let x = x.try_into().expect("coordinate is too large for QR code"); let y = y.try_into().expect("coordinate is too large for QR code"); canvas::is_functional(self.version, self.version.width(), x, y) } /// Converts the QR code into a human-readable string. This is mainly for /// debugging only. pub fn to_debug_str(&self, on_char: char, off_char: char) -> String { self.render().quiet_zone(false).dark_color(on_char).light_color(off_char).build() } /// Converts the QR code to a vector of booleans. Each entry represents the /// color of the module, with "true" means dark and "false" means light. #[deprecated(since = "0.4.0", note = "use `to_colors()` instead")] pub fn to_vec(&self) -> Vec { self.content.iter().map(|c| *c != Color::Light).collect() } /// Converts the QR code to a vector of booleans. Each entry represents the /// color of the module, with "true" means dark and "false" means light. #[deprecated(since = "0.4.0", note = "use `into_colors()` instead")] pub fn into_vec(self) -> Vec { self.content.into_iter().map(|c| c != Color::Light).collect() } /// Converts the QR code to a vector of colors. pub fn to_colors(&self) -> Vec { self.content.clone() } /// Converts the QR code to a vector of colors. pub fn into_colors(self) -> Vec { self.content } /// Renders the QR code into an image. The result is an image builder, which /// you may do some additional configuration before copying it into a /// concrete image. /// Note: the`image` crate itself also provides method to rotate the image, /// or overlay a logo on top of the QR code. /// # Examples /// #[cfg_attr(feature = "image", doc = " ```rust")] #[cfg_attr(not(feature = "image"), doc = " ```ignore")] /// # use qrcode_rs::QrCode; /// # use image::Rgb; /// /// let image = QrCode::new(b"hello").unwrap() /// .render() /// .dark_color(Rgb([0, 0, 128])) /// .light_color(Rgb([224, 224, 224])) // adjust colors /// .quiet_zone(false) // disable quiet zone (white border) /// .min_dimensions(300, 300) // sets minimum image size /// .build(); /// ``` /// pub fn render(&self) -> Renderer

{ let quiet_zone = if self.version.is_micro() { 2 } else { 4 }; Renderer::new(&self.content, self.width, quiet_zone) } } impl Index<(usize, usize)> for QrCode { type Output = Color; fn index(&self, (x, y): (usize, usize)) -> &Color { let index = y * self.width + x; &self.content[index] } } #[cfg(test)] mod tests { use crate::{EcLevel, QrCode, Version}; #[test] fn test_annex_i_qr() { // This uses the ISO Annex I as test vector. let code = QrCode::with_version(b"01234567", Version::Normal(1), EcLevel::M).unwrap(); assert_eq!( &*code.to_debug_str('#', '.'), "\ #######..#.##.#######\n\ #.....#..####.#.....#\n\ #.###.#.#.....#.###.#\n\ #.###.#.##....#.###.#\n\ #.###.#.#.###.#.###.#\n\ #.....#.#...#.#.....#\n\ #######.#.#.#.#######\n\ ........#..##........\n\ #.#####..#..#.#####..\n\ ...#.#.##.#.#..#.##..\n\ ..#...##.#.#.#..#####\n\ ....#....#.....####..\n\ ...######..#.#..#....\n\ ........#.#####..##..\n\ #######..##.#.##.....\n\ #.....#.#.#####...#.#\n\ #.###.#.#...#..#.##..\n\ #.###.#.##..#..#.....\n\ #.###.#.#.##.#..#.#..\n\ #.....#........##.##.\n\ #######.####.#..#.#.." ); } #[test] fn test_annex_i_micro_qr() { let code = QrCode::with_version(b"01234567", Version::Micro(2), EcLevel::L).unwrap(); assert_eq!( &*code.to_debug_str('#', '.'), "\ #######.#.#.#\n\ #.....#.###.#\n\ #.###.#..##.#\n\ #.###.#..####\n\ #.###.#.###..\n\ #.....#.#...#\n\ #######..####\n\ .........##..\n\ ##.#....#...#\n\ .##.#.#.#.#.#\n\ ###..#######.\n\ ...#.#....##.\n\ ###.#..##.###" ); } } #[cfg(all(test, feature = "image"))] mod image_tests { use crate::{EcLevel, QrCode, Version}; use image::{load_from_memory, Luma, Rgb}; #[test] fn test_annex_i_qr_as_image() { let code = QrCode::new(b"01234567").unwrap(); let image = code.render::>().build(); let expected = load_from_memory(include_bytes!("../docs/images/test_annex_i_qr_as_image.png")).unwrap().to_luma8(); assert_eq!(image.dimensions(), expected.dimensions()); assert_eq!(image.into_raw(), expected.into_raw()); } #[test] fn test_annex_i_micro_qr_as_image() { let code = QrCode::with_version(b"01234567", Version::Micro(2), EcLevel::L).unwrap(); let image = code .render() .min_dimensions(200, 200) .dark_color(Rgb([128, 0, 0])) .light_color(Rgb([255, 255, 128])) .build(); let expected = load_from_memory(include_bytes!("../docs/images/test_annex_i_micro_qr_as_image.png")).unwrap().to_rgb8(); assert_eq!(image.dimensions(), expected.dimensions()); assert_eq!(image.into_raw(), expected.into_raw()); } } #[cfg(all(test, feature = "svg"))] mod svg_tests { use crate::render::svg::Color as SvgColor; use crate::{EcLevel, QrCode, Version}; #[test] fn test_annex_i_qr_as_svg() { let code = QrCode::new(b"01234567").unwrap(); let image = code.render::().build(); let expected = include_str!("../docs/images/test_annex_i_qr_as_svg.svg"); assert_eq!(&image, expected); } #[test] fn test_annex_i_micro_qr_as_svg() { let code = QrCode::with_version(b"01234567", Version::Micro(2), EcLevel::L).unwrap(); let image = code .render() .min_dimensions(200, 200) .dark_color(SvgColor("#800000")) .light_color(SvgColor("#ffff80")) .build(); let expected = include_str!("../docs/images/test_annex_i_micro_qr_as_svg.svg"); assert_eq!(&image, expected); } }