The 0.0.1 version which supports encoded to QR code and Micro QR code.
This commit is contained in:
parent
d4f4624bf5
commit
072cadec95
906
src/bits.rs
Normal file
906
src/bits.rs
Normal file
|
@ -0,0 +1,906 @@
|
||||||
|
#![unstable]
|
||||||
|
|
||||||
|
//! The `bits` module encodes binary data into raw bits used in a QR code.
|
||||||
|
|
||||||
|
use std::cmp::min;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
use test::Bencher;
|
||||||
|
|
||||||
|
use types::{QrResult, DataTooLong, UnsupportedCharacterSet, InvalidEciDesignator,
|
||||||
|
InvalidCharacter,
|
||||||
|
Mode, Numeric, Alphanumeric, Byte, Kanji,
|
||||||
|
ErrorCorrectionLevel, Version, MicroVersion};
|
||||||
|
use optimize::{Parser, Optimizer, total_encoded_len, Segment};
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
//{{{ Bits
|
||||||
|
|
||||||
|
/// The `Bits` structure stores the encoded data for a QR code.
|
||||||
|
pub struct Bits {
|
||||||
|
data: Vec<u8>,
|
||||||
|
bit_offset: uint,
|
||||||
|
version: Version,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Bits {
|
||||||
|
/// Constructs a new, empty bits structure.
|
||||||
|
pub fn new(version: Version) -> Bits {
|
||||||
|
Bits { data: Vec::new(), bit_offset: 0, version: version }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pushes an N-bit big-endian integer to the end of the bits.
|
||||||
|
///
|
||||||
|
/// Note: It is up to the developer to ensure that `number` really only is
|
||||||
|
/// `n` bit in size. Otherwise the excess bits may stomp on the existing
|
||||||
|
/// ones.
|
||||||
|
fn push_number(&mut self, n: uint, number: u16) {
|
||||||
|
/*
|
||||||
|
debug_assert!(n == 16 || n < 16 && number < (1 << n),
|
||||||
|
"{} is too big as a {}-bit number", number, n);
|
||||||
|
*/
|
||||||
|
|
||||||
|
let b = self.bit_offset + n;
|
||||||
|
match (self.bit_offset, b) {
|
||||||
|
(0, 0..8) => {
|
||||||
|
self.data.push((number << (8-b)) as u8);
|
||||||
|
}
|
||||||
|
(0, _) => {
|
||||||
|
self.data.push((number >> (b-8)) as u8);
|
||||||
|
self.data.push((number << (16-b)) as u8);
|
||||||
|
}
|
||||||
|
(_, 0..8) => {
|
||||||
|
*self.data.mut_last().unwrap() |= (number << (8-b)) as u8;
|
||||||
|
}
|
||||||
|
(_, 9..16) => {
|
||||||
|
*self.data.mut_last().unwrap() |= (number >> (b-8)) as u8;
|
||||||
|
self.data.push((number << (16-b)) as u8);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
*self.data.mut_last().unwrap() |= (number >> (b-8)) as u8;
|
||||||
|
self.data.push((number >> (b-16)) as u8);
|
||||||
|
self.data.push((number << (24-b)) as u8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.bit_offset = b & 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pushes an N-bit big-endian integer to the end of the bits, and check
|
||||||
|
/// that the number does not overflow the bits.
|
||||||
|
///
|
||||||
|
/// Returns `Err(DataTooLong)` on overflow.
|
||||||
|
fn push_number_checked(&mut self, n: uint, number: uint) -> QrResult<()> {
|
||||||
|
if n > 16 || number >= (1 << n) {
|
||||||
|
Err(DataTooLong)
|
||||||
|
} else {
|
||||||
|
self.push_number(n, number as u16);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reserves `n` extra bits of space for pushing.
|
||||||
|
fn reserve_additional(&mut self, n: uint) {
|
||||||
|
let extra_bytes = (n + (8 - self.bit_offset) % 8) / 8;
|
||||||
|
self.data.reserve_additional(extra_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert the bits into a bytes vector.
|
||||||
|
pub fn into_bytes(self) -> Vec<u8> {
|
||||||
|
self.data
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Total number of bits.
|
||||||
|
pub fn len(&self) -> uint {
|
||||||
|
if self.bit_offset == 0 {
|
||||||
|
self.data.len() * 8
|
||||||
|
} else {
|
||||||
|
(self.data.len() - 1) * 8 + self.bit_offset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_push_number() {
|
||||||
|
let mut bits = Bits::new(Version(1));
|
||||||
|
|
||||||
|
bits.push_number(3, 0b010); // 0:0 .. 0:3
|
||||||
|
bits.push_number(3, 0b110); // 0:3 .. 0:6
|
||||||
|
bits.push_number(3, 0b101); // 0:6 .. 1:1
|
||||||
|
bits.push_number(7, 0b001_1010);// 1:1 .. 2:0
|
||||||
|
bits.push_number(4, 0b1100); // 2:0 .. 2:4
|
||||||
|
bits.push_number(12, 0b1011_0110_1101); // 2:4 .. 4:0
|
||||||
|
bits.push_number(10, 0b01_1001_0001); // 4:0 .. 5:2
|
||||||
|
bits.push_number(15, 0b111_0010_1110_0011); // 5:2 .. 7:1
|
||||||
|
|
||||||
|
let bytes = bits.into_bytes();
|
||||||
|
|
||||||
|
assert_eq!(bytes, vec![0b010__110__10, // 90
|
||||||
|
0b1__001_1010, // 154
|
||||||
|
0b1100__1011, // 203
|
||||||
|
0b0110_1101, // 109
|
||||||
|
0b01_1001_00, // 100
|
||||||
|
0b01__111_001, // 121
|
||||||
|
0b0_1110_001, // 113
|
||||||
|
0b1__0000000]); // 128
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_push_splitted_bytes(bencher: &mut Bencher) {
|
||||||
|
bencher.iter(|| {
|
||||||
|
let mut bits = Bits::new(Version(40));
|
||||||
|
bits.push_number(4, 0b0101);
|
||||||
|
for _ in range(0u, 1024) {
|
||||||
|
bits.push_number(8, 0b10101010);
|
||||||
|
}
|
||||||
|
bits.into_bytes()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//}}}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
//{{{ Mode indicator
|
||||||
|
|
||||||
|
/// An "extended" mode indicator, includes all indicators supported by QR code
|
||||||
|
/// beyond those bearing data.
|
||||||
|
pub enum ExtendedMode {
|
||||||
|
/// ECI mode indicator, to introduce an ECI designator.
|
||||||
|
Eci,
|
||||||
|
|
||||||
|
/// The normal mode to introduce data.
|
||||||
|
Data(Mode),
|
||||||
|
|
||||||
|
/// FNC-1 mode in the first position.
|
||||||
|
Fnc1First,
|
||||||
|
|
||||||
|
/// FNC-1 mode in the second position.
|
||||||
|
Fnc1Second,
|
||||||
|
|
||||||
|
/// Structured append.
|
||||||
|
StructuredAppend,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Bits {
|
||||||
|
/// Push the mode indicator to the end of the bits.
|
||||||
|
///
|
||||||
|
/// If the mode is not supported in the provided version, this method
|
||||||
|
/// returns `Err(UnsupportedCharacterSet)`.
|
||||||
|
#[unstable]
|
||||||
|
pub fn push_mode_indicator(&mut self, mode: ExtendedMode) -> QrResult<()> {
|
||||||
|
let number = match (self.version, mode) {
|
||||||
|
(MicroVersion(1), Data(Numeric)) => return Ok(()),
|
||||||
|
(MicroVersion(_), Data(Numeric)) => 0,
|
||||||
|
(MicroVersion(_), Data(Alphanumeric)) => 1,
|
||||||
|
(MicroVersion(_), Data(Byte)) => 0b10,
|
||||||
|
(MicroVersion(_), Data(Kanji)) => 0b11,
|
||||||
|
(MicroVersion(_), _) => return Err(UnsupportedCharacterSet),
|
||||||
|
(_, Data(Numeric)) => 0b0001,
|
||||||
|
(_, Data(Alphanumeric)) => 0b0010,
|
||||||
|
(_, Data(Byte)) => 0b0100,
|
||||||
|
(_, Data(Kanji)) => 0b1000,
|
||||||
|
(_, Eci) => 0b0111,
|
||||||
|
(_, Fnc1First) => 0b0101,
|
||||||
|
(_, Fnc1Second) => 0b1001,
|
||||||
|
(_, StructuredAppend) => 0b0011,
|
||||||
|
};
|
||||||
|
let bits = self.version.mode_bits_count();
|
||||||
|
self.push_number_checked(bits, number).or(Err(UnsupportedCharacterSet))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//}}}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
//{{{ ECI
|
||||||
|
|
||||||
|
impl Bits {
|
||||||
|
/// Push an ECI (Extended Channel Interpretation) designator to the bits.
|
||||||
|
///
|
||||||
|
/// An ECI designator is a 6-digit number to specify the character set of
|
||||||
|
/// the following binary data. After calling this method, one could call
|
||||||
|
/// `.push_byte_data()` or similar methods to insert the actual data, e.g.
|
||||||
|
///
|
||||||
|
/// #![allow(unused_must_use)]
|
||||||
|
///
|
||||||
|
/// use qrcode::bits::Bits;
|
||||||
|
/// use qrcode::types::Version;
|
||||||
|
///
|
||||||
|
/// let mut bits = Bits::new(Version(1));
|
||||||
|
/// bits.push_eci_designator(9); // 9 = ISO-8859-7 (Greek).
|
||||||
|
/// bits.push_byte_data(b"\xa1\xa2\xa3\xa4\xa5"); // ΑΒΓΔΕ
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// The full list of ECI designator values can be found from
|
||||||
|
/// http://strokescribe.com/en/ECI.html. Some example values are:
|
||||||
|
///
|
||||||
|
/// ECI # | Character set
|
||||||
|
/// ------|-------------------------------------
|
||||||
|
/// 3 | ISO-8859-1 (Western European)
|
||||||
|
/// 20 | Shift JIS (Japanese)
|
||||||
|
/// 23 | Windows 1252 (Latin 1) (Western European)
|
||||||
|
/// 25 | UTF-16 Big Endian
|
||||||
|
/// 26 | UTF-8
|
||||||
|
/// 28 | Big 5 (Traditional Chinese)
|
||||||
|
/// 29 | GB-18030 (Simplified Chinese)
|
||||||
|
/// 30 | EUC-KR (Korean)
|
||||||
|
///
|
||||||
|
/// If the QR code version does not support ECI, this method will return
|
||||||
|
/// `Err(UnsupportedCharacterSet)`.
|
||||||
|
///
|
||||||
|
/// If the designator is outside of the expected range, this method will
|
||||||
|
/// return `Err(InvalidECIDesignator)`.
|
||||||
|
#[experimental]
|
||||||
|
pub fn push_eci_designator(&mut self, eci_designator: u32) -> QrResult<()> {
|
||||||
|
self.reserve_additional(12); // assume the common case that eci_designator <= 127.
|
||||||
|
try!(self.push_mode_indicator(Eci));
|
||||||
|
match eci_designator {
|
||||||
|
0..127 => {
|
||||||
|
self.push_number(8, eci_designator as u16);
|
||||||
|
}
|
||||||
|
128..16383 => {
|
||||||
|
self.push_number(2, 0b10);
|
||||||
|
self.push_number(14, eci_designator as u16);
|
||||||
|
}
|
||||||
|
16384..999999 => {
|
||||||
|
self.push_number(3, 0b110);
|
||||||
|
self.push_number(5, (eci_designator >> 16) as u16);
|
||||||
|
self.push_number(16, (eci_designator & 0xffff) as u16);
|
||||||
|
}
|
||||||
|
_ => return Err(InvalidEciDesignator),
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod eci_tests {
|
||||||
|
use bits::Bits;
|
||||||
|
use types::{MicroVersion, Version, InvalidEciDesignator, UnsupportedCharacterSet};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_9() {
|
||||||
|
let mut bits = Bits::new(Version(1));
|
||||||
|
assert_eq!(bits.push_eci_designator(9), Ok(()));
|
||||||
|
assert_eq!(bits.into_bytes(), vec![0b0111__0000, 0b1001__0000]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_899() {
|
||||||
|
let mut bits = Bits::new(Version(1));
|
||||||
|
assert_eq!(bits.push_eci_designator(899), Ok(()));
|
||||||
|
assert_eq!(bits.into_bytes(), vec![0b0111__10_00, 0b00111000, 0b0011__0000]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_999999() {
|
||||||
|
let mut bits = Bits::new(Version(1));
|
||||||
|
assert_eq!(bits.push_eci_designator(999999), Ok(()));
|
||||||
|
assert_eq!(bits.into_bytes(), vec![0b0111__110_0,
|
||||||
|
0b11110100,
|
||||||
|
0b00100011,
|
||||||
|
0b1111__0000]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_invalid_designator() {
|
||||||
|
let mut bits = Bits::new(Version(1));
|
||||||
|
assert_eq!(bits.push_eci_designator(1000000), Err(InvalidEciDesignator));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_unsupported_character_set() {
|
||||||
|
let mut bits = Bits::new(MicroVersion(4));
|
||||||
|
assert_eq!(bits.push_eci_designator(9), Err(UnsupportedCharacterSet));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//}}}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
//{{{ Numeric mode
|
||||||
|
|
||||||
|
impl Bits {
|
||||||
|
fn push_header(&mut self, mode: Mode, raw_data_len: uint) -> QrResult<()> {
|
||||||
|
let length_bits = mode.length_bits_count(self.version);
|
||||||
|
self.reserve_additional(length_bits + 4 + mode.data_bits_count(raw_data_len));
|
||||||
|
try!(self.push_mode_indicator(Data(mode)));
|
||||||
|
try!(self.push_number_checked(length_bits, raw_data_len));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Encodes a numeric string to the bits.
|
||||||
|
///
|
||||||
|
/// The data should only contain the characters 0 to 9.
|
||||||
|
#[unstable]
|
||||||
|
pub fn push_numeric_data(&mut self, data: &[u8]) -> QrResult<()> {
|
||||||
|
try!(self.push_header(Numeric, data.len()));
|
||||||
|
for chunk in data.chunks(3) {
|
||||||
|
let number = chunk.iter().map(|b| (b - b'0') as u16).fold(0, |a, b| a*10 + b);
|
||||||
|
let length = chunk.len() * 3 + 1;
|
||||||
|
self.push_number(length, number);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod numeric_tests {
|
||||||
|
use bits::Bits;
|
||||||
|
use types::{Version, MicroVersion, DataTooLong};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_iso_18004_2006_example_1() {
|
||||||
|
let mut bits = Bits::new(Version(1));
|
||||||
|
assert_eq!(bits.push_numeric_data(b"01234567"), Ok(()));
|
||||||
|
assert_eq!(bits.into_bytes(), vec![0b0001_0000,
|
||||||
|
0b001000_00,
|
||||||
|
0b00001100,
|
||||||
|
0b01010110,
|
||||||
|
0b01_100001,
|
||||||
|
0b1__0000000]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_iso_18004_2000_example_2() {
|
||||||
|
let mut bits = Bits::new(Version(1));
|
||||||
|
assert_eq!(bits.push_numeric_data(b"0123456789012345"), Ok(()));
|
||||||
|
assert_eq!(bits.into_bytes(), vec![0b0001_0000,
|
||||||
|
0b010000_00,
|
||||||
|
0b00001100,
|
||||||
|
0b01010110,
|
||||||
|
0b01_101010,
|
||||||
|
0b0110_1110,
|
||||||
|
0b000101_00,
|
||||||
|
0b11101010,
|
||||||
|
0b0101__0000]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_iso_18004_2006_example_2() {
|
||||||
|
let mut bits = Bits::new(MicroVersion(3));
|
||||||
|
assert_eq!(bits.push_numeric_data(b"0123456789012345"), Ok(()));
|
||||||
|
assert_eq!(bits.into_bytes(), vec![0b00_10000_0,
|
||||||
|
0b00000110,
|
||||||
|
0b0_0101011,
|
||||||
|
0b001_10101,
|
||||||
|
0b00110_111,
|
||||||
|
0b0000101_0,
|
||||||
|
0b01110101,
|
||||||
|
0b00101__000]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_data_too_long_error() {
|
||||||
|
let mut bits = Bits::new(MicroVersion(1));
|
||||||
|
assert_eq!(bits.push_numeric_data(b"12345678"), Err(DataTooLong));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//}}}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
//{{{ Alphanumeric mode
|
||||||
|
|
||||||
|
/// In QR code "Alphanumeric" mode, a pair of alphanumeric characters will be
|
||||||
|
/// encoded as a base-45 integer. `alphanumeric_digit` converts each character
|
||||||
|
/// into its corresponding base-45 digit.
|
||||||
|
///
|
||||||
|
/// The conversion is specified in ISO/IEC 18004:2006, §8.4.3, Table 5.
|
||||||
|
#[inline]
|
||||||
|
fn alphanumeric_digit(character: u8) -> u16 {
|
||||||
|
match character {
|
||||||
|
b'0' .. b'9' => (character - b'0') as u16,
|
||||||
|
b'A' .. b'Z' => (character - b'A') as u16 + 10,
|
||||||
|
b' ' => 36,
|
||||||
|
b'$' => 37,
|
||||||
|
b'%' => 38,
|
||||||
|
b'*' => 39,
|
||||||
|
b'+' => 40,
|
||||||
|
b'-' => 41,
|
||||||
|
b'.' => 42,
|
||||||
|
b'/' => 43,
|
||||||
|
b':' => 44,
|
||||||
|
_ => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Bits {
|
||||||
|
/// Encodes an alphanumeric string to the bits.
|
||||||
|
///
|
||||||
|
/// The data should only contain the charaters A to Z (excluding lowercase),
|
||||||
|
/// 0 to 9, space, `$`, `%`, `*`, `+`, `-`, `.`, `/` or `:`.
|
||||||
|
#[unstable]
|
||||||
|
pub fn push_alphanumeric_data(&mut self, data: &[u8]) -> QrResult<()> {
|
||||||
|
try!(self.push_header(Alphanumeric, data.len()));
|
||||||
|
for chunk in data.chunks(2) {
|
||||||
|
let number = chunk.iter().map(|b| alphanumeric_digit(*b)).fold(0, |a, b| a*45 + b);
|
||||||
|
let length = chunk.len() * 5 + 1;
|
||||||
|
self.push_number(length, number);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod alphanumeric_tests {
|
||||||
|
use bits::Bits;
|
||||||
|
use types::{Version, MicroVersion, UnsupportedCharacterSet, DataTooLong};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_iso_18004_2006_example() {
|
||||||
|
let mut bits = Bits::new(Version(1));
|
||||||
|
assert_eq!(bits.push_alphanumeric_data(b"AC-42"), Ok(()));
|
||||||
|
assert_eq!(bits.into_bytes(), vec![0b0010_0000,
|
||||||
|
0b00101_001,
|
||||||
|
0b11001110,
|
||||||
|
0b11100111,
|
||||||
|
0b001_00001,
|
||||||
|
0b0__0000000]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_micro_qr_unsupported() {
|
||||||
|
let mut bits = Bits::new(MicroVersion(1));
|
||||||
|
assert_eq!(bits.push_alphanumeric_data(b"A"), Err(UnsupportedCharacterSet));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_data_too_long() {
|
||||||
|
let mut bits = Bits::new(MicroVersion(2));
|
||||||
|
assert_eq!(bits.push_alphanumeric_data(b"ABCDEFGH"), Err(DataTooLong));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//}}}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
//{{{ Byte mode
|
||||||
|
|
||||||
|
impl Bits {
|
||||||
|
/// Encodes 8-bit byte data to the bits.
|
||||||
|
#[unstable]
|
||||||
|
pub fn push_byte_data(&mut self, data: &[u8]) -> QrResult<()> {
|
||||||
|
try!(self.push_header(Byte, data.len()));
|
||||||
|
for b in data.iter() {
|
||||||
|
self.push_number(8, *b as u16);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod byte_tests {
|
||||||
|
use bits::Bits;
|
||||||
|
use types::{Version, MicroVersion, UnsupportedCharacterSet, DataTooLong};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test() {
|
||||||
|
let mut bits = Bits::new(Version(1));
|
||||||
|
assert_eq!(bits.push_byte_data(b"\x12\x34\x56\x78\x9a\xbc\xde\xf0"), Ok(()));
|
||||||
|
assert_eq!(bits.into_bytes(), vec![0b0100_0000,
|
||||||
|
0b1000_0001,
|
||||||
|
0b0010_0011,
|
||||||
|
0b0100_0101,
|
||||||
|
0b0110_0111,
|
||||||
|
0b1000_1001,
|
||||||
|
0b1010_1011,
|
||||||
|
0b1100_1101,
|
||||||
|
0b1110_1111,
|
||||||
|
0b0000__0000]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_micro_qr_unsupported() {
|
||||||
|
let mut bits = Bits::new(MicroVersion(2));
|
||||||
|
assert_eq!(bits.push_byte_data(b"?"), Err(UnsupportedCharacterSet));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_data_too_long() {
|
||||||
|
let mut bits = Bits::new(MicroVersion(3));
|
||||||
|
assert_eq!(bits.push_byte_data(b"0123456701234567"), Err(DataTooLong));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//}}}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
//{{{ Kanji mode
|
||||||
|
|
||||||
|
impl Bits {
|
||||||
|
/// Encodes Shift JIS double-byte data to the bits.
|
||||||
|
#[unstable]
|
||||||
|
pub fn push_kanji_data(&mut self, data: &[u8]) -> QrResult<()> {
|
||||||
|
try!(self.push_header(Kanji, data.len()/2));
|
||||||
|
for kanji in data.chunks(2) {
|
||||||
|
if kanji.len() != 2 {
|
||||||
|
return Err(InvalidCharacter);
|
||||||
|
}
|
||||||
|
let cp = (kanji[0] as u16) * 256 + (kanji[1] as u16);
|
||||||
|
let bytes = if cp < 0xe040 { cp - 0x8140 } else { cp - 0xc140 };
|
||||||
|
let number = (bytes >> 8) * 0xc0 + (bytes & 0xff);
|
||||||
|
self.push_number(13, number);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod kanji_tests {
|
||||||
|
use bits::Bits;
|
||||||
|
use types::{Version, MicroVersion, UnsupportedCharacterSet, DataTooLong};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_iso_18004_example() {
|
||||||
|
let mut bits = Bits::new(Version(1));
|
||||||
|
assert_eq!(bits.push_kanji_data(b"\x93\x5f\xe4\xaa"), Ok(()));
|
||||||
|
assert_eq!(bits.into_bytes(), vec![0b1000_0000,
|
||||||
|
0b0010_0110,
|
||||||
|
0b11001111,
|
||||||
|
0b1_1101010,
|
||||||
|
0b101010__00]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_micro_qr_unsupported() {
|
||||||
|
let mut bits = Bits::new(MicroVersion(2));
|
||||||
|
assert_eq!(bits.push_kanji_data(b"?"), Err(UnsupportedCharacterSet));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_data_too_long() {
|
||||||
|
let mut bits = Bits::new(MicroVersion(3));
|
||||||
|
assert_eq!(bits.push_kanji_data(b"\x93_\x93_\x93_\x93_\x93_\x93_\x93_\x93_"),
|
||||||
|
Err(DataTooLong));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//}}}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
//{{{ FNC1 mode
|
||||||
|
|
||||||
|
impl Bits {
|
||||||
|
/// Encodes an indicator that the following data are formatted according to
|
||||||
|
/// the UCC/EAN Application Identifiers standard.
|
||||||
|
///
|
||||||
|
/// #![allow(unused_must_use)]
|
||||||
|
///
|
||||||
|
/// use qrcode::bits::Bits;
|
||||||
|
/// use qrcode::types::Version;
|
||||||
|
///
|
||||||
|
/// let mut bits = Bits::new(Version(1));
|
||||||
|
/// bits.push_fnc1_first_position();
|
||||||
|
/// bits.push_numeric_data(b"01049123451234591597033130128");
|
||||||
|
/// bits.push_alphanumeric_data(b"%10ABC123");
|
||||||
|
///
|
||||||
|
/// In QR code, the character `%` is used as the data field separator (0x1D).
|
||||||
|
#[experimental]
|
||||||
|
pub fn push_fnc1_first_position(&mut self) -> QrResult<()> {
|
||||||
|
self.push_mode_indicator(Fnc1First)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Encodes an indicator that the following data are formatted in accordance
|
||||||
|
/// with specific industry or application specifications previously agreed
|
||||||
|
/// with AIM International.
|
||||||
|
///
|
||||||
|
/// #![allow(unused_must_use)]
|
||||||
|
///
|
||||||
|
/// use qrcode::bits::Bits;
|
||||||
|
/// use qrcode::types::Version;
|
||||||
|
///
|
||||||
|
/// let mut bits = Bits::new(Version(1));
|
||||||
|
/// bits.push_fnc1_second_position(37);
|
||||||
|
/// bits.push_alphanumeric_data(b"AA1234BBB112");
|
||||||
|
/// bits.push_byte_data(b"text text text text\r");
|
||||||
|
///
|
||||||
|
/// If the application indicator is a single Latin alphabet (a–z / A–Z),
|
||||||
|
/// please pass in its ASCII value + 100:
|
||||||
|
///
|
||||||
|
/// ```ignore
|
||||||
|
/// bits.push_fnc1_second_position(b'A' + 100);
|
||||||
|
/// ```
|
||||||
|
#[experimental]
|
||||||
|
pub fn push_fnc1_second_position(&mut self, application_indicator: u8) -> QrResult<()> {
|
||||||
|
try!(self.push_mode_indicator(Fnc1Second));
|
||||||
|
self.push_number(8, application_indicator as u16);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//}}}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
//{{{ Finish
|
||||||
|
|
||||||
|
// This table is copied from ISO/IEC 18004:2006 §6.4.10, Table 7.
|
||||||
|
static DATA_LENGTHS: [[uint, ..4], ..44] = [
|
||||||
|
// Normal versions
|
||||||
|
[152, 128, 104, 72],
|
||||||
|
[272, 224, 176, 128],
|
||||||
|
[440, 352, 272, 208],
|
||||||
|
[640, 512, 384, 288],
|
||||||
|
[864, 688, 496, 368],
|
||||||
|
[1088, 864, 608, 480],
|
||||||
|
[1248, 992, 704, 528],
|
||||||
|
[1552, 1232, 880, 688],
|
||||||
|
[1856, 1456, 1056, 800],
|
||||||
|
[2192, 1728, 1232, 976],
|
||||||
|
[2592, 2032, 1440, 1120],
|
||||||
|
[2960, 2320, 1648, 1264],
|
||||||
|
[3424, 2672, 1952, 1440],
|
||||||
|
[3688, 2920, 2088, 1576],
|
||||||
|
[4184, 3320, 2360, 1784],
|
||||||
|
[4712, 3624, 2600, 2024],
|
||||||
|
[5176, 4056, 2936, 2264],
|
||||||
|
[5768, 4504, 3176, 2504],
|
||||||
|
[6360, 5016, 3560, 2728],
|
||||||
|
[6888, 5352, 3880, 3080],
|
||||||
|
[7456, 5712, 4096, 3248],
|
||||||
|
[8048, 6256, 4544, 3536],
|
||||||
|
[8752, 6880, 4912, 3712],
|
||||||
|
[9392, 7312, 5312, 4112],
|
||||||
|
[10208, 8000, 5744, 4304],
|
||||||
|
[10960, 8496, 6032, 4768],
|
||||||
|
[11744, 9024, 6464, 5024],
|
||||||
|
[12248, 9544, 6968, 5288],
|
||||||
|
[13048, 10136, 7288, 5608],
|
||||||
|
[13880, 10984, 7880, 5960],
|
||||||
|
[14744, 11640, 8264, 6344],
|
||||||
|
[15640, 12328, 8920, 6760],
|
||||||
|
[16568, 13048, 9368, 7208],
|
||||||
|
[17528, 13800, 9848, 7688],
|
||||||
|
[18448, 14496, 10288, 7888],
|
||||||
|
[19472, 15312, 10832, 8432],
|
||||||
|
[20528, 15936, 11408, 8768],
|
||||||
|
[21616, 16816, 12016, 9136],
|
||||||
|
[22496, 17728, 12656, 9776],
|
||||||
|
[23648, 18672, 13328, 10208],
|
||||||
|
|
||||||
|
// Micro versions
|
||||||
|
[20, 0, 0, 0],
|
||||||
|
[40, 32, 0, 0],
|
||||||
|
[84, 68, 0, 0],
|
||||||
|
[128, 112, 80, 0],
|
||||||
|
];
|
||||||
|
|
||||||
|
impl Bits {
|
||||||
|
/// Pushes the ending bits to indicate no more data.
|
||||||
|
#[unstable]
|
||||||
|
pub fn push_terminator(&mut self, ec_level: ErrorCorrectionLevel) -> QrResult<()> {
|
||||||
|
let terminator_size = match self.version {
|
||||||
|
MicroVersion(a) => (a as uint) * 2 + 1,
|
||||||
|
_ => 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
let cur_length = self.len();
|
||||||
|
let data_length = try!(self.version.fetch(ec_level, DATA_LENGTHS.as_slice()));
|
||||||
|
if cur_length > data_length {
|
||||||
|
return Err(DataTooLong);
|
||||||
|
}
|
||||||
|
|
||||||
|
let terminator_size = min(terminator_size, data_length - cur_length);
|
||||||
|
if terminator_size > 0 {
|
||||||
|
self.push_number(terminator_size, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.len() < data_length {
|
||||||
|
self.bit_offset = 0;
|
||||||
|
let data_bytes_length = data_length / 8;
|
||||||
|
let padding_bytes_count = data_bytes_length - self.data.len();
|
||||||
|
self.data.grow_fn(padding_bytes_count,
|
||||||
|
|i| if i % 2 == 0 { 0b11101100 } else { 0b00010001 });
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.len() < data_length {
|
||||||
|
self.data.push(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod finish_tests {
|
||||||
|
use bits::Bits;
|
||||||
|
use types::{Version, MicroVersion, L, Q, DataTooLong};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hello_world() {
|
||||||
|
let mut bits = Bits::new(Version(1));
|
||||||
|
assert_eq!(bits.push_alphanumeric_data(b"HELLO WORLD"), Ok(()));
|
||||||
|
assert_eq!(bits.push_terminator(Q), Ok(()));
|
||||||
|
assert_eq!(bits.into_bytes(), vec![0b00100000, 0b01011011, 0b00001011,
|
||||||
|
0b01111000, 0b11010001, 0b01110010,
|
||||||
|
0b11011100, 0b01001101, 0b01000011,
|
||||||
|
0b01000000, 0b11101100, 0b00010001,
|
||||||
|
0b11101100]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_too_long() {
|
||||||
|
let mut bits = Bits::new(MicroVersion(1));
|
||||||
|
assert_eq!(bits.push_numeric_data(b"9999999"), Ok(()));
|
||||||
|
assert_eq!(bits.push_terminator(L), Err(DataTooLong));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_no_terminator() {
|
||||||
|
let mut bits = Bits::new(MicroVersion(1));
|
||||||
|
assert_eq!(bits.push_numeric_data(b"99999"), Ok(()));
|
||||||
|
assert_eq!(bits.push_terminator(L), Ok(()));
|
||||||
|
assert_eq!(bits.into_bytes(), vec![0b101_11111, 0b00111_110, 0b0011__0000]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_no_padding() {
|
||||||
|
let mut bits = Bits::new(MicroVersion(1));
|
||||||
|
assert_eq!(bits.push_numeric_data(b"9999"), Ok(()));
|
||||||
|
assert_eq!(bits.push_terminator(L), Ok(()));
|
||||||
|
assert_eq!(bits.into_bytes(), vec![0b100_11111, 0b00111_100, 0b1_000__0000]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_micro_version_1_half_byte_padding() {
|
||||||
|
let mut bits = Bits::new(MicroVersion(1));
|
||||||
|
assert_eq!(bits.push_numeric_data(b"999"), Ok(()));
|
||||||
|
assert_eq!(bits.push_terminator(L), Ok(()));
|
||||||
|
assert_eq!(bits.into_bytes(), vec![0b011_11111, 0b00111_000, 0b0000__0000]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_micro_version_1_full_byte_padding() {
|
||||||
|
let mut bits = Bits::new(MicroVersion(1));
|
||||||
|
assert_eq!(bits.push_numeric_data(b""), Ok(()));
|
||||||
|
assert_eq!(bits.push_terminator(L), Ok(()));
|
||||||
|
assert_eq!(bits.into_bytes(), vec![0b000_000_00, 0b11101100, 0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//}}}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
//{{{ Front end.
|
||||||
|
|
||||||
|
impl Bits {
|
||||||
|
/// Push a segmented data to the bits, and then terminate it.
|
||||||
|
#[experimental]
|
||||||
|
pub fn push_segments_terminated<I: Iterator<Segment>>(&mut self,
|
||||||
|
data: &[u8],
|
||||||
|
mut segments_iter: I,
|
||||||
|
ec_level: ErrorCorrectionLevel,
|
||||||
|
data_length: uint) -> QrResult<()> {
|
||||||
|
self.data.reserve(data_length);
|
||||||
|
for segment in segments_iter {
|
||||||
|
let slice = data.slice(segment.begin, segment.end);
|
||||||
|
try!(match segment.mode {
|
||||||
|
Numeric => self.push_numeric_data(slice),
|
||||||
|
Alphanumeric => self.push_alphanumeric_data(slice),
|
||||||
|
Byte => self.push_byte_data(slice),
|
||||||
|
Kanji => self.push_kanji_data(slice),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
try!(self.push_terminator(ec_level));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Encodes a binary string to the raw QR code bits.
|
||||||
|
#[experimental]
|
||||||
|
pub fn encode(data: &[u8], version: Version, ec_level: ErrorCorrectionLevel) -> QrResult<Vec<u8>> {
|
||||||
|
let segments = Parser::new(data);
|
||||||
|
let opt_segments = segments.optimize(version);
|
||||||
|
let mut bits = Bits::new(version);
|
||||||
|
try!(bits.push_segments_terminated(data, opt_segments, ec_level, 0u));
|
||||||
|
Ok(bits.into_bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod encode_tests {
|
||||||
|
use bits::encode;
|
||||||
|
use types::{Version, MicroVersion, DataTooLong, L, Q, H};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_alphanumeric() {
|
||||||
|
let res = encode(b"HELLO WORLD", Version(1), Q);
|
||||||
|
assert_eq!(res, Ok(vec![0b00100000, 0b01011011, 0b00001011,
|
||||||
|
0b01111000, 0b11010001, 0b01110010,
|
||||||
|
0b11011100, 0b01001101, 0b01000011,
|
||||||
|
0b01000000, 0b11101100, 0b00010001,
|
||||||
|
0b11101100]));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_auto_mode_switch() {
|
||||||
|
let res = encode(b"123A", MicroVersion(2), L);
|
||||||
|
assert_eq!(res, Ok(vec![0b0_0011_000, 0b1111011_1, 0b001_00101,
|
||||||
|
0b0_00000__00, 0b11101100]));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_too_long() {
|
||||||
|
let res = encode(b">>>>>>>>", Version(1), H);
|
||||||
|
assert_eq!(res, Err(DataTooLong));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//}}}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
//{{{ Auto version minimization
|
||||||
|
|
||||||
|
/// Automatically determines the minimum version to store the data, and encode
|
||||||
|
/// the result.
|
||||||
|
///
|
||||||
|
/// This method will not consider any Micro QR code versions.
|
||||||
|
#[experimental]
|
||||||
|
pub fn encode_auto(data: &[u8], ec_level: ErrorCorrectionLevel) -> QrResult<(Vec<u8>, Version)> {
|
||||||
|
let segments = Parser::new(data).collect::<Vec<Segment>>();
|
||||||
|
for version in [Version(9), Version(26), Version(40)].iter() {
|
||||||
|
let opt_segments = Optimizer::new(segments.iter().map(|s| *s), *version).collect::<Vec<Segment>>();
|
||||||
|
let total_len = total_encoded_len(opt_segments.as_slice(), *version);
|
||||||
|
let data_capacity = version.fetch(ec_level, DATA_LENGTHS.as_slice()).unwrap();
|
||||||
|
if total_len <= data_capacity {
|
||||||
|
let min_version = find_min_version(total_len, ec_level);
|
||||||
|
let mut bits = Bits::new(min_version);
|
||||||
|
try!(bits.push_segments_terminated(data, opt_segments.move_iter(),
|
||||||
|
ec_level, total_len));
|
||||||
|
return Ok((bits.into_bytes(), min_version));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(DataTooLong)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Finds the smallest version (QR code only) that can store N bits of data
|
||||||
|
/// in the given error correction level.
|
||||||
|
#[unstable]
|
||||||
|
fn find_min_version(length: uint, ec_level: ErrorCorrectionLevel) -> Version {
|
||||||
|
let mut min = 0u;
|
||||||
|
let mut max = 39u;
|
||||||
|
while min < max {
|
||||||
|
let half = (min + max) / 2;
|
||||||
|
if DATA_LENGTHS[half][ec_level as uint] < length {
|
||||||
|
min = half + 1;
|
||||||
|
} else {
|
||||||
|
max = half;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Version((min + 1) as i16)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod encode_auto_tests {
|
||||||
|
use bits::{find_min_version, encode_auto};
|
||||||
|
use types::{Version, L, Q, H};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_find_min_version() {
|
||||||
|
assert_eq!(find_min_version(60, L), Version(1));
|
||||||
|
assert_eq!(find_min_version(200, L), Version(2));
|
||||||
|
assert_eq!(find_min_version(200, H), Version(3));
|
||||||
|
assert_eq!(find_min_version(20000, L), Version(37));
|
||||||
|
assert_eq!(find_min_version(640, L), Version(4));
|
||||||
|
assert_eq!(find_min_version(641, L), Version(5));
|
||||||
|
assert_eq!(find_min_version(999999, H), Version(40));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_alpha_q() {
|
||||||
|
match encode_auto(b"HELLO WORLD", Q) {
|
||||||
|
Ok((_, Version(1))) => {},
|
||||||
|
x => fail!("{}", x),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_alpha_h() {
|
||||||
|
match encode_auto(b"HELLO WORLD", H) {
|
||||||
|
Ok((_, Version(2))) => {},
|
||||||
|
x => fail!("{}", x),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_mixed() {
|
||||||
|
match encode_auto(b"This is a mixed data test. 1234567890", H) {
|
||||||
|
Ok((_, Version(4))) => {},
|
||||||
|
x => fail!("{}", x),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//}}}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
2126
src/canvas.rs
Normal file
2126
src/canvas.rs
Normal file
File diff suppressed because it is too large
Load diff
382
src/ec.rs
Normal file
382
src/ec.rs
Normal file
|
@ -0,0 +1,382 @@
|
||||||
|
//! The `ec` module applies the Reed-Solomon error correction codes.
|
||||||
|
|
||||||
|
use types::{QrResult, Version, ErrorCorrectionLevel};
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
//{{{ Error correction primitive
|
||||||
|
|
||||||
|
/// Creates the error correction code in N bytes.
|
||||||
|
///
|
||||||
|
/// This method only supports computing the error-correction code up to
|
||||||
|
/// 69 bytes. Longer blocks will result in failure.
|
||||||
|
///
|
||||||
|
/// This method treats the data as a polynomial of the form
|
||||||
|
/// (a[0] x<sup>m+n</sup> + a[1] x<sup>m+n-1</sup> + … + a[m] x<sup>n</sup>) in
|
||||||
|
/// GF(256), and then computes the polynomial modulus with a generator
|
||||||
|
/// polynomial of degree N.
|
||||||
|
pub fn create_error_correction_code(data: &[u8], ec_code_size: uint) -> Vec<u8> {
|
||||||
|
let mut res = Vec::from_slice(data);
|
||||||
|
res.grow(ec_code_size, &0);
|
||||||
|
|
||||||
|
let data_len = data.len();
|
||||||
|
let log_den = GENERATOR_POLYNOMIALS[ec_code_size];
|
||||||
|
|
||||||
|
for i in range(0, data_len) {
|
||||||
|
let lead_coeff = res[i] as uint;
|
||||||
|
if lead_coeff == 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let log_lead_coeff = LOG_TABLE[lead_coeff] as uint;
|
||||||
|
for (u, v) in res.mut_slice_from(i+1).mut_iter().zip(log_den.iter()) {
|
||||||
|
*u ^= EXP_TABLE[((*v as uint + log_lead_coeff) % 255) as uint];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res.move_iter().skip(data_len).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod ec_tests {
|
||||||
|
use ec::create_error_correction_code;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_poly_mod_1() {
|
||||||
|
let res = create_error_correction_code(b" [\x0bx\xd1r\xdcMC@\xec\x11\xec\x11\xec\x11", 10);
|
||||||
|
assert_eq!(res.as_slice(), b"\xc4#'w\xeb\xd7\xe7\xe2]\x17");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_poly_mod_2() {
|
||||||
|
let res = create_error_correction_code(b" [\x0bx\xd1r\xdcMC@\xec\x11\xec", 13);
|
||||||
|
assert_eq!(res.as_slice(), b"\xa8H\x16R\xd96\x9c\x00.\x0f\xb4z\x10");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_poly_mod_3() {
|
||||||
|
let res = create_error_correction_code(b"CUF\x86W&U\xc2w2\x06\x12\x06g&", 18);
|
||||||
|
assert_eq!(res.as_slice(), b"\xd5\xc7\x0b-s\xf7\xf1\xdf\xe5\xf8\x9au\x9aoV\xa1o'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//}}}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
//{{{ Interleave support
|
||||||
|
|
||||||
|
// TODO Make Vec implements ImmutableVector, or &[T] implements Index.
|
||||||
|
trait InterleaveSupport<T> {
|
||||||
|
fn push_item_to(&self, i: uint, v: &mut Vec<T>);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Copy> InterleaveSupport<T> for Vec<T> {
|
||||||
|
fn push_item_to(&self, i: uint, v: &mut Vec<T>) {
|
||||||
|
if i < self.len() {
|
||||||
|
v.push(self[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: Copy> InterleaveSupport<T> for &'a [T] {
|
||||||
|
fn push_item_to(&self, i: uint, v: &mut Vec<T>) {
|
||||||
|
if i < self.len() {
|
||||||
|
v.push(self[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This method interleaves a vector of slices into a single vector.
|
||||||
|
///
|
||||||
|
/// It will first insert all the first elements of the slices in `blocks`, then
|
||||||
|
/// all the second elements, then all the third elements, and so on.
|
||||||
|
///
|
||||||
|
/// The longest slice must be at the last of `blocks`, and `blocks` must not be
|
||||||
|
/// empty.
|
||||||
|
fn interleave<'a, 'b, T: Copy,
|
||||||
|
V: InterleaveSupport<T> + Collection>(blocks: &Vec<V>) -> Vec<T> {
|
||||||
|
let last_block_len = blocks.last().unwrap().len();
|
||||||
|
let mut res = Vec::with_capacity(last_block_len * blocks.len());
|
||||||
|
for i in range(0, last_block_len) {
|
||||||
|
for t in blocks.iter() {
|
||||||
|
t.push_item_to(i, &mut res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_interleave() {
|
||||||
|
let res = interleave(&vec![b"1234", b"5678", b"abcdef", b"ghijkl"]);
|
||||||
|
assert_eq!(res.as_slice(), b"15ag26bh37ci48djekfl");
|
||||||
|
}
|
||||||
|
|
||||||
|
//}}}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
//{{{ QR code error correction
|
||||||
|
|
||||||
|
/// Constructs data and error correction codewords ready to be put in the QR
|
||||||
|
/// code matrix.
|
||||||
|
pub fn construct_codewords(rawbits: &[u8],
|
||||||
|
version: Version,
|
||||||
|
ec_level: ErrorCorrectionLevel) -> QrResult<(Vec<u8>, Vec<u8>)> {
|
||||||
|
let (block_1_size, block_1_count, block_2_size, block_2_count) =
|
||||||
|
try!(version.fetch(ec_level, DATA_BYTES_PER_BLOCK.as_slice()));
|
||||||
|
|
||||||
|
let blocks_count = block_1_count + block_2_count;
|
||||||
|
let block_1_end = block_1_size * block_1_count;
|
||||||
|
let total_size = block_1_end + block_2_size * block_2_count;
|
||||||
|
|
||||||
|
debug_assert_eq!(rawbits.len(), total_size);
|
||||||
|
|
||||||
|
// Divide the data into blocks.
|
||||||
|
let mut blocks = Vec::with_capacity(blocks_count);
|
||||||
|
blocks.extend(rawbits.slice_to(block_1_end).chunks(block_1_size));
|
||||||
|
if block_2_size > 0 {
|
||||||
|
blocks.extend(rawbits.slice_from(block_1_end).chunks(block_2_size));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate EC codes.
|
||||||
|
let ec_bytes = try!(version.fetch(ec_level, EC_BYTES_PER_BLOCK.as_slice()));
|
||||||
|
let ec_codes = blocks.iter()
|
||||||
|
.map(|block| create_error_correction_code(*block, ec_bytes))
|
||||||
|
.collect::<Vec<Vec<u8>>>();
|
||||||
|
|
||||||
|
let blocks_vec = interleave(&blocks);
|
||||||
|
let ec_vec = interleave(&ec_codes);
|
||||||
|
|
||||||
|
Ok((blocks_vec, ec_vec))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod construct_codewords_test {
|
||||||
|
use ec::construct_codewords;
|
||||||
|
use types::{Version, M, Q};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_add_ec_simple() {
|
||||||
|
let msg = b" [\x0bx\xd1r\xdcMC@\xec\x11\xec\x11\xec\x11";
|
||||||
|
let (blocks_vec, ec_vec) = construct_codewords(msg, Version(1), M).unwrap();
|
||||||
|
assert_eq!(blocks_vec.as_slice(), msg);
|
||||||
|
assert_eq!(ec_vec.as_slice(), b"\xc4#'w\xeb\xd7\xe7\xe2]\x17");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_add_ec_complex() {
|
||||||
|
let msg = b"CUF\x86W&U\xc2w2\x06\x12\x06g&\xf6\xf6B\x07v\x86\xf2\x07&V\x16\xc6\xc7\x92\x06\xb6\xe6\xf7w2\x07v\x86W&R\x06\x86\x972\x07F\xf7vV\xc2\x06\x972\x10\xec\x11\xec\x11\xec\x11\xec";
|
||||||
|
let (blocks_vec, ec_vec) = construct_codewords(msg, Version(5), Q).unwrap();
|
||||||
|
assert_eq!(blocks_vec.as_slice(),
|
||||||
|
b"C\xf6\xb6FU\xf6\xe6\xf7FB\xf7v\x86\x07wVWv2\xc2&\x86\x07\x06U\xf2v\x97\xc2\x07\x862w&W\x102V&\xec\x06\x16R\x11\x12\xc6\x06\xec\x06\xc7\x86\x11g\x92\x97\xec&\x062\x11\x07\xec");
|
||||||
|
assert_eq!(ec_vec.as_slice(),
|
||||||
|
b"\xd5W\x94\xeb\xc7\xcct\x9f\x0b`\xb1\x05-<\xd4\xads\xcaL\x18\xf7\xb6\x85\x93\xf1|K;\xdf\x9d\xf2!\xe5\xc8\xeej\xf8\x86L(\x9a\x1b\xc3\xffu\x81\xe6\xac\x9a\xd1\xbdRo\x11\n\x02V\xa3l\x83\xa1\xa3\xf0 ox\xc0\xb2'\x85\x8d\xec");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//}}}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
//{{{ Precomputed tables for GF(256).
|
||||||
|
|
||||||
|
/// `EXP_TABLE` encodes the value of 2<sup>n</sup> in the Galois Field GF(256).
|
||||||
|
static EXP_TABLE: &'static [u8] = b"\x01\x02\x04\x08\x10\x20\x40\x80\x1d\x3a\x74\xe8\xcd\x87\x13\x26\x4c\x98\x2d\x5a\xb4\x75\xea\xc9\x8f\x03\x06\x0c\x18\x30\x60\xc0\x9d\x27\x4e\x9c\x25\x4a\x94\x35\x6a\xd4\xb5\x77\xee\xc1\x9f\x23\x46\x8c\x05\x0a\x14\x28\x50\xa0\x5d\xba\x69\xd2\xb9\x6f\xde\xa1\x5f\xbe\x61\xc2\x99\x2f\x5e\xbc\x65\xca\x89\x0f\x1e\x3c\x78\xf0\xfd\xe7\xd3\xbb\x6b\xd6\xb1\x7f\xfe\xe1\xdf\xa3\x5b\xb6\x71\xe2\xd9\xaf\x43\x86\x11\x22\x44\x88\x0d\x1a\x34\x68\xd0\xbd\x67\xce\x81\x1f\x3e\x7c\xf8\xed\xc7\x93\x3b\x76\xec\xc5\x97\x33\x66\xcc\x85\x17\x2e\x5c\xb8\x6d\xda\xa9\x4f\x9e\x21\x42\x84\x15\x2a\x54\xa8\x4d\x9a\x29\x52\xa4\x55\xaa\x49\x92\x39\x72\xe4\xd5\xb7\x73\xe6\xd1\xbf\x63\xc6\x91\x3f\x7e\xfc\xe5\xd7\xb3\x7b\xf6\xf1\xff\xe3\xdb\xab\x4b\x96\x31\x62\xc4\x95\x37\x6e\xdc\xa5\x57\xae\x41\x82\x19\x32\x64\xc8\x8d\x07\x0e\x1c\x38\x70\xe0\xdd\xa7\x53\xa6\x51\xa2\x59\xb2\x79\xf2\xf9\xef\xc3\x9b\x2b\x56\xac\x45\x8a\x09\x12\x24\x48\x90\x3d\x7a\xf4\xf5\xf7\xf3\xfb\xeb\xcb\x8b\x0b\x16\x2c\x58\xb0\x7d\xfa\xe9\xcf\x83\x1b\x36\x6c\xd8\xad\x47\x8e\x01";
|
||||||
|
|
||||||
|
/// `LOG_TABLE` is the inverse function of `EXP_TABLE`.
|
||||||
|
static LOG_TABLE: &'static [u8] = b"\xff\x00\x01\x19\x02\x32\x1a\xc6\x03\xdf\x33\xee\x1b\x68\xc7\x4b\x04\x64\xe0\x0e\x34\x8d\xef\x81\x1c\xc1\x69\xf8\xc8\x08\x4c\x71\x05\x8a\x65\x2f\xe1\x24\x0f\x21\x35\x93\x8e\xda\xf0\x12\x82\x45\x1d\xb5\xc2\x7d\x6a\x27\xf9\xb9\xc9\x9a\x09\x78\x4d\xe4\x72\xa6\x06\xbf\x8b\x62\x66\xdd\x30\xfd\xe2\x98\x25\xb3\x10\x91\x22\x88\x36\xd0\x94\xce\x8f\x96\xdb\xbd\xf1\xd2\x13\x5c\x83\x38\x46\x40\x1e\x42\xb6\xa3\xc3\x48\x7e\x6e\x6b\x3a\x28\x54\xfa\x85\xba\x3d\xca\x5e\x9b\x9f\x0a\x15\x79\x2b\x4e\xd4\xe5\xac\x73\xf3\xa7\x57\x07\x70\xc0\xf7\x8c\x80\x63\x0d\x67\x4a\xde\xed\x31\xc5\xfe\x18\xe3\xa5\x99\x77\x26\xb8\xb4\x7c\x11\x44\x92\xd9\x23\x20\x89\x2e\x37\x3f\xd1\x5b\x95\xbc\xcf\xcd\x90\x87\x97\xb2\xdc\xfc\xbe\x61\xf2\x56\xd3\xab\x14\x2a\x5d\x9e\x84\x3c\x39\x53\x47\x6d\x41\xa2\x1f\x2d\x43\xd8\xb7\x7b\xa4\x76\xc4\x17\x49\xec\x7f\x0c\x6f\xf6\x6c\xa1\x3b\x52\x29\x9d\x55\xaa\xfb\x60\x86\xb1\xbb\xcc\x3e\x5a\xcb\x59\x5f\xb0\x9c\xa9\xa0\x51\x0b\xf5\x16\xeb\x7a\x75\x2c\xd7\x4f\xae\xd5\xe9\xe6\xe7\xad\xe8\x74\xd6\xf4\xea\xa8\x50\x58\xaf";
|
||||||
|
|
||||||
|
/// The generator polynomial list.
|
||||||
|
///
|
||||||
|
/// `GENERATOR_POLYNOMIALS[i]` is the polynomial for `i` error correction code
|
||||||
|
/// words. Each entry encodes the log coefficients of the expanded polynomial
|
||||||
|
/// (x − 2<sup>0</sup>)(x − 2<sup>1</sup>)…(x − 2<sup>i-1</sup>). Each entry is
|
||||||
|
/// used as the denominator for polynomial division to obtain the modulus which
|
||||||
|
/// is the Reed-Solomon error correction code.
|
||||||
|
///
|
||||||
|
/// A partial list can be found from ISO/IEC 18004:2006 Annex A.
|
||||||
|
static GENERATOR_POLYNOMIALS: [&'static [u8], ..70] = [
|
||||||
|
b"",
|
||||||
|
b"\x00",
|
||||||
|
b"\x19\x01",
|
||||||
|
b"\xc6\xc7\x03",
|
||||||
|
b"\x4b\xf9\x4e\x06",
|
||||||
|
b"\x71\xa4\xa6\x77\x0a",
|
||||||
|
b"\xa6\x00\x86\x05\xb0\x0f",
|
||||||
|
b"\x57\xe5\x92\x95\xee\x66\x15",
|
||||||
|
b"\xaf\xee\xd0\xf9\xd7\xfc\xc4\x1c",
|
||||||
|
b"\x5f\xf6\x89\xe7\xeb\x95\x0b\x7b\x24",
|
||||||
|
b"\xfb\x43\x2e\x3d\x76\x46\x40\x5e\x20\x2d",
|
||||||
|
b"\xdc\xc0\x5b\xc2\xac\xb1\xd1\x74\xe3\x0a\x37",
|
||||||
|
b"\x66\x2b\x62\x79\xbb\x71\xc6\x8f\x83\x57\x9d\x42",
|
||||||
|
b"\x4a\x98\xb0\x64\x56\x64\x6a\x68\x82\xda\xce\x8c\x4e",
|
||||||
|
b"\xc7\xf9\x9b\x30\xbe\x7c\xda\x89\xd8\x57\xcf\x3b\x16\x5b",
|
||||||
|
b"\x08\xb7\x3d\x5b\xca\x25\x33\x3a\x3a\xed\x8c\x7c\x05\x63\x69",
|
||||||
|
b"\x78\x68\x6b\x6d\x66\xa1\x4c\x03\x5b\xbf\x93\xa9\xb6\xc2\xe1\x78",
|
||||||
|
b"\x2b\x8b\xce\x4e\x2b\xef\x7b\xce\xd6\x93\x18\x63\x96\x27\xf3\xa3\x88",
|
||||||
|
b"\xd7\xea\x9e\x5e\xb8\x61\x76\xaa\x4f\xbb\x98\x94\xfc\xb3\x05\x62\x60\x99",
|
||||||
|
b"\x43\x03\x69\x99\x34\x5a\x53\x11\x96\x9f\x2c\x80\x99\x85\xfc\xde\x8a\xdc\xab",
|
||||||
|
b"\x11\x3c\x4f\x32\x3d\xa3\x1a\xbb\xca\xb4\xdd\xe1\x53\xef\x9c\xa4\xd4\xd4\xbc\xbe",
|
||||||
|
b"\xf0\xe9\x68\xf7\xb5\x8c\x43\x62\x55\xc8\xd2\x73\x94\x89\xe6\x24\x7a\xfe\x94\xaf\xd2",
|
||||||
|
b"\xd2\xab\xf7\xf2\x5d\xe6\x0e\x6d\xdd\x35\xc8\x4a\x08\xac\x62\x50\xdb\x86\xa0\x69\xa5\xe7",
|
||||||
|
b"\xab\x66\x92\x5b\x31\x67\x41\x11\xc1\x96\x0e\x19\xb7\xf8\x5e\xa4\xe0\xc0\x01\x4e\x38\x93\xfd",
|
||||||
|
b"\xe5\x79\x87\x30\xd3\x75\xfb\x7e\x9f\xb4\xa9\x98\xc0\xe2\xe4\xda\x6f\x00\x75\xe8\x57\x60\xe3\x15",
|
||||||
|
b"\xe7\xb5\x9c\x27\xaa\x1a\x0c\x3b\x0f\x94\xc9\x36\x42\xed\xd0\x63\xa7\x90\xb6\x5f\xf3\x81\xb2\xfc\x2d",
|
||||||
|
b"\xad\x7d\x9e\x02\x67\xb6\x76\x11\x91\xc9\x6f\x1c\xa5\x35\xa1\x15\xf5\x8e\x0d\x66\x30\xe3\x99\x91\xda\x46",
|
||||||
|
b"\x4f\xe4\x08\xa5\xe3\x15\xb4\x1d\x09\xed\x46\x63\x2d\x3a\x8a\x87\x49\x7e\xac\x5e\xd8\xc1\x9d\x1a\x11\x95\x60",
|
||||||
|
b"\xa8\xdf\xc8\x68\xe0\xea\x6c\xb4\x6e\xbe\xc3\x93\xcd\x1b\xe8\xc9\x15\x2b\xf5\x57\x2a\xc3\xd4\x77\xf2\x25\x09\x7b",
|
||||||
|
b"\x9c\x2d\xb7\x1d\x97\xdb\x36\x60\xf9\x18\x88\x05\xf1\xaf\xbd\x1c\x4b\xea\x96\x94\x17\x09\xca\xa2\x44\xfa\x8c\x18\x97",
|
||||||
|
b"\x29\xad\x91\x98\xd8\x1f\xb3\xb6\x32\x30\x6e\x56\xef\x60\xde\x7d\x2a\xad\xe2\xc1\xe0\x82\x9c\x25\xfb\xd8\xee\x28\xc0\xb4",
|
||||||
|
b"\x14\x25\xfc\x5d\x3f\x4b\xe1\x1f\x73\x53\x71\x27\x2c\x49\x7a\x89\x76\x77\x90\xf8\xf8\x37\x01\xe1\x69\x7b\xb7\x75\xbb\xc8\xd2",
|
||||||
|
b"\x0a\x06\x6a\xbe\xf9\xa7\x04\x43\xd1\x8a\x8a\x20\xf2\x7b\x59\x1b\x78\xb9\x50\x9c\x26\x45\xab\x3c\x1c\xde\x50\x34\xfe\xb9\xdc\xf1",
|
||||||
|
b"\xf5\xe7\x37\x18\x47\x4e\x4c\x51\xe1\xd4\xad\x25\xd7\x2e\x77\xe5\xf5\xa7\x7e\x48\xb5\x5e\xa5\xd2\x62\x7d\x9f\xb8\xa9\xe8\xb9\xe7\x12",
|
||||||
|
b"\x6f\x4d\x92\x5e\x1a\x15\x6c\x13\x69\x5e\x71\xc1\x56\x8c\xa3\x7d\x3a\x9e\xe5\xef\xda\x67\x38\x46\x72\x3d\xb7\x81\xa7\x0d\x62\x3e\x81\x33",
|
||||||
|
b"\x07\x5e\x8f\x51\xf7\x7f\xca\xca\xc2\x7d\x92\x1d\x8a\xa2\x99\x41\x69\x7a\x74\xee\x1a\x24\xd8\x70\x7d\xe4\x0f\x31\x08\xa2\x1e\x7e\x6f\x3a\x55",
|
||||||
|
b"\xc8\xb7\x62\x10\xac\x1f\xf6\xea\x3c\x98\x73\x00\xa7\x98\x71\xf8\xee\x6b\x12\x3f\xda\x25\x57\xd2\x69\xb1\x78\x4a\x79\xc4\x75\xfb\x71\xe9\x1e\x78",
|
||||||
|
b"\x9a\x4b\x8d\xb4\x3d\xa5\x68\xe8\x2e\xe3\x60\xb2\x5c\x87\x39\xa2\x78\xc2\xd4\xae\xfc\xb7\x2a\x23\x9d\x6f\x17\x85\x64\x08\x69\x25\xc0\xbd\x9f\x13\x9c",
|
||||||
|
b"\x9f\x22\x26\xe4\xe6\x3b\xf3\x5f\x31\xda\xb0\xa4\x14\x41\x2d\x6f\x27\x51\x31\x76\x71\xde\xc1\xfa\xf2\xa8\xd9\x29\xa4\xf7\xb1\x1e\xee\x12\x78\x99\x3c\xc1",
|
||||||
|
b"\x51\xd8\xae\x2f\xc8\x96\x3b\x9c\x59\x8f\x59\xa6\xb7\xaa\x98\x15\xa5\xb1\x71\x84\xea\x05\x9a\x44\x7c\xaf\xc4\x9d\xf9\xe9\x53\x18\x99\xf1\x7e\x24\x74\x13\xe7",
|
||||||
|
b"\x3b\x74\x4f\xa1\xfc\x62\x80\xcd\x80\xa1\xf7\x39\xa3\x38\xeb\x6a\x35\x1a\xbb\xae\xe2\x68\xaa\x07\xaf\x23\xb5\x72\x58\x29\x2f\xa3\x7d\x86\x48\x14\xe8\x35\x23\x0f",
|
||||||
|
b"\x84\xa7\x34\x8b\xb8\xdf\x95\x5c\xfa\x12\x53\x21\x7f\x6d\xc2\x07\xd3\xf2\x6d\x42\x56\xa9\x57\x60\xbb\x9f\x72\xac\x76\xd0\xb7\xc8\x52\xb3\x26\x27\x22\xf2\x8e\x93\x37",
|
||||||
|
b"\xfa\x67\xdd\xe6\x19\x12\x89\xe7\x00\x03\x3a\xf2\xdd\xbf\x6e\x54\xe6\x08\xbc\x6a\x60\x93\x0f\x83\x8b\x22\x65\xdf\x27\x65\xd5\xc7\xed\xfe\xc9\x7b\xab\xa2\xc2\x75\x32\x60",
|
||||||
|
b"\x60\x43\x03\xf5\xd9\xd7\x21\x41\xf0\x6d\x90\x3f\x15\x83\x26\x65\x99\x80\x37\x1f\xed\x03\x5e\xa0\x14\x57\x4d\x38\xbf\x7b\xcf\x4b\x52\x00\x7a\x84\x65\x91\xd7\x0f\x79\xc0\x8a",
|
||||||
|
b"\xbe\x07\x3d\x79\x47\xf6\x45\x37\xa8\xbc\x59\xf3\xbf\x19\x48\x7b\x09\x91\x0e\xf7\x01\xee\x2c\x4e\x8f\x3e\xe0\x7e\x76\x72\x44\xa3\x34\xc2\xd9\x93\xcc\xa9\x25\x82\x71\x66\x49\xb5",
|
||||||
|
b"\x06\xac\x48\xfa\x12\xab\xab\xa2\xe5\xbb\xef\x04\xbb\x0b\x25\xe4\x66\x48\x66\x16\x21\x49\x5f\x63\x84\x01\x0f\x59\x04\x70\x82\x5f\xd3\xeb\xe3\x3a\x23\x58\x84\x17\x2c\xa5\x36\xbb\xe1",
|
||||||
|
b"\x70\x5e\x58\x70\xfd\xe0\xca\x73\xbb\x63\x59\x05\x36\x71\x81\x2c\x3a\x10\x87\xd8\xa9\xd3\x24\x01\x04\x60\x3c\xf1\x49\x68\xea\x08\xf9\xf5\x77\xae\x34\x19\x9d\xe0\x2b\xca\xdf\x13\x52\x0f",
|
||||||
|
b"\x4c\xa4\xe5\x5c\x4f\xa8\xdb\x6e\x68\x15\xdc\x4a\x13\xc7\xc3\x64\x5d\xbf\x2b\xd5\x48\x38\x8a\xa1\x7d\xbb\x77\xfa\xbd\x89\xbe\x4c\x7e\xf7\x5d\x1e\x84\x06\x3a\xd5\xd0\xa5\xe0\x98\x85\x5b\x3d",
|
||||||
|
b"\xe4\x19\xc4\x82\xd3\x92\x3c\x18\xfb\x5a\x27\x66\xf0\x3d\xb2\x3f\x2e\x7b\x73\x12\xdd\x6f\x87\xa0\xb6\xcd\x6b\xce\x5f\x96\x78\xb8\x5b\x15\xf7\x9c\x8c\xee\xbf\x0b\x5e\xe3\x54\x32\xa3\x27\x22\x6c",
|
||||||
|
b"\xac\x79\x01\x29\xc1\xde\xed\x40\x6d\xb5\x34\x78\xd4\xe2\xef\xf5\xd0\x14\xf6\x22\xe1\xcc\x86\x65\x7d\xce\x45\x8a\xfa\x00\x4d\x3a\x8f\xb9\xdc\xfe\xd2\xbe\x70\x58\x5b\x39\x5a\x6d\x05\x0d\xb5\x19\x9c",
|
||||||
|
b"\xe8\x7d\x9d\xa1\xa4\x09\x76\x2e\xd1\x63\xcb\xc1\x23\x03\xd1\x6f\xc3\xf2\xcb\xe1\x2e\x0d\x20\xa0\x7e\xd1\x82\xa0\xf2\xd7\xf2\x4b\x4d\x2a\xbd\x20\x71\x41\x7c\x45\xe4\x72\xeb\xaf\x7c\xaa\xd7\xe8\x85\xcd",
|
||||||
|
b"\xd5\xa6\x8e\x2b\x0a\xd8\x8d\xa3\xac\xb4\x66\x46\x59\x3e\xde\x3e\x2a\xd2\x97\xa3\xda\x46\x4d\x27\xa6\xbf\x72\xca\xf5\xbc\xb7\xdd\x4b\xd4\x1b\xed\x7f\xcc\xeb\x3e\xbe\xe8\x12\x2e\xab\x0f\x62\xf7\x42\xa3\x00",
|
||||||
|
b"\x74\x32\x56\xba\x32\xdc\xfb\x59\xc0\x2e\x56\x7f\x7c\x13\xb8\xe9\x97\xd7\x16\x0e\x3b\x91\x25\xf2\xcb\x86\xfe\x59\xbe\x5e\x3b\x41\x7c\x71\x64\xe9\xeb\x79\x16\x4c\x56\x61\x27\xf2\xc8\xdc\x65\x21\xef\xfe\x74\x33",
|
||||||
|
b"\x7a\xd6\xe7\x88\xc7\x0b\x06\xcd\x7c\x48\xd5\x75\xbb\x3c\x93\xc9\x49\x4b\x21\x92\xab\xf7\x76\xd0\x9d\xb1\xcb\xeb\x53\x2d\xe2\xca\xe5\xa8\x07\x39\xed\xeb\xc8\x7c\x6a\xfe\xa5\x0e\x93\x00\x39\x2a\x1f\xb2\xd5\xad\x67",
|
||||||
|
b"\xb7\x1a\xc9\x57\xd2\xdd\x71\x15\x2e\x41\x2d\x32\xee\xb8\xf9\xe1\x66\x3a\xd1\xda\x6d\xa5\x1a\x5f\xb8\xc0\x34\xf5\x23\xfe\xee\xaf\xac\x4f\x7b\x19\x7a\x2b\x78\x6c\xd7\x50\x80\xc9\xeb\x08\x99\x3b\x65\x1f\xc6\x4c\x1f\x9c",
|
||||||
|
b"\x26\xc5\x7b\xa7\x10\x57\xb2\xee\xe3\x61\x94\xf7\x1a\x5a\xe4\xb6\xec\xc5\x2f\xf9\x24\xd5\x36\x71\xb5\x4a\xb1\xcc\x9b\x3d\x2f\x2a\x00\x84\x90\xfb\xc8\x26\x26\x8a\x36\x2c\x40\x13\x16\xce\x10\x0a\xe4\xd3\xa1\xab\x2c\xc2\xd2",
|
||||||
|
b"\x6a\x78\x6b\x9d\xa4\xd8\x70\x74\x02\x5b\xf8\xa3\x24\xc9\xca\xe5\x06\x90\xfe\x9b\x87\xd0\xaa\xd1\x0c\x8b\x7f\x8e\xb6\xf9\xb1\xae\xbe\x1c\x0a\x55\xef\xb8\x65\x7c\x98\xce\x60\x17\xa3\x3d\x1b\xc4\xf7\x97\x9a\xca\xcf\x14\x3d\x0a",
|
||||||
|
b"\x3a\x8c\xed\x5d\x6a\x3d\xc1\x02\x57\x49\xc2\xd7\x9f\xa3\x0a\x9b\x05\x79\x99\x3b\xf8\x04\x75\x16\x3c\xb1\x90\x2c\x48\xe4\x3e\x01\x13\xaa\x71\x9e\x19\xaf\xc7\x8b\x5a\x01\xd2\x07\x77\x9a\x59\x9f\x82\x7a\x2e\x93\xbe\x87\x5e\x44\x42",
|
||||||
|
b"\x52\x74\x1a\xf7\x42\x1b\x3e\x6b\xfc\xb6\xc8\xb9\xeb\x37\xfb\xf2\xd2\x90\x9a\xed\xb0\x8d\xc0\xf8\x98\xf9\xce\x55\xfd\x8e\x41\xa5\x7d\x17\x18\x1e\x7a\xf0\xd6\x06\x81\xda\x1d\x91\x7f\x86\xce\xf5\x75\x1d\x29\x3f\x9f\x8e\xe9\x7d\x94\x7b",
|
||||||
|
b"\x39\x73\xe8\x0b\xc3\xd9\x03\xce\x4d\x43\x1d\xa6\xb4\x6a\x76\xcb\x11\x45\x98\xd5\x4a\x2c\x31\x2b\x62\x3d\xfd\x7a\x0e\x2b\xd1\x8f\x09\x68\x6b\xab\xe0\x39\xfe\xfb\xe2\xe8\xdd\xc2\xf0\x75\xa1\x52\xb2\xf6\xb2\x21\x32\x56\xd7\xef\xb4\xb4\xb5",
|
||||||
|
b"\x6b\x8c\x1a\x0c\x09\x8d\xf3\xc5\xe2\xc5\xdb\x2d\xd3\x65\xdb\x78\x1c\xb5\x7f\x06\x64\xf7\x02\xcd\xc6\x39\x73\xdb\x65\x6d\xa0\x52\x25\x26\xee\x31\xa0\xd1\x79\x56\x0b\x7c\x1e\xb5\x54\x19\xc2\x57\x41\x66\xbe\xdc\x46\x1b\xd1\x10\x59\x07\x21\xf0",
|
||||||
|
b"\xa1\xf4\x69\x73\x40\x09\xdd\xec\x10\x91\x94\x22\x90\xba\x0d\x14\xfe\xf6\x26\x23\xca\x48\x04\xd4\x9f\xd3\xa5\x87\xfc\xfa\x19\x57\x1e\x78\xe2\xea\x5c\xc7\x48\x07\x9b\xda\xe7\x2c\x7d\xb2\x9c\xae\x7c\x2b\x64\x1f\x38\x65\xcc\x40\xaf\xe1\xa9\x92\x2d",
|
||||||
|
b"\x41\xca\x71\x62\x47\xdf\xf8\x76\xd6\x5e\x00\x7a\x25\x17\x02\xe4\x3a\x79\x07\x69\x87\x4e\xf3\x76\x46\x4c\xdf\x59\x48\x32\x46\x6f\xc2\x11\xd4\x7e\xb5\x23\xdd\x75\xeb\x0b\xe5\x95\x93\x7b\xd5\x28\x73\x06\xc8\x64\x1a\xf6\xb6\xda\x7f\xd7\x24\xba\x6e\x6a",
|
||||||
|
b"\x1e\x47\x24\x47\x13\xc3\xac\x6e\x3d\x02\xa9\xc2\x5a\x88\x3b\xb6\xe7\x91\x66\x27\xaa\xe7\xd6\x43\xc4\xcf\x35\x70\xf6\x5a\x5a\x79\xb7\x92\x4a\x4d\x26\x59\x16\xe7\x37\x38\xf2\x70\xd9\x6e\x7b\x3e\xc9\xd9\x80\xa5\x3c\xb5\x25\xa1\xf6\x84\xf6\x12\x73\x88\xa8",
|
||||||
|
b"\x2d\x33\xaf\x09\x07\x9e\x9f\x31\x44\x77\x5c\x7b\xb1\xcc\xbb\xfe\xc8\x4e\x8d\x95\x77\x1a\x7f\x35\xa0\x5d\xc7\xd4\x1d\x18\x91\x9c\xd0\x96\xda\xd1\x04\xd8\x5b\x2f\xb8\x92\x2f\x8c\xc3\xc3\x7d\xf2\xee\x3f\x63\x6c\x8c\xe6\xf2\x1f\xcc\x0b\xb2\xf3\xd9\x9c\xd5\xe7",
|
||||||
|
b"\x89\x9e\xf7\xf0\x25\xee\xd6\x80\x63\xda\x2e\x8a\xc6\x80\x5c\xdb\x6d\x8b\xa6\x19\x42\x43\x0e\x3a\xee\x95\xb1\xc3\xdd\x9a\xab\x30\x50\x0c\x3b\xbe\xe4\x13\x37\xd0\x5c\x70\xe5\x25\x3c\x0a\x2f\x51\x00\xc0\x25\xab\xaf\x93\x80\x49\xa6\x3d\x95\x0c\x18\x5f\x46\x71\x28",
|
||||||
|
b"\x05\x76\xde\xb4\x88\x88\xa2\x33\x2e\x75\x0d\xd7\x51\x11\x8b\xf7\xc5\xab\x5f\xad\x41\x89\xb2\x44\x6f\x5f\x65\x29\x48\xd6\xa9\xc5\x5f\x07\x2c\x9a\x4d\x6f\xec\x28\x79\x8f\x3f\x57\x50\xfd\xf0\x7e\xd9\x4d\x22\xe8\x6a\x32\xa8\x52\x4c\x92\x43\x6a\xab\x19\x84\x5d\x2d\x69",
|
||||||
|
b"\xbf\xac\x71\x56\x07\xa6\xf6\xb9\x9b\xfa\x62\x71\x59\x56\xd6\xe1\x9c\xbe\x3a\x21\x90\x43\xb3\xa3\x34\x9a\xe9\x97\x68\xfb\xa0\x7e\xaf\xd0\xe1\x46\xe3\x92\x04\x98\x8b\x67\x19\x6b\x3d\xcc\x9f\xfa\xc1\xe1\x69\xa0\x62\xa7\x02\x35\x10\xf2\x53\xd2\xc4\x67\xf8\x56\xd3\x29\xab",
|
||||||
|
b"\xf7\x9f\xdf\x21\xe0\x5d\x4d\x46\x5a\xa0\x20\xfe\x2b\x96\x54\x65\xbe\xcd\x85\x34\x3c\xca\xa5\xdc\xcb\x97\x5d\x54\x0f\x54\xfd\xad\xa0\x59\xe3\x34\xc7\x61\x5f\xe7\x34\xb1\x29\x7d\x89\xf1\xa6\xe1\x76\x02\x36\x20\x52\xd7\xaf\xc6\x2b\xee\xeb\x1b\x65\xb8\x7f\x03\x05\x08\xa3\xee",
|
||||||
|
b"\x69\x49\x44\x01\x1d\xa8\x75\x0e\x58\xd0\x37\x2e\x2a\xd9\x06\x54\xb3\x61\x06\xf0\xc0\xe7\x9e\x40\x76\xa0\xcb\x39\x3d\x6c\xc7\x7c\x41\xbb\xdd\xa7\x27\xb6\x9f\xb4\xf4\xcb\xe4\xfe\x0d\xaf\x3d\x5a\xce\x28\xc7\x5e\x43\x39\x51\xe5\x2e\x7b\x59\x25\x1f\xca\x42\xfa\x23\xaa\xf3\x58\x33",
|
||||||
|
];
|
||||||
|
|
||||||
|
//}}}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
//{{{ Tables for error correction sizes
|
||||||
|
|
||||||
|
/// `EC_BYTES_PER_BLOCK` provides the number of codewords (bytes) used for error
|
||||||
|
/// correction per block in each version.
|
||||||
|
///
|
||||||
|
/// This is a copy of ISO/IEC 18004:2006, §6.5.1, Table 9.
|
||||||
|
pub static EC_BYTES_PER_BLOCK: [[uint, ..4], ..44] = [
|
||||||
|
// Normal versions.
|
||||||
|
[7, 10, 13, 17],
|
||||||
|
[10, 16, 22, 28],
|
||||||
|
[15, 26, 18, 22],
|
||||||
|
[20, 18, 26, 16],
|
||||||
|
[26, 24, 18, 22],
|
||||||
|
[18, 16, 24, 28],
|
||||||
|
[20, 18, 18, 26],
|
||||||
|
[24, 22, 22, 26],
|
||||||
|
[30, 22, 20, 24],
|
||||||
|
[18, 26, 24, 28],
|
||||||
|
[20, 30, 28, 24],
|
||||||
|
[24, 22, 26, 28],
|
||||||
|
[26, 22, 24, 22],
|
||||||
|
[30, 24, 20, 24],
|
||||||
|
[22, 24, 30, 24],
|
||||||
|
[24, 28, 24, 30],
|
||||||
|
[28, 28, 28, 28],
|
||||||
|
[30, 26, 28, 28],
|
||||||
|
[28, 26, 26, 26],
|
||||||
|
[28, 26, 30, 28],
|
||||||
|
[28, 26, 28, 30],
|
||||||
|
[28, 28, 30, 24],
|
||||||
|
[30, 28, 30, 30],
|
||||||
|
[30, 28, 30, 30],
|
||||||
|
[26, 28, 30, 30],
|
||||||
|
[28, 28, 28, 30],
|
||||||
|
[30, 28, 30, 30],
|
||||||
|
[30, 28, 30, 30],
|
||||||
|
[30, 28, 30, 30],
|
||||||
|
[30, 28, 30, 30],
|
||||||
|
[30, 28, 30, 30],
|
||||||
|
[30, 28, 30, 30],
|
||||||
|
[30, 28, 30, 30],
|
||||||
|
[30, 28, 30, 30],
|
||||||
|
[30, 28, 30, 30],
|
||||||
|
[30, 28, 30, 30],
|
||||||
|
[30, 28, 30, 30],
|
||||||
|
[30, 28, 30, 30],
|
||||||
|
[30, 28, 30, 30],
|
||||||
|
[30, 28, 30, 30],
|
||||||
|
|
||||||
|
// Micro versions.
|
||||||
|
[2, 0, 0, 0],
|
||||||
|
[5, 6, 0, 0],
|
||||||
|
[6, 8, 0, 0],
|
||||||
|
[8, 10, 14, 0],
|
||||||
|
];
|
||||||
|
|
||||||
|
/// `DATA_BYTES_PER_BLOCK` provides the number of codewords (bytes) used for
|
||||||
|
/// real data per block in each version.
|
||||||
|
///
|
||||||
|
/// This is a copy of ISO/IEC 18004:2006, §6.5.1, Table 9.
|
||||||
|
///
|
||||||
|
/// Every entry is a 4-tuple. Take `DATA_BYTES_PER_BLOCK[39][3] == (15, 20, 16, 61)`
|
||||||
|
/// as an example, this means in version 40 with correction level H, there are
|
||||||
|
/// 20 blocks with 15 bytes in size, and 61 blocks with 16 bytes in size.
|
||||||
|
pub static DATA_BYTES_PER_BLOCK: [[(uint, uint, uint, uint), ..4], ..44] = [
|
||||||
|
// Normal versions.
|
||||||
|
[(19, 1, 0, 0), (16, 1, 0, 0), (13, 1, 0, 0), (9, 1, 0, 0)],
|
||||||
|
[(34, 1, 0, 0), (28, 1, 0, 0), (22, 1, 0, 0), (16, 1, 0, 0)],
|
||||||
|
[(55, 1, 0, 0), (44, 1, 0, 0), (17, 2, 0, 0), (13, 2, 0, 0)],
|
||||||
|
[(80, 1, 0, 0), (32, 2, 0, 0), (24, 2, 0, 0), (9, 4, 0, 0)],
|
||||||
|
[(108, 1, 0, 0), (43, 2, 0, 0), (15, 2, 16, 2), (11, 2, 12, 2)],
|
||||||
|
[(68, 2, 0, 0), (27, 4, 0, 0), (19, 4, 0, 0), (15, 4, 0, 0)],
|
||||||
|
[(78, 2, 0, 0), (31, 4, 0, 0), (14, 2, 15, 4), (13, 4, 14, 1)],
|
||||||
|
[(97, 2, 0, 0), (38, 2, 39, 2), (18, 4, 19, 2), (14, 4, 15, 2)],
|
||||||
|
[(116, 2, 0, 0), (36, 3, 37, 2), (16, 4, 17, 4), (12, 4, 13, 4)],
|
||||||
|
[(68, 2, 69, 2), (43, 4, 44, 1), (19, 6, 20, 2), (15, 6, 16, 2)],
|
||||||
|
[(81, 4, 0, 0), (50, 1, 51, 4), (22, 4, 23, 4), (12, 3, 13, 8)],
|
||||||
|
[(92, 2, 93, 2), (36, 6, 37, 2), (20, 4, 21, 6), (14, 7, 15, 4)],
|
||||||
|
[(107, 4, 0, 0), (37, 8, 38, 1), (20, 8, 21, 4), (11, 12, 12, 4)],
|
||||||
|
[(115, 3, 116, 1), (40, 4, 41, 5), (16, 11, 17, 5), (12, 11, 13, 5)],
|
||||||
|
[(87, 5, 88, 1), (41, 5, 42, 5), (24, 5, 25, 7), (12, 11, 13, 7)],
|
||||||
|
[(98, 5, 99, 1), (45, 7, 46, 3), (19, 15, 20, 2), (15, 3, 16, 13)],
|
||||||
|
[(107, 1, 108, 5), (46, 10, 47, 1), (22, 1, 23, 15), (14, 2, 15, 17)],
|
||||||
|
[(120, 5, 121, 1), (43, 9, 44, 4), (22, 17, 23, 1), (14, 2, 15, 19)],
|
||||||
|
[(113, 3, 114, 4), (44, 3, 45, 11), (21, 17, 22, 4), (13, 9, 14, 16)],
|
||||||
|
[(107, 3, 108, 5), (41, 3, 42, 13), (24, 15, 25, 5), (15, 15, 16, 10)],
|
||||||
|
[(116, 4, 117, 4), (42, 17, 0, 0), (22, 17, 23, 6), (16, 19, 17, 6)],
|
||||||
|
[(111, 2, 112, 7), (46, 17, 0, 0), (24, 7, 25, 16), (13, 34, 0, 0)],
|
||||||
|
[(121, 4, 122, 5), (47, 4, 48, 14), (24, 11, 25, 14), (15, 16, 16, 14)],
|
||||||
|
[(117, 6, 118, 4), (45, 6, 46, 14), (24, 11, 25, 16), (16, 30, 17, 2)],
|
||||||
|
[(106, 8, 107, 4), (47, 8, 48, 13), (24, 7, 25, 22), (15, 22, 16, 13)],
|
||||||
|
[(114, 10, 115, 2), (46, 19, 47, 4), (22, 28, 23, 6), (16, 33, 17, 4)],
|
||||||
|
[(122, 8, 123, 4), (45, 22, 46, 3), (23, 8, 24, 27), (15, 12, 16, 28)],
|
||||||
|
[(117, 3, 118, 10), (45, 3, 46, 23), (24, 4, 25, 31), (15, 11, 16, 31)],
|
||||||
|
[(116, 7, 117, 7), (45, 21, 46, 7), (23, 1, 24, 37), (15, 19, 16, 26)],
|
||||||
|
[(115, 5, 116, 10), (47, 19, 48, 10), (24, 15, 25, 25), (15, 23, 16, 25)],
|
||||||
|
[(115, 13, 116, 3), (46, 2, 47, 29), (24, 42, 25, 1), (15, 23, 16, 28)],
|
||||||
|
[(115, 17, 0, 0), (46, 10, 47, 23), (24, 10, 25, 35), (15, 19, 16, 35)],
|
||||||
|
[(115, 17, 116, 1), (46, 14, 47, 21), (24, 29, 25, 19), (15, 11, 16, 46)],
|
||||||
|
[(115, 13, 116, 6), (46, 14, 47, 23), (24, 44, 25, 7), (16, 59, 17, 1)],
|
||||||
|
[(121, 12, 122, 7), (47, 12, 48, 26), (24, 39, 25, 14), (15, 22, 16, 41)],
|
||||||
|
[(121, 6, 122, 14), (47, 6, 48, 34), (24, 46, 25, 10), (15, 2, 16, 64)],
|
||||||
|
[(122, 17, 123, 4), (46, 29, 47, 14), (24, 49, 25, 10), (15, 24, 16, 46)],
|
||||||
|
[(122, 4, 123, 18), (46, 13, 47, 32), (24, 48, 25, 14), (15, 42, 16, 32)],
|
||||||
|
[(117, 20, 118, 4), (47, 40, 48, 7), (24, 43, 25, 22), (15, 10, 16, 67)],
|
||||||
|
[(118, 19, 119, 6), (47, 18, 48, 31), (24, 34, 25, 34), (15, 20, 16, 61)],
|
||||||
|
|
||||||
|
// Micro versions.
|
||||||
|
[(3, 1, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0)],
|
||||||
|
[(5, 1, 0, 0), (4, 1, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0)],
|
||||||
|
[(11, 1, 0, 0), (9, 1, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0)],
|
||||||
|
[(16, 1, 0, 0), (14, 1, 0, 0), (10, 1, 0, 0), (0, 0, 0, 0)],
|
||||||
|
];
|
||||||
|
|
||||||
|
//}}}
|
||||||
|
|
25
src/errors.rs
Normal file
25
src/errors.rs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
/// The error correction level. QR code contains redundant bits to allow correct
|
||||||
|
/// information be recovered even if some parts of code are damaged.
|
||||||
|
///
|
||||||
|
pub enum ErrorCorrectionLevel {
|
||||||
|
/// Low error correction. Allows up to 7% of wrong blocks.
|
||||||
|
L = 0,
|
||||||
|
|
||||||
|
/// Medium error correction (default). Allows up to 15% of wrong blocks.
|
||||||
|
M = 1,
|
||||||
|
|
||||||
|
/// "Quartile" error correction. Allows up to 25% of wrong blocks.
|
||||||
|
Q = 2,
|
||||||
|
|
||||||
|
/// High error correction. Allows up to 30% of wrong blocks.
|
||||||
|
H = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
190
src/lib.rs
190
src/lib.rs
|
@ -1,3 +1,189 @@
|
||||||
#[test]
|
//! QRCode encoder
|
||||||
fn it_works() {
|
//!
|
||||||
|
//! This crate provides a QR code and Micro QR code encoder for binary data.
|
||||||
|
//!
|
||||||
|
//! use qrcode::QrCode;
|
||||||
|
//!
|
||||||
|
//! let code = QrCode::new(b"Some content here.");
|
||||||
|
//! match code {
|
||||||
|
//! Err(err) => fail!("Failed to encode the QR code: {}", err),
|
||||||
|
//! Ok(code) => {
|
||||||
|
//! for y in range(0, code.width()) {
|
||||||
|
//! for x in range(0, code.width()) {
|
||||||
|
//! let color = if code[(x, y)] { "black" } else { "white" };
|
||||||
|
//! // render color at position (x, y)
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
|
||||||
|
#![unstable]
|
||||||
|
|
||||||
|
extern crate test;
|
||||||
|
|
||||||
|
use std::slice::CloneableVector;
|
||||||
|
pub use types::{QrResult, ErrorCorrectionLevel, L, M, Q, H, Version, MicroVersion};
|
||||||
|
|
||||||
|
pub mod types;
|
||||||
|
pub mod bits;
|
||||||
|
pub mod optimize;
|
||||||
|
pub mod ec;
|
||||||
|
pub mod canvas;
|
||||||
|
|
||||||
|
/// The encoded QR code symbol.
|
||||||
|
#[deriving(Clone)]
|
||||||
|
pub struct QrCode {
|
||||||
|
content: Vec<bool>,
|
||||||
|
version: Version,
|
||||||
|
ec_level: ErrorCorrectionLevel,
|
||||||
|
width: uint,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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. Please use `.with_error_correction()` and
|
||||||
|
/// `.with_version()` for finer adjustment.
|
||||||
|
pub fn new(data: &[u8]) -> QrResult<QrCode> {
|
||||||
|
QrCode::with_error_correction(data, 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.
|
||||||
|
pub fn with_error_correction(data: &[u8], ec_level: ErrorCorrectionLevel) -> QrResult<QrCode> {
|
||||||
|
let (encoded_data, version) = try!(bits::encode_auto(data, ec_level));
|
||||||
|
QrCode::with_encoded_data(encoded_data.as_slice(), version, ec_level)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructs a new QR code for the given version and error correction
|
||||||
|
/// level.
|
||||||
|
pub fn with_version(data: &[u8],
|
||||||
|
version: Version,
|
||||||
|
ec_level: ErrorCorrectionLevel) -> QrResult<QrCode> {
|
||||||
|
let encoded_data = try!(bits::encode(data, version, ec_level));
|
||||||
|
QrCode::with_encoded_data(encoded_data.as_slice(), version, ec_level)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_encoded_data(data: &[u8],
|
||||||
|
version: Version,
|
||||||
|
ec_level: ErrorCorrectionLevel) -> QrResult<QrCode> {
|
||||||
|
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.as_slice(), ec_data.as_slice());
|
||||||
|
let canvas = canvas.apply_best_mask();
|
||||||
|
Ok(QrCode {
|
||||||
|
content: canvas.to_bools(),
|
||||||
|
version: version,
|
||||||
|
ec_level: ec_level,
|
||||||
|
width: version.width() as uint,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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) -> ErrorCorrectionLevel {
|
||||||
|
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) -> uint {
|
||||||
|
self.width
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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));
|
||||||
|
for i in range(0, width) {
|
||||||
|
res.push_char('\n');
|
||||||
|
for _ in range(0, width) {
|
||||||
|
res.push_char(if self.content[k] { on_char } else { off_char });
|
||||||
|
k += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Index<(uint, uint), bool> for QrCode {
|
||||||
|
fn index<'a>(&'a self, &(x, y): &(uint, uint)) -> &'a bool {
|
||||||
|
let index = y * self.width + x;
|
||||||
|
self.content.index(&index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CloneableVector<bool> for QrCode {
|
||||||
|
fn to_vec(&self) -> Vec<bool> {
|
||||||
|
self.content.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_vec(self) -> Vec<bool> {
|
||||||
|
self.content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use {QrCode, Version, MicroVersion, L, M};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_annex_i_qr() {
|
||||||
|
// This uses the ISO Annex I as test vector.
|
||||||
|
let code = QrCode::with_version(b"01234567", Version(1), M).unwrap();
|
||||||
|
assert_eq!(code.to_debug_str('#', '.').as_slice(), "\n\
|
||||||
|
#######..#.##.#######\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", MicroVersion(2), L).unwrap();
|
||||||
|
assert_eq!(code.to_debug_str('#', '.').as_slice(), "\n\
|
||||||
|
#######.#.#.#\n\
|
||||||
|
#.....#.###.#\n\
|
||||||
|
#.###.#..##.#\n\
|
||||||
|
#.###.#..####\n\
|
||||||
|
#.###.#.###..\n\
|
||||||
|
#.....#.#...#\n\
|
||||||
|
#######..####\n\
|
||||||
|
.........##..\n\
|
||||||
|
##.#....#...#\n\
|
||||||
|
.##.#.#.#.#.#\n\
|
||||||
|
###..#######.\n\
|
||||||
|
...#.#....##.\n\
|
||||||
|
###.#..##.###");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
659
src/optimize.rs
Normal file
659
src/optimize.rs
Normal file
|
@ -0,0 +1,659 @@
|
||||||
|
#![experimental]
|
||||||
|
|
||||||
|
//! Find the optimal data mode sequence to encode a piece of data.
|
||||||
|
|
||||||
|
use std::slice::Items;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
use test::Bencher;
|
||||||
|
|
||||||
|
use types::{Mode, Version, Numeric, Alphanumeric, Byte, Kanji};
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
//{{{ Segment
|
||||||
|
|
||||||
|
/// A segment of data committed to an encoding mode.
|
||||||
|
#[deriving(PartialEq, Eq, Show)]
|
||||||
|
pub struct Segment {
|
||||||
|
/// The encoding mode of the segment of data.
|
||||||
|
pub mode: Mode,
|
||||||
|
|
||||||
|
/// The start index of the segment.
|
||||||
|
pub begin: uint,
|
||||||
|
|
||||||
|
/// The end index (exclusive) of the segment.
|
||||||
|
pub end: uint,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Segment {
|
||||||
|
/// Compute the number of bits (including the size of the mode indicator and
|
||||||
|
/// length bits) when this segment is encoded.
|
||||||
|
pub fn encoded_len(&self, version: Version) -> uint {
|
||||||
|
let byte_size = self.end - self.begin;
|
||||||
|
let chars_count = if self.mode == Kanji { byte_size / 2 } else { byte_size };
|
||||||
|
|
||||||
|
let mode_bits_count = version.mode_bits_count();
|
||||||
|
let length_bits_count = self.mode.length_bits_count(version);
|
||||||
|
let data_bits_count = self.mode.data_bits_count(chars_count);
|
||||||
|
|
||||||
|
mode_bits_count + length_bits_count + data_bits_count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//}}}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
//{{{ Parser
|
||||||
|
|
||||||
|
/// This iterator is basically equivalent to
|
||||||
|
///
|
||||||
|
/// ```ignore
|
||||||
|
/// data.map(|c| ExclusiveCharacterSet::from_u8(*c)).chain(Some(EEnd).move_iter()).enumerate()
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// But the type is too hard to write, thus the new type.
|
||||||
|
///
|
||||||
|
struct EcsIter<I> {
|
||||||
|
base: I,
|
||||||
|
index: uint,
|
||||||
|
ended: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, I: Iterator<&'a u8>> Iterator<(uint, ExclusiveCharacterSet)> for EcsIter<I> {
|
||||||
|
fn next(&mut self) -> Option<(uint, ExclusiveCharacterSet)> {
|
||||||
|
if self.ended {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.base.next() {
|
||||||
|
None => {
|
||||||
|
self.ended = true;
|
||||||
|
Some((self.index, EEnd))
|
||||||
|
}
|
||||||
|
Some(c) => {
|
||||||
|
let old_index = self.index;
|
||||||
|
self.index += 1;
|
||||||
|
Some((old_index, ExclusiveCharacterSet::from_u8(*c)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// QR code data parser to classify the input into distinct segments.
|
||||||
|
pub struct Parser<'a> {
|
||||||
|
ecs_iter: EcsIter<Items<'a, u8>>,
|
||||||
|
state: SegmentParseState,
|
||||||
|
begin: uint,
|
||||||
|
pending_single_byte: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Parser<'a> {
|
||||||
|
/// Creates a new iterator which parse the data into segments that only
|
||||||
|
/// contains their exclusive subsets. No optimization is done at this point.
|
||||||
|
///
|
||||||
|
/// use qrcode::optimize::{Parser, Segment};
|
||||||
|
/// use qrcode::types::{Numeric, Alphanumeric, Byte};
|
||||||
|
///
|
||||||
|
/// let parse_res = Parser::new(b"ABC123abcd").collect::<Vec<Segment>>();
|
||||||
|
/// assert_eq!(parse_res, vec![Segment { mode: Alphanumeric, begin: 0, end: 3 },
|
||||||
|
/// Segment { mode: Numeric, begin: 3, end: 6 },
|
||||||
|
/// Segment { mode: Byte, begin: 6, end: 10 }]);
|
||||||
|
///
|
||||||
|
pub fn new(data: &'a [u8]) -> Parser {
|
||||||
|
Parser {
|
||||||
|
ecs_iter: EcsIter { base: data.iter(), index: 0, ended: false },
|
||||||
|
state: SInit,
|
||||||
|
begin: 0,
|
||||||
|
pending_single_byte: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator<Segment> for Parser<'a> {
|
||||||
|
fn next(&mut self) -> Option<Segment> {
|
||||||
|
if self.pending_single_byte {
|
||||||
|
self.pending_single_byte = false;
|
||||||
|
self.begin += 1;
|
||||||
|
return Some(Segment { mode: Byte, begin: self.begin-1, end: self.begin });
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let (i, ecs) = match self.ecs_iter.next() {
|
||||||
|
None => { return None; },
|
||||||
|
Some(a) => a
|
||||||
|
};
|
||||||
|
let (next_state, action) = STATE_TRANSITION[self.state as uint + ecs as uint];
|
||||||
|
self.state = next_state;
|
||||||
|
|
||||||
|
let old_begin = self.begin;
|
||||||
|
let push_mode = match action {
|
||||||
|
AIdle => { continue; }
|
||||||
|
ANumeric => Numeric,
|
||||||
|
AAlpha => Alphanumeric,
|
||||||
|
AByte => Byte,
|
||||||
|
AKanji => Kanji,
|
||||||
|
AKanjiAndSingleByte => {
|
||||||
|
let next_begin = i - 1;
|
||||||
|
if self.begin == next_begin {
|
||||||
|
Byte
|
||||||
|
} else {
|
||||||
|
self.pending_single_byte = true;
|
||||||
|
self.begin = next_begin;
|
||||||
|
return Some(Segment { mode: Kanji, begin: old_begin, end: next_begin });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.begin = i;
|
||||||
|
return Some(Segment { mode: push_mode, begin: old_begin, end: i });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod parse_tests {
|
||||||
|
use optimize::{Parser, Segment};
|
||||||
|
use types::{Numeric, Alphanumeric, Byte, Kanji};
|
||||||
|
|
||||||
|
fn parse(data: &[u8]) -> Vec<Segment> {
|
||||||
|
Parser::new(data).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_1() {
|
||||||
|
let segs = parse(b"01049123451234591597033130128%10ABC123");
|
||||||
|
assert_eq!(segs, vec![Segment { mode: Numeric, begin: 0, end: 29 },
|
||||||
|
Segment { mode: Alphanumeric, begin: 29, end: 30 },
|
||||||
|
Segment { mode: Numeric, begin: 30, end: 32 },
|
||||||
|
Segment { mode: Alphanumeric, begin: 32, end: 35 },
|
||||||
|
Segment { mode: Numeric, begin: 35, end: 38 }]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_shift_jis_example_1() {
|
||||||
|
let segs = parse(b"\x82\xa0\x81\x41\x41\xb1\x81\xf0"); // "あ、AアÅ"
|
||||||
|
assert_eq!(segs, vec![Segment { mode: Kanji, begin: 0, end: 4 },
|
||||||
|
Segment { mode: Alphanumeric, begin: 4, end: 5 },
|
||||||
|
Segment { mode: Byte, begin: 5, end: 6 },
|
||||||
|
Segment { mode: Kanji, begin: 6, end: 8 }]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_utf_8() {
|
||||||
|
// Mojibake?
|
||||||
|
let segs = parse(b"\xe3\x81\x82\xe3\x80\x81A\xef\xbd\xb1\xe2\x84\xab");
|
||||||
|
assert_eq!(segs, vec![Segment { mode: Kanji, begin: 0, end: 4 },
|
||||||
|
Segment { mode: Byte, begin: 4, end: 5 },
|
||||||
|
Segment { mode: Kanji, begin: 5, end: 7 },
|
||||||
|
Segment { mode: Byte, begin: 7, end: 10 },
|
||||||
|
Segment { mode: Kanji, begin: 10, end: 12 },
|
||||||
|
Segment { mode: Byte, begin: 12, end: 13 }]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_not_kanji_1() {
|
||||||
|
let segs = parse(b"\x81\x30");
|
||||||
|
assert_eq!(segs, vec![Segment { mode: Byte, begin: 0, end: 1 },
|
||||||
|
Segment { mode: Numeric, begin: 1, end: 2 }]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_not_kanji_2() {
|
||||||
|
// Note that it's implementation detail that the byte seq is split into
|
||||||
|
// two. Perhaps adjust the test to check for this.
|
||||||
|
let segs = parse(b"\xeb\xc0");
|
||||||
|
assert_eq!(segs, vec![Segment { mode: Byte, begin: 0, end: 1 },
|
||||||
|
Segment { mode: Byte, begin: 1, end: 2 }]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_not_kanji_3() {
|
||||||
|
let segs = parse(b"\x81\x7f");
|
||||||
|
assert_eq!(segs, vec![Segment { mode: Byte, begin: 0, end: 1 },
|
||||||
|
Segment { mode: Byte, begin: 1, end: 2 }]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_not_kanji_4() {
|
||||||
|
let segs = parse(b"\x81\x40\x81");
|
||||||
|
assert_eq!(segs, vec![Segment { mode: Kanji, begin: 0, end: 2 },
|
||||||
|
Segment { mode: Byte, begin: 2, end: 3 }]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//}}}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
//{{{ Optimizer
|
||||||
|
|
||||||
|
pub struct Optimizer<I> {
|
||||||
|
parser: I,
|
||||||
|
last_segment: Segment,
|
||||||
|
last_segment_size: uint,
|
||||||
|
version: Version,
|
||||||
|
ended: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: Iterator<Segment>> Optimizer<I> {
|
||||||
|
pub fn new(mut parser: I, version: Version) -> Optimizer<I> {
|
||||||
|
match parser.next() {
|
||||||
|
None => Optimizer {
|
||||||
|
parser: parser,
|
||||||
|
last_segment: Segment { mode: Numeric, begin: 0, end: 0 },
|
||||||
|
last_segment_size: 0,
|
||||||
|
version: version,
|
||||||
|
ended: true,
|
||||||
|
},
|
||||||
|
Some(segment) => Optimizer {
|
||||||
|
parser: parser,
|
||||||
|
last_segment: segment,
|
||||||
|
last_segment_size: segment.encoded_len(version),
|
||||||
|
version: version,
|
||||||
|
ended: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Parser<'a> {
|
||||||
|
/// Optimize the segments by combining adjacent segments when possible.
|
||||||
|
///
|
||||||
|
/// Currently this method uses a greedy algorithm by combining segments from
|
||||||
|
/// left to right until the new segment is longer than before. This method
|
||||||
|
/// does *not* use Annex J from the ISO standard.
|
||||||
|
///
|
||||||
|
pub fn optimize(self, version: Version) -> Optimizer<Parser<'a>> {
|
||||||
|
Optimizer::new(self, version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: Iterator<Segment>> Iterator<Segment> for Optimizer<I> {
|
||||||
|
fn next(&mut self) -> Option<Segment> {
|
||||||
|
if self.ended {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match self.parser.next() {
|
||||||
|
None => {
|
||||||
|
self.ended = true;
|
||||||
|
return Some(self.last_segment);
|
||||||
|
}
|
||||||
|
Some(segment) => {
|
||||||
|
let seg_size = segment.encoded_len(self.version);
|
||||||
|
|
||||||
|
let new_segment = Segment {
|
||||||
|
mode: self.last_segment.mode.max(segment.mode),
|
||||||
|
begin: self.last_segment.begin,
|
||||||
|
end: segment.end,
|
||||||
|
};
|
||||||
|
let new_size = new_segment.encoded_len(self.version);
|
||||||
|
|
||||||
|
if self.last_segment_size + seg_size >= new_size {
|
||||||
|
self.last_segment = new_segment;
|
||||||
|
self.last_segment_size = new_size;
|
||||||
|
} else {
|
||||||
|
let old_segment = self.last_segment;
|
||||||
|
self.last_segment = segment;
|
||||||
|
self.last_segment_size = seg_size;
|
||||||
|
return Some(old_segment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Computes the total encoded length of all segments.
|
||||||
|
pub fn total_encoded_len(segments: &[Segment], version: Version) -> uint {
|
||||||
|
use std::iter::AdditiveIterator;
|
||||||
|
segments.iter().map(|seg| seg.encoded_len(version)).sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod optimize_tests {
|
||||||
|
use optimize::{Optimizer, total_encoded_len, Segment};
|
||||||
|
use types::{Numeric, Alphanumeric, Byte, Kanji, Version, MicroVersion};
|
||||||
|
|
||||||
|
fn test_optimization_result(given: Vec<Segment>, expected: Vec<Segment>, version: Version) {
|
||||||
|
let prev_len = total_encoded_len(given.as_slice(), version);
|
||||||
|
let opt_segs = Optimizer::new(given.iter().map(|seg| *seg), version).collect::<Vec<Segment>>();
|
||||||
|
let new_len = total_encoded_len(opt_segs.as_slice(), version);
|
||||||
|
if given != opt_segs {
|
||||||
|
assert!(prev_len > new_len, "{} > {}", prev_len, new_len);
|
||||||
|
}
|
||||||
|
assert!(opt_segs == expected,
|
||||||
|
"Optimization gave something better: {} < {} ({})",
|
||||||
|
new_len, total_encoded_len(expected.as_slice(), version), opt_segs);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_example_1() {
|
||||||
|
test_optimization_result(
|
||||||
|
|
||||||
|
vec![Segment { mode: Alphanumeric, begin: 0, end: 3 },
|
||||||
|
Segment { mode: Numeric, begin: 3, end: 6 },
|
||||||
|
Segment { mode: Byte, begin: 6, end: 10 }],
|
||||||
|
|
||||||
|
vec![Segment { mode: Alphanumeric, begin: 0, end: 6 },
|
||||||
|
Segment { mode: Byte, begin: 6, end: 10 }],
|
||||||
|
|
||||||
|
Version(1)
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_example_2() {
|
||||||
|
test_optimization_result(
|
||||||
|
|
||||||
|
vec![Segment { mode: Numeric, begin: 0, end: 29 },
|
||||||
|
Segment { mode: Alphanumeric, begin: 29, end: 30 },
|
||||||
|
Segment { mode: Numeric, begin: 30, end: 32 },
|
||||||
|
Segment { mode: Alphanumeric, begin: 32, end: 35 },
|
||||||
|
Segment { mode: Numeric, begin: 35, end: 38 }],
|
||||||
|
|
||||||
|
vec![Segment { mode: Numeric, begin: 0, end: 29 },
|
||||||
|
Segment { mode: Alphanumeric, begin: 29, end: 38 }],
|
||||||
|
|
||||||
|
Version(9)
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_example_3() {
|
||||||
|
test_optimization_result(
|
||||||
|
|
||||||
|
vec![Segment { mode: Kanji, begin: 0, end: 4 },
|
||||||
|
Segment { mode: Alphanumeric, begin: 4, end: 5 },
|
||||||
|
Segment { mode: Byte, begin: 5, end: 6 },
|
||||||
|
Segment { mode: Kanji, begin: 6, end: 8 }],
|
||||||
|
|
||||||
|
vec![Segment { mode: Byte, begin: 0, end: 8 }],
|
||||||
|
|
||||||
|
Version(1)
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_example_4() {
|
||||||
|
test_optimization_result(
|
||||||
|
|
||||||
|
vec![Segment { mode: Kanji, begin: 0, end: 10 },
|
||||||
|
Segment { mode: Byte, begin: 10, end: 11 }],
|
||||||
|
|
||||||
|
vec![Segment { mode: Kanji, begin: 0, end: 10 },
|
||||||
|
Segment { mode: Byte, begin: 10, end: 11 }],
|
||||||
|
|
||||||
|
Version(1)
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_annex_j_guideline_1a() {
|
||||||
|
test_optimization_result(
|
||||||
|
|
||||||
|
vec![Segment { mode: Numeric, begin: 0, end: 3 },
|
||||||
|
Segment { mode: Alphanumeric, begin: 3, end: 4 }],
|
||||||
|
|
||||||
|
vec![Segment { mode: Numeric, begin: 0, end: 3 },
|
||||||
|
Segment { mode: Alphanumeric, begin: 3, end: 4 }],
|
||||||
|
|
||||||
|
MicroVersion(2)
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_annex_j_guideline_1b() {
|
||||||
|
test_optimization_result(
|
||||||
|
|
||||||
|
vec![Segment { mode: Numeric, begin: 0, end: 2 },
|
||||||
|
Segment { mode: Alphanumeric, begin: 2, end: 4 }],
|
||||||
|
|
||||||
|
vec![Segment { mode: Alphanumeric, begin: 0, end: 4 }],
|
||||||
|
|
||||||
|
MicroVersion(2)
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_annex_j_guideline_1c() {
|
||||||
|
test_optimization_result(
|
||||||
|
|
||||||
|
vec![Segment { mode: Numeric, begin: 0, end: 3 },
|
||||||
|
Segment { mode: Alphanumeric, begin: 3, end: 4 }],
|
||||||
|
|
||||||
|
vec![Segment { mode: Alphanumeric, begin: 0, end: 4 }],
|
||||||
|
|
||||||
|
MicroVersion(3)
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn bench_optimize(bencher: &mut Bencher) {
|
||||||
|
let data = b"QR\x83R\x81[\x83h\x81i\x83L\x83\x85\x81[\x83A\x81[\x83\x8b\x83R\x81[\x83h\x81j\x82\xc6\x82\xcd\x81A1994\x94N\x82\xc9\x83f\x83\x93\x83\\\x81[\x82\xcc\x8aJ\x94\xad\x95\x94\x96\xe5\x81i\x8c\xbb\x8d\xdd\x82\xcd\x95\xaa\x97\xa3\x82\xb5\x83f\x83\x93\x83\\\x81[\x83E\x83F\x81[\x83u\x81j\x82\xaa\x8aJ\x94\xad\x82\xb5\x82\xbd\x83}\x83g\x83\x8a\x83b\x83N\x83X\x8c^\x93\xf1\x8e\x9f\x8c\xb3\x83R\x81[\x83h\x82\xc5\x82\xa0\x82\xe9\x81B\x82\xc8\x82\xa8\x81AQR\x83R\x81[\x83h\x82\xc6\x82\xa2\x82\xa4\x96\xbc\x8f\xcc\x81i\x82\xa8\x82\xe6\x82\xd1\x92P\x8c\xea\x81j\x82\xcd\x83f\x83\x93\x83\\\x81[\x83E\x83F\x81[\x83u\x82\xcc\x93o\x98^\x8f\xa4\x95W\x81i\x91\xe64075066\x8d\x86\x81j\x82\xc5\x82\xa0\x82\xe9\x81BQR\x82\xcdQuick Response\x82\xc9\x97R\x97\x88\x82\xb5\x81A\x8d\x82\x91\xac\x93\xc7\x82\xdd\x8e\xe6\x82\xe8\x82\xaa\x82\xc5\x82\xab\x82\xe9\x82\xe6\x82\xa4\x82\xc9\x8aJ\x94\xad\x82\xb3\x82\xea\x82\xbd\x81B\x93\x96\x8f\x89\x82\xcd\x8e\xa9\x93\xae\x8e\xd4\x95\x94\x95i\x8dH\x8f\xea\x82\xe2\x94z\x91\x97\x83Z\x83\x93\x83^\x81[\x82\xc8\x82\xc7\x82\xc5\x82\xcc\x8eg\x97p\x82\xf0\x94O\x93\xaa\x82\xc9\x8aJ\x94\xad\x82\xb3\x82\xea\x82\xbd\x82\xaa\x81A\x8c\xbb\x8d\xdd\x82\xc5\x82\xcd\x83X\x83}\x81[\x83g\x83t\x83H\x83\x93\x82\xcc\x95\x81\x8by\x82\xc8\x82\xc7\x82\xc9\x82\xe6\x82\xe8\x93\xfa\x96{\x82\xc9\x8c\xc0\x82\xe7\x82\xb8\x90\xa2\x8aE\x93I\x82\xc9\x95\x81\x8by\x82\xb5\x82\xc4\x82\xa2\x82\xe9\x81B";
|
||||||
|
bencher.iter(|| {
|
||||||
|
Parser::new(data).optimize(Version(15))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//}}}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
//{{{ Internal types and data for parsing
|
||||||
|
|
||||||
|
// All the silly prefixes are to avoid name collision with each other.
|
||||||
|
|
||||||
|
/// All values of `u8` can be split into 9 different character sets when
|
||||||
|
/// determining which encoding to use. This enum represents these groupings for
|
||||||
|
/// parsing purpose.
|
||||||
|
enum ExclusiveCharacterSet {
|
||||||
|
/// The end of string.
|
||||||
|
EEnd = 0,
|
||||||
|
|
||||||
|
/// All symbols supported by the Alphanumeric encoding, i.e. space, `$`, `%`,
|
||||||
|
/// `*`, `+`, `-`, `.`, `/` and `:`.
|
||||||
|
ESymbol = 1,
|
||||||
|
|
||||||
|
/// All numbers (0–9).
|
||||||
|
ENumeric = 2,
|
||||||
|
|
||||||
|
/// All uppercase letters (A–Z). These characters may also appear in the
|
||||||
|
/// second byte of a Shift JIS 2-byte encoding.
|
||||||
|
EAlpha = 3,
|
||||||
|
|
||||||
|
/// The first byte of a Shift JIS 2-byte encoding, in the range 0x81–0x9f.
|
||||||
|
EKanjiHi1 = 4,
|
||||||
|
|
||||||
|
/// The first byte of a Shift JIS 2-byte encoding, in the range 0xe0–0xea.
|
||||||
|
EKanjiHi2 = 5,
|
||||||
|
|
||||||
|
/// The first byte of a Shift JIS 2-byte encoding, of value 0xeb. This is
|
||||||
|
/// different from the other two range that the second byte has a smaller
|
||||||
|
/// range.
|
||||||
|
EKanjiHi3 = 6,
|
||||||
|
|
||||||
|
/// The second byte of a Shift JIS 2-byte encoding, in the range 0x40–0xbf,
|
||||||
|
/// excluding letters (covered by `Alpha`), 0x81–0x9f (covered by `KanjiHi1`),
|
||||||
|
/// and the invalid byte 0x7f.
|
||||||
|
EKanjiLo1 = 7,
|
||||||
|
|
||||||
|
/// The second byte of a Shift JIS 2-byte encoding, in the range 0xc0–0xfc,
|
||||||
|
/// excluding the range 0xe0–0xeb (covered by `KanjiHi2` and `KanjiHi3`).
|
||||||
|
/// This half of byte-pair cannot appear as the second byte leaded by
|
||||||
|
/// `KanjiHi3`.
|
||||||
|
EKanjiLo2 = 8,
|
||||||
|
|
||||||
|
/// Any other values not covered by the above character sets.
|
||||||
|
EByte = 9,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExclusiveCharacterSet {
|
||||||
|
/// Determines which character set a byte is in.
|
||||||
|
fn from_u8(c: u8) -> ExclusiveCharacterSet {
|
||||||
|
match c {
|
||||||
|
0x20 | 0x24 | 0x25 | 0x2a | 0x2b | 0x2d .. 0x2f | 0x3a => ESymbol,
|
||||||
|
0x30 .. 0x39 => ENumeric,
|
||||||
|
0x41 .. 0x5a => EAlpha,
|
||||||
|
0x81 .. 0x9f => EKanjiHi1,
|
||||||
|
0xe0 .. 0xea => EKanjiHi2,
|
||||||
|
0xeb => EKanjiHi3,
|
||||||
|
0x40 | 0x5b .. 0x7e | 0x80 | 0xa0 .. 0xbf => EKanjiLo1,
|
||||||
|
0xc0 .. 0xdf | 0xec .. 0xfc => EKanjiLo2,
|
||||||
|
_ => EByte
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The current parsing state.
|
||||||
|
enum SegmentParseState {
|
||||||
|
/// Just initialized.
|
||||||
|
SInit = 0,
|
||||||
|
|
||||||
|
/// Inside a string that can be exclusively encoded as Numeric.
|
||||||
|
SNumeric = 10,
|
||||||
|
|
||||||
|
/// Inside a string that can be exclusively encoded as Alphanumeric.
|
||||||
|
SAlpha = 20,
|
||||||
|
|
||||||
|
/// Inside a string that can be exclusively encoded as 8-Bit Byte.
|
||||||
|
SByte = 30,
|
||||||
|
|
||||||
|
/// Just encountered the first byte of a Shift JIS 2-byte sequence of the
|
||||||
|
/// set `KanjiHi1` or `KanjiHi2`.
|
||||||
|
SKanjiHi12 = 40,
|
||||||
|
|
||||||
|
/// Just encountered the first byte of a Shift JIS 2-byte sequence of the
|
||||||
|
/// set `KanjiHi3`.
|
||||||
|
SKanjiHi3 = 50,
|
||||||
|
|
||||||
|
/// Inside a string that can be exclusively encoded as Kanji.
|
||||||
|
SKanji = 60,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// What should the parser do after a state transition.
|
||||||
|
enum ParserAction {
|
||||||
|
/// The parser should do nothing.
|
||||||
|
AIdle,
|
||||||
|
|
||||||
|
/// Push the current segment as a Numeric string, and reset the marks.
|
||||||
|
ANumeric,
|
||||||
|
|
||||||
|
/// Push the current segment as an Alphanumeric string, and reset the marks.
|
||||||
|
AAlpha,
|
||||||
|
|
||||||
|
/// Push the current segment as a 8-Bit Byte string, and reset the marks.
|
||||||
|
AByte,
|
||||||
|
|
||||||
|
/// Push the current segment as a Kanji string, and reset the marks.
|
||||||
|
AKanji,
|
||||||
|
|
||||||
|
/// Push the current segment excluding the last byte as a Kanji string, then
|
||||||
|
/// push the remaining single byte as a Byte string, and reset the marks.
|
||||||
|
AKanjiAndSingleByte,
|
||||||
|
}
|
||||||
|
|
||||||
|
static STATE_TRANSITION: [(SegmentParseState, ParserAction), ..70] = [
|
||||||
|
// STATE_TRANSITION[current_state + next_character] == (next_state, what_to_do)
|
||||||
|
|
||||||
|
// Init state:
|
||||||
|
|
||||||
|
(SInit, AIdle), // End
|
||||||
|
(SAlpha, AIdle), // Symbol
|
||||||
|
(SNumeric, AIdle), // Numeric
|
||||||
|
(SAlpha, AIdle), // Alpha
|
||||||
|
(SKanjiHi12, AIdle), // KanjiHi1
|
||||||
|
(SKanjiHi12, AIdle), // KanjiHi2
|
||||||
|
(SKanjiHi3, AIdle), // KanjiHi3
|
||||||
|
(SByte, AIdle), // KanjiLo1
|
||||||
|
(SByte, AIdle), // KanjiLo2
|
||||||
|
(SByte, AIdle), // Byte
|
||||||
|
|
||||||
|
// Numeric state:
|
||||||
|
|
||||||
|
(SInit, ANumeric), // End
|
||||||
|
(SAlpha, ANumeric), // Symbol
|
||||||
|
(SNumeric, AIdle), // Numeric
|
||||||
|
(SAlpha, ANumeric), // Alpha
|
||||||
|
(SKanjiHi12, ANumeric), // KanjiHi1
|
||||||
|
(SKanjiHi12, ANumeric), // KanjiHi2
|
||||||
|
(SKanjiHi3, ANumeric), // KanjiHi3
|
||||||
|
(SByte, ANumeric), // KanjiLo1
|
||||||
|
(SByte, ANumeric), // KanjiLo2
|
||||||
|
(SByte, ANumeric), // Byte
|
||||||
|
|
||||||
|
// Alpha state:
|
||||||
|
|
||||||
|
(SInit, AAlpha), // End
|
||||||
|
(SAlpha, AIdle), // Symbol
|
||||||
|
(SNumeric, AAlpha), // Numeric
|
||||||
|
(SAlpha, AIdle), // Alpha
|
||||||
|
(SKanjiHi12, AAlpha), // KanjiHi1
|
||||||
|
(SKanjiHi12, AAlpha), // KanjiHi2
|
||||||
|
(SKanjiHi3, AAlpha), // KanjiHi3
|
||||||
|
(SByte, AAlpha), // KanjiLo1
|
||||||
|
(SByte, AAlpha), // KanjiLo2
|
||||||
|
(SByte, AAlpha), // Byte
|
||||||
|
|
||||||
|
// Byte state:
|
||||||
|
|
||||||
|
(SInit, AByte), // End
|
||||||
|
(SAlpha, AByte), // Symbol
|
||||||
|
(SNumeric, AByte), // Numeric
|
||||||
|
(SAlpha, AByte), // Alpha
|
||||||
|
(SKanjiHi12, AByte), // KanjiHi1
|
||||||
|
(SKanjiHi12, AByte), // KanjiHi2
|
||||||
|
(SKanjiHi3, AByte), // KanjiHi3
|
||||||
|
(SByte, AIdle), // KanjiLo1
|
||||||
|
(SByte, AIdle), // KanjiLo2
|
||||||
|
(SByte, AIdle), // Byte
|
||||||
|
|
||||||
|
// KanjiHi12 state:
|
||||||
|
|
||||||
|
(SInit, AKanjiAndSingleByte), // End
|
||||||
|
(SAlpha, AKanjiAndSingleByte), // Symbol
|
||||||
|
(SNumeric, AKanjiAndSingleByte), // Numeric
|
||||||
|
(SKanji, AIdle), // Alpha
|
||||||
|
(SKanji, AIdle), // KanjiHi1
|
||||||
|
(SKanji, AIdle), // KanjiHi2
|
||||||
|
(SKanji, AIdle), // KanjiHi3
|
||||||
|
(SKanji, AIdle), // KanjiLo1
|
||||||
|
(SKanji, AIdle), // KanjiLo2
|
||||||
|
(SByte, AKanjiAndSingleByte), // Byte
|
||||||
|
|
||||||
|
// KanjiHi3 state:
|
||||||
|
|
||||||
|
(SInit, AKanjiAndSingleByte), // End
|
||||||
|
(SAlpha, AKanjiAndSingleByte), // Symbol
|
||||||
|
(SNumeric, AKanjiAndSingleByte), // Numeric
|
||||||
|
(SKanji, AIdle), // Alpha
|
||||||
|
(SKanji, AIdle), // KanjiHi1
|
||||||
|
(SKanjiHi12, AKanjiAndSingleByte), // KanjiHi2
|
||||||
|
(SKanjiHi3, AKanjiAndSingleByte), // KanjiHi3
|
||||||
|
(SKanji, AIdle), // KanjiLo1
|
||||||
|
(SByte, AKanjiAndSingleByte), // KanjiLo2
|
||||||
|
(SByte, AKanjiAndSingleByte), // Byte
|
||||||
|
|
||||||
|
// Kanji state:
|
||||||
|
|
||||||
|
(SInit, AKanji), // End
|
||||||
|
(SAlpha, AKanji), // Symbol
|
||||||
|
(SNumeric, AKanji), // Numeric
|
||||||
|
(SAlpha, AKanji), // Alpha
|
||||||
|
(SKanjiHi12, AIdle), // KanjiHi1
|
||||||
|
(SKanjiHi12, AIdle), // KanjiHi2
|
||||||
|
(SKanjiHi3, AIdle), // KanjiHi3
|
||||||
|
(SByte, AKanji), // KanjiLo1
|
||||||
|
(SByte, AKanji), // KanjiLo2
|
||||||
|
(SByte, AKanji), // Byte
|
||||||
|
];
|
||||||
|
|
||||||
|
//}}}
|
||||||
|
|
||||||
|
|
283
src/types.rs
Normal file
283
src/types.rs
Normal file
|
@ -0,0 +1,283 @@
|
||||||
|
#![unstable]
|
||||||
|
|
||||||
|
use std::default::Default;
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
//{{{ QrResult
|
||||||
|
|
||||||
|
/// `QrError` encodes the error encountered when generating a QR code.
|
||||||
|
#[unstable]
|
||||||
|
#[deriving(Show, PartialEq, Eq)]
|
||||||
|
pub enum QrError {
|
||||||
|
/// The data is too long to encode into a QR code for the given version.
|
||||||
|
DataTooLong,
|
||||||
|
|
||||||
|
/// The provided version / error correction level combination is invalid.
|
||||||
|
InvalidVersion,
|
||||||
|
|
||||||
|
/// Some characters in the data cannot be supported by the provided QR code
|
||||||
|
/// version.
|
||||||
|
UnsupportedCharacterSet,
|
||||||
|
|
||||||
|
/// The provided ECI designator is invalid. A valid designator should be
|
||||||
|
/// between 0 and 999999.
|
||||||
|
#[experimental]
|
||||||
|
InvalidEciDesignator,
|
||||||
|
|
||||||
|
/// A character not belonging to the character set is found.
|
||||||
|
InvalidCharacter,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `QrResult` is a convenient alias for a QR code generation result.
|
||||||
|
#[stable]
|
||||||
|
pub type QrResult<T> = Result<T, QrError>;
|
||||||
|
|
||||||
|
//}}}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
//{{{ Error correction level
|
||||||
|
|
||||||
|
/// The error correction level. It allows the original information be recovered
|
||||||
|
/// even if parts of the code is damaged.
|
||||||
|
#[deriving(Show, PartialEq, Eq, Copy, Clone, PartialOrd, Ord)]
|
||||||
|
#[unstable]
|
||||||
|
pub enum ErrorCorrectionLevel {
|
||||||
|
/// Low error correction. Allows up to 7% of wrong blocks.
|
||||||
|
L = 0,
|
||||||
|
|
||||||
|
/// Medium error correction (default). Allows up to 15% of wrong blocks.
|
||||||
|
M = 1,
|
||||||
|
|
||||||
|
/// "Quartile" error correction. Allows up to 25% of wrong blocks.
|
||||||
|
Q = 2,
|
||||||
|
|
||||||
|
/// High error correction. Allows up to 30% of wrong blocks.
|
||||||
|
H = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
//}}}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
//{{{ Version
|
||||||
|
|
||||||
|
/// In QR code terminology, `Version` means the size of the generated image.
|
||||||
|
/// Larger version means the size of code is larger, and therefore can carry
|
||||||
|
/// more information.
|
||||||
|
///
|
||||||
|
/// The smallest version is `Version(1)` of size 21×21, and the largest is
|
||||||
|
/// `Version(40)` of size 177×177.
|
||||||
|
#[unstable]
|
||||||
|
#[deriving(Show, PartialEq, Eq, Copy, Clone)]
|
||||||
|
pub enum Version {
|
||||||
|
/// A normal QR code version. The parameter should be between 1 and 40.
|
||||||
|
#[unstable]
|
||||||
|
Version(i16),
|
||||||
|
|
||||||
|
/// A Micro QR code version. The parameter should be between 1 and 4.
|
||||||
|
///
|
||||||
|
/// This crate currently does not fully support Micro QR code yet, and using
|
||||||
|
/// these versions will likely result in `Err(InvalidVersion)`.
|
||||||
|
#[experimental]
|
||||||
|
MicroVersion(i16),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Version {
|
||||||
|
/// Get the number of "modules" on each size of the QR code, i.e. the width
|
||||||
|
/// and height of the code.
|
||||||
|
#[unstable]
|
||||||
|
pub fn width(&self) -> i16 {
|
||||||
|
match *self {
|
||||||
|
Version(v) => v * 4 + 17,
|
||||||
|
MicroVersion(v) => v * 2 + 9,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Obtains an object from a hard-coded table.
|
||||||
|
///
|
||||||
|
/// The table must be a 44×4 array. The outer array represents the content
|
||||||
|
/// for each version. The first 40 entry corresponds to QR code versions 1
|
||||||
|
/// to 40, and the last 4 corresponds to Micro QR code version 1 to 4. The
|
||||||
|
/// inner array represents the content in each error correction level, in
|
||||||
|
/// the order [L, M, Q, H].
|
||||||
|
///
|
||||||
|
/// If the entry compares equal to the default value of T, this method
|
||||||
|
/// returns `Err(InvalidVersion)`.
|
||||||
|
pub fn fetch<T: PartialEq + Default + Copy>(&self,
|
||||||
|
ec_level: ErrorCorrectionLevel,
|
||||||
|
table: &[[T, ..4]]) -> QrResult<T> {
|
||||||
|
match *self {
|
||||||
|
Version(v @ 1..40) => Ok(table[v as uint - 1][ec_level as uint]),
|
||||||
|
MicroVersion(v @ 1..4) => {
|
||||||
|
let obj = table[v as uint + 39][ec_level as uint];
|
||||||
|
if obj != Default::default() {
|
||||||
|
Ok(obj)
|
||||||
|
} else {
|
||||||
|
Err(InvalidVersion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => Err(InvalidVersion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The number of bits needed to encode the mode indicator.
|
||||||
|
#[unstable]
|
||||||
|
pub fn mode_bits_count(&self) -> uint {
|
||||||
|
match *self {
|
||||||
|
MicroVersion(a) => (a - 1) as uint,
|
||||||
|
_ => 4,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check whether is version refers to a Micro QR code.
|
||||||
|
#[unstable]
|
||||||
|
pub fn is_micro(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
Version(_) => false,
|
||||||
|
MicroVersion(_) => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//}}}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
//{{{ Mode indicator
|
||||||
|
|
||||||
|
/// The mode indicator, which specifies the character set of the encoded data.
|
||||||
|
#[unstable]
|
||||||
|
#[deriving(Show, PartialEq, Eq)]
|
||||||
|
pub enum Mode {
|
||||||
|
/// The data contains only characters 0 to 9.
|
||||||
|
Numeric,
|
||||||
|
|
||||||
|
/// The data contains only uppercase letters (A–Z), numbers (0–9) and a few
|
||||||
|
/// punctuations marks (space, `$`, `%`, `*`, `+`, `-`, `.`, `/`, `:`).
|
||||||
|
Alphanumeric,
|
||||||
|
|
||||||
|
/// The data contains arbitrary binary data.
|
||||||
|
Byte,
|
||||||
|
|
||||||
|
/// The data contains Shift-JIS-encoded double-byte text.
|
||||||
|
Kanji,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mode {
|
||||||
|
/// Computes the number of bits needed to encode the data length.
|
||||||
|
///
|
||||||
|
/// use qrcode::types::{Version, Numeric};
|
||||||
|
///
|
||||||
|
/// assert_eq!(Numeric.length_bits_count(Version(1)), 10);
|
||||||
|
///
|
||||||
|
/// This method will return `Err(UnsupportedCharacterSet)` if the is not
|
||||||
|
/// supported in the given version.
|
||||||
|
#[unstable]
|
||||||
|
pub fn length_bits_count(&self, version: Version) -> uint {
|
||||||
|
match version {
|
||||||
|
MicroVersion(a) => {
|
||||||
|
let a = a as uint;
|
||||||
|
match *self {
|
||||||
|
Numeric => 2 + a,
|
||||||
|
Alphanumeric | Byte => 1 + a,
|
||||||
|
Kanji => a,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Version(1..9) => match *self {
|
||||||
|
Numeric => 10,
|
||||||
|
Alphanumeric => 9,
|
||||||
|
Byte => 8,
|
||||||
|
Kanji => 8,
|
||||||
|
},
|
||||||
|
Version(10..26) => match *self {
|
||||||
|
Numeric => 12,
|
||||||
|
Alphanumeric => 11,
|
||||||
|
Byte => 16,
|
||||||
|
Kanji => 10,
|
||||||
|
},
|
||||||
|
Version(_) => match *self {
|
||||||
|
Numeric => 14,
|
||||||
|
Alphanumeric => 13,
|
||||||
|
Byte => 16,
|
||||||
|
Kanji => 12,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Computes the number of bits needed to some data of a given raw length.
|
||||||
|
///
|
||||||
|
/// use qrcode::types::Numeric;
|
||||||
|
///
|
||||||
|
/// assert_eq!(Numeric.data_bits_count(7), 24);
|
||||||
|
///
|
||||||
|
/// Note that in Kanji mode, the `raw_data_len` is the number of Kanjis,
|
||||||
|
/// i.e. half the total size of bytes.
|
||||||
|
#[unstable]
|
||||||
|
pub fn data_bits_count(&self, raw_data_len: uint) -> uint {
|
||||||
|
match *self {
|
||||||
|
Numeric => (raw_data_len * 10 + 2) / 3,
|
||||||
|
Alphanumeric => (raw_data_len * 11 + 1) / 2,
|
||||||
|
Byte => raw_data_len * 8,
|
||||||
|
Kanji => raw_data_len * 13,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find the lowest common mode which both modes are compatible with.
|
||||||
|
///
|
||||||
|
/// use qrcode::types::{Numeric, Kanji};
|
||||||
|
///
|
||||||
|
/// let a = Numeric;
|
||||||
|
/// let b = Kanji;
|
||||||
|
/// let c = a.max(b);
|
||||||
|
/// assert!(a <= c);
|
||||||
|
/// assert!(b <= c);
|
||||||
|
///
|
||||||
|
pub fn max(&self, other: Mode) -> Mode {
|
||||||
|
match self.partial_cmp(&other) {
|
||||||
|
Some(Less) | Some(Equal) => other,
|
||||||
|
Some(Greater) => *self,
|
||||||
|
None => Byte,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for Mode {
|
||||||
|
/// Defines a partial ordering between modes. If `a <= b`, then `b` contains
|
||||||
|
/// a superset of all characters supported by `a`.
|
||||||
|
fn partial_cmp(&self, other: &Mode) -> Option<Ordering> {
|
||||||
|
match (*self, *other) {
|
||||||
|
(Numeric, Alphanumeric) => Some(Less),
|
||||||
|
(Alphanumeric, Numeric) => Some(Greater),
|
||||||
|
(Numeric, Byte) => Some(Less),
|
||||||
|
(Byte, Numeric) => Some(Greater),
|
||||||
|
(Alphanumeric, Byte) => Some(Less),
|
||||||
|
(Byte, Alphanumeric) => Some(Greater),
|
||||||
|
(Kanji, Byte) => Some(Less),
|
||||||
|
(Byte, Kanji) => Some(Greater),
|
||||||
|
(a, b) if a == b => Some(Equal),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod mode_tests {
|
||||||
|
use types::{Numeric, Alphanumeric, Byte, Kanji};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_mode_order() {
|
||||||
|
assert!(Numeric < Alphanumeric);
|
||||||
|
assert!(Byte > Kanji);
|
||||||
|
assert!(!(Numeric < Kanji));
|
||||||
|
assert!(!(Numeric >= Kanji));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_max() {
|
||||||
|
assert_eq!(Byte.max(Kanji), Byte);
|
||||||
|
assert_eq!(Numeric.max(Alphanumeric), Alphanumeric);
|
||||||
|
assert_eq!(Alphanumeric.max(Alphanumeric), Alphanumeric);
|
||||||
|
assert_eq!(Numeric.max(Kanji), Byte);
|
||||||
|
assert_eq!(Kanji.max(Numeric), Byte);
|
||||||
|
assert_eq!(Alphanumeric.max(Numeric), Alphanumeric);
|
||||||
|
assert_eq!(Kanji.max(Kanji), Kanji);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//}}}
|
||||||
|
|
Loading…
Reference in a new issue