qrcode-rust/src/lib.rs

357 lines
12 KiB
Rust
Raw Normal View History

//! 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")]
2016-06-07 11:31:11 -05:00
//! extern crate qrcode;
//! extern crate image;
//!
2016-06-07 11:31:11 -05:00
//! use qrcode::QrCode;
//! use image::Luma;
2016-06-07 11:31:11 -05:00
//!
//! fn main() {
//! // Encode some data into bits.
//! let code = QrCode::new(b"01234567").unwrap();
2016-06-07 11:31:11 -05:00
//!
//! // Render the bits into an image.
//! let image = code.render::<Luma<u8>>().build();
2016-06-07 11:31:11 -05:00
//!
//! // 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);
//! }
2016-06-07 11:31:11 -05:00
//! ```
#![cfg_attr(feature="bench", feature(test))] // Unstable libraries
2016-06-07 11:31:11 -05:00
#[cfg(feature="bench")] extern crate test;
#[cfg(feature="image")] extern crate image;
use std::ops::Index;
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, Color, EcLevel, Version};
2016-06-07 11:31:11 -05:00
use render::{Pixel, Renderer};
/// The encoded QR code symbol.
#[derive(Clone)]
pub struct QrCode {
content: Vec<Color>,
version: Version,
ec_level: EcLevel,
width: usize,
2014-08-18 14:25:40 -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
/// 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> {
QrCode::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();
///
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));
QrCode::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();
///
2016-05-16 08:42:05 -05:00
pub fn with_version<D: AsRef<[u8]>>(data: D, version: Version, ec_level: EcLevel) -> QrResult<QrCode> {
let mut bits = bits::Bits::new(version);
2016-05-16 08:42:05 -05:00
try!(bits.push_optimal_data(data.as_ref()));
try!(bits.push_terminator(ec_level));
QrCode::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<QrCode> {
let version = bits.version();
let data = bits.into_bytes();
let (encoded_data, ec_data) = try!(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(QrCode {
content: canvas.into_colors(),
version: version,
ec_level: 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).unwrap()
}
2014-12-29 14:46:22 -06:00
/// Checks whether a module at coordinate (x, y) is a functional module or
/// not.
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)
}
/// 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<bool> {
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<bool> {
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<Color> {
self.content.clone()
}
/// Converts the QR code to a vector of colors.
pub fn into_colors(self) -> Vec<Color> {
2016-05-16 08:42:23 -05:00
self.content
}
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
///
#[cfg_attr(feature="image", doc=" ```rust")]
#[cfg_attr(not(feature="image"), doc=" ```ignore")]
2016-06-07 12:03:16 -05:00
/// # 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();
2016-06-07 12:03:16 -05:00
///
/// # }
/// ```
///
/// 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<P: Pixel>(&self) -> Renderer<P> {
2016-06-07 11:31:11 -05:00
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 {QrCode, Version, EcLevel};
#[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\
###.#..##.###");
}
}
2016-06-07 11:31:11 -05:00
#[cfg(all(test, feature="image"))]
mod image_tests {
use image::{Luma, Rgb, load_from_memory};
2016-06-07 11:31:11 -05:00
use {QrCode, Version, EcLevel};
#[test]
fn test_annex_i_qr_as_image() {
let code = QrCode::new(b"01234567").unwrap();
let image = code.render::<Luma<u8>>().build();
2016-06-07 11:31:11 -05:00
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();
2016-06-07 11:31:11 -05:00
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
2017-05-22 16:49:51 -05:00
#[cfg(all(test, feature="svg"))]
mod svg_tests {
use render::svg::Color as SvgColor;
use {QrCode, Version, EcLevel};
#[test]
fn test_annex_i_qr_as_svg() {
let code = QrCode::new(b"01234567").unwrap();
let image = code.render::<SvgColor>().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);
}
}