Rustfmt and clippy.
This commit is contained in:
parent
8be25fa56a
commit
21efb6205e
10
.travis.yml
10
.travis.yml
|
@ -15,20 +15,16 @@ addons:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- os: linux
|
- os: linux
|
||||||
rust: 1.17.0
|
rust: 1.20.0
|
||||||
- os: linux
|
- os: linux
|
||||||
rust: 1.21.0
|
rust: stable
|
||||||
- os: osx
|
- os: osx
|
||||||
rust: 1.21.0
|
rust: stable
|
||||||
- os: linux
|
- os: linux
|
||||||
rust: beta
|
rust: beta
|
||||||
- os: linux
|
- os: linux
|
||||||
rust: nightly
|
rust: nightly
|
||||||
|
|
||||||
matrix:
|
|
||||||
allow_failures:
|
|
||||||
- rust: beta
|
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- if [ "$TRAVIS_OS_NAME" = 'linux' ]; then OS=unknown-linux-gnu; else OS=apple-darwin; fi
|
- if [ "$TRAVIS_OS_NAME" = 'linux' ]; then OS=unknown-linux-gnu; else OS=apple-darwin; fi
|
||||||
- rustup target add i686-$OS
|
- rustup target add i686-$OS
|
||||||
|
|
|
@ -21,6 +21,7 @@ maintenance = { status = "passively-maintained" }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
image = { version = "0.17", optional = true }
|
image = { version = "0.17", optional = true }
|
||||||
|
checked_int_cast = "1"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["image", "svg"]
|
default = ["image", "svg"]
|
||||||
|
|
|
@ -13,14 +13,14 @@ Cargo.toml
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[dependencies]
|
[dependencies]
|
||||||
qrcode = "0.4"
|
qrcode = "0.5"
|
||||||
```
|
```
|
||||||
|
|
||||||
The default settings will depend on the `image` crate. If you don't need image generation capability, disable the `default-features`:
|
The default settings will depend on the `image` crate. If you don't need image generation capability, disable the `default-features`:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[dependencies]
|
[dependencies]
|
||||||
qrcode = { version = "0.4", default-features = false }
|
qrcode = { version = "0.5", default-features = false }
|
||||||
```
|
```
|
||||||
|
|
||||||
Example
|
Example
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
extern crate qrcode;
|
|
||||||
extern crate image;
|
extern crate image;
|
||||||
|
extern crate qrcode;
|
||||||
|
|
||||||
use qrcode::QrCode;
|
use qrcode::QrCode;
|
||||||
use image::Luma;
|
use image::Luma;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
extern crate qrcode;
|
extern crate qrcode;
|
||||||
|
|
||||||
use qrcode::{QrCode, Version, EcLevel};
|
use qrcode::{EcLevel, QrCode, Version};
|
||||||
use qrcode::render::svg;
|
use qrcode::render::svg;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
8
rustfmt.toml
Normal file
8
rustfmt.toml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
array_width = 100
|
||||||
|
chain_width = 100
|
||||||
|
fn_call_width = 100
|
||||||
|
# single_line_if_else_max_width = 100
|
||||||
|
struct_lit_width = 100
|
||||||
|
struct_variant_width = 100
|
||||||
|
error_on_line_overflow = false
|
||||||
|
|
|
@ -6,9 +6,11 @@ pub fn main() {
|
||||||
let arg = env::args().nth(1).unwrap();
|
let arg = env::args().nth(1).unwrap();
|
||||||
let code = qrcode::QrCode::new(arg.as_bytes()).unwrap();
|
let code = qrcode::QrCode::new(arg.as_bytes()).unwrap();
|
||||||
|
|
||||||
print!("{}", code.render()
|
print!(
|
||||||
|
"{}",
|
||||||
|
code.render()
|
||||||
.dark_color("\x1b[7m \x1b[0m")
|
.dark_color("\x1b[7m \x1b[0m")
|
||||||
.light_color("\x1b[49m \x1b[0m")
|
.light_color("\x1b[49m \x1b[0m")
|
||||||
.build());
|
.build()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
286
src/bits.rs
286
src/bits.rs
|
@ -5,8 +5,9 @@ use std::cmp::min;
|
||||||
#[cfg(feature = "bench")]
|
#[cfg(feature = "bench")]
|
||||||
use test::Bencher;
|
use test::Bencher;
|
||||||
|
|
||||||
use types::{QrResult, QrError, Mode, EcLevel, Version};
|
use types::{EcLevel, Mode, QrError, QrResult, Version};
|
||||||
use optimize::{Parser, Optimizer, total_encoded_len, Segment};
|
use optimize::{total_encoded_len, Optimizer, Parser, Segment};
|
||||||
|
use cast::{As, Truncate};
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
//{{{ Bits
|
//{{{ Bits
|
||||||
|
@ -20,8 +21,8 @@ pub struct Bits {
|
||||||
|
|
||||||
impl Bits {
|
impl Bits {
|
||||||
/// Constructs a new, empty bits structure.
|
/// Constructs a new, empty bits structure.
|
||||||
pub fn new(version: Version) -> Bits {
|
pub fn new(version: Version) -> Self {
|
||||||
Bits { data: Vec::new(), bit_offset: 0, version: version }
|
Self { data: Vec::new(), bit_offset: 0, version: version }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pushes an N-bit big-endian integer to the end of the bits.
|
/// Pushes an N-bit big-endian integer to the end of the bits.
|
||||||
|
@ -30,29 +31,34 @@ impl Bits {
|
||||||
/// `n` bit in size. Otherwise the excess bits may stomp on the existing
|
/// `n` bit in size. Otherwise the excess bits may stomp on the existing
|
||||||
/// ones.
|
/// ones.
|
||||||
fn push_number(&mut self, n: usize, number: u16) {
|
fn push_number(&mut self, n: usize, number: u16) {
|
||||||
debug_assert!(n == 16 || n < 16 && number < (1 << n),
|
debug_assert!(
|
||||||
"{} is too big as a {}-bit number", number, n);
|
n == 16 || n < 16 && number < (1 << n),
|
||||||
|
"{} is too big as a {}-bit number",
|
||||||
|
number,
|
||||||
|
n
|
||||||
|
);
|
||||||
|
|
||||||
let b = self.bit_offset + n;
|
let b = self.bit_offset + n;
|
||||||
|
let last_index = self.data.len().wrapping_sub(1);
|
||||||
match (self.bit_offset, b) {
|
match (self.bit_offset, b) {
|
||||||
(0, 0...8) => {
|
(0, 0...8) => {
|
||||||
self.data.push((number << (8-b)) as u8);
|
self.data.push((number << (8 - b)).truncate_as_u8());
|
||||||
}
|
}
|
||||||
(0, _) => {
|
(0, _) => {
|
||||||
self.data.push((number >> (b-8)) as u8);
|
self.data.push((number >> (b - 8)).truncate_as_u8());
|
||||||
self.data.push((number << (16-b)) as u8);
|
self.data.push((number << (16 - b)).truncate_as_u8());
|
||||||
}
|
}
|
||||||
(_, 0...8) => {
|
(_, 0...8) => {
|
||||||
*self.data.last_mut().unwrap() |= (number << (8-b)) as u8;
|
self.data[last_index] |= (number << (8 - b)).truncate_as_u8();
|
||||||
}
|
}
|
||||||
(_, 9...16) => {
|
(_, 9...16) => {
|
||||||
*self.data.last_mut().unwrap() |= (number >> (b-8)) as u8;
|
self.data[last_index] |= (number >> (b - 8)).truncate_as_u8();
|
||||||
self.data.push((number << (16-b)) as u8);
|
self.data.push((number << (16 - b)).truncate_as_u8());
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
*self.data.last_mut().unwrap() |= (number >> (b-8)) as u8;
|
self.data[last_index] |= (number >> (b - 8)).truncate_as_u8();
|
||||||
self.data.push((number >> (b-16)) as u8);
|
self.data.push((number >> (b - 16)).truncate_as_u8());
|
||||||
self.data.push((number << (24-b)) as u8);
|
self.data.push((number << (24 - b)).truncate_as_u8());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.bit_offset = b & 7;
|
self.bit_offset = b & 7;
|
||||||
|
@ -66,7 +72,7 @@ impl Bits {
|
||||||
if n > 16 || number >= (1 << n) {
|
if n > 16 || number >= (1 << n) {
|
||||||
Err(QrError::DataTooLong)
|
Err(QrError::DataTooLong)
|
||||||
} else {
|
} else {
|
||||||
self.push_number(n, number as u16);
|
self.push_number(n, number.as_u16());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,6 +97,11 @@ impl Bits {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether there are any bits pushed.
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.data.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
/// The maximum number of bits allowed by the provided QR code version and
|
/// The maximum number of bits allowed by the provided QR code version and
|
||||||
/// error correction level.
|
/// error correction level.
|
||||||
pub fn max_len(&self, ec_level: EcLevel) -> QrResult<usize> {
|
pub fn max_len(&self, ec_level: EcLevel) -> QrResult<usize> {
|
||||||
|
@ -118,14 +129,19 @@ fn test_push_number() {
|
||||||
|
|
||||||
let bytes = bits.into_bytes();
|
let bytes = bits.into_bytes();
|
||||||
|
|
||||||
assert_eq!(bytes, vec![0b010__110__10, // 90
|
assert_eq!(
|
||||||
|
bytes,
|
||||||
|
vec![
|
||||||
|
0b010__110__10, // 90
|
||||||
0b1__001_1010, // 154
|
0b1__001_1010, // 154
|
||||||
0b1100__1011, // 203
|
0b1100__1011, // 203
|
||||||
0b0110_1101, // 109
|
0b0110_1101, // 109
|
||||||
0b01_1001_00, // 100
|
0b01_1001_00, // 100
|
||||||
0b01__111_001, // 121
|
0b01__111_001, // 121
|
||||||
0b0_1110_001, // 113
|
0b0_1110_001, // 113
|
||||||
0b1__0000000]); // 128
|
0b1__0000000, // 128
|
||||||
|
]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "bench")]
|
#[cfg(feature = "bench")]
|
||||||
|
@ -171,6 +187,7 @@ impl Bits {
|
||||||
/// If the mode is not supported in the provided version, this method
|
/// If the mode is not supported in the provided version, this method
|
||||||
/// returns `Err(QrError::UnsupportedCharacterSet)`.
|
/// returns `Err(QrError::UnsupportedCharacterSet)`.
|
||||||
pub fn push_mode_indicator(&mut self, mode: ExtendedMode) -> QrResult<()> {
|
pub fn push_mode_indicator(&mut self, mode: ExtendedMode) -> QrResult<()> {
|
||||||
|
#[cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
|
||||||
let number = match (self.version, mode) {
|
let number = match (self.version, mode) {
|
||||||
(Version::Micro(1), ExtendedMode::Data(Mode::Numeric)) => return Ok(()),
|
(Version::Micro(1), ExtendedMode::Data(Mode::Numeric)) => return Ok(()),
|
||||||
(Version::Micro(_), ExtendedMode::Data(Mode::Numeric)) => 0,
|
(Version::Micro(_), ExtendedMode::Data(Mode::Numeric)) => 0,
|
||||||
|
@ -188,7 +205,8 @@ impl Bits {
|
||||||
(_, ExtendedMode::StructuredAppend) => 0b0011,
|
(_, ExtendedMode::StructuredAppend) => 0b0011,
|
||||||
};
|
};
|
||||||
let bits = self.version.mode_bits_count();
|
let bits = self.version.mode_bits_count();
|
||||||
self.push_number_checked(bits, number).or(Err(QrError::UnsupportedCharacterSet))
|
self.push_number_checked(bits, number)
|
||||||
|
.or(Err(QrError::UnsupportedCharacterSet))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,19 +252,19 @@ impl Bits {
|
||||||
/// return `Err(QrError::InvalidECIDesignator)`.
|
/// return `Err(QrError::InvalidECIDesignator)`.
|
||||||
pub fn push_eci_designator(&mut self, eci_designator: u32) -> QrResult<()> {
|
pub fn push_eci_designator(&mut self, eci_designator: u32) -> QrResult<()> {
|
||||||
self.reserve(12); // assume the common case that eci_designator <= 127.
|
self.reserve(12); // assume the common case that eci_designator <= 127.
|
||||||
try!(self.push_mode_indicator(ExtendedMode::Eci));
|
self.push_mode_indicator(ExtendedMode::Eci)?;
|
||||||
match eci_designator {
|
match eci_designator {
|
||||||
0...127 => {
|
0...127 => {
|
||||||
self.push_number(8, eci_designator as u16);
|
self.push_number(8, eci_designator.as_u16());
|
||||||
}
|
}
|
||||||
128...16383 => {
|
128...16383 => {
|
||||||
self.push_number(2, 0b10);
|
self.push_number(2, 0b10);
|
||||||
self.push_number(14, eci_designator as u16);
|
self.push_number(14, eci_designator.as_u16());
|
||||||
}
|
}
|
||||||
16384...999999 => {
|
16384...999999 => {
|
||||||
self.push_number(3, 0b110);
|
self.push_number(3, 0b110);
|
||||||
self.push_number(5, (eci_designator >> 16) as u16);
|
self.push_number(5, (eci_designator >> 16).as_u16());
|
||||||
self.push_number(16, (eci_designator & 0xffff) as u16);
|
self.push_number(16, (eci_designator & 0xffff).as_u16());
|
||||||
}
|
}
|
||||||
_ => return Err(QrError::InvalidEciDesignator),
|
_ => return Err(QrError::InvalidEciDesignator),
|
||||||
}
|
}
|
||||||
|
@ -257,7 +275,7 @@ impl Bits {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod eci_tests {
|
mod eci_tests {
|
||||||
use bits::Bits;
|
use bits::Bits;
|
||||||
use types::{Version, QrError};
|
use types::{QrError, Version};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_9() {
|
fn test_9() {
|
||||||
|
@ -277,10 +295,7 @@ mod eci_tests {
|
||||||
fn test_999999() {
|
fn test_999999() {
|
||||||
let mut bits = Bits::new(Version::Normal(1));
|
let mut bits = Bits::new(Version::Normal(1));
|
||||||
assert_eq!(bits.push_eci_designator(999999), Ok(()));
|
assert_eq!(bits.push_eci_designator(999999), Ok(()));
|
||||||
assert_eq!(bits.into_bytes(), vec![0b0111__110_0,
|
assert_eq!(bits.into_bytes(), vec![0b0111__110_0, 0b11110100, 0b00100011, 0b1111__0000]);
|
||||||
0b11110100,
|
|
||||||
0b00100011,
|
|
||||||
0b1111__0000]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -304,8 +319,8 @@ impl Bits {
|
||||||
fn push_header(&mut self, mode: Mode, raw_data_len: usize) -> QrResult<()> {
|
fn push_header(&mut self, mode: Mode, raw_data_len: usize) -> QrResult<()> {
|
||||||
let length_bits = mode.length_bits_count(self.version);
|
let length_bits = mode.length_bits_count(self.version);
|
||||||
self.reserve(length_bits + 4 + mode.data_bits_count(raw_data_len));
|
self.reserve(length_bits + 4 + mode.data_bits_count(raw_data_len));
|
||||||
try!(self.push_mode_indicator(ExtendedMode::Data(mode)));
|
self.push_mode_indicator(ExtendedMode::Data(mode))?;
|
||||||
try!(self.push_number_checked(length_bits, raw_data_len));
|
self.push_number_checked(length_bits, raw_data_len)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -313,9 +328,12 @@ impl Bits {
|
||||||
///
|
///
|
||||||
/// The data should only contain the characters 0 to 9.
|
/// The data should only contain the characters 0 to 9.
|
||||||
pub fn push_numeric_data(&mut self, data: &[u8]) -> QrResult<()> {
|
pub fn push_numeric_data(&mut self, data: &[u8]) -> QrResult<()> {
|
||||||
try!(self.push_header(Mode::Numeric, data.len()));
|
self.push_header(Mode::Numeric, data.len())?;
|
||||||
for chunk in data.chunks(3) {
|
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 number = chunk
|
||||||
|
.iter()
|
||||||
|
.map(|b| u16::from(*b - b'0'))
|
||||||
|
.fold(0, |a, b| a * 10 + b);
|
||||||
let length = chunk.len() * 3 + 1;
|
let length = chunk.len() * 3 + 1;
|
||||||
self.push_number(length, number);
|
self.push_number(length, number);
|
||||||
}
|
}
|
||||||
|
@ -326,25 +344,26 @@ impl Bits {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod numeric_tests {
|
mod numeric_tests {
|
||||||
use bits::Bits;
|
use bits::Bits;
|
||||||
use types::{Version, QrError};
|
use types::{QrError, Version};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_iso_18004_2006_example_1() {
|
fn test_iso_18004_2006_example_1() {
|
||||||
let mut bits = Bits::new(Version::Normal(1));
|
let mut bits = Bits::new(Version::Normal(1));
|
||||||
assert_eq!(bits.push_numeric_data(b"01234567"), Ok(()));
|
assert_eq!(bits.push_numeric_data(b"01234567"), Ok(()));
|
||||||
assert_eq!(bits.into_bytes(), vec![0b0001_0000,
|
assert_eq!(
|
||||||
0b001000_00,
|
bits.into_bytes(),
|
||||||
0b00001100,
|
vec![0b0001_0000, 0b001000_00, 0b00001100, 0b01010110, 0b01_100001, 0b1__0000000]
|
||||||
0b01010110,
|
);
|
||||||
0b01_100001,
|
|
||||||
0b1__0000000]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_iso_18004_2000_example_2() {
|
fn test_iso_18004_2000_example_2() {
|
||||||
let mut bits = Bits::new(Version::Normal(1));
|
let mut bits = Bits::new(Version::Normal(1));
|
||||||
assert_eq!(bits.push_numeric_data(b"0123456789012345"), Ok(()));
|
assert_eq!(bits.push_numeric_data(b"0123456789012345"), Ok(()));
|
||||||
assert_eq!(bits.into_bytes(), vec![0b0001_0000,
|
assert_eq!(
|
||||||
|
bits.into_bytes(),
|
||||||
|
vec![
|
||||||
|
0b0001_0000,
|
||||||
0b010000_00,
|
0b010000_00,
|
||||||
0b00001100,
|
0b00001100,
|
||||||
0b01010110,
|
0b01010110,
|
||||||
|
@ -352,21 +371,28 @@ mod numeric_tests {
|
||||||
0b0110_1110,
|
0b0110_1110,
|
||||||
0b000101_00,
|
0b000101_00,
|
||||||
0b11101010,
|
0b11101010,
|
||||||
0b0101__0000]);
|
0b0101__0000,
|
||||||
|
]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_iso_18004_2006_example_2() {
|
fn test_iso_18004_2006_example_2() {
|
||||||
let mut bits = Bits::new(Version::Micro(3));
|
let mut bits = Bits::new(Version::Micro(3));
|
||||||
assert_eq!(bits.push_numeric_data(b"0123456789012345"), Ok(()));
|
assert_eq!(bits.push_numeric_data(b"0123456789012345"), Ok(()));
|
||||||
assert_eq!(bits.into_bytes(), vec![0b00_10000_0,
|
assert_eq!(
|
||||||
|
bits.into_bytes(),
|
||||||
|
vec![
|
||||||
|
0b00_10000_0,
|
||||||
0b00000110,
|
0b00000110,
|
||||||
0b0_0101011,
|
0b0_0101011,
|
||||||
0b001_10101,
|
0b001_10101,
|
||||||
0b00110_111,
|
0b00110_111,
|
||||||
0b0000101_0,
|
0b0000101_0,
|
||||||
0b01110101,
|
0b01110101,
|
||||||
0b00101__000]);
|
0b00101__000,
|
||||||
|
]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -380,16 +406,16 @@ mod numeric_tests {
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
//{{{ Mode::Alphanumeric mode
|
//{{{ Mode::Alphanumeric mode
|
||||||
|
|
||||||
/// In QR code "Mode::Alphanumeric" mode, a pair of alphanumeric characters will be
|
/// In QR code `Mode::Alphanumeric` mode, a pair of alphanumeric characters will
|
||||||
/// encoded as a base-45 integer. `alphanumeric_digit` converts each character
|
/// be encoded as a base-45 integer. `alphanumeric_digit` converts each
|
||||||
/// into its corresponding base-45 digit.
|
/// character into its corresponding base-45 digit.
|
||||||
///
|
///
|
||||||
/// The conversion is specified in ISO/IEC 18004:2006, §8.4.3, Table 5.
|
/// The conversion is specified in ISO/IEC 18004:2006, §8.4.3, Table 5.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn alphanumeric_digit(character: u8) -> u16 {
|
fn alphanumeric_digit(character: u8) -> u16 {
|
||||||
match character {
|
match character {
|
||||||
b'0' ... b'9' => (character - b'0') as u16,
|
b'0'...b'9' => u16::from(character - b'0'),
|
||||||
b'A' ... b'Z' => (character - b'A') as u16 + 10,
|
b'A'...b'Z' => u16::from(character - b'A') + 10,
|
||||||
b' ' => 36,
|
b' ' => 36,
|
||||||
b'$' => 37,
|
b'$' => 37,
|
||||||
b'%' => 38,
|
b'%' => 38,
|
||||||
|
@ -409,9 +435,12 @@ impl Bits {
|
||||||
/// The data should only contain the charaters A to Z (excluding lowercase),
|
/// The data should only contain the charaters A to Z (excluding lowercase),
|
||||||
/// 0 to 9, space, `$`, `%`, `*`, `+`, `-`, `.`, `/` or `:`.
|
/// 0 to 9, space, `$`, `%`, `*`, `+`, `-`, `.`, `/` or `:`.
|
||||||
pub fn push_alphanumeric_data(&mut self, data: &[u8]) -> QrResult<()> {
|
pub fn push_alphanumeric_data(&mut self, data: &[u8]) -> QrResult<()> {
|
||||||
try!(self.push_header(Mode::Alphanumeric, data.len()));
|
self.push_header(Mode::Alphanumeric, data.len())?;
|
||||||
for chunk in data.chunks(2) {
|
for chunk in data.chunks(2) {
|
||||||
let number = chunk.iter().map(|b| alphanumeric_digit(*b)).fold(0, |a, b| a*45 + b);
|
let number = chunk
|
||||||
|
.iter()
|
||||||
|
.map(|b| alphanumeric_digit(*b))
|
||||||
|
.fold(0, |a, b| a * 45 + b);
|
||||||
let length = chunk.len() * 5 + 1;
|
let length = chunk.len() * 5 + 1;
|
||||||
self.push_number(length, number);
|
self.push_number(length, number);
|
||||||
}
|
}
|
||||||
|
@ -422,18 +451,16 @@ impl Bits {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod alphanumeric_tests {
|
mod alphanumeric_tests {
|
||||||
use bits::Bits;
|
use bits::Bits;
|
||||||
use types::{Version, QrError};
|
use types::{QrError, Version};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_iso_18004_2006_example() {
|
fn test_iso_18004_2006_example() {
|
||||||
let mut bits = Bits::new(Version::Normal(1));
|
let mut bits = Bits::new(Version::Normal(1));
|
||||||
assert_eq!(bits.push_alphanumeric_data(b"AC-42"), Ok(()));
|
assert_eq!(bits.push_alphanumeric_data(b"AC-42"), Ok(()));
|
||||||
assert_eq!(bits.into_bytes(), vec![0b0010_0000,
|
assert_eq!(
|
||||||
0b00101_001,
|
bits.into_bytes(),
|
||||||
0b11001110,
|
vec![0b0010_0000, 0b00101_001, 0b11001110, 0b11100111, 0b001_00001, 0b0__0000000]
|
||||||
0b11100111,
|
);
|
||||||
0b001_00001,
|
|
||||||
0b0__0000000]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -456,9 +483,9 @@ mod alphanumeric_tests {
|
||||||
impl Bits {
|
impl Bits {
|
||||||
/// Encodes 8-bit byte data to the bits.
|
/// Encodes 8-bit byte data to the bits.
|
||||||
pub fn push_byte_data(&mut self, data: &[u8]) -> QrResult<()> {
|
pub fn push_byte_data(&mut self, data: &[u8]) -> QrResult<()> {
|
||||||
try!(self.push_header(Mode::Byte, data.len()));
|
self.push_header(Mode::Byte, data.len())?;
|
||||||
for b in data {
|
for b in data {
|
||||||
self.push_number(8, *b as u16);
|
self.push_number(8, u16::from(*b));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -467,13 +494,16 @@ impl Bits {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod byte_tests {
|
mod byte_tests {
|
||||||
use bits::Bits;
|
use bits::Bits;
|
||||||
use types::{Version, QrError};
|
use types::{QrError, Version};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test() {
|
fn test() {
|
||||||
let mut bits = Bits::new(Version::Normal(1));
|
let mut bits = Bits::new(Version::Normal(1));
|
||||||
assert_eq!(bits.push_byte_data(b"\x12\x34\x56\x78\x9a\xbc\xde\xf0"), Ok(()));
|
assert_eq!(bits.push_byte_data(b"\x12\x34\x56\x78\x9a\xbc\xde\xf0"), Ok(()));
|
||||||
assert_eq!(bits.into_bytes(), vec![0b0100_0000,
|
assert_eq!(
|
||||||
|
bits.into_bytes(),
|
||||||
|
vec![
|
||||||
|
0b0100_0000,
|
||||||
0b1000_0001,
|
0b1000_0001,
|
||||||
0b0010_0011,
|
0b0010_0011,
|
||||||
0b0100_0101,
|
0b0100_0101,
|
||||||
|
@ -482,7 +512,9 @@ mod byte_tests {
|
||||||
0b1010_1011,
|
0b1010_1011,
|
||||||
0b1100_1101,
|
0b1100_1101,
|
||||||
0b1110_1111,
|
0b1110_1111,
|
||||||
0b0000__0000]);
|
0b0000__0000,
|
||||||
|
]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -505,13 +537,17 @@ mod byte_tests {
|
||||||
impl Bits {
|
impl Bits {
|
||||||
/// Encodes Shift JIS double-byte data to the bits.
|
/// Encodes Shift JIS double-byte data to the bits.
|
||||||
pub fn push_kanji_data(&mut self, data: &[u8]) -> QrResult<()> {
|
pub fn push_kanji_data(&mut self, data: &[u8]) -> QrResult<()> {
|
||||||
try!(self.push_header(Mode::Kanji, data.len()/2));
|
self.push_header(Mode::Kanji, data.len() / 2)?;
|
||||||
for kanji in data.chunks(2) {
|
for kanji in data.chunks(2) {
|
||||||
if kanji.len() != 2 {
|
if kanji.len() != 2 {
|
||||||
return Err(QrError::InvalidCharacter);
|
return Err(QrError::InvalidCharacter);
|
||||||
}
|
}
|
||||||
let cp = (kanji[0] as u16) * 256 + (kanji[1] as u16);
|
let cp = u16::from(kanji[0]) * 256 + u16::from(kanji[1]);
|
||||||
let bytes = if cp < 0xe040 { cp - 0x8140 } else { cp - 0xc140 };
|
let bytes = if cp < 0xe040 {
|
||||||
|
cp - 0x8140
|
||||||
|
} else {
|
||||||
|
cp - 0xc140
|
||||||
|
};
|
||||||
let number = (bytes >> 8) * 0xc0 + (bytes & 0xff);
|
let number = (bytes >> 8) * 0xc0 + (bytes & 0xff);
|
||||||
self.push_number(13, number);
|
self.push_number(13, number);
|
||||||
}
|
}
|
||||||
|
@ -522,17 +558,16 @@ impl Bits {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod kanji_tests {
|
mod kanji_tests {
|
||||||
use bits::Bits;
|
use bits::Bits;
|
||||||
use types::{Version, QrError};
|
use types::{QrError, Version};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_iso_18004_example() {
|
fn test_iso_18004_example() {
|
||||||
let mut bits = Bits::new(Version::Normal(1));
|
let mut bits = Bits::new(Version::Normal(1));
|
||||||
assert_eq!(bits.push_kanji_data(b"\x93\x5f\xe4\xaa"), Ok(()));
|
assert_eq!(bits.push_kanji_data(b"\x93\x5f\xe4\xaa"), Ok(()));
|
||||||
assert_eq!(bits.into_bytes(), vec![0b1000_0000,
|
assert_eq!(
|
||||||
0b0010_0110,
|
bits.into_bytes(),
|
||||||
0b11001111,
|
vec![0b1000_0000, 0b0010_0110, 0b11001111, 0b1_1101010, 0b101010__00]
|
||||||
0b1_1101010,
|
);
|
||||||
0b101010__00]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -544,8 +579,10 @@ mod kanji_tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_data_too_long() {
|
fn test_data_too_long() {
|
||||||
let mut bits = Bits::new(Version::Micro(3));
|
let mut bits = Bits::new(Version::Micro(3));
|
||||||
assert_eq!(bits.push_kanji_data(b"\x93_\x93_\x93_\x93_\x93_\x93_\x93_\x93_"),
|
assert_eq!(
|
||||||
Err(QrError::DataTooLong));
|
bits.push_kanji_data(b"\x93_\x93_\x93_\x93_\x93_\x93_\x93_\x93_"),
|
||||||
|
Err(QrError::DataTooLong)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -593,8 +630,8 @@ impl Bits {
|
||||||
/// bits.push_fnc1_second_position(b'A' + 100);
|
/// bits.push_fnc1_second_position(b'A' + 100);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn push_fnc1_second_position(&mut self, application_indicator: u8) -> QrResult<()> {
|
pub fn push_fnc1_second_position(&mut self, application_indicator: u8) -> QrResult<()> {
|
||||||
try!(self.push_mode_indicator(ExtendedMode::Fnc1Second));
|
self.push_mode_indicator(ExtendedMode::Fnc1Second)?;
|
||||||
self.push_number(8, application_indicator as u16);
|
self.push_number(8, u16::from(application_indicator));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -646,7 +683,6 @@ static DATA_LENGTHS: [[usize; 4]; 44] = [
|
||||||
[21616, 16816, 12016, 9136],
|
[21616, 16816, 12016, 9136],
|
||||||
[22496, 17728, 12656, 9776],
|
[22496, 17728, 12656, 9776],
|
||||||
[23648, 18672, 13328, 10208],
|
[23648, 18672, 13328, 10208],
|
||||||
|
|
||||||
// Micro versions
|
// Micro versions
|
||||||
[20, 0, 0, 0],
|
[20, 0, 0, 0],
|
||||||
[40, 32, 0, 0],
|
[40, 32, 0, 0],
|
||||||
|
@ -658,12 +694,12 @@ impl Bits {
|
||||||
/// Pushes the ending bits to indicate no more data.
|
/// Pushes the ending bits to indicate no more data.
|
||||||
pub fn push_terminator(&mut self, ec_level: EcLevel) -> QrResult<()> {
|
pub fn push_terminator(&mut self, ec_level: EcLevel) -> QrResult<()> {
|
||||||
let terminator_size = match self.version {
|
let terminator_size = match self.version {
|
||||||
Version::Micro(a) => (a as usize) * 2 + 1,
|
Version::Micro(a) => a.as_usize() * 2 + 1,
|
||||||
_ => 4,
|
_ => 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
let cur_length = self.len();
|
let cur_length = self.len();
|
||||||
let data_length = try!(self.max_len(ec_level));
|
let data_length = self.max_len(ec_level)?;
|
||||||
if cur_length > data_length {
|
if cur_length > data_length {
|
||||||
return Err(QrError::DataTooLong);
|
return Err(QrError::DataTooLong);
|
||||||
}
|
}
|
||||||
|
@ -679,7 +715,11 @@ impl Bits {
|
||||||
self.bit_offset = 0;
|
self.bit_offset = 0;
|
||||||
let data_bytes_length = data_length / 8;
|
let data_bytes_length = data_length / 8;
|
||||||
let padding_bytes_count = data_bytes_length - self.data.len();
|
let padding_bytes_count = data_bytes_length - self.data.len();
|
||||||
let padding = PADDING_BYTES.iter().cloned().cycle().take(padding_bytes_count);
|
let padding = PADDING_BYTES
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.cycle()
|
||||||
|
.take(padding_bytes_count);
|
||||||
self.data.extend(padding);
|
self.data.extend(padding);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -694,18 +734,31 @@ impl Bits {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod finish_tests {
|
mod finish_tests {
|
||||||
use bits::Bits;
|
use bits::Bits;
|
||||||
use types::{Version, EcLevel, QrError};
|
use types::{EcLevel, QrError, Version};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_hello_world() {
|
fn test_hello_world() {
|
||||||
let mut bits = Bits::new(Version::Normal(1));
|
let mut bits = Bits::new(Version::Normal(1));
|
||||||
assert_eq!(bits.push_alphanumeric_data(b"HELLO WORLD"), Ok(()));
|
assert_eq!(bits.push_alphanumeric_data(b"HELLO WORLD"), Ok(()));
|
||||||
assert_eq!(bits.push_terminator(EcLevel::Q), Ok(()));
|
assert_eq!(bits.push_terminator(EcLevel::Q), Ok(()));
|
||||||
assert_eq!(bits.into_bytes(), vec![0b00100000, 0b01011011, 0b00001011,
|
assert_eq!(
|
||||||
0b01111000, 0b11010001, 0b01110010,
|
bits.into_bytes(),
|
||||||
0b11011100, 0b01001101, 0b01000011,
|
vec![
|
||||||
0b01000000, 0b11101100, 0b00010001,
|
0b00100000,
|
||||||
0b11101100]);
|
0b01011011,
|
||||||
|
0b00001011,
|
||||||
|
0b01111000,
|
||||||
|
0b11010001,
|
||||||
|
0b01110010,
|
||||||
|
0b11011100,
|
||||||
|
0b01001101,
|
||||||
|
0b01000011,
|
||||||
|
0b01000000,
|
||||||
|
0b11101100,
|
||||||
|
0b00010001,
|
||||||
|
0b11101100,
|
||||||
|
]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -755,16 +808,17 @@ mod finish_tests {
|
||||||
impl Bits {
|
impl Bits {
|
||||||
/// Push a segmented data to the bits, and then terminate it.
|
/// Push a segmented data to the bits, and then terminate it.
|
||||||
pub fn push_segments<I>(&mut self, data: &[u8], segments_iter: I) -> QrResult<()>
|
pub fn push_segments<I>(&mut self, data: &[u8], segments_iter: I) -> QrResult<()>
|
||||||
where I: Iterator<Item=Segment>
|
where
|
||||||
|
I: Iterator<Item = Segment>,
|
||||||
{
|
{
|
||||||
for segment in segments_iter {
|
for segment in segments_iter {
|
||||||
let slice = &data[segment.begin..segment.end];
|
let slice = &data[segment.begin..segment.end];
|
||||||
try!(match segment.mode {
|
match segment.mode {
|
||||||
Mode::Numeric => self.push_numeric_data(slice),
|
Mode::Numeric => self.push_numeric_data(slice),
|
||||||
Mode::Alphanumeric => self.push_alphanumeric_data(slice),
|
Mode::Alphanumeric => self.push_alphanumeric_data(slice),
|
||||||
Mode::Byte => self.push_byte_data(slice),
|
Mode::Byte => self.push_byte_data(slice),
|
||||||
Mode::Kanji => self.push_kanji_data(slice),
|
Mode::Kanji => self.push_kanji_data(slice),
|
||||||
});
|
}?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -774,36 +828,50 @@ impl Bits {
|
||||||
let segments = Parser::new(data).optimize(self.version);
|
let segments = Parser::new(data).optimize(self.version);
|
||||||
self.push_segments(data, segments)
|
self.push_segments(data, segments)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod encode_tests {
|
mod encode_tests {
|
||||||
use bits::Bits;
|
use bits::Bits;
|
||||||
use types::{Version, QrError, QrResult, EcLevel};
|
use types::{EcLevel, QrError, QrResult, Version};
|
||||||
|
|
||||||
fn encode(data: &[u8], version: Version, ec_level: EcLevel) -> QrResult<Vec<u8>> {
|
fn encode(data: &[u8], version: Version, ec_level: EcLevel) -> QrResult<Vec<u8>> {
|
||||||
let mut bits = Bits::new(version);
|
let mut bits = Bits::new(version);
|
||||||
try!(bits.push_optimal_data(data));
|
bits.push_optimal_data(data)?;
|
||||||
try!(bits.push_terminator(ec_level));
|
bits.push_terminator(ec_level)?;
|
||||||
Ok(bits.into_bytes())
|
Ok(bits.into_bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_alphanumeric() {
|
fn test_alphanumeric() {
|
||||||
let res = encode(b"HELLO WORLD", Version::Normal(1), EcLevel::Q);
|
let res = encode(b"HELLO WORLD", Version::Normal(1), EcLevel::Q);
|
||||||
assert_eq!(res, Ok(vec![0b00100000, 0b01011011, 0b00001011,
|
assert_eq!(
|
||||||
0b01111000, 0b11010001, 0b01110010,
|
res,
|
||||||
0b11011100, 0b01001101, 0b01000011,
|
Ok(vec![
|
||||||
0b01000000, 0b11101100, 0b00010001,
|
0b00100000,
|
||||||
0b11101100]));
|
0b01011011,
|
||||||
|
0b00001011,
|
||||||
|
0b01111000,
|
||||||
|
0b11010001,
|
||||||
|
0b01110010,
|
||||||
|
0b11011100,
|
||||||
|
0b01001101,
|
||||||
|
0b01000011,
|
||||||
|
0b01000000,
|
||||||
|
0b11101100,
|
||||||
|
0b00010001,
|
||||||
|
0b11101100,
|
||||||
|
])
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_auto_mode_switch() {
|
fn test_auto_mode_switch() {
|
||||||
let res = encode(b"123A", Version::Micro(2), EcLevel::L);
|
let res = encode(b"123A", Version::Micro(2), EcLevel::L);
|
||||||
assert_eq!(res, Ok(vec![0b0_0011_000, 0b1111011_1, 0b001_00101,
|
assert_eq!(
|
||||||
0b0_00000__00, 0b11101100]));
|
res,
|
||||||
|
Ok(vec![0b0_0011_000, 0b1111011_1, 0b001_00101, 0b0_00000__00, 0b11101100])
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -824,15 +892,17 @@ mod encode_tests {
|
||||||
pub fn encode_auto(data: &[u8], ec_level: EcLevel) -> QrResult<Bits> {
|
pub fn encode_auto(data: &[u8], ec_level: EcLevel) -> QrResult<Bits> {
|
||||||
let segments = Parser::new(data).collect::<Vec<Segment>>();
|
let segments = Parser::new(data).collect::<Vec<Segment>>();
|
||||||
for version in &[Version::Normal(9), Version::Normal(26), Version::Normal(40)] {
|
for version in &[Version::Normal(9), Version::Normal(26), Version::Normal(40)] {
|
||||||
let opt_segments = Optimizer::new(segments.iter().map(|s| *s), *version).collect::<Vec<_>>();
|
let opt_segments = Optimizer::new(segments.iter().cloned(), *version).collect::<Vec<_>>();
|
||||||
let total_len = total_encoded_len(&*opt_segments, *version);
|
let total_len = total_encoded_len(&*opt_segments, *version);
|
||||||
let data_capacity = version.fetch(ec_level, &DATA_LENGTHS).unwrap();
|
let data_capacity = version
|
||||||
|
.fetch(ec_level, &DATA_LENGTHS)
|
||||||
|
.expect("invalid DATA_LENGTHS");
|
||||||
if total_len <= data_capacity {
|
if total_len <= data_capacity {
|
||||||
let min_version = find_min_version(total_len, ec_level);
|
let min_version = find_min_version(total_len, ec_level);
|
||||||
let mut bits = Bits::new(min_version);
|
let mut bits = Bits::new(min_version);
|
||||||
bits.reserve(total_len);
|
bits.reserve(total_len);
|
||||||
try!(bits.push_segments(data, opt_segments.into_iter()));
|
bits.push_segments(data, opt_segments.into_iter())?;
|
||||||
try!(bits.push_terminator(ec_level));
|
bits.push_terminator(ec_level)?;
|
||||||
return Ok(bits);
|
return Ok(bits);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -852,13 +922,13 @@ fn find_min_version(length: usize, ec_level: EcLevel) -> Version {
|
||||||
max = half;
|
max = half;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Version::Normal((min + 1) as i16)
|
Version::Normal((min + 1).as_i16())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod encode_auto_tests {
|
mod encode_auto_tests {
|
||||||
use bits::{find_min_version, encode_auto};
|
use bits::{encode_auto, find_min_version};
|
||||||
use types::{Version, EcLevel};
|
use types::{EcLevel, Version};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_find_min_version() {
|
fn test_find_min_version() {
|
||||||
|
@ -893,5 +963,3 @@ mod encode_auto_tests {
|
||||||
|
|
||||||
//}}}
|
//}}}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
719
src/canvas.rs
719
src/canvas.rs
File diff suppressed because it is too large
Load diff
90
src/cast.rs
Normal file
90
src/cast.rs
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
use checked_int_cast::CheckedIntCast;
|
||||||
|
|
||||||
|
pub trait Truncate {
|
||||||
|
fn truncate_as_u8(self) -> u8;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Truncate for u16 {
|
||||||
|
#[cfg_attr(feature = "cargo-clippy", allow(cast_possible_truncation))]
|
||||||
|
fn truncate_as_u8(self) -> u8 {
|
||||||
|
(self & 0xff) as u8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait As {
|
||||||
|
fn as_u16(self) -> u16;
|
||||||
|
fn as_i16(self) -> i16;
|
||||||
|
fn as_u32(self) -> u32;
|
||||||
|
fn as_usize(self) -> usize;
|
||||||
|
fn as_isize(self) -> isize;
|
||||||
|
}
|
||||||
|
|
||||||
|
trait ExpectOrOverflow {
|
||||||
|
type Output;
|
||||||
|
fn expect_or_overflow<D: Display>(self, value: D, ty: &str) -> Self::Output;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> ExpectOrOverflow for Option<T> {
|
||||||
|
type Output = T;
|
||||||
|
fn expect_or_overflow<D: Display>(self, value: D, ty: &str) -> Self::Output {
|
||||||
|
match self {
|
||||||
|
Some(v) => v,
|
||||||
|
None => panic!("{} overflows {}", value, ty),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_as {
|
||||||
|
($ty:ty) => {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
impl As for $ty {
|
||||||
|
fn as_u16(self) -> u16 {
|
||||||
|
self.as_u16_checked().expect_or_overflow(self, "u16")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_i16(self) -> i16 {
|
||||||
|
self.as_i16_checked().expect_or_overflow(self, "i16")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_u32(self) -> u32 {
|
||||||
|
self.as_u32_checked().expect_or_overflow(self, "u32")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_usize(self) -> usize {
|
||||||
|
self.as_usize_checked().expect_or_overflow(self, "usize")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_isize(self) -> isize {
|
||||||
|
self.as_isize_checked().expect_or_overflow(self, "usize")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
#[cfg_attr(feature = "cargo-clippy", allow(cast_possible_truncation))]
|
||||||
|
impl As for $ty {
|
||||||
|
fn as_u16(self) -> u16 {
|
||||||
|
self as u16
|
||||||
|
}
|
||||||
|
fn as_i16(self) -> i16 {
|
||||||
|
self as i16
|
||||||
|
}
|
||||||
|
fn as_u32(self) -> u32 {
|
||||||
|
self as u32
|
||||||
|
}
|
||||||
|
fn as_usize(self) -> usize {
|
||||||
|
self as usize
|
||||||
|
}
|
||||||
|
fn as_isize(self) -> isize {
|
||||||
|
self as isize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_as!(i16);
|
||||||
|
impl_as!(u32);
|
||||||
|
impl_as!(usize);
|
||||||
|
impl_as!(isize);
|
116
src/ec.rs
116
src/ec.rs
|
@ -1,9 +1,8 @@
|
||||||
//! The `ec` module applies the Reed-Solomon error correction codes.
|
//! The `ec` module applies the Reed-Solomon error correction codes.
|
||||||
|
|
||||||
use std::iter::repeat;
|
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
use types::{QrResult, Version, EcLevel};
|
use types::{EcLevel, QrResult, Version};
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
//{{{ Error correction primitive
|
//{{{ Error correction primitive
|
||||||
|
@ -18,25 +17,27 @@ use types::{QrResult, Version, EcLevel};
|
||||||
/// GF(256), and then computes the polynomial modulus with a generator
|
/// GF(256), and then computes the polynomial modulus with a generator
|
||||||
/// polynomial of degree N.
|
/// polynomial of degree N.
|
||||||
pub fn create_error_correction_code(data: &[u8], ec_code_size: usize) -> Vec<u8> {
|
pub fn create_error_correction_code(data: &[u8], ec_code_size: usize) -> Vec<u8> {
|
||||||
let mut res = data.to_vec();
|
|
||||||
res.extend(repeat(0).take(ec_code_size));
|
|
||||||
|
|
||||||
let data_len = data.len();
|
let data_len = data.len();
|
||||||
let log_den = GENERATOR_POLYNOMIALS[ec_code_size];
|
let log_den = GENERATOR_POLYNOMIALS[ec_code_size];
|
||||||
|
|
||||||
|
let mut res = data.to_vec();
|
||||||
|
res.resize(ec_code_size + data_len, 0);
|
||||||
|
|
||||||
|
// rust-lang-nursery/rust-clippy#2213
|
||||||
|
#[cfg_attr(feature = "cargo-clippy", allow(needless_range_loop))]
|
||||||
for i in 0..data_len {
|
for i in 0..data_len {
|
||||||
let lead_coeff = res[i] as usize;
|
let lead_coeff = res[i] as usize;
|
||||||
if lead_coeff == 0 {
|
if lead_coeff == 0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let log_lead_coeff = LOG_TABLE[lead_coeff] as usize;
|
let log_lead_coeff = usize::from(LOG_TABLE[lead_coeff]);
|
||||||
for (u, v) in res[i + 1..].iter_mut().zip(log_den.iter()) {
|
for (u, v) in res[i + 1..].iter_mut().zip(log_den.iter()) {
|
||||||
*u ^= EXP_TABLE[((*v as usize + log_lead_coeff) % 255) as usize];
|
*u ^= EXP_TABLE[(usize::from(*v) + log_lead_coeff) % 255];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
res.into_iter().skip(data_len).collect()
|
res.split_off(data_len)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -73,8 +74,8 @@ mod ec_tests {
|
||||||
///
|
///
|
||||||
/// The longest slice must be at the last of `blocks`, and `blocks` must not be
|
/// The longest slice must be at the last of `blocks`, and `blocks` must not be
|
||||||
/// empty.
|
/// empty.
|
||||||
fn interleave<T: Copy, V: Deref<Target=[T]>>(blocks: &Vec<V>) -> Vec<T> {
|
fn interleave<T: Copy, V: Deref<Target = [T]>>(blocks: &[V]) -> Vec<T> {
|
||||||
let last_block_len = blocks.last().unwrap().len();
|
let last_block_len = blocks.last().expect("non-empty blocks").len();
|
||||||
let mut res = Vec::with_capacity(last_block_len * blocks.len());
|
let mut res = Vec::with_capacity(last_block_len * blocks.len());
|
||||||
for i in 0..last_block_len {
|
for i in 0..last_block_len {
|
||||||
for t in blocks {
|
for t in blocks {
|
||||||
|
@ -88,7 +89,7 @@ fn interleave<T: Copy, V: Deref<Target=[T]>>(blocks: &Vec<V>) -> Vec<T> {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_interleave() {
|
fn test_interleave() {
|
||||||
let res = interleave(&vec![&b"1234"[..], b"5678", b"abcdef", b"ghijkl"]);
|
let res = interleave(&[&b"1234"[..], b"5678", b"abcdef", b"ghijkl"]);
|
||||||
assert_eq!(&*res, b"15ag26bh37ci48djekfl");
|
assert_eq!(&*res, b"15ag26bh37ci48djekfl");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,11 +99,13 @@ fn test_interleave() {
|
||||||
|
|
||||||
/// Constructs data and error correction codewords ready to be put in the QR
|
/// Constructs data and error correction codewords ready to be put in the QR
|
||||||
/// code matrix.
|
/// code matrix.
|
||||||
pub fn construct_codewords(rawbits: &[u8],
|
pub fn construct_codewords(
|
||||||
|
rawbits: &[u8],
|
||||||
version: Version,
|
version: Version,
|
||||||
ec_level: EcLevel) -> QrResult<(Vec<u8>, Vec<u8>)> {
|
ec_level: EcLevel,
|
||||||
|
) -> QrResult<(Vec<u8>, Vec<u8>)> {
|
||||||
let (block_1_size, block_1_count, block_2_size, block_2_count) =
|
let (block_1_size, block_1_count, block_2_size, block_2_count) =
|
||||||
try!(version.fetch(ec_level, &DATA_BYTES_PER_BLOCK));
|
version.fetch(ec_level, &DATA_BYTES_PER_BLOCK)?;
|
||||||
|
|
||||||
let blocks_count = block_1_count + block_2_count;
|
let blocks_count = block_1_count + block_2_count;
|
||||||
let block_1_end = block_1_size * block_1_count;
|
let block_1_end = block_1_size * block_1_count;
|
||||||
|
@ -118,8 +121,9 @@ pub fn construct_codewords(rawbits: &[u8],
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate EC codes.
|
// Generate EC codes.
|
||||||
let ec_bytes = try!(version.fetch(ec_level, &EC_BYTES_PER_BLOCK));
|
let ec_bytes = version.fetch(ec_level, &EC_BYTES_PER_BLOCK)?;
|
||||||
let ec_codes = blocks.iter()
|
let ec_codes = blocks
|
||||||
|
.iter()
|
||||||
.map(|block| create_error_correction_code(*block, ec_bytes))
|
.map(|block| create_error_correction_code(*block, ec_bytes))
|
||||||
.collect::<Vec<Vec<u8>>>();
|
.collect::<Vec<Vec<u8>>>();
|
||||||
|
|
||||||
|
@ -132,24 +136,34 @@ pub fn construct_codewords(rawbits: &[u8],
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod construct_codewords_test {
|
mod construct_codewords_test {
|
||||||
use ec::construct_codewords;
|
use ec::construct_codewords;
|
||||||
use types::{Version, EcLevel};
|
use types::{EcLevel, Version};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_add_ec_simple() {
|
fn test_add_ec_simple() {
|
||||||
let msg = b" [\x0bx\xd1r\xdcMC@\xec\x11\xec\x11\xec\x11";
|
let msg = b" [\x0bx\xd1r\xdcMC@\xec\x11\xec\x11\xec\x11";
|
||||||
let (blocks_vec, ec_vec) = construct_codewords(msg, Version::Normal(1), EcLevel::M).unwrap();
|
let (blocks_vec, ec_vec) =
|
||||||
|
construct_codewords(msg, Version::Normal(1), EcLevel::M).unwrap();
|
||||||
assert_eq!(&*blocks_vec, msg);
|
assert_eq!(&*blocks_vec, msg);
|
||||||
assert_eq!(&*ec_vec, b"\xc4#'w\xeb\xd7\xe7\xe2]\x17");
|
assert_eq!(&*ec_vec, b"\xc4#'w\xeb\xd7\xe7\xe2]\x17");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_add_ec_complex() {
|
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 msg = b"CUF\x86W&U\xc2w2\x06\x12\x06g&\xf6\xf6B\x07v\x86\xf2\x07&V\x16\xc6\xc7\x92\x06\
|
||||||
let (blocks_vec, ec_vec) = construct_codewords(msg, Version::Normal(5), EcLevel::Q).unwrap();
|
\xb6\xe6\xf7w2\x07v\x86W&R\x06\x86\x972\x07F\xf7vV\xc2\x06\x972\x10\xec\x11\xec\
|
||||||
assert_eq!(&*blocks_vec,
|
\x11\xec\x11\xec";
|
||||||
&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"[..]);
|
let expected_blocks = b"C\xf6\xb6FU\xf6\xe6\xf7FB\xf7v\x86\x07wVWv2\xc2&\x86\x07\x06U\xf2v\
|
||||||
assert_eq!(&*ec_vec,
|
\x97\xc2\x07\x862w&W\x102V&\xec\x06\x16R\x11\x12\xc6\x06\xec\x06\
|
||||||
&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"[..]);
|
\xc7\x86\x11g\x92\x97\xec&\x062\x11\x07\xec";
|
||||||
|
let expected_ec = 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";
|
||||||
|
|
||||||
|
let (blocks_vec, ec_vec) =
|
||||||
|
construct_codewords(msg, Version::Normal(5), EcLevel::Q).unwrap();
|
||||||
|
assert_eq!(&*blocks_vec, &expected_blocks[..]);
|
||||||
|
assert_eq!(&*ec_vec, &expected_ec[..]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,8 +184,8 @@ pub fn max_allowed_errors(version: Version, ec_level: EcLevel) -> QrResult<usize
|
||||||
_ => 0,
|
_ => 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
let ec_bytes_per_block = try!(version.fetch(ec_level, &EC_BYTES_PER_BLOCK));
|
let ec_bytes_per_block = version.fetch(ec_level, &EC_BYTES_PER_BLOCK)?;
|
||||||
let (_, count1, _, count2) = try!(version.fetch(ec_level, &DATA_BYTES_PER_BLOCK));
|
let (_, count1, _, count2) = version.fetch(ec_level, &DATA_BYTES_PER_BLOCK)?;
|
||||||
let ec_bytes = (count1 + count2) * ec_bytes_per_block;
|
let ec_bytes = (count1 + count2) * ec_bytes_per_block;
|
||||||
|
|
||||||
Ok((ec_bytes - p) / 2)
|
Ok((ec_bytes - p) / 2)
|
||||||
|
@ -180,7 +194,7 @@ pub fn max_allowed_errors(version: Version, ec_level: EcLevel) -> QrResult<usize
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod max_allowed_errors_test {
|
mod max_allowed_errors_test {
|
||||||
use ec::max_allowed_errors;
|
use ec::max_allowed_errors;
|
||||||
use types::{Version, EcLevel};
|
use types::{EcLevel, Version};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_low_versions() {
|
fn test_low_versions() {
|
||||||
|
@ -231,10 +245,42 @@ mod max_allowed_errors_test {
|
||||||
//{{{ Precomputed tables for GF(256).
|
//{{{ Precomputed tables for GF(256).
|
||||||
|
|
||||||
/// `EXP_TABLE` encodes the value of 2<sup>n</sup> in the Galois Field 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";
|
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`.
|
/// `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";
|
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.
|
/// The generator polynomial list.
|
||||||
///
|
///
|
||||||
|
@ -245,6 +291,8 @@ static LOG_TABLE: &'static [u8] = b"\xff\x00\x01\x19\x02\x32\x1a\xc6\x03\xdf\x33
|
||||||
/// is the Reed-Solomon error correction code.
|
/// is the Reed-Solomon error correction code.
|
||||||
///
|
///
|
||||||
/// A partial list can be found from ISO/IEC 18004:2006 Annex A.
|
/// A partial list can be found from ISO/IEC 18004:2006 Annex A.
|
||||||
|
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||||
|
// ^ this attribute is currently useless, see rust-lang-nursery/rustfmt#1080 and 1298
|
||||||
static GENERATOR_POLYNOMIALS: [&'static [u8]; 70] = [
|
static GENERATOR_POLYNOMIALS: [&'static [u8]; 70] = [
|
||||||
b"",
|
b"",
|
||||||
b"\x00",
|
b"\x00",
|
||||||
|
@ -339,7 +387,6 @@ static EC_BYTES_PER_BLOCK: [[usize; 4]; 44] = [
|
||||||
[24, 22, 22, 26], // 8
|
[24, 22, 22, 26], // 8
|
||||||
[30, 22, 20, 24], // 9
|
[30, 22, 20, 24], // 9
|
||||||
[18, 26, 24, 28], // 10
|
[18, 26, 24, 28], // 10
|
||||||
|
|
||||||
[20, 30, 28, 24], // 11
|
[20, 30, 28, 24], // 11
|
||||||
[24, 22, 26, 28], // 12
|
[24, 22, 26, 28], // 12
|
||||||
[26, 22, 24, 22], // 13
|
[26, 22, 24, 22], // 13
|
||||||
|
@ -350,7 +397,6 @@ static EC_BYTES_PER_BLOCK: [[usize; 4]; 44] = [
|
||||||
[30, 26, 28, 28], // 18
|
[30, 26, 28, 28], // 18
|
||||||
[28, 26, 26, 26], // 19
|
[28, 26, 26, 26], // 19
|
||||||
[28, 26, 30, 28], // 20
|
[28, 26, 30, 28], // 20
|
||||||
|
|
||||||
[28, 26, 28, 30], // 21
|
[28, 26, 28, 30], // 21
|
||||||
[28, 28, 30, 24], // 22
|
[28, 28, 30, 24], // 22
|
||||||
[30, 28, 30, 30], // 23
|
[30, 28, 30, 30], // 23
|
||||||
|
@ -361,7 +407,6 @@ static EC_BYTES_PER_BLOCK: [[usize; 4]; 44] = [
|
||||||
[30, 28, 30, 30], // 28
|
[30, 28, 30, 30], // 28
|
||||||
[30, 28, 30, 30], // 29
|
[30, 28, 30, 30], // 29
|
||||||
[30, 28, 30, 30], // 30
|
[30, 28, 30, 30], // 30
|
||||||
|
|
||||||
[30, 28, 30, 30], // 31
|
[30, 28, 30, 30], // 31
|
||||||
[30, 28, 30, 30], // 32
|
[30, 28, 30, 30], // 32
|
||||||
[30, 28, 30, 30], // 33
|
[30, 28, 30, 30], // 33
|
||||||
|
@ -372,7 +417,6 @@ static EC_BYTES_PER_BLOCK: [[usize; 4]; 44] = [
|
||||||
[30, 28, 30, 30], // 38
|
[30, 28, 30, 30], // 38
|
||||||
[30, 28, 30, 30], // 39
|
[30, 28, 30, 30], // 39
|
||||||
[30, 28, 30, 30], // 40
|
[30, 28, 30, 30], // 40
|
||||||
|
|
||||||
// Micro versions.
|
// Micro versions.
|
||||||
[2, 0, 0, 0], // M1
|
[2, 0, 0, 0], // M1
|
||||||
[5, 6, 0, 0], // M2
|
[5, 6, 0, 0], // M2
|
||||||
|
@ -401,7 +445,6 @@ static DATA_BYTES_PER_BLOCK: [[(usize, usize, usize, usize); 4]; 44] = [
|
||||||
[(97, 2, 0, 0), (38, 2, 39, 2), (18, 4, 19, 2), (14, 4, 15, 2)], // 8
|
[(97, 2, 0, 0), (38, 2, 39, 2), (18, 4, 19, 2), (14, 4, 15, 2)], // 8
|
||||||
[(116, 2, 0, 0), (36, 3, 37, 2), (16, 4, 17, 4), (12, 4, 13, 4)], // 9
|
[(116, 2, 0, 0), (36, 3, 37, 2), (16, 4, 17, 4), (12, 4, 13, 4)], // 9
|
||||||
[(68, 2, 69, 2), (43, 4, 44, 1), (19, 6, 20, 2), (15, 6, 16, 2)], // 10
|
[(68, 2, 69, 2), (43, 4, 44, 1), (19, 6, 20, 2), (15, 6, 16, 2)], // 10
|
||||||
|
|
||||||
[(81, 4, 0, 0), (50, 1, 51, 4), (22, 4, 23, 4), (12, 3, 13, 8)], // 11
|
[(81, 4, 0, 0), (50, 1, 51, 4), (22, 4, 23, 4), (12, 3, 13, 8)], // 11
|
||||||
[(92, 2, 93, 2), (36, 6, 37, 2), (20, 4, 21, 6), (14, 7, 15, 4)], // 12
|
[(92, 2, 93, 2), (36, 6, 37, 2), (20, 4, 21, 6), (14, 7, 15, 4)], // 12
|
||||||
[(107, 4, 0, 0), (37, 8, 38, 1), (20, 8, 21, 4), (11, 12, 12, 4)], // 13
|
[(107, 4, 0, 0), (37, 8, 38, 1), (20, 8, 21, 4), (11, 12, 12, 4)], // 13
|
||||||
|
@ -412,7 +455,6 @@ static DATA_BYTES_PER_BLOCK: [[(usize, usize, usize, usize); 4]; 44] = [
|
||||||
[(120, 5, 121, 1), (43, 9, 44, 4), (22, 17, 23, 1), (14, 2, 15, 19)], // 18
|
[(120, 5, 121, 1), (43, 9, 44, 4), (22, 17, 23, 1), (14, 2, 15, 19)], // 18
|
||||||
[(113, 3, 114, 4), (44, 3, 45, 11), (21, 17, 22, 4), (13, 9, 14, 16)], // 19
|
[(113, 3, 114, 4), (44, 3, 45, 11), (21, 17, 22, 4), (13, 9, 14, 16)], // 19
|
||||||
[(107, 3, 108, 5), (41, 3, 42, 13), (24, 15, 25, 5), (15, 15, 16, 10)], // 20
|
[(107, 3, 108, 5), (41, 3, 42, 13), (24, 15, 25, 5), (15, 15, 16, 10)], // 20
|
||||||
|
|
||||||
[(116, 4, 117, 4), (42, 17, 0, 0), (22, 17, 23, 6), (16, 19, 17, 6)], // 21
|
[(116, 4, 117, 4), (42, 17, 0, 0), (22, 17, 23, 6), (16, 19, 17, 6)], // 21
|
||||||
[(111, 2, 112, 7), (46, 17, 0, 0), (24, 7, 25, 16), (13, 34, 0, 0)], // 22
|
[(111, 2, 112, 7), (46, 17, 0, 0), (24, 7, 25, 16), (13, 34, 0, 0)], // 22
|
||||||
[(121, 4, 122, 5), (47, 4, 48, 14), (24, 11, 25, 14), (15, 16, 16, 14)], // 23
|
[(121, 4, 122, 5), (47, 4, 48, 14), (24, 11, 25, 14), (15, 16, 16, 14)], // 23
|
||||||
|
@ -423,7 +465,6 @@ static DATA_BYTES_PER_BLOCK: [[(usize, usize, usize, usize); 4]; 44] = [
|
||||||
[(117, 3, 118, 10), (45, 3, 46, 23), (24, 4, 25, 31), (15, 11, 16, 31)], // 28
|
[(117, 3, 118, 10), (45, 3, 46, 23), (24, 4, 25, 31), (15, 11, 16, 31)], // 28
|
||||||
[(116, 7, 117, 7), (45, 21, 46, 7), (23, 1, 24, 37), (15, 19, 16, 26)], // 29
|
[(116, 7, 117, 7), (45, 21, 46, 7), (23, 1, 24, 37), (15, 19, 16, 26)], // 29
|
||||||
[(115, 5, 116, 10), (47, 19, 48, 10), (24, 15, 25, 25), (15, 23, 16, 25)], // 30
|
[(115, 5, 116, 10), (47, 19, 48, 10), (24, 15, 25, 25), (15, 23, 16, 25)], // 30
|
||||||
|
|
||||||
[(115, 13, 116, 3), (46, 2, 47, 29), (24, 42, 25, 1), (15, 23, 16, 28)], // 31
|
[(115, 13, 116, 3), (46, 2, 47, 29), (24, 42, 25, 1), (15, 23, 16, 28)], // 31
|
||||||
[(115, 17, 0, 0), (46, 10, 47, 23), (24, 10, 25, 35), (15, 19, 16, 35)], // 32
|
[(115, 17, 0, 0), (46, 10, 47, 23), (24, 10, 25, 35), (15, 19, 16, 35)], // 32
|
||||||
[(115, 17, 116, 1), (46, 14, 47, 21), (24, 29, 25, 19), (15, 11, 16, 46)], // 33
|
[(115, 17, 116, 1), (46, 14, 47, 21), (24, 29, 25, 19), (15, 11, 16, 46)], // 33
|
||||||
|
@ -434,7 +475,6 @@ static DATA_BYTES_PER_BLOCK: [[(usize, usize, usize, usize); 4]; 44] = [
|
||||||
[(122, 4, 123, 18), (46, 13, 47, 32), (24, 48, 25, 14), (15, 42, 16, 32)], // 38
|
[(122, 4, 123, 18), (46, 13, 47, 32), (24, 48, 25, 14), (15, 42, 16, 32)], // 38
|
||||||
[(117, 20, 118, 4), (47, 40, 48, 7), (24, 43, 25, 22), (15, 10, 16, 67)], // 39
|
[(117, 20, 118, 4), (47, 40, 48, 7), (24, 43, 25, 22), (15, 10, 16, 67)], // 39
|
||||||
[(118, 19, 119, 6), (47, 18, 48, 31), (24, 34, 25, 34), (15, 20, 16, 61)], // 40
|
[(118, 19, 119, 6), (47, 18, 48, 31), (24, 34, 25, 34), (15, 20, 16, 61)], // 40
|
||||||
|
|
||||||
// Micro versions.
|
// Micro versions.
|
||||||
[(3, 1, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0)], // M1
|
[(3, 1, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0)], // M1
|
||||||
[(5, 1, 0, 0), (4, 1, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0)], // M2
|
[(5, 1, 0, 0), (4, 1, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0)], // M2
|
||||||
|
@ -443,5 +483,3 @@ static DATA_BYTES_PER_BLOCK: [[(usize, usize, usize, usize); 4]; 44] = [
|
||||||
];
|
];
|
||||||
|
|
||||||
//}}}
|
//}}}
|
||||||
|
|
||||||
|
|
||||||
|
|
93
src/lib.rs
93
src/lib.rs
|
@ -30,9 +30,16 @@
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
#![cfg_attr(feature = "bench", feature(test))] // Unstable libraries
|
#![cfg_attr(feature = "bench", feature(test))] // Unstable libraries
|
||||||
|
#![cfg_attr(feature = "cargo-clippy", deny(warnings, clippy_pedantic))]
|
||||||
|
#![cfg_attr(feature = "cargo-clippy",
|
||||||
|
allow(unreadable_literal, missing_docs_in_private_items, shadow_reuse,
|
||||||
|
range_plus_one))]
|
||||||
|
|
||||||
#[cfg(feature="bench")] extern crate test;
|
extern crate checked_int_cast;
|
||||||
#[cfg(feature="image")] extern crate image;
|
#[cfg(feature = "image")]
|
||||||
|
extern crate image;
|
||||||
|
#[cfg(feature = "bench")]
|
||||||
|
extern crate test;
|
||||||
|
|
||||||
use std::ops::Index;
|
use std::ops::Index;
|
||||||
|
|
||||||
|
@ -42,10 +49,13 @@ pub mod optimize;
|
||||||
pub mod ec;
|
pub mod ec;
|
||||||
pub mod canvas;
|
pub mod canvas;
|
||||||
pub mod render;
|
pub mod render;
|
||||||
|
mod cast;
|
||||||
|
|
||||||
pub use types::{QrResult, Color, EcLevel, Version};
|
pub use types::{Color, EcLevel, QrResult, Version};
|
||||||
|
|
||||||
use render::{Pixel, Renderer};
|
use render::{Pixel, Renderer};
|
||||||
|
use checked_int_cast::CheckedIntCast;
|
||||||
|
use cast::As;
|
||||||
|
|
||||||
/// The encoded QR code symbol.
|
/// The encoded QR code symbol.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -66,8 +76,8 @@ impl QrCode {
|
||||||
///
|
///
|
||||||
/// let code = QrCode::new(b"Some data").unwrap();
|
/// let code = QrCode::new(b"Some data").unwrap();
|
||||||
///
|
///
|
||||||
pub fn new<D: AsRef<[u8]>>(data: D) -> QrResult<QrCode> {
|
pub fn new<D: AsRef<[u8]>>(data: D) -> QrResult<Self> {
|
||||||
QrCode::with_error_correction_level(data, EcLevel::M)
|
Self::with_error_correction_level(data, EcLevel::M)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructs a new QR code which automatically encodes the given data at a
|
/// Constructs a new QR code which automatically encodes the given data at a
|
||||||
|
@ -79,9 +89,12 @@ impl QrCode {
|
||||||
///
|
///
|
||||||
/// let code = QrCode::with_error_correction_level(b"Some data", EcLevel::H).unwrap();
|
/// let code = QrCode::with_error_correction_level(b"Some data", EcLevel::H).unwrap();
|
||||||
///
|
///
|
||||||
pub fn with_error_correction_level<D: AsRef<[u8]>>(data: D, ec_level: EcLevel) -> QrResult<QrCode> {
|
pub fn with_error_correction_level<D: AsRef<[u8]>>(
|
||||||
let bits = try!(bits::encode_auto(data.as_ref(), ec_level));
|
data: D,
|
||||||
QrCode::with_bits(bits, ec_level)
|
ec_level: EcLevel,
|
||||||
|
) -> QrResult<Self> {
|
||||||
|
let bits = bits::encode_auto(data.as_ref(), ec_level)?;
|
||||||
|
Self::with_bits(bits, ec_level)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructs a new QR code for the given version and error correction
|
/// Constructs a new QR code for the given version and error correction
|
||||||
|
@ -97,11 +110,15 @@ impl QrCode {
|
||||||
///
|
///
|
||||||
/// let micro_code = QrCode::with_version(b"123", Version::Micro(1), EcLevel::L).unwrap();
|
/// let micro_code = QrCode::with_version(b"123", Version::Micro(1), EcLevel::L).unwrap();
|
||||||
///
|
///
|
||||||
pub fn with_version<D: AsRef<[u8]>>(data: D, version: Version, ec_level: EcLevel) -> QrResult<QrCode> {
|
pub fn with_version<D: AsRef<[u8]>>(
|
||||||
|
data: D,
|
||||||
|
version: Version,
|
||||||
|
ec_level: EcLevel,
|
||||||
|
) -> QrResult<Self> {
|
||||||
let mut bits = bits::Bits::new(version);
|
let mut bits = bits::Bits::new(version);
|
||||||
try!(bits.push_optimal_data(data.as_ref()));
|
bits.push_optimal_data(data.as_ref())?;
|
||||||
try!(bits.push_terminator(ec_level));
|
bits.push_terminator(ec_level)?;
|
||||||
QrCode::with_bits(bits, ec_level)
|
Self::with_bits(bits, ec_level)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructs a new QR code with encoded bits.
|
/// Constructs a new QR code with encoded bits.
|
||||||
|
@ -126,19 +143,19 @@ impl QrCode {
|
||||||
/// bits.push_terminator(EcLevel::L);
|
/// bits.push_terminator(EcLevel::L);
|
||||||
/// let qrcode = QrCode::with_bits(bits, EcLevel::L);
|
/// let qrcode = QrCode::with_bits(bits, EcLevel::L);
|
||||||
///
|
///
|
||||||
pub fn with_bits(bits: bits::Bits, ec_level: EcLevel) -> QrResult<QrCode> {
|
pub fn with_bits(bits: bits::Bits, ec_level: EcLevel) -> QrResult<Self> {
|
||||||
let version = bits.version();
|
let version = bits.version();
|
||||||
let data = bits.into_bytes();
|
let data = bits.into_bytes();
|
||||||
let (encoded_data, ec_data) = try!(ec::construct_codewords(&*data, version, ec_level));
|
let (encoded_data, ec_data) = ec::construct_codewords(&*data, version, ec_level)?;
|
||||||
let mut canvas = canvas::Canvas::new(version, ec_level);
|
let mut canvas = canvas::Canvas::new(version, ec_level);
|
||||||
canvas.draw_all_functional_patterns();
|
canvas.draw_all_functional_patterns();
|
||||||
canvas.draw_data(&*encoded_data, &*ec_data);
|
canvas.draw_data(&*encoded_data, &*ec_data);
|
||||||
let canvas = canvas.apply_best_mask();
|
let canvas = canvas.apply_best_mask();
|
||||||
Ok(QrCode {
|
Ok(Self {
|
||||||
content: canvas.into_colors(),
|
content: canvas.into_colors(),
|
||||||
version: version,
|
version: version,
|
||||||
ec_level: ec_level,
|
ec_level: ec_level,
|
||||||
width: version.width() as usize,
|
width: version.width().as_usize(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,13 +180,17 @@ impl QrCode {
|
||||||
/// before the data becomes corrupted. Note that errors should not be
|
/// before the data becomes corrupted. Note that errors should not be
|
||||||
/// introduced to functional modules.
|
/// introduced to functional modules.
|
||||||
pub fn max_allowed_errors(&self) -> usize {
|
pub fn max_allowed_errors(&self) -> usize {
|
||||||
ec::max_allowed_errors(self.version, self.ec_level).unwrap()
|
ec::max_allowed_errors(self.version, self.ec_level).expect("invalid version or ec_level")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks whether a module at coordinate (x, y) is a functional module or
|
/// Checks whether a module at coordinate (x, y) is a functional module or
|
||||||
/// not.
|
/// not.
|
||||||
pub fn is_functional(&self, x: usize, y: usize) -> bool {
|
pub fn is_functional(&self, x: usize, y: usize) -> bool {
|
||||||
canvas::is_functional(self.version, self.version.width(), x as i16, y as i16)
|
let x = x.as_i16_checked()
|
||||||
|
.expect("coordinate is too large for QR code");
|
||||||
|
let y = y.as_i16_checked()
|
||||||
|
.expect("coordinate is too large for QR code");
|
||||||
|
canvas::is_functional(self.version, self.version.width(), x, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts the QR code into a human-readable string. This is mainly for
|
/// Converts the QR code into a human-readable string. This is mainly for
|
||||||
|
@ -193,7 +214,10 @@ impl QrCode {
|
||||||
/// color of the module, with "true" means dark and "false" means light.
|
/// color of the module, with "true" means dark and "false" means light.
|
||||||
#[deprecated(since = "0.4.0", note = "use `into_colors()` instead")]
|
#[deprecated(since = "0.4.0", note = "use `into_colors()` instead")]
|
||||||
pub fn into_vec(self) -> Vec<bool> {
|
pub fn into_vec(self) -> Vec<bool> {
|
||||||
self.content.into_iter().map(|c| c != Color::Light).collect()
|
self.content
|
||||||
|
.into_iter()
|
||||||
|
.map(|c| c != Color::Light)
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts the QR code to a vector of colors.
|
/// Converts the QR code to a vector of colors.
|
||||||
|
@ -250,13 +274,15 @@ impl Index<(usize, usize)> for QrCode {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use {QrCode, Version, EcLevel};
|
use {EcLevel, QrCode, Version};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_annex_i_qr() {
|
fn test_annex_i_qr() {
|
||||||
// This uses the ISO Annex I as test vector.
|
// This uses the ISO Annex I as test vector.
|
||||||
let code = QrCode::with_version(b"01234567", Version::Normal(1), EcLevel::M).unwrap();
|
let code = QrCode::with_version(b"01234567", Version::Normal(1), EcLevel::M).unwrap();
|
||||||
assert_eq!(&*code.to_debug_str('#', '.'), "\
|
assert_eq!(
|
||||||
|
&*code.to_debug_str('#', '.'),
|
||||||
|
"\
|
||||||
#######..#.##.#######\n\
|
#######..#.##.#######\n\
|
||||||
#.....#..####.#.....#\n\
|
#.....#..####.#.....#\n\
|
||||||
#.###.#.#.....#.###.#\n\
|
#.###.#.#.....#.###.#\n\
|
||||||
|
@ -277,13 +303,16 @@ mod tests {
|
||||||
#.###.#.##..#..#.....\n\
|
#.###.#.##..#..#.....\n\
|
||||||
#.###.#.#.##.#..#.#..\n\
|
#.###.#.#.##.#..#.#..\n\
|
||||||
#.....#........##.##.\n\
|
#.....#........##.##.\n\
|
||||||
#######.####.#..#.#..");
|
#######.####.#..#.#.."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_annex_i_micro_qr() {
|
fn test_annex_i_micro_qr() {
|
||||||
let code = QrCode::with_version(b"01234567", Version::Micro(2), EcLevel::L).unwrap();
|
let code = QrCode::with_version(b"01234567", Version::Micro(2), EcLevel::L).unwrap();
|
||||||
assert_eq!(&*code.to_debug_str('#', '.'), "\
|
assert_eq!(
|
||||||
|
&*code.to_debug_str('#', '.'),
|
||||||
|
"\
|
||||||
#######.#.#.#\n\
|
#######.#.#.#\n\
|
||||||
#.....#.###.#\n\
|
#.....#.###.#\n\
|
||||||
#.###.#..##.#\n\
|
#.###.#..##.#\n\
|
||||||
|
@ -296,20 +325,23 @@ mod tests {
|
||||||
.##.#.#.#.#.#\n\
|
.##.#.#.#.#.#\n\
|
||||||
###..#######.\n\
|
###..#######.\n\
|
||||||
...#.#....##.\n\
|
...#.#....##.\n\
|
||||||
###.#..##.###");
|
###.#..##.###"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(test, feature = "image"))]
|
#[cfg(all(test, feature = "image"))]
|
||||||
mod image_tests {
|
mod image_tests {
|
||||||
use image::{Luma, Rgb, load_from_memory};
|
use image::{load_from_memory, Luma, Rgb};
|
||||||
use {QrCode, Version, EcLevel};
|
use {EcLevel, QrCode, Version};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_annex_i_qr_as_image() {
|
fn test_annex_i_qr_as_image() {
|
||||||
let code = QrCode::new(b"01234567").unwrap();
|
let code = QrCode::new(b"01234567").unwrap();
|
||||||
let image = code.render::<Luma<u8>>().build();
|
let image = code.render::<Luma<u8>>().build();
|
||||||
let expected = load_from_memory(include_bytes!("test_annex_i_qr_as_image.png")).unwrap().to_luma();
|
let expected = load_from_memory(include_bytes!("test_annex_i_qr_as_image.png"))
|
||||||
|
.unwrap()
|
||||||
|
.to_luma();
|
||||||
assert_eq!(image.dimensions(), expected.dimensions());
|
assert_eq!(image.dimensions(), expected.dimensions());
|
||||||
assert_eq!(image.into_raw(), expected.into_raw());
|
assert_eq!(image.into_raw(), expected.into_raw());
|
||||||
}
|
}
|
||||||
|
@ -322,7 +354,9 @@ mod image_tests {
|
||||||
.dark_color(Rgb { data: [128, 0, 0] })
|
.dark_color(Rgb { data: [128, 0, 0] })
|
||||||
.light_color(Rgb { data: [255, 255, 128] })
|
.light_color(Rgb { data: [255, 255, 128] })
|
||||||
.build();
|
.build();
|
||||||
let expected = load_from_memory(include_bytes!("test_annex_i_micro_qr_as_image.png")).unwrap().to_rgb();
|
let expected = load_from_memory(include_bytes!("test_annex_i_micro_qr_as_image.png"))
|
||||||
|
.unwrap()
|
||||||
|
.to_rgb();
|
||||||
assert_eq!(image.dimensions(), expected.dimensions());
|
assert_eq!(image.dimensions(), expected.dimensions());
|
||||||
assert_eq!(image.into_raw(), expected.into_raw());
|
assert_eq!(image.into_raw(), expected.into_raw());
|
||||||
}
|
}
|
||||||
|
@ -331,7 +365,7 @@ mod image_tests {
|
||||||
#[cfg(all(test, feature = "svg"))]
|
#[cfg(all(test, feature = "svg"))]
|
||||||
mod svg_tests {
|
mod svg_tests {
|
||||||
use render::svg::Color as SvgColor;
|
use render::svg::Color as SvgColor;
|
||||||
use {QrCode, Version, EcLevel};
|
use {EcLevel, QrCode, Version};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_annex_i_qr_as_svg() {
|
fn test_annex_i_qr_as_svg() {
|
||||||
|
@ -353,4 +387,3 @@ mod svg_tests {
|
||||||
assert_eq!(&image, expected);
|
assert_eq!(&image, expected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
266
src/optimize.rs
266
src/optimize.rs
|
@ -26,7 +26,11 @@ impl Segment {
|
||||||
/// length bits) when this segment is encoded.
|
/// length bits) when this segment is encoded.
|
||||||
pub fn encoded_len(&self, version: Version) -> usize {
|
pub fn encoded_len(&self, version: Version) -> usize {
|
||||||
let byte_size = self.end - self.begin;
|
let byte_size = self.end - self.begin;
|
||||||
let chars_count = if self.mode == Mode::Kanji { byte_size / 2 } else { byte_size };
|
let chars_count = if self.mode == Mode::Kanji {
|
||||||
|
byte_size / 2
|
||||||
|
} else {
|
||||||
|
byte_size
|
||||||
|
};
|
||||||
|
|
||||||
let mode_bits_count = version.mode_bits_count();
|
let mode_bits_count = version.mode_bits_count();
|
||||||
let length_bits_count = self.mode.length_bits_count(version);
|
let length_bits_count = self.mode.length_bits_count(version);
|
||||||
|
@ -115,24 +119,20 @@ impl<'a> Iterator for Parser<'a> {
|
||||||
if self.pending_single_byte {
|
if self.pending_single_byte {
|
||||||
self.pending_single_byte = false;
|
self.pending_single_byte = false;
|
||||||
self.begin += 1;
|
self.begin += 1;
|
||||||
return Some(Segment {
|
return Some(Segment { mode: Mode::Byte, begin: self.begin - 1, end: self.begin });
|
||||||
mode: Mode::Byte,
|
|
||||||
begin: self.begin-1,
|
|
||||||
end: self.begin,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let (i, ecs) = match self.ecs_iter.next() {
|
let (i, ecs) = match self.ecs_iter.next() {
|
||||||
None => { return None; },
|
None => return None,
|
||||||
Some(a) => a
|
Some(a) => a,
|
||||||
};
|
};
|
||||||
let (next_state, action) = STATE_TRANSITION[self.state as usize + ecs as usize];
|
let (next_state, action) = STATE_TRANSITION[self.state as usize + ecs as usize];
|
||||||
self.state = next_state;
|
self.state = next_state;
|
||||||
|
|
||||||
let old_begin = self.begin;
|
let old_begin = self.begin;
|
||||||
let push_mode = match action {
|
let push_mode = match action {
|
||||||
Action::Idle => { continue; }
|
Action::Idle => continue,
|
||||||
Action::Numeric => Mode::Numeric,
|
Action::Numeric => Mode::Numeric,
|
||||||
Action::Alpha => Mode::Alphanumeric,
|
Action::Alpha => Mode::Alphanumeric,
|
||||||
Action::Byte => Mode::Byte,
|
Action::Byte => Mode::Byte,
|
||||||
|
@ -144,21 +144,15 @@ impl<'a> Iterator for Parser<'a> {
|
||||||
} else {
|
} else {
|
||||||
self.pending_single_byte = true;
|
self.pending_single_byte = true;
|
||||||
self.begin = next_begin;
|
self.begin = next_begin;
|
||||||
return Some(Segment {
|
return Some(
|
||||||
mode: Mode::Kanji,
|
Segment { mode: Mode::Kanji, begin: old_begin, end: next_begin },
|
||||||
begin: old_begin,
|
);
|
||||||
end: next_begin,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
self.begin = i;
|
self.begin = i;
|
||||||
return Some(Segment {
|
return Some(Segment { mode: push_mode, begin: old_begin, end: i });
|
||||||
mode: push_mode,
|
|
||||||
begin: old_begin,
|
|
||||||
end: i,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -175,39 +169,59 @@ mod parse_tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_1() {
|
fn test_parse_1() {
|
||||||
let segs = parse(b"01049123451234591597033130128%10ABC123");
|
let segs = parse(b"01049123451234591597033130128%10ABC123");
|
||||||
assert_eq!(segs, vec![Segment { mode: Mode::Numeric, begin: 0, end: 29 },
|
assert_eq!(
|
||||||
|
segs,
|
||||||
|
vec![
|
||||||
|
Segment { mode: Mode::Numeric, begin: 0, end: 29 },
|
||||||
Segment { mode: Mode::Alphanumeric, begin: 29, end: 30 },
|
Segment { mode: Mode::Alphanumeric, begin: 29, end: 30 },
|
||||||
Segment { mode: Mode::Numeric, begin: 30, end: 32 },
|
Segment { mode: Mode::Numeric, begin: 30, end: 32 },
|
||||||
Segment { mode: Mode::Alphanumeric, begin: 32, end: 35 },
|
Segment { mode: Mode::Alphanumeric, begin: 32, end: 35 },
|
||||||
Segment { mode: Mode::Numeric, begin: 35, end: 38 }]);
|
Segment { mode: Mode::Numeric, begin: 35, end: 38 },
|
||||||
|
]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_shift_jis_example_1() {
|
fn test_parse_shift_jis_example_1() {
|
||||||
let segs = parse(b"\x82\xa0\x81\x41\x41\xb1\x81\xf0"); // "あ、AアÅ"
|
let segs = parse(b"\x82\xa0\x81\x41\x41\xb1\x81\xf0"); // "あ、AアÅ"
|
||||||
assert_eq!(segs, vec![Segment { mode: Mode::Kanji, begin: 0, end: 4 },
|
assert_eq!(
|
||||||
|
segs,
|
||||||
|
vec![
|
||||||
|
Segment { mode: Mode::Kanji, begin: 0, end: 4 },
|
||||||
Segment { mode: Mode::Alphanumeric, begin: 4, end: 5 },
|
Segment { mode: Mode::Alphanumeric, begin: 4, end: 5 },
|
||||||
Segment { mode: Mode::Byte, begin: 5, end: 6 },
|
Segment { mode: Mode::Byte, begin: 5, end: 6 },
|
||||||
Segment { mode: Mode::Kanji, begin: 6, end: 8 }]);
|
Segment { mode: Mode::Kanji, begin: 6, end: 8 },
|
||||||
|
]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_utf_8() {
|
fn test_parse_utf_8() {
|
||||||
// Mojibake?
|
// Mojibake?
|
||||||
let segs = parse(b"\xe3\x81\x82\xe3\x80\x81A\xef\xbd\xb1\xe2\x84\xab");
|
let segs = parse(b"\xe3\x81\x82\xe3\x80\x81A\xef\xbd\xb1\xe2\x84\xab");
|
||||||
assert_eq!(segs, vec![Segment { mode: Mode::Kanji, begin: 0, end: 4 },
|
assert_eq!(
|
||||||
|
segs,
|
||||||
|
vec![
|
||||||
|
Segment { mode: Mode::Kanji, begin: 0, end: 4 },
|
||||||
Segment { mode: Mode::Byte, begin: 4, end: 5 },
|
Segment { mode: Mode::Byte, begin: 4, end: 5 },
|
||||||
Segment { mode: Mode::Kanji, begin: 5, end: 7 },
|
Segment { mode: Mode::Kanji, begin: 5, end: 7 },
|
||||||
Segment { mode: Mode::Byte, begin: 7, end: 10 },
|
Segment { mode: Mode::Byte, begin: 7, end: 10 },
|
||||||
Segment { mode: Mode::Kanji, begin: 10, end: 12 },
|
Segment { mode: Mode::Kanji, begin: 10, end: 12 },
|
||||||
Segment { mode: Mode::Byte, begin: 12, end: 13 }]);
|
Segment { mode: Mode::Byte, begin: 12, end: 13 },
|
||||||
|
]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_not_kanji_1() {
|
fn test_not_kanji_1() {
|
||||||
let segs = parse(b"\x81\x30");
|
let segs = parse(b"\x81\x30");
|
||||||
assert_eq!(segs, vec![Segment { mode: Mode::Byte, begin: 0, end: 1 },
|
assert_eq!(
|
||||||
Segment { mode: Mode::Numeric, begin: 1, end: 2 }]);
|
segs,
|
||||||
|
vec![
|
||||||
|
Segment { mode: Mode::Byte, begin: 0, end: 1 },
|
||||||
|
Segment { mode: Mode::Numeric, begin: 1, end: 2 },
|
||||||
|
]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -215,22 +229,37 @@ mod parse_tests {
|
||||||
// Note that it's implementation detail that the byte seq is split into
|
// Note that it's implementation detail that the byte seq is split into
|
||||||
// two. Perhaps adjust the test to check for this.
|
// two. Perhaps adjust the test to check for this.
|
||||||
let segs = parse(b"\xeb\xc0");
|
let segs = parse(b"\xeb\xc0");
|
||||||
assert_eq!(segs, vec![Segment { mode: Mode::Byte, begin: 0, end: 1 },
|
assert_eq!(
|
||||||
Segment { mode: Mode::Byte, begin: 1, end: 2 }]);
|
segs,
|
||||||
|
vec![
|
||||||
|
Segment { mode: Mode::Byte, begin: 0, end: 1 },
|
||||||
|
Segment { mode: Mode::Byte, begin: 1, end: 2 },
|
||||||
|
]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_not_kanji_3() {
|
fn test_not_kanji_3() {
|
||||||
let segs = parse(b"\x81\x7f");
|
let segs = parse(b"\x81\x7f");
|
||||||
assert_eq!(segs, vec![Segment { mode: Mode::Byte, begin: 0, end: 1 },
|
assert_eq!(
|
||||||
Segment { mode: Mode::Byte, begin: 1, end: 2 }]);
|
segs,
|
||||||
|
vec![
|
||||||
|
Segment { mode: Mode::Byte, begin: 0, end: 1 },
|
||||||
|
Segment { mode: Mode::Byte, begin: 1, end: 2 },
|
||||||
|
]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_not_kanji_4() {
|
fn test_not_kanji_4() {
|
||||||
let segs = parse(b"\x81\x40\x81");
|
let segs = parse(b"\x81\x40\x81");
|
||||||
assert_eq!(segs, vec![Segment { mode: Mode::Kanji, begin: 0, end: 2 },
|
assert_eq!(
|
||||||
Segment { mode: Mode::Byte, begin: 2, end: 3 }]);
|
segs,
|
||||||
|
vec![
|
||||||
|
Segment { mode: Mode::Kanji, begin: 0, end: 2 },
|
||||||
|
Segment { mode: Mode::Byte, begin: 2, end: 3 },
|
||||||
|
]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -238,6 +267,7 @@ mod parse_tests {
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
//{{{ Optimizer
|
//{{{ Optimizer
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "cargo-clippy", allow(stutter))] // rust-lang-nursery/rust-clippy#2212 ಠ_ಠ
|
||||||
pub struct Optimizer<I> {
|
pub struct Optimizer<I> {
|
||||||
parser: I,
|
parser: I,
|
||||||
last_segment: Segment,
|
last_segment: Segment,
|
||||||
|
@ -253,16 +283,16 @@ impl<I: Iterator<Item=Segment>> Optimizer<I> {
|
||||||
/// left to right until the new segment is longer than before. This method
|
/// left to right until the new segment is longer than before. This method
|
||||||
/// does *not* use Annex J from the ISO standard.
|
/// does *not* use Annex J from the ISO standard.
|
||||||
///
|
///
|
||||||
pub fn new(mut segments: I, version: Version) -> Optimizer<I> {
|
pub fn new(mut segments: I, version: Version) -> Self {
|
||||||
match segments.next() {
|
match segments.next() {
|
||||||
None => Optimizer {
|
None => Self {
|
||||||
parser: segments,
|
parser: segments,
|
||||||
last_segment: Segment { mode: Mode::Numeric, begin: 0, end: 0 },
|
last_segment: Segment { mode: Mode::Numeric, begin: 0, end: 0 },
|
||||||
last_segment_size: 0,
|
last_segment_size: 0,
|
||||||
version: version,
|
version: version,
|
||||||
ended: true,
|
ended: true,
|
||||||
},
|
},
|
||||||
Some(segment) => Optimizer {
|
Some(segment) => Self {
|
||||||
parser: segments,
|
parser: segments,
|
||||||
last_segment: segment,
|
last_segment: segment,
|
||||||
last_segment_size: segment.encoded_len(version),
|
last_segment_size: segment.encoded_len(version),
|
||||||
|
@ -321,12 +351,14 @@ impl<I: Iterator<Item=Segment>> Iterator for Optimizer<I> {
|
||||||
/// Computes the total encoded length of all segments.
|
/// Computes the total encoded length of all segments.
|
||||||
pub fn total_encoded_len(segments: &[Segment], version: Version) -> usize {
|
pub fn total_encoded_len(segments: &[Segment], version: Version) -> usize {
|
||||||
// TODO revert to `.map().sum()` after `sum()` is stable.
|
// TODO revert to `.map().sum()` after `sum()` is stable.
|
||||||
segments.iter().fold(0, |acc, seg| acc + seg.encoded_len(version))
|
segments
|
||||||
|
.iter()
|
||||||
|
.fold(0, |acc, seg| acc + seg.encoded_len(version))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod optimize_tests {
|
mod optimize_tests {
|
||||||
use optimize::{Optimizer, total_encoded_len, Segment};
|
use optimize::{total_encoded_len, Optimizer, Segment};
|
||||||
use types::{Mode, Version};
|
use types::{Mode, Version};
|
||||||
|
|
||||||
fn test_optimization_result(given: Vec<Segment>, expected: Vec<Segment>, version: Version) {
|
fn test_optimization_result(given: Vec<Segment>, expected: Vec<Segment>, version: Version) {
|
||||||
|
@ -336,116 +368,114 @@ mod optimize_tests {
|
||||||
if given != opt_segs {
|
if given != opt_segs {
|
||||||
assert!(prev_len > new_len, "{} > {}", prev_len, new_len);
|
assert!(prev_len > new_len, "{} > {}", prev_len, new_len);
|
||||||
}
|
}
|
||||||
assert!(opt_segs == expected,
|
assert!(
|
||||||
|
opt_segs == expected,
|
||||||
"Optimization gave something better: {} < {} ({:?})",
|
"Optimization gave something better: {} < {} ({:?})",
|
||||||
new_len, total_encoded_len(&*expected, version), opt_segs);
|
new_len,
|
||||||
|
total_encoded_len(&*expected, version),
|
||||||
|
opt_segs
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_example_1() {
|
fn test_example_1() {
|
||||||
test_optimization_result(
|
test_optimization_result(
|
||||||
|
vec![
|
||||||
vec![Segment { mode: Mode::Alphanumeric, begin: 0, end: 3 },
|
Segment { mode: Mode::Alphanumeric, begin: 0, end: 3 },
|
||||||
Segment { mode: Mode::Numeric, begin: 3, end: 6 },
|
Segment { mode: Mode::Numeric, begin: 3, end: 6 },
|
||||||
Segment { mode: Mode::Byte, begin: 6, end: 10 }],
|
Segment { mode: Mode::Byte, begin: 6, end: 10 },
|
||||||
|
],
|
||||||
vec![Segment { mode: Mode::Alphanumeric, begin: 0, end: 6 },
|
vec![
|
||||||
Segment { mode: Mode::Byte, begin: 6, end: 10 }],
|
Segment { mode: Mode::Alphanumeric, begin: 0, end: 6 },
|
||||||
|
Segment { mode: Mode::Byte, begin: 6, end: 10 },
|
||||||
Version::Normal(1)
|
],
|
||||||
|
Version::Normal(1),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_example_2() {
|
fn test_example_2() {
|
||||||
test_optimization_result(
|
test_optimization_result(
|
||||||
|
vec![
|
||||||
vec![Segment { mode: Mode::Numeric, begin: 0, end: 29 },
|
Segment { mode: Mode::Numeric, begin: 0, end: 29 },
|
||||||
Segment { mode: Mode::Alphanumeric, begin: 29, end: 30 },
|
Segment { mode: Mode::Alphanumeric, begin: 29, end: 30 },
|
||||||
Segment { mode: Mode::Numeric, begin: 30, end: 32 },
|
Segment { mode: Mode::Numeric, begin: 30, end: 32 },
|
||||||
Segment { mode: Mode::Alphanumeric, begin: 32, end: 35 },
|
Segment { mode: Mode::Alphanumeric, begin: 32, end: 35 },
|
||||||
Segment { mode: Mode::Numeric, begin: 35, end: 38 }],
|
Segment { mode: Mode::Numeric, begin: 35, end: 38 },
|
||||||
|
],
|
||||||
vec![Segment { mode: Mode::Numeric, begin: 0, end: 29 },
|
vec![
|
||||||
Segment { mode: Mode::Alphanumeric, begin: 29, end: 38 }],
|
Segment { mode: Mode::Numeric, begin: 0, end: 29 },
|
||||||
|
Segment { mode: Mode::Alphanumeric, begin: 29, end: 38 },
|
||||||
Version::Normal(9)
|
],
|
||||||
|
Version::Normal(9),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_example_3() {
|
fn test_example_3() {
|
||||||
test_optimization_result(
|
test_optimization_result(
|
||||||
|
vec![
|
||||||
vec![Segment { mode: Mode::Kanji, begin: 0, end: 4 },
|
Segment { mode: Mode::Kanji, begin: 0, end: 4 },
|
||||||
Segment { mode: Mode::Alphanumeric, begin: 4, end: 5 },
|
Segment { mode: Mode::Alphanumeric, begin: 4, end: 5 },
|
||||||
Segment { mode: Mode::Byte, begin: 5, end: 6 },
|
Segment { mode: Mode::Byte, begin: 5, end: 6 },
|
||||||
Segment { mode: Mode::Kanji, begin: 6, end: 8 }],
|
Segment { mode: Mode::Kanji, begin: 6, end: 8 },
|
||||||
|
],
|
||||||
vec![Segment { mode: Mode::Byte, begin: 0, end: 8 }],
|
vec![Segment { mode: Mode::Byte, begin: 0, end: 8 }],
|
||||||
|
Version::Normal(1),
|
||||||
Version::Normal(1)
|
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_example_4() {
|
fn test_example_4() {
|
||||||
test_optimization_result(
|
test_optimization_result(
|
||||||
|
vec![
|
||||||
vec![Segment { mode: Mode::Kanji, begin: 0, end: 10 },
|
Segment { mode: Mode::Kanji, begin: 0, end: 10 },
|
||||||
Segment { mode: Mode::Byte, begin: 10, end: 11 }],
|
Segment { mode: Mode::Byte, begin: 10, end: 11 },
|
||||||
|
],
|
||||||
vec![Segment { mode: Mode::Kanji, begin: 0, end: 10 },
|
vec![
|
||||||
Segment { mode: Mode::Byte, begin: 10, end: 11 }],
|
Segment { mode: Mode::Kanji, begin: 0, end: 10 },
|
||||||
|
Segment { mode: Mode::Byte, begin: 10, end: 11 },
|
||||||
Version::Normal(1)
|
],
|
||||||
|
Version::Normal(1),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_annex_j_guideline_1a() {
|
fn test_annex_j_guideline_1a() {
|
||||||
test_optimization_result(
|
test_optimization_result(
|
||||||
|
vec![
|
||||||
vec![Segment { mode: Mode::Numeric, begin: 0, end: 3 },
|
Segment { mode: Mode::Numeric, begin: 0, end: 3 },
|
||||||
Segment { mode: Mode::Alphanumeric, begin: 3, end: 4 }],
|
Segment { mode: Mode::Alphanumeric, begin: 3, end: 4 },
|
||||||
|
],
|
||||||
vec![Segment { mode: Mode::Numeric, begin: 0, end: 3 },
|
vec![
|
||||||
Segment { mode: Mode::Alphanumeric, begin: 3, end: 4 }],
|
Segment { mode: Mode::Numeric, begin: 0, end: 3 },
|
||||||
|
Segment { mode: Mode::Alphanumeric, begin: 3, end: 4 },
|
||||||
Version::Micro(2)
|
],
|
||||||
|
Version::Micro(2),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_annex_j_guideline_1b() {
|
fn test_annex_j_guideline_1b() {
|
||||||
test_optimization_result(
|
test_optimization_result(
|
||||||
|
vec![
|
||||||
vec![Segment { mode: Mode::Numeric, begin: 0, end: 2 },
|
Segment { mode: Mode::Numeric, begin: 0, end: 2 },
|
||||||
Segment { mode: Mode::Alphanumeric, begin: 2, end: 4 }],
|
Segment { mode: Mode::Alphanumeric, begin: 2, end: 4 },
|
||||||
|
],
|
||||||
vec![Segment { mode: Mode::Alphanumeric, begin: 0, end: 4 }],
|
vec![Segment { mode: Mode::Alphanumeric, begin: 0, end: 4 }],
|
||||||
|
Version::Micro(2),
|
||||||
Version::Micro(2)
|
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_annex_j_guideline_1c() {
|
fn test_annex_j_guideline_1c() {
|
||||||
test_optimization_result(
|
test_optimization_result(
|
||||||
|
vec![
|
||||||
vec![Segment { mode: Mode::Numeric, begin: 0, end: 3 },
|
Segment { mode: Mode::Numeric, begin: 0, end: 3 },
|
||||||
Segment { mode: Mode::Alphanumeric, begin: 3, end: 4 }],
|
Segment { mode: Mode::Alphanumeric, begin: 3, end: 4 },
|
||||||
|
],
|
||||||
vec![Segment { mode: Mode::Alphanumeric, begin: 0, end: 4 }],
|
vec![Segment { mode: Mode::Alphanumeric, begin: 0, end: 4 }],
|
||||||
|
Version::Micro(3),
|
||||||
Version::Micro(3)
|
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -456,10 +486,25 @@ mod optimize_tests {
|
||||||
fn bench_optimize(bencher: &mut Bencher) {
|
fn bench_optimize(bencher: &mut Bencher) {
|
||||||
use types::Version;
|
use types::Version;
|
||||||
|
|
||||||
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";
|
let data = b"QR\x83R\x81[\x83h\x81i\x83L\x83\x85\x81[\x83A\x81[\x83\x8b\x83R\x81[\x83h\x81j\
|
||||||
bencher.iter(|| {
|
\x82\xc6\x82\xcd\x81A1994\x94N\x82\xc9\x83f\x83\x93\x83\\\x81[\x82\xcc\x8aJ\
|
||||||
Parser::new(data).optimize(Version::Normal(15))
|
\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\xcd\
|
||||||
|
Quick 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::Normal(15)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -514,7 +559,7 @@ enum ExclCharSet {
|
||||||
|
|
||||||
impl ExclCharSet {
|
impl ExclCharSet {
|
||||||
/// Determines which character set a byte is in.
|
/// Determines which character set a byte is in.
|
||||||
fn from_u8(c: u8) -> ExclCharSet {
|
fn from_u8(c: u8) -> Self {
|
||||||
match c {
|
match c {
|
||||||
0x20 | 0x24 | 0x25 | 0x2a | 0x2b | 0x2d...0x2f | 0x3a => ExclCharSet::Symbol,
|
0x20 | 0x24 | 0x25 | 0x2a | 0x2b | 0x2d...0x2f | 0x3a => ExclCharSet::Symbol,
|
||||||
0x30...0x39 => ExclCharSet::Numeric,
|
0x30...0x39 => ExclCharSet::Numeric,
|
||||||
|
@ -524,7 +569,7 @@ impl ExclCharSet {
|
||||||
0xeb => ExclCharSet::KanjiHi3,
|
0xeb => ExclCharSet::KanjiHi3,
|
||||||
0x40 | 0x5b...0x7e | 0x80 | 0xa0...0xbf => ExclCharSet::KanjiLo1,
|
0x40 | 0x5b...0x7e | 0x80 | 0xa0...0xbf => ExclCharSet::KanjiLo1,
|
||||||
0xc0...0xdf | 0xec...0xfc => ExclCharSet::KanjiLo2,
|
0xc0...0xdf | 0xec...0xfc => ExclCharSet::KanjiLo2,
|
||||||
_ => ExclCharSet::Byte
|
_ => ExclCharSet::Byte,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -583,7 +628,6 @@ static STATE_TRANSITION: [(State, Action); 70] = [
|
||||||
// STATE_TRANSITION[current_state + next_character] == (next_state, what_to_do)
|
// STATE_TRANSITION[current_state + next_character] == (next_state, what_to_do)
|
||||||
|
|
||||||
// Init state:
|
// Init state:
|
||||||
|
|
||||||
(State::Init, Action::Idle), // End
|
(State::Init, Action::Idle), // End
|
||||||
(State::Alpha, Action::Idle), // Symbol
|
(State::Alpha, Action::Idle), // Symbol
|
||||||
(State::Numeric, Action::Idle), // Numeric
|
(State::Numeric, Action::Idle), // Numeric
|
||||||
|
@ -594,9 +638,7 @@ static STATE_TRANSITION: [(State, Action); 70] = [
|
||||||
(State::Byte, Action::Idle), // KanjiLo1
|
(State::Byte, Action::Idle), // KanjiLo1
|
||||||
(State::Byte, Action::Idle), // KanjiLo2
|
(State::Byte, Action::Idle), // KanjiLo2
|
||||||
(State::Byte, Action::Idle), // Byte
|
(State::Byte, Action::Idle), // Byte
|
||||||
|
|
||||||
// Numeric state:
|
// Numeric state:
|
||||||
|
|
||||||
(State::Init, Action::Numeric), // End
|
(State::Init, Action::Numeric), // End
|
||||||
(State::Alpha, Action::Numeric), // Symbol
|
(State::Alpha, Action::Numeric), // Symbol
|
||||||
(State::Numeric, Action::Idle), // Numeric
|
(State::Numeric, Action::Idle), // Numeric
|
||||||
|
@ -607,9 +649,7 @@ static STATE_TRANSITION: [(State, Action); 70] = [
|
||||||
(State::Byte, Action::Numeric), // KanjiLo1
|
(State::Byte, Action::Numeric), // KanjiLo1
|
||||||
(State::Byte, Action::Numeric), // KanjiLo2
|
(State::Byte, Action::Numeric), // KanjiLo2
|
||||||
(State::Byte, Action::Numeric), // Byte
|
(State::Byte, Action::Numeric), // Byte
|
||||||
|
|
||||||
// Alpha state:
|
// Alpha state:
|
||||||
|
|
||||||
(State::Init, Action::Alpha), // End
|
(State::Init, Action::Alpha), // End
|
||||||
(State::Alpha, Action::Idle), // Symbol
|
(State::Alpha, Action::Idle), // Symbol
|
||||||
(State::Numeric, Action::Alpha), // Numeric
|
(State::Numeric, Action::Alpha), // Numeric
|
||||||
|
@ -620,9 +660,7 @@ static STATE_TRANSITION: [(State, Action); 70] = [
|
||||||
(State::Byte, Action::Alpha), // KanjiLo1
|
(State::Byte, Action::Alpha), // KanjiLo1
|
||||||
(State::Byte, Action::Alpha), // KanjiLo2
|
(State::Byte, Action::Alpha), // KanjiLo2
|
||||||
(State::Byte, Action::Alpha), // Byte
|
(State::Byte, Action::Alpha), // Byte
|
||||||
|
|
||||||
// Byte state:
|
// Byte state:
|
||||||
|
|
||||||
(State::Init, Action::Byte), // End
|
(State::Init, Action::Byte), // End
|
||||||
(State::Alpha, Action::Byte), // Symbol
|
(State::Alpha, Action::Byte), // Symbol
|
||||||
(State::Numeric, Action::Byte), // Numeric
|
(State::Numeric, Action::Byte), // Numeric
|
||||||
|
@ -633,9 +671,7 @@ static STATE_TRANSITION: [(State, Action); 70] = [
|
||||||
(State::Byte, Action::Idle), // KanjiLo1
|
(State::Byte, Action::Idle), // KanjiLo1
|
||||||
(State::Byte, Action::Idle), // KanjiLo2
|
(State::Byte, Action::Idle), // KanjiLo2
|
||||||
(State::Byte, Action::Idle), // Byte
|
(State::Byte, Action::Idle), // Byte
|
||||||
|
|
||||||
// KanjiHi12 state:
|
// KanjiHi12 state:
|
||||||
|
|
||||||
(State::Init, Action::KanjiAndSingleByte), // End
|
(State::Init, Action::KanjiAndSingleByte), // End
|
||||||
(State::Alpha, Action::KanjiAndSingleByte), // Symbol
|
(State::Alpha, Action::KanjiAndSingleByte), // Symbol
|
||||||
(State::Numeric, Action::KanjiAndSingleByte), // Numeric
|
(State::Numeric, Action::KanjiAndSingleByte), // Numeric
|
||||||
|
@ -646,9 +682,7 @@ static STATE_TRANSITION: [(State, Action); 70] = [
|
||||||
(State::Kanji, Action::Idle), // KanjiLo1
|
(State::Kanji, Action::Idle), // KanjiLo1
|
||||||
(State::Kanji, Action::Idle), // KanjiLo2
|
(State::Kanji, Action::Idle), // KanjiLo2
|
||||||
(State::Byte, Action::KanjiAndSingleByte), // Byte
|
(State::Byte, Action::KanjiAndSingleByte), // Byte
|
||||||
|
|
||||||
// KanjiHi3 state:
|
// KanjiHi3 state:
|
||||||
|
|
||||||
(State::Init, Action::KanjiAndSingleByte), // End
|
(State::Init, Action::KanjiAndSingleByte), // End
|
||||||
(State::Alpha, Action::KanjiAndSingleByte), // Symbol
|
(State::Alpha, Action::KanjiAndSingleByte), // Symbol
|
||||||
(State::Numeric, Action::KanjiAndSingleByte), // Numeric
|
(State::Numeric, Action::KanjiAndSingleByte), // Numeric
|
||||||
|
@ -659,9 +693,7 @@ static STATE_TRANSITION: [(State, Action); 70] = [
|
||||||
(State::Kanji, Action::Idle), // KanjiLo1
|
(State::Kanji, Action::Idle), // KanjiLo1
|
||||||
(State::Byte, Action::KanjiAndSingleByte), // KanjiLo2
|
(State::Byte, Action::KanjiAndSingleByte), // KanjiLo2
|
||||||
(State::Byte, Action::KanjiAndSingleByte), // Byte
|
(State::Byte, Action::KanjiAndSingleByte), // Byte
|
||||||
|
|
||||||
// Kanji state:
|
// Kanji state:
|
||||||
|
|
||||||
(State::Init, Action::Kanji), // End
|
(State::Init, Action::Kanji), // End
|
||||||
(State::Alpha, Action::Kanji), // Symbol
|
(State::Alpha, Action::Kanji), // Symbol
|
||||||
(State::Numeric, Action::Kanji), // Numeric
|
(State::Numeric, Action::Kanji), // Numeric
|
||||||
|
@ -675,5 +707,3 @@ static STATE_TRANSITION: [(State, Action); 70] = [
|
||||||
];
|
];
|
||||||
|
|
||||||
//}}}
|
//}}}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
#![cfg(feature="image")]
|
#![cfg(feature="image")]
|
||||||
|
|
||||||
use render::{Pixel, Canvas};
|
use render::{Canvas, Pixel};
|
||||||
use types::Color;
|
use types::Color;
|
||||||
|
|
||||||
use image::{Pixel as ImagePixel, Rgb, Rgba, Luma, LumaA, Primitive, ImageBuffer};
|
use image::{ImageBuffer, Luma, LumaA, Pixel as ImagePixel, Primitive, Rgb, Rgba};
|
||||||
|
|
||||||
macro_rules! impl_pixel_for_image_pixel {
|
macro_rules! impl_pixel_for_image_pixel {
|
||||||
($p:ident<$s:ident>: $c:pat => $d:expr) => {
|
($p:ident<$s:ident>: $c:pat => $d:expr) => {
|
||||||
|
@ -50,12 +50,26 @@ mod render_tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_render_luma8_unsized() {
|
fn test_render_luma8_unsized() {
|
||||||
let image = Renderer::<Luma<u8>>::new(&[
|
let image = Renderer::<Luma<u8>>::new(
|
||||||
Color::Light, Color::Dark, Color::Dark,
|
&[
|
||||||
Color::Dark, Color::Light, Color::Light,
|
Color::Light,
|
||||||
Color::Light, Color::Dark, Color::Light,
|
Color::Dark,
|
||||||
], 3, 1).module_dimensions(1, 1).build();
|
Color::Dark,
|
||||||
|
//
|
||||||
|
Color::Dark,
|
||||||
|
Color::Light,
|
||||||
|
Color::Light,
|
||||||
|
//
|
||||||
|
Color::Light,
|
||||||
|
Color::Dark,
|
||||||
|
Color::Light,
|
||||||
|
],
|
||||||
|
3,
|
||||||
|
1,
|
||||||
|
).module_dimensions(1, 1)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||||
let expected = [
|
let expected = [
|
||||||
255, 255, 255, 255, 255,
|
255, 255, 255, 255, 255,
|
||||||
255, 255, 0, 0, 255,
|
255, 255, 0, 0, 255,
|
||||||
|
@ -68,11 +82,12 @@ mod render_tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_render_rgba_unsized() {
|
fn test_render_rgba_unsized() {
|
||||||
let image = Renderer::<Rgba<u8>>::new(&[
|
let image =
|
||||||
Color::Light, Color::Dark,
|
Renderer::<Rgba<u8>>::new(&[Color::Light, Color::Dark, Color::Dark, Color::Dark], 2, 1)
|
||||||
Color::Dark, Color::Dark,
|
.module_dimensions(1, 1)
|
||||||
], 2, 1).module_dimensions(1, 1).build();
|
.build();
|
||||||
|
|
||||||
|
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||||
let expected: &[u8] = &[
|
let expected: &[u8] = &[
|
||||||
255,255,255,255, 255,255,255,255, 255,255,255,255, 255,255,255,255,
|
255,255,255,255, 255,255,255,255, 255,255,255,255, 255,255,255,255,
|
||||||
255,255,255,255, 255,255,255,255, 0, 0, 0,255, 255,255,255,255,
|
255,255,255,255, 255,255,255,255, 0, 0, 0,255, 255,255,255,255,
|
||||||
|
@ -85,11 +100,14 @@ mod render_tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_render_resized_min() {
|
fn test_render_resized_min() {
|
||||||
let image = Renderer::<Luma<u8>>::new(&[
|
let image = Renderer::<Luma<u8>>::new(
|
||||||
Color::Dark, Color::Light,
|
&[Color::Dark, Color::Light, Color::Light, Color::Dark],
|
||||||
Color::Light, Color::Dark,
|
2,
|
||||||
], 2, 1).min_dimensions(10, 10).build();
|
1,
|
||||||
|
).min_dimensions(10, 10)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||||
let expected: &[u8] = &[
|
let expected: &[u8] = &[
|
||||||
255,255,255, 255,255,255, 255,255,255, 255,255,255,
|
255,255,255, 255,255,255, 255,255,255, 255,255,255,
|
||||||
255,255,255, 255,255,255, 255,255,255, 255,255,255,
|
255,255,255, 255,255,255, 255,255,255, 255,255,255,
|
||||||
|
@ -114,11 +132,14 @@ mod render_tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_render_resized_max() {
|
fn test_render_resized_max() {
|
||||||
let image = Renderer::<Luma<u8>>::new(&[
|
let image = Renderer::<Luma<u8>>::new(
|
||||||
Color::Dark, Color::Light,
|
&[Color::Dark, Color::Light, Color::Light, Color::Dark],
|
||||||
Color::Light, Color::Dark,
|
2,
|
||||||
], 2, 1).max_dimensions(10, 5).build();
|
1,
|
||||||
|
).max_dimensions(10, 5)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||||
let expected: &[u8] = &[
|
let expected: &[u8] = &[
|
||||||
255,255, 255,255, 255,255, 255,255,
|
255,255, 255,255, 255,255, 255,255,
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
use std::cmp::max;
|
use std::cmp::max;
|
||||||
use types::Color;
|
use types::Color;
|
||||||
|
use cast::As;
|
||||||
|
|
||||||
pub mod image;
|
pub mod image;
|
||||||
pub mod string;
|
pub mod string;
|
||||||
|
@ -57,6 +58,7 @@ pub trait Canvas: Sized {
|
||||||
|
|
||||||
/// A QR code renderer. This is a builder type which converts a bool-vector into
|
/// A QR code renderer. This is a builder type which converts a bool-vector into
|
||||||
/// an image.
|
/// an image.
|
||||||
|
#[cfg_attr(feature = "cargo-clippy", allow(stutter))] // rust-lang-nursery/rust-clippy#2212 ಠ_ಠ
|
||||||
pub struct Renderer<'a, P: Pixel> {
|
pub struct Renderer<'a, P: Pixel> {
|
||||||
content: &'a [Color],
|
content: &'a [Color],
|
||||||
modules_count: u32, // <- we call it `modules_count` here to avoid ambiguity of `width`.
|
modules_count: u32, // <- we call it `modules_count` here to avoid ambiguity of `width`.
|
||||||
|
@ -74,7 +76,7 @@ impl<'a, P: Pixel> Renderer<'a, P> {
|
||||||
assert!(modules_count * modules_count == content.len());
|
assert!(modules_count * modules_count == content.len());
|
||||||
Renderer {
|
Renderer {
|
||||||
content,
|
content,
|
||||||
modules_count: modules_count as u32,
|
modules_count: modules_count.as_u32(),
|
||||||
quiet_zone,
|
quiet_zone,
|
||||||
module_size: P::default_unit_size(),
|
module_size: P::default_unit_size(),
|
||||||
dark_color: P::default_color(Color::Dark),
|
dark_color: P::default_color(Color::Dark),
|
||||||
|
@ -154,7 +156,8 @@ impl<'a, P: Pixel> Renderer<'a, P> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Renders the QR code into an image.
|
/// Renders the QR code into an image.
|
||||||
#[deprecated(since="0.4.0", note="renamed to `.build()` to de-emphasize the image connection")]
|
#[deprecated(since = "0.4.0",
|
||||||
|
note = "renamed to `.build()` to de-emphasize the image connection")]
|
||||||
pub fn to_image(&self) -> P::Image {
|
pub fn to_image(&self) -> P::Image {
|
||||||
self.build()
|
self.build()
|
||||||
}
|
}
|
||||||
|
@ -162,7 +165,11 @@ impl<'a, P: Pixel> Renderer<'a, P> {
|
||||||
/// Renders the QR code into an image.
|
/// Renders the QR code into an image.
|
||||||
pub fn build(&self) -> P::Image {
|
pub fn build(&self) -> P::Image {
|
||||||
let w = self.modules_count;
|
let w = self.modules_count;
|
||||||
let qz = if self.has_quiet_zone { self.quiet_zone } else { 0 };
|
let qz = if self.has_quiet_zone {
|
||||||
|
self.quiet_zone
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
let width = w + 2 * qz;
|
let width = w + 2 * qz;
|
||||||
|
|
||||||
let (mw, mh) = self.module_size;
|
let (mw, mh) = self.module_size;
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
//! String rendering support.
|
//! String rendering support.
|
||||||
|
|
||||||
use render::{Pixel, Canvas as RenderCanvas};
|
use render::{Canvas as RenderCanvas, Pixel};
|
||||||
use types::Color;
|
use types::Color;
|
||||||
|
use cast::As;
|
||||||
|
|
||||||
pub trait Element: Copy {
|
pub trait Element: Copy {
|
||||||
fn default_color(color: Color) -> Self;
|
fn default_color(color: Color) -> Self;
|
||||||
|
@ -11,7 +12,7 @@ pub trait Element: Copy {
|
||||||
|
|
||||||
impl Element for char {
|
impl Element for char {
|
||||||
fn default_color(color: Color) -> Self {
|
fn default_color(color: Color) -> Self {
|
||||||
color.select('█', ' ')
|
color.select('\u{2588}', ' ')
|
||||||
}
|
}
|
||||||
|
|
||||||
fn strlen(self) -> usize {
|
fn strlen(self) -> usize {
|
||||||
|
@ -25,7 +26,7 @@ impl Element for char {
|
||||||
|
|
||||||
impl<'a> Element for &'a str {
|
impl<'a> Element for &'a str {
|
||||||
fn default_color(color: Color) -> Self {
|
fn default_color(color: Color) -> Self {
|
||||||
color.select("█", " ")
|
color.select("\u{2588}", " ")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn strlen(self) -> usize {
|
fn strlen(self) -> usize {
|
||||||
|
@ -47,7 +48,7 @@ pub struct Canvas<P: Element> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: Element> Pixel for P {
|
impl<P: Element> Pixel for P {
|
||||||
type Canvas = Canvas<P>;
|
type Canvas = Canvas<Self>;
|
||||||
type Image = String;
|
type Image = String;
|
||||||
|
|
||||||
fn default_unit_size() -> (u32, u32) {
|
fn default_unit_size() -> (u32, u32) {
|
||||||
|
@ -64,29 +65,29 @@ impl<P: Element> RenderCanvas for Canvas<P> {
|
||||||
type Image = String;
|
type Image = String;
|
||||||
|
|
||||||
fn new(width: u32, height: u32, dark_pixel: P, light_pixel: P) -> Self {
|
fn new(width: u32, height: u32, dark_pixel: P, light_pixel: P) -> Self {
|
||||||
let width = width as usize;
|
let width = width.as_usize();
|
||||||
let height = height as isize;
|
let height = height.as_isize();
|
||||||
let dark_cap = dark_pixel.strlen() as isize;
|
let dark_cap = dark_pixel.strlen().as_isize();
|
||||||
let light_cap = light_pixel.strlen() as isize;
|
let light_cap = light_pixel.strlen().as_isize();
|
||||||
Canvas {
|
Self {
|
||||||
buffer: vec![light_pixel; width * (height as usize)],
|
buffer: vec![light_pixel; width * height.as_usize()],
|
||||||
width,
|
width,
|
||||||
dark_pixel,
|
dark_pixel,
|
||||||
dark_cap_inc: dark_cap - light_cap,
|
dark_cap_inc: dark_cap - light_cap,
|
||||||
capacity: light_cap * (width as isize) * height + (height - 1),
|
capacity: light_cap * width.as_isize() * height + (height - 1),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_dark_pixel(&mut self, x: u32, y: u32) {
|
fn draw_dark_pixel(&mut self, x: u32, y: u32) {
|
||||||
let x = x as usize;
|
let x = x.as_usize();
|
||||||
let y = y as usize;
|
let y = y.as_usize();
|
||||||
self.capacity += self.dark_cap_inc;
|
self.capacity += self.dark_cap_inc;
|
||||||
self.buffer[x + y * self.width] = self.dark_pixel;
|
self.buffer[x + y * self.width] = self.dark_pixel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn into_image(self) -> String {
|
fn into_image(self) -> String {
|
||||||
let mut result = String::with_capacity(self.capacity as usize);
|
let mut result = String::with_capacity(self.capacity.as_usize());
|
||||||
for (i, pixel) in self.buffer.into_iter().enumerate() {
|
for (i, pixel) in self.buffer.into_iter().enumerate() {
|
||||||
if i != 0 && i % self.width == 0 {
|
if i != 0 && i % self.width == 0 {
|
||||||
result.push('\n');
|
result.push('\n');
|
||||||
|
@ -101,12 +102,9 @@ impl<P: Element> RenderCanvas for Canvas<P> {
|
||||||
fn test_render_to_string() {
|
fn test_render_to_string() {
|
||||||
use render::Renderer;
|
use render::Renderer;
|
||||||
|
|
||||||
let colors = &[
|
let colors = &[Color::Dark, Color::Light, Color::Light, Color::Dark];
|
||||||
Color::Dark, Color::Light,
|
|
||||||
Color::Light, Color::Dark,
|
|
||||||
];
|
|
||||||
let image: String = Renderer::<char>::new(colors, 2, 1).build();
|
let image: String = Renderer::<char>::new(colors, 2, 1).build();
|
||||||
assert_eq!(&image, " \n █ \n █ \n ");
|
assert_eq!(&image, " \n \u{2588} \n \u{2588} \n ");
|
||||||
|
|
||||||
let image2 = Renderer::new(colors, 2, 1)
|
let image2 = Renderer::new(colors, 2, 1)
|
||||||
.light_color("A")
|
.light_color("A")
|
||||||
|
@ -114,13 +112,15 @@ fn test_render_to_string() {
|
||||||
.module_dimensions(2, 2)
|
.module_dimensions(2, 2)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
assert_eq!(&image2, "\
|
assert_eq!(
|
||||||
AAAAAAAA\n\
|
&image2,
|
||||||
|
"AAAAAAAA\n\
|
||||||
AAAAAAAA\n\
|
AAAAAAAA\n\
|
||||||
AA!B!!B!AAAA\n\
|
AA!B!!B!AAAA\n\
|
||||||
AA!B!!B!AAAA\n\
|
AA!B!!B!AAAA\n\
|
||||||
AAAA!B!!B!AA\n\
|
AAAA!B!!B!AA\n\
|
||||||
AAAA!B!!B!AA\n\
|
AAAA!B!!B!AA\n\
|
||||||
AAAAAAAA\n\
|
AAAAAAAA\n\
|
||||||
AAAAAAAA");
|
AAAAAAAA"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use render::{Pixel, Canvas as RenderCanvas};
|
use render::{Canvas as RenderCanvas, Pixel};
|
||||||
use types::Color as ModuleColor;
|
use types::Color as ModuleColor;
|
||||||
|
|
||||||
/// An SVG color.
|
/// An SVG color.
|
||||||
|
@ -48,8 +48,17 @@ impl<'a> RenderCanvas for Canvas<'a> {
|
||||||
fn new(width: u32, height: u32, dark_pixel: Color<'a>, light_pixel: Color<'a>) -> Self {
|
fn new(width: u32, height: u32, dark_pixel: Color<'a>, light_pixel: Color<'a>) -> Self {
|
||||||
Canvas {
|
Canvas {
|
||||||
svg: format!(
|
svg: format!(
|
||||||
r#"<?xml version="1.0" standalone="yes"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="{w}" height="{h}" shape-rendering="crispEdges"><rect x="0" y="0" width="{w}" height="{h}" fill="{bg}"/><path fill="{fg}" d=""#,
|
concat!(
|
||||||
w=width, h=height, fg=dark_pixel.0, bg=light_pixel.0
|
r#"<?xml version="1.0" standalone="yes"?>"#,
|
||||||
|
r#"<svg xmlns="http://www.w3.org/2000/svg""#,
|
||||||
|
r#" version="1.1" width="{w}" height="{h}" shape-rendering="crispEdges">"#,
|
||||||
|
r#"<rect x="0" y="0" width="{w}" height="{h}" fill="{bg}"/>"#,
|
||||||
|
r#"<path fill="{fg}" d=""#,
|
||||||
|
),
|
||||||
|
w = width,
|
||||||
|
h = height,
|
||||||
|
fg = dark_pixel.0,
|
||||||
|
bg = light_pixel.0
|
||||||
),
|
),
|
||||||
marker: PhantomData,
|
marker: PhantomData,
|
||||||
}
|
}
|
||||||
|
@ -60,10 +69,12 @@ impl<'a> RenderCanvas for Canvas<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_dark_rect(&mut self, left: u32, top: u32, width: u32, height: u32) {
|
fn draw_dark_rect(&mut self, left: u32, top: u32, width: u32, height: u32) {
|
||||||
write!(self.svg, "M{l} {t}h{w}v{h}H{l}V{t}", l=left, t=top, w=width, h=height).unwrap();
|
write!(self.svg, "M{l} {t}h{w}v{h}H{l}V{t}", l = left, t = top, w = width, h = height)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn into_image(self) -> String {
|
fn into_image(mut self) -> String {
|
||||||
self.svg + r#""/></svg>"#
|
self.svg.push_str(r#""/></svg>"#);
|
||||||
|
self.svg
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
58
src/types.rs
58
src/types.rs
|
@ -1,7 +1,8 @@
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
use std::cmp::{PartialOrd, Ordering};
|
use std::cmp::{Ordering, PartialOrd};
|
||||||
use std::fmt::{Display, Formatter, Error};
|
use std::fmt::{Display, Error, Formatter};
|
||||||
use std::ops::Not;
|
use std::ops::Not;
|
||||||
|
use cast::As;
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
//{{{ QrResult
|
//{{{ QrResult
|
||||||
|
@ -76,8 +77,8 @@ impl Color {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Not for Color {
|
impl Not for Color {
|
||||||
type Output = Color;
|
type Output = Self;
|
||||||
fn not(self) -> Color {
|
fn not(self) -> Self {
|
||||||
match self {
|
match self {
|
||||||
Color::Light => Color::Dark,
|
Color::Light => Color::Dark,
|
||||||
Color::Dark => Color::Light,
|
Color::Dark => Color::Light,
|
||||||
|
@ -146,26 +147,28 @@ impl Version {
|
||||||
/// If the entry compares equal to the default value of T, this method
|
/// If the entry compares equal to the default value of T, this method
|
||||||
/// returns `Err(QrError::InvalidVersion)`.
|
/// returns `Err(QrError::InvalidVersion)`.
|
||||||
pub fn fetch<T>(&self, ec_level: EcLevel, table: &[[T; 4]]) -> QrResult<T>
|
pub fn fetch<T>(&self, ec_level: EcLevel, table: &[[T; 4]]) -> QrResult<T>
|
||||||
where T: PartialEq + Default + Copy
|
where
|
||||||
|
T: PartialEq + Default + Copy,
|
||||||
{
|
{
|
||||||
match *self {
|
match *self {
|
||||||
Version::Normal(v @ 1...40) => Ok(table[v as usize - 1][ec_level as usize]),
|
Version::Normal(v @ 1...40) => {
|
||||||
|
return Ok(table[(v - 1).as_usize()][ec_level as usize]);
|
||||||
|
}
|
||||||
Version::Micro(v @ 1...4) => {
|
Version::Micro(v @ 1...4) => {
|
||||||
let obj = table[v as usize + 39][ec_level as usize];
|
let obj = table[(v + 39).as_usize()][ec_level as usize];
|
||||||
if obj != Default::default() {
|
if obj != T::default() {
|
||||||
Ok(obj)
|
return Ok(obj);
|
||||||
} else {
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
Err(QrError::InvalidVersion)
|
Err(QrError::InvalidVersion)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
_ => Err(QrError::InvalidVersion)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The number of bits needed to encode the mode indicator.
|
/// The number of bits needed to encode the mode indicator.
|
||||||
pub fn mode_bits_count(&self) -> usize {
|
pub fn mode_bits_count(&self) -> usize {
|
||||||
match *self {
|
match *self {
|
||||||
Version::Micro(a) => (a - 1) as usize,
|
Version::Micro(a) => (a - 1).as_usize(),
|
||||||
_ => 4,
|
_ => 4,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -213,7 +216,7 @@ impl Mode {
|
||||||
pub fn length_bits_count(&self, version: Version) -> usize {
|
pub fn length_bits_count(&self, version: Version) -> usize {
|
||||||
match version {
|
match version {
|
||||||
Version::Micro(a) => {
|
Version::Micro(a) => {
|
||||||
let a = a as usize;
|
let a = a.as_usize();
|
||||||
match *self {
|
match *self {
|
||||||
Mode::Numeric => 2 + a,
|
Mode::Numeric => 2 + a,
|
||||||
Mode::Alphanumeric | Mode::Byte => 1 + a,
|
Mode::Alphanumeric | Mode::Byte => 1 + a,
|
||||||
|
@ -223,8 +226,7 @@ impl Mode {
|
||||||
Version::Normal(1...9) => match *self {
|
Version::Normal(1...9) => match *self {
|
||||||
Mode::Numeric => 10,
|
Mode::Numeric => 10,
|
||||||
Mode::Alphanumeric => 9,
|
Mode::Alphanumeric => 9,
|
||||||
Mode::Byte => 8,
|
Mode::Byte | Mode::Kanji => 8,
|
||||||
Mode::Kanji => 8,
|
|
||||||
},
|
},
|
||||||
Version::Normal(10...26) => match *self {
|
Version::Normal(10...26) => match *self {
|
||||||
Mode::Numeric => 12,
|
Mode::Numeric => 12,
|
||||||
|
@ -268,7 +270,7 @@ impl Mode {
|
||||||
/// assert!(a <= c);
|
/// assert!(a <= c);
|
||||||
/// assert!(b <= c);
|
/// assert!(b <= c);
|
||||||
///
|
///
|
||||||
pub fn max(&self, other: Mode) -> Mode {
|
pub fn max(&self, other: Self) -> Self {
|
||||||
match self.partial_cmp(&other) {
|
match self.partial_cmp(&other) {
|
||||||
Some(Ordering::Less) | Some(Ordering::Equal) => other,
|
Some(Ordering::Less) | Some(Ordering::Equal) => other,
|
||||||
Some(Ordering::Greater) => *self,
|
Some(Ordering::Greater) => *self,
|
||||||
|
@ -280,15 +282,15 @@ impl Mode {
|
||||||
impl PartialOrd for Mode {
|
impl PartialOrd for Mode {
|
||||||
/// Defines a partial ordering between modes. If `a <= b`, then `b` contains
|
/// Defines a partial ordering between modes. If `a <= b`, then `b` contains
|
||||||
/// a superset of all characters supported by `a`.
|
/// a superset of all characters supported by `a`.
|
||||||
fn partial_cmp(&self, other: &Mode) -> Option<Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
match (*self, *other) {
|
match (*self, *other) {
|
||||||
(Mode::Numeric, Mode::Alphanumeric) => Some(Ordering::Less),
|
(Mode::Numeric, Mode::Alphanumeric) |
|
||||||
(Mode::Alphanumeric, Mode::Numeric) => Some(Ordering::Greater),
|
(Mode::Numeric, Mode::Byte) |
|
||||||
(Mode::Numeric, Mode::Byte) => Some(Ordering::Less),
|
(Mode::Alphanumeric, Mode::Byte) |
|
||||||
(Mode::Byte, Mode::Numeric) => Some(Ordering::Greater),
|
|
||||||
(Mode::Alphanumeric, Mode::Byte) => Some(Ordering::Less),
|
|
||||||
(Mode::Byte, Mode::Alphanumeric) => Some(Ordering::Greater),
|
|
||||||
(Mode::Kanji, Mode::Byte) => Some(Ordering::Less),
|
(Mode::Kanji, Mode::Byte) => Some(Ordering::Less),
|
||||||
|
(Mode::Alphanumeric, Mode::Numeric) |
|
||||||
|
(Mode::Byte, Mode::Numeric) |
|
||||||
|
(Mode::Byte, Mode::Alphanumeric) |
|
||||||
(Mode::Byte, Mode::Kanji) => Some(Ordering::Greater),
|
(Mode::Byte, Mode::Kanji) => Some(Ordering::Greater),
|
||||||
(a, b) if a == b => Some(Ordering::Equal),
|
(a, b) if a == b => Some(Ordering::Equal),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
@ -298,7 +300,7 @@ impl PartialOrd for Mode {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod mode_tests {
|
mod mode_tests {
|
||||||
use types::Mode::{Numeric, Alphanumeric, Byte, Kanji};
|
use types::Mode::{Alphanumeric, Byte, Kanji, Numeric};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_mode_order() {
|
fn test_mode_order() {
|
||||||
|
@ -321,5 +323,3 @@ mod mode_tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
//}}}
|
//}}}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue