//! 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")] //! extern crate qrcode; //! extern crate image; //! //! use qrcode::QrCode; //! use image::Luma; //! //! fn main() { //! // 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. //! 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, external_doc))] // Unstable libraries #![cfg_attr(feature = "cargo-clippy", deny(warnings, clippy_pedantic))] #![cfg_attr( feature = "cargo-clippy", allow( indexing_slicing, write_literal, // see https://github.com/rust-lang-nursery/rust-clippy/issues/2976 ) )] #![cfg_attr(feature = "bench", doc(include = "../README.md"))] // ^ make sure we can test our README.md. #![cfg_attr(feature = "cargo-clippy", allow())] extern crate checked_int_cast; #[cfg(feature = "image")] extern crate image; #[cfg(feature = "bench")] extern crate test; use std::ops::Index; pub mod bits; pub mod canvas; mod cast; pub mod ec; pub mod optimize; pub mod render; pub mod types; pub use types::{Color, EcLevel, QrResult, Version}; use cast::As; use checked_int_cast::CheckedIntCast; use render::{Pixel, Renderer}; /// 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::QrCode; /// /// let code = QrCode::new(b"Some data").unwrap(); /// 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::{QrCode, EcLevel}; /// /// let code = QrCode::with_error_correction_level(b"Some data", EcLevel::H).unwrap(); /// 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::{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::{QrCode, Version, EcLevel}; /// /// let micro_code = QrCode::with_version(b"123", Version::Micro(1), EcLevel::L).unwrap(); /// 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::{QrCode, Version, EcLevel}; /// use qrcode::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); /// 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.as_i16_checked().expect("coordinate is too large for QR code"); let y = y.as_i16_checked().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. /// /// # Examples /// #[cfg_attr(feature = "image", doc = " ```rust")] #[cfg_attr(not(feature = "image"), doc = " ```ignore")] /// # extern crate image; /// # extern crate qrcode; /// # use qrcode::QrCode; /// # use image::Rgb; /// # fn main() { /// /// let image = QrCode::new(b"hello").unwrap() /// .render() /// .dark_color(Rgb { data: [0, 0, 128] }) /// .light_color(Rgb { data: [224, 224, 224] }) // adjust colors /// .quiet_zone(false) // disable quiet zone (white border) /// .min_dimensions(300, 300) // sets minimum image size /// .build(); /// /// # } /// ``` /// /// Note: the `image` crate itself also provides method to rotate the image, /// or overlay a logo on top of the QR code. 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 {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 image::{load_from_memory, Luma, Rgb}; use {EcLevel, QrCode, Version}; #[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!("test_annex_i_qr_as_image.png")).unwrap().to_luma(); 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 { data: [128, 0, 0] }) .light_color(Rgb { data: [255, 255, 128] }) .build(); let expected = load_from_memory(include_bytes!("test_annex_i_micro_qr_as_image.png")).unwrap().to_rgb(); assert_eq!(image.dimensions(), expected.dimensions()); assert_eq!(image.into_raw(), expected.into_raw()); } } #[cfg(all(test, feature = "svg"))] mod svg_tests { use render::svg::Color as SvgColor; use {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!("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!("test_annex_i_micro_qr_as_svg.svg"); assert_eq!(&image, expected); } }