2014-08-18 14:26:49 -05:00
|
|
|
//! QRCode encoder
|
|
|
|
//!
|
|
|
|
//! This crate provides a QR code and Micro QR code encoder for binary data.
|
|
|
|
//!
|
2016-06-07 11:31:11 -05:00
|
|
|
//! ```
|
|
|
|
//! extern crate qrcode;
|
2017-05-18 09:21:31 -05:00
|
|
|
//! # #[cfg(feature="image")]
|
2017-01-26 11:02:34 -06:00
|
|
|
//! extern crate image;
|
2014-08-18 14:26:49 -05:00
|
|
|
//!
|
2016-06-07 11:31:11 -05:00
|
|
|
//! use qrcode::QrCode;
|
2017-05-18 09:21:31 -05:00
|
|
|
//! # #[cfg(feature="image")]
|
2017-01-26 11:02:34 -06:00
|
|
|
//! use image::GrayImage;
|
2016-06-07 11:31:11 -05:00
|
|
|
//!
|
2017-01-26 11:02:34 -06:00
|
|
|
//! fn main() {
|
|
|
|
//! // Encode some data into bits.
|
|
|
|
//! let code = QrCode::new(b"01234567").unwrap();
|
2016-06-07 11:31:11 -05:00
|
|
|
//!
|
2017-01-26 11:02:34 -06:00
|
|
|
//! // Render the bits into an image.
|
2017-05-18 09:21:31 -05:00
|
|
|
//! # #[cfg(feature="image")]
|
2017-01-26 11:02:34 -06:00
|
|
|
//! let image: GrayImage = code.render().to_image();
|
2016-06-07 11:31:11 -05:00
|
|
|
//!
|
2017-01-26 11:02:34 -06:00
|
|
|
//! // Save the image.
|
2017-05-18 09:21:31 -05:00
|
|
|
//! # #[cfg(feature="image")]
|
2017-01-26 11:02:34 -06:00
|
|
|
//! image.save("/tmp/qrcode.png").unwrap();
|
|
|
|
//! }
|
2016-06-07 11:31:11 -05:00
|
|
|
//! ```
|
2014-08-18 14:26:49 -05:00
|
|
|
|
2015-07-04 14:40:04 -05:00
|
|
|
#![cfg_attr(feature="bench", feature(test))] // Unstable libraries
|
2014-08-18 14:26:49 -05:00
|
|
|
|
2016-06-07 11:31:11 -05:00
|
|
|
#[cfg(feature="bench")] extern crate test;
|
2016-06-04 12:28:15 -05:00
|
|
|
extern crate num_traits;
|
2016-06-07 11:31:11 -05:00
|
|
|
#[cfg(feature="image")] extern crate image;
|
2014-08-18 14:26:49 -05:00
|
|
|
|
2015-01-10 07:53:34 -06:00
|
|
|
use std::ops::Index;
|
|
|
|
|
2014-08-18 14:26:49 -05:00
|
|
|
pub mod types;
|
|
|
|
pub mod bits;
|
|
|
|
pub mod optimize;
|
|
|
|
pub mod ec;
|
|
|
|
pub mod canvas;
|
2016-06-07 11:31:11 -05:00
|
|
|
pub mod render;
|
|
|
|
|
|
|
|
pub use types::{QrResult, EcLevel, Version};
|
|
|
|
|
|
|
|
#[cfg(feature="image")] use render::{BlankAndWhitePixel, Renderer};
|
2014-08-18 14:26:49 -05:00
|
|
|
|
|
|
|
/// The encoded QR code symbol.
|
2015-01-10 07:53:34 -06:00
|
|
|
#[derive(Clone)]
|
2014-08-18 14:26:49 -05:00
|
|
|
pub struct QrCode {
|
|
|
|
content: Vec<bool>,
|
2014-11-28 15:29:01 -06:00
|
|
|
version: Version,
|
|
|
|
ec_level: EcLevel,
|
2015-01-10 07:53:34 -06:00
|
|
|
width: usize,
|
2014-08-18 14:25:40 -05:00
|
|
|
}
|
2014-08-18 14:26:49 -05:00
|
|
|
|
|
|
|
impl QrCode {
|
|
|
|
/// Constructs a new QR code which automatically encodes the given data.
|
|
|
|
///
|
|
|
|
/// This method uses the "medium" error correction level and automatically
|
2014-08-18 15:18:30 -05:00
|
|
|
/// chooses the smallest QR code.
|
|
|
|
///
|
|
|
|
/// use qrcode::QrCode;
|
|
|
|
///
|
|
|
|
/// let code = QrCode::new(b"Some data").unwrap();
|
|
|
|
///
|
2016-05-16 08:42:05 -05:00
|
|
|
pub fn new<D: AsRef<[u8]>>(data: D) -> QrResult<QrCode> {
|
2014-11-28 15:29:01 -06:00
|
|
|
QrCode::with_error_correction_level(data, EcLevel::M)
|
2014-08-18 14:26:49 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/// 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.
|
2014-08-18 15:18:30 -05:00
|
|
|
///
|
2014-11-28 15:29:01 -06:00
|
|
|
/// use qrcode::{QrCode, EcLevel};
|
2014-08-18 15:18:30 -05:00
|
|
|
///
|
2014-11-28 15:29:01 -06:00
|
|
|
/// let code = QrCode::with_error_correction_level(b"Some data", EcLevel::H).unwrap();
|
2014-08-18 15:18:30 -05:00
|
|
|
///
|
2016-05-16 08:42:05 -05:00
|
|
|
pub fn with_error_correction_level<D: AsRef<[u8]>>(data: D, ec_level: EcLevel) -> QrResult<QrCode> {
|
|
|
|
let bits = try!(bits::encode_auto(data.as_ref(), ec_level));
|
2014-08-18 15:18:30 -05:00
|
|
|
QrCode::with_bits(bits, ec_level)
|
2014-08-18 14:26:49 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Constructs a new QR code for the given version and error correction
|
|
|
|
/// level.
|
2014-08-18 15:18:30 -05:00
|
|
|
///
|
2014-11-28 15:29:01 -06:00
|
|
|
/// use qrcode::{QrCode, Version, EcLevel};
|
2014-08-18 15:18:30 -05:00
|
|
|
///
|
2014-11-28 15:29:01 -06:00
|
|
|
/// let code = QrCode::with_version(b"Some data", Version::Normal(5), EcLevel::M).unwrap();
|
2014-08-18 15:18:30 -05:00
|
|
|
///
|
|
|
|
/// This method can also be used to generate Micro QR code.
|
|
|
|
///
|
2014-11-28 15:29:01 -06:00
|
|
|
/// use qrcode::{QrCode, Version, EcLevel};
|
2014-08-18 15:18:30 -05:00
|
|
|
///
|
2014-11-28 15:29:01 -06:00
|
|
|
/// let micro_code = QrCode::with_version(b"123", Version::Micro(1), EcLevel::L).unwrap();
|
2014-08-18 15:18:30 -05:00
|
|
|
///
|
2016-05-16 08:42:05 -05:00
|
|
|
pub fn with_version<D: AsRef<[u8]>>(data: D, version: Version, ec_level: EcLevel) -> QrResult<QrCode> {
|
2014-08-18 15:18:30 -05:00
|
|
|
let mut bits = bits::Bits::new(version);
|
2016-05-16 08:42:05 -05:00
|
|
|
try!(bits.push_optimal_data(data.as_ref()));
|
2014-08-18 15:18:30 -05:00
|
|
|
try!(bits.push_terminator(ec_level));
|
|
|
|
QrCode::with_bits(bits, ec_level)
|
2014-08-18 14:26:49 -05:00
|
|
|
}
|
|
|
|
|
2014-08-18 15:18:30 -05:00
|
|
|
/// 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.
|
|
|
|
///
|
2014-11-28 15:29:01 -06:00
|
|
|
/// #![allow(unused_must_use)]
|
2014-08-18 15:18:30 -05:00
|
|
|
///
|
2014-11-28 15:29:01 -06:00
|
|
|
/// use qrcode::{QrCode, Version, EcLevel};
|
2014-08-18 15:18:30 -05:00
|
|
|
/// use qrcode::bits::Bits;
|
|
|
|
///
|
2014-11-28 15:29:01 -06:00
|
|
|
/// let mut bits = Bits::new(Version::Normal(1));
|
2014-08-18 15:18:30 -05:00
|
|
|
/// bits.push_eci_designator(9);
|
|
|
|
/// bits.push_byte_data(b"\xca\xfe\xe4\xe9\xea\xe1\xf2 QR");
|
2014-11-28 15:29:01 -06:00
|
|
|
/// bits.push_terminator(EcLevel::L);
|
|
|
|
/// let qrcode = QrCode::with_bits(bits, EcLevel::L);
|
2014-08-18 15:18:30 -05:00
|
|
|
///
|
2014-11-28 15:29:01 -06:00
|
|
|
pub fn with_bits(bits: bits::Bits, ec_level: EcLevel) -> QrResult<QrCode> {
|
2014-08-18 15:18:30 -05:00
|
|
|
let version = bits.version();
|
|
|
|
let data = bits.into_bytes();
|
2015-01-10 07:53:34 -06:00
|
|
|
let (encoded_data, ec_data) = try!(ec::construct_codewords(&*data, version, ec_level));
|
2014-08-18 14:26:49 -05:00
|
|
|
let mut canvas = canvas::Canvas::new(version, ec_level);
|
|
|
|
canvas.draw_all_functional_patterns();
|
2014-11-28 15:29:01 -06:00
|
|
|
canvas.draw_data(&*encoded_data, &*ec_data);
|
2014-08-18 14:26:49 -05:00
|
|
|
let canvas = canvas.apply_best_mask();
|
|
|
|
Ok(QrCode {
|
|
|
|
content: canvas.to_bools(),
|
|
|
|
version: version,
|
|
|
|
ec_level: ec_level,
|
2015-01-10 07:53:34 -06:00
|
|
|
width: version.width() as usize,
|
2014-08-18 14:26:49 -05:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Gets the version of this QR code.
|
2014-11-28 15:29:01 -06:00
|
|
|
pub fn version(&self) -> Version {
|
2014-08-18 14:26:49 -05:00
|
|
|
self.version
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Gets the error correction level of this QR code.
|
2014-11-28 15:29:01 -06:00
|
|
|
pub fn error_correction_level(&self) -> EcLevel {
|
2014-08-18 14:26:49 -05:00
|
|
|
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.
|
2015-01-10 07:53:34 -06:00
|
|
|
pub fn width(&self) -> usize {
|
2014-08-18 14:26:49 -05:00
|
|
|
self.width
|
|
|
|
}
|
|
|
|
|
2014-12-29 14:02:57 -06:00
|
|
|
/// 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.
|
2015-01-10 07:53:34 -06:00
|
|
|
pub fn max_allowed_errors(&self) -> usize {
|
2014-12-29 14:02:57 -06:00
|
|
|
ec::max_allowed_errors(self.version, self.ec_level).unwrap()
|
|
|
|
}
|
|
|
|
|
2014-12-29 14:46:22 -06:00
|
|
|
/// Checks whether a module at coordinate (x, y) is a functional module or
|
|
|
|
/// not.
|
2015-01-10 07:53:34 -06:00
|
|
|
pub fn is_functional(&self, x: usize, y: usize) -> bool {
|
2014-12-29 14:46:22 -06:00
|
|
|
canvas::is_functional(self.version, self.version.width(), x as i16, y as i16)
|
|
|
|
}
|
|
|
|
|
2014-08-18 14:26:49 -05:00
|
|
|
/// 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 {
|
|
|
|
let width = self.width;
|
|
|
|
let mut k = 0;
|
|
|
|
let mut res = String::with_capacity(width * (width + 1));
|
2015-02-06 05:42:58 -06:00
|
|
|
for _ in 0 .. width {
|
2014-09-27 11:59:20 -05:00
|
|
|
res.push('\n');
|
2015-02-06 05:42:58 -06:00
|
|
|
for _ in 0 .. width {
|
2014-09-27 11:59:20 -05:00
|
|
|
res.push(if self.content[k] { on_char } else { off_char });
|
2014-08-18 14:26:49 -05:00
|
|
|
k += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
res
|
|
|
|
}
|
2014-11-10 18:36:43 -06:00
|
|
|
|
|
|
|
/// 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.
|
|
|
|
pub fn to_vec(&self) -> Vec<bool> {
|
|
|
|
self.content.clone()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// 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.
|
|
|
|
pub fn into_vec(self) -> Vec<bool> {
|
2016-05-16 08:42:23 -05:00
|
|
|
self.content
|
2014-11-10 18:36:43 -06:00
|
|
|
}
|
2016-06-07 11:31:11 -05:00
|
|
|
|
2016-06-07 12:03:16 -05:00
|
|
|
/// 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
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// # 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_width(300) // sets minimum image size
|
|
|
|
/// .to_image();
|
|
|
|
///
|
|
|
|
/// # }
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// Note: the `image` crate itself also provides method to rotate the image,
|
|
|
|
/// or overlay a logo on top of the QR code.
|
2016-06-07 11:31:11 -05:00
|
|
|
#[cfg(feature="image")]
|
|
|
|
pub fn render<P: BlankAndWhitePixel + 'static>(&self) -> Renderer<P> {
|
|
|
|
let quiet_zone = if self.version.is_micro() { 2 } else { 4 };
|
|
|
|
Renderer::new(&self.content, self.width, quiet_zone)
|
|
|
|
}
|
2014-08-18 14:26:49 -05:00
|
|
|
}
|
|
|
|
|
2015-01-10 07:53:34 -06:00
|
|
|
impl Index<(usize, usize)> for QrCode {
|
|
|
|
type Output = bool;
|
|
|
|
|
2015-03-26 02:50:43 -05:00
|
|
|
fn index(&self, (x, y): (usize, usize)) -> &bool {
|
2014-08-18 14:26:49 -05:00
|
|
|
let index = y * self.width + x;
|
2015-01-10 07:53:34 -06:00
|
|
|
&self.content[index]
|
2014-08-18 14:26:49 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2014-11-28 15:29:01 -06:00
|
|
|
use {QrCode, Version, EcLevel};
|
2014-08-18 14:26:49 -05:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_annex_i_qr() {
|
|
|
|
// This uses the ISO Annex I as test vector.
|
2014-11-28 15:29:01 -06:00
|
|
|
let code = QrCode::with_version(b"01234567", Version::Normal(1), EcLevel::M).unwrap();
|
2014-11-10 18:36:43 -06:00
|
|
|
assert_eq!(&*code.to_debug_str('#', '.'), "\n\
|
2014-08-18 14:26:49 -05:00
|
|
|
#######..#.##.#######\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() {
|
2014-11-28 15:29:01 -06:00
|
|
|
let code = QrCode::with_version(b"01234567", Version::Micro(2), EcLevel::L).unwrap();
|
2014-11-10 18:36:43 -06:00
|
|
|
assert_eq!(&*code.to_debug_str('#', '.'), "\n\
|
2014-08-18 14:26:49 -05:00
|
|
|
#######.#.#.#\n\
|
|
|
|
#.....#.###.#\n\
|
|
|
|
#.###.#..##.#\n\
|
|
|
|
#.###.#..####\n\
|
|
|
|
#.###.#.###..\n\
|
|
|
|
#.....#.#...#\n\
|
|
|
|
#######..####\n\
|
|
|
|
.........##..\n\
|
|
|
|
##.#....#...#\n\
|
|
|
|
.##.#.#.#.#.#\n\
|
|
|
|
###..#######.\n\
|
|
|
|
...#.#....##.\n\
|
|
|
|
###.#..##.###");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-07 11:31:11 -05:00
|
|
|
#[cfg(all(test, feature="image"))]
|
|
|
|
mod image_tests {
|
|
|
|
use image::{GrayImage, Rgb, load_from_memory};
|
|
|
|
use {QrCode, Version, EcLevel};
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_annex_i_qr_as_image() {
|
|
|
|
let code = QrCode::new(b"01234567").unwrap();
|
|
|
|
let image: GrayImage = code.render().to_image();
|
|
|
|
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_width(200)
|
|
|
|
.dark_color(Rgb { data: [128, 0, 0] })
|
|
|
|
.light_color(Rgb { data: [255, 255, 128] })
|
|
|
|
.to_image();
|
|
|
|
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());
|
|
|
|
}
|
|
|
|
}
|
2014-11-28 15:57:11 -06:00
|
|
|
|