Update doc. Refactor bits API. De-experimentalize everything.
This commit is contained in:
parent
072cadec95
commit
6b4d8aa366
75
src/bits.rs
75
src/bits.rs
|
@ -97,6 +97,11 @@ impl Bits {
|
||||||
(self.data.len() - 1) * 8 + self.bit_offset
|
(self.data.len() - 1) * 8 + self.bit_offset
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Version of the QR code.
|
||||||
|
pub fn version(&self) -> Version {
|
||||||
|
self.version
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -227,7 +232,6 @@ impl Bits {
|
||||||
///
|
///
|
||||||
/// If the designator is outside of the expected range, this method will
|
/// If the designator is outside of the expected range, this method will
|
||||||
/// return `Err(InvalidECIDesignator)`.
|
/// return `Err(InvalidECIDesignator)`.
|
||||||
#[experimental]
|
|
||||||
pub fn push_eci_designator(&mut self, eci_designator: u32) -> QrResult<()> {
|
pub fn push_eci_designator(&mut self, eci_designator: u32) -> QrResult<()> {
|
||||||
self.reserve_additional(12); // assume the common case that eci_designator <= 127.
|
self.reserve_additional(12); // assume the common case that eci_designator <= 127.
|
||||||
try!(self.push_mode_indicator(Eci));
|
try!(self.push_mode_indicator(Eci));
|
||||||
|
@ -568,7 +572,6 @@ impl Bits {
|
||||||
/// bits.push_alphanumeric_data(b"%10ABC123");
|
/// bits.push_alphanumeric_data(b"%10ABC123");
|
||||||
///
|
///
|
||||||
/// In QR code, the character `%` is used as the data field separator (0x1D).
|
/// In QR code, the character `%` is used as the data field separator (0x1D).
|
||||||
#[experimental]
|
|
||||||
pub fn push_fnc1_first_position(&mut self) -> QrResult<()> {
|
pub fn push_fnc1_first_position(&mut self) -> QrResult<()> {
|
||||||
self.push_mode_indicator(Fnc1First)
|
self.push_mode_indicator(Fnc1First)
|
||||||
}
|
}
|
||||||
|
@ -593,7 +596,6 @@ impl Bits {
|
||||||
/// ```ignore
|
/// ```ignore
|
||||||
/// bits.push_fnc1_second_position(b'A' + 100);
|
/// bits.push_fnc1_second_position(b'A' + 100);
|
||||||
/// ```
|
/// ```
|
||||||
#[experimental]
|
|
||||||
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(Fnc1Second));
|
try!(self.push_mode_indicator(Fnc1Second));
|
||||||
self.push_number(8, application_indicator as u16);
|
self.push_number(8, application_indicator as u16);
|
||||||
|
@ -755,13 +757,9 @@ 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.
|
||||||
#[experimental]
|
pub fn push_segments<I: Iterator<Segment>>(&mut self,
|
||||||
pub fn push_segments_terminated<I: Iterator<Segment>>(&mut self,
|
data: &[u8],
|
||||||
data: &[u8],
|
mut segments_iter: I) -> QrResult<()> {
|
||||||
mut segments_iter: I,
|
|
||||||
ec_level: ErrorCorrectionLevel,
|
|
||||||
data_length: uint) -> QrResult<()> {
|
|
||||||
self.data.reserve(data_length);
|
|
||||||
for segment in segments_iter {
|
for segment in segments_iter {
|
||||||
let slice = data.slice(segment.begin, segment.end);
|
let slice = data.slice(segment.begin, segment.end);
|
||||||
try!(match segment.mode {
|
try!(match segment.mode {
|
||||||
|
@ -771,26 +769,29 @@ impl Bits {
|
||||||
Kanji => self.push_kanji_data(slice),
|
Kanji => self.push_kanji_data(slice),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
try!(self.push_terminator(ec_level));
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Pushes the data the bits, using the optimal encoding.
|
||||||
|
pub fn push_optimal_data(&mut self, data: &[u8]) -> QrResult<()> {
|
||||||
|
let segments = Parser::new(data).optimize(self.version);
|
||||||
|
self.push_segments(data, segments)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Encodes a binary string to the raw QR code bits.
|
|
||||||
#[experimental]
|
|
||||||
pub fn encode(data: &[u8], version: Version, ec_level: ErrorCorrectionLevel) -> QrResult<Vec<u8>> {
|
|
||||||
let segments = Parser::new(data);
|
|
||||||
let opt_segments = segments.optimize(version);
|
|
||||||
let mut bits = Bits::new(version);
|
|
||||||
try!(bits.push_segments_terminated(data, opt_segments, ec_level, 0u));
|
|
||||||
Ok(bits.into_bytes())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod encode_tests {
|
mod encode_tests {
|
||||||
use bits::encode;
|
use bits::Bits;
|
||||||
use types::{Version, MicroVersion, DataTooLong, L, Q, H};
|
use types::{Version, MicroVersion, DataTooLong, QrResult,
|
||||||
|
L, Q, H, ErrorCorrectionLevel};
|
||||||
|
|
||||||
|
fn encode(data: &[u8], version: Version, ec_level: ErrorCorrectionLevel) -> QrResult<Vec<u8>> {
|
||||||
|
let mut bits = Bits::new(version);
|
||||||
|
try!(bits.push_optimal_data(data));
|
||||||
|
try!(bits.push_terminator(ec_level));
|
||||||
|
Ok(bits.into_bytes())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_alphanumeric() {
|
fn test_alphanumeric() {
|
||||||
|
@ -824,8 +825,7 @@ mod encode_tests {
|
||||||
/// the result.
|
/// the result.
|
||||||
///
|
///
|
||||||
/// This method will not consider any Micro QR code versions.
|
/// This method will not consider any Micro QR code versions.
|
||||||
#[experimental]
|
pub fn encode_auto(data: &[u8], ec_level: ErrorCorrectionLevel) -> QrResult<Bits> {
|
||||||
pub fn encode_auto(data: &[u8], ec_level: ErrorCorrectionLevel) -> QrResult<(Vec<u8>, Version)> {
|
|
||||||
let segments = Parser::new(data).collect::<Vec<Segment>>();
|
let segments = Parser::new(data).collect::<Vec<Segment>>();
|
||||||
for version in [Version(9), Version(26), Version(40)].iter() {
|
for version in [Version(9), Version(26), Version(40)].iter() {
|
||||||
let opt_segments = Optimizer::new(segments.iter().map(|s| *s), *version).collect::<Vec<Segment>>();
|
let opt_segments = Optimizer::new(segments.iter().map(|s| *s), *version).collect::<Vec<Segment>>();
|
||||||
|
@ -834,9 +834,10 @@ pub fn encode_auto(data: &[u8], ec_level: ErrorCorrectionLevel) -> QrResult<(Vec
|
||||||
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);
|
||||||
try!(bits.push_segments_terminated(data, opt_segments.move_iter(),
|
bits.reserve_additional(total_len);
|
||||||
ec_level, total_len));
|
try!(bits.push_segments(data, opt_segments.move_iter()));
|
||||||
return Ok((bits.into_bytes(), min_version));
|
try!(bits.push_terminator(ec_level));
|
||||||
|
return Ok(bits);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(DataTooLong)
|
Err(DataTooLong)
|
||||||
|
@ -878,26 +879,20 @@ mod encode_auto_tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_alpha_q() {
|
fn test_alpha_q() {
|
||||||
match encode_auto(b"HELLO WORLD", Q) {
|
let bits = encode_auto(b"HELLO WORLD", Q).unwrap();
|
||||||
Ok((_, Version(1))) => {},
|
assert_eq!(bits.version(), Version(1));
|
||||||
x => fail!("{}", x),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_alpha_h() {
|
fn test_alpha_h() {
|
||||||
match encode_auto(b"HELLO WORLD", H) {
|
let bits = encode_auto(b"HELLO WORLD", H).unwrap();
|
||||||
Ok((_, Version(2))) => {},
|
assert_eq!(bits.version(), Version(2));
|
||||||
x => fail!("{}", x),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_mixed() {
|
fn test_mixed() {
|
||||||
match encode_auto(b"This is a mixed data test. 1234567890", H) {
|
let bits = encode_auto(b"This is a mixed data test. 1234567890", H).unwrap();
|
||||||
Ok((_, Version(4))) => {},
|
assert_eq!(bits.version(), Version(4));
|
||||||
x => fail!("{}", x),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
15
src/ec.rs
15
src/ec.rs
|
@ -65,6 +65,17 @@ mod ec_tests {
|
||||||
|
|
||||||
// TODO Make Vec implements ImmutableVector, or &[T] implements Index.
|
// TODO Make Vec implements ImmutableVector, or &[T] implements Index.
|
||||||
trait InterleaveSupport<T> {
|
trait InterleaveSupport<T> {
|
||||||
|
/// Equivalent to
|
||||||
|
///
|
||||||
|
/// ```ignore
|
||||||
|
/// if i < self.len() {
|
||||||
|
/// v.push(self[i]);
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// This trait exists solely because `Vec<T>` does not implement
|
||||||
|
/// ImmutableVector, or `&[T]` does not implement Index. This trait can be
|
||||||
|
/// deprecated once either of above are implemented.
|
||||||
fn push_item_to(&self, i: uint, v: &mut Vec<T>);
|
fn push_item_to(&self, i: uint, v: &mut Vec<T>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -270,7 +281,7 @@ static GENERATOR_POLYNOMIALS: [&'static [u8], ..70] = [
|
||||||
/// correction per block in each version.
|
/// correction per block in each version.
|
||||||
///
|
///
|
||||||
/// This is a copy of ISO/IEC 18004:2006, §6.5.1, Table 9.
|
/// This is a copy of ISO/IEC 18004:2006, §6.5.1, Table 9.
|
||||||
pub static EC_BYTES_PER_BLOCK: [[uint, ..4], ..44] = [
|
static EC_BYTES_PER_BLOCK: [[uint, ..4], ..44] = [
|
||||||
// Normal versions.
|
// Normal versions.
|
||||||
[7, 10, 13, 17],
|
[7, 10, 13, 17],
|
||||||
[10, 16, 22, 28],
|
[10, 16, 22, 28],
|
||||||
|
@ -328,7 +339,7 @@ pub static EC_BYTES_PER_BLOCK: [[uint, ..4], ..44] = [
|
||||||
/// Every entry is a 4-tuple. Take `DATA_BYTES_PER_BLOCK[39][3] == (15, 20, 16, 61)`
|
/// Every entry is a 4-tuple. Take `DATA_BYTES_PER_BLOCK[39][3] == (15, 20, 16, 61)`
|
||||||
/// as an example, this means in version 40 with correction level H, there are
|
/// as an example, this means in version 40 with correction level H, there are
|
||||||
/// 20 blocks with 15 bytes in size, and 61 blocks with 16 bytes in size.
|
/// 20 blocks with 15 bytes in size, and 61 blocks with 16 bytes in size.
|
||||||
pub static DATA_BYTES_PER_BLOCK: [[(uint, uint, uint, uint), ..4], ..44] = [
|
static DATA_BYTES_PER_BLOCK: [[(uint, uint, uint, uint), ..4], ..44] = [
|
||||||
// Normal versions.
|
// Normal versions.
|
||||||
[(19, 1, 0, 0), (16, 1, 0, 0), (13, 1, 0, 0), (9, 1, 0, 0)],
|
[(19, 1, 0, 0), (16, 1, 0, 0), (13, 1, 0, 0), (9, 1, 0, 0)],
|
||||||
[(34, 1, 0, 0), (28, 1, 0, 0), (22, 1, 0, 0), (16, 1, 0, 0)],
|
[(34, 1, 0, 0), (28, 1, 0, 0), (22, 1, 0, 0), (16, 1, 0, 0)],
|
||||||
|
|
70
src/lib.rs
70
src/lib.rs
|
@ -44,34 +44,80 @@ impl QrCode {
|
||||||
/// Constructs a new QR code which automatically encodes the given data.
|
/// Constructs a new QR code which automatically encodes the given data.
|
||||||
///
|
///
|
||||||
/// This method uses the "medium" error correction level and automatically
|
/// This method uses the "medium" error correction level and automatically
|
||||||
/// chooses the smallest QR code. Please use `.with_error_correction()` and
|
/// chooses the smallest QR code.
|
||||||
/// `.with_version()` for finer adjustment.
|
///
|
||||||
|
/// use qrcode::QrCode;
|
||||||
|
///
|
||||||
|
/// let code = QrCode::new(b"Some data").unwrap();
|
||||||
|
///
|
||||||
pub fn new(data: &[u8]) -> QrResult<QrCode> {
|
pub fn new(data: &[u8]) -> QrResult<QrCode> {
|
||||||
QrCode::with_error_correction(data, M)
|
QrCode::with_error_correction_level(data, 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
|
||||||
/// specific error correction level.
|
/// specific error correction level.
|
||||||
///
|
///
|
||||||
/// This method automatically chooses the smallest QR code.
|
/// This method automatically chooses the smallest QR code.
|
||||||
pub fn with_error_correction(data: &[u8], ec_level: ErrorCorrectionLevel) -> QrResult<QrCode> {
|
///
|
||||||
let (encoded_data, version) = try!(bits::encode_auto(data, ec_level));
|
/// use qrcode::{QrCode, H};
|
||||||
QrCode::with_encoded_data(encoded_data.as_slice(), version, ec_level)
|
///
|
||||||
|
/// let code = QrCode::with_error_correction_level(b"Some data", H).unwrap();
|
||||||
|
///
|
||||||
|
pub fn with_error_correction_level(data: &[u8], ec_level: ErrorCorrectionLevel) -> QrResult<QrCode> {
|
||||||
|
let bits = try!(bits::encode_auto(data, ec_level));
|
||||||
|
QrCode::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
|
||||||
/// level.
|
/// level.
|
||||||
|
///
|
||||||
|
/// use qrcode::{QrCode, Version, M};
|
||||||
|
///
|
||||||
|
/// let code = QrCode::with_version(b"Some data", Version(5), M).unwrap();
|
||||||
|
///
|
||||||
|
/// This method can also be used to generate Micro QR code.
|
||||||
|
///
|
||||||
|
/// use qrcode::{QrCode, MicroVersion, L};
|
||||||
|
///
|
||||||
|
/// let micro_code = QrCode::with_version(b"123", MicroVersion(1), L).unwrap();
|
||||||
|
///
|
||||||
pub fn with_version(data: &[u8],
|
pub fn with_version(data: &[u8],
|
||||||
version: Version,
|
version: Version,
|
||||||
ec_level: ErrorCorrectionLevel) -> QrResult<QrCode> {
|
ec_level: ErrorCorrectionLevel) -> QrResult<QrCode> {
|
||||||
let encoded_data = try!(bits::encode(data, version, ec_level));
|
let mut bits = bits::Bits::new(version);
|
||||||
QrCode::with_encoded_data(encoded_data.as_slice(), version, ec_level)
|
try!(bits.push_optimal_data(data));
|
||||||
|
try!(bits.push_terminator(ec_level));
|
||||||
|
QrCode::with_bits(bits, ec_level)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_encoded_data(data: &[u8],
|
/// Constructs a new QR code with encoded bits.
|
||||||
version: Version,
|
///
|
||||||
ec_level: ErrorCorrectionLevel) -> QrResult<QrCode> {
|
/// Use this method only if there are very special need to manipulate the
|
||||||
let (encoded_data, ec_data) = try!(ec::construct_codewords(data, version, ec_level));
|
/// raw bits before encoding. Some examples are:
|
||||||
|
///
|
||||||
|
/// * Encode data using specific character set with ECI
|
||||||
|
/// * Use the FNC1 modes
|
||||||
|
/// * Avoid the optimal segmentation algorithm
|
||||||
|
///
|
||||||
|
/// See the `Bits` structure for detail.
|
||||||
|
///
|
||||||
|
/// #![allow(unused_must_use)];
|
||||||
|
///
|
||||||
|
/// use qrcode::{QrCode, Version, L};
|
||||||
|
/// use qrcode::bits::Bits;
|
||||||
|
///
|
||||||
|
/// let mut bits = Bits::new(Version(1));
|
||||||
|
/// bits.push_eci_designator(9);
|
||||||
|
/// bits.push_byte_data(b"\xca\xfe\xe4\xe9\xea\xe1\xf2 QR");
|
||||||
|
/// bits.push_terminator(L);
|
||||||
|
/// let qrcode = QrCode::with_bits(bits, L);
|
||||||
|
///
|
||||||
|
pub fn with_bits(bits: bits::Bits,
|
||||||
|
ec_level: ErrorCorrectionLevel) -> QrResult<QrCode> {
|
||||||
|
let version = bits.version();
|
||||||
|
let data = bits.into_bytes();
|
||||||
|
let (encoded_data, ec_data) = try!(ec::construct_codewords(data.as_slice(),
|
||||||
|
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.as_slice(), ec_data.as_slice());
|
canvas.draw_data(encoded_data.as_slice(), ec_data.as_slice());
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#![experimental]
|
#![unstable]
|
||||||
|
|
||||||
//! Find the optimal data mode sequence to encode a piece of data.
|
//! Find the optimal data mode sequence to encode a piece of data.
|
||||||
|
|
||||||
|
@ -233,17 +233,23 @@ pub struct Optimizer<I> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I: Iterator<Segment>> Optimizer<I> {
|
impl<I: Iterator<Segment>> Optimizer<I> {
|
||||||
pub fn new(mut parser: I, version: Version) -> Optimizer<I> {
|
/// Optimize the segments by combining adjacent segments when possible.
|
||||||
match parser.next() {
|
///
|
||||||
|
/// Currently this method uses a greedy algorithm by combining segments from
|
||||||
|
/// left to right until the new segment is longer than before. This method
|
||||||
|
/// does *not* use Annex J from the ISO standard.
|
||||||
|
///
|
||||||
|
pub fn new(mut segments: I, version: Version) -> Optimizer<I> {
|
||||||
|
match segments.next() {
|
||||||
None => Optimizer {
|
None => Optimizer {
|
||||||
parser: parser,
|
parser: segments,
|
||||||
last_segment: Segment { mode: Numeric, begin: 0, end: 0 },
|
last_segment: Segment { 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) => Optimizer {
|
||||||
parser: parser,
|
parser: segments,
|
||||||
last_segment: segment,
|
last_segment: segment,
|
||||||
last_segment_size: segment.encoded_len(version),
|
last_segment_size: segment.encoded_len(version),
|
||||||
version: version,
|
version: version,
|
||||||
|
@ -254,12 +260,6 @@ impl<I: Iterator<Segment>> Optimizer<I> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Parser<'a> {
|
impl<'a> Parser<'a> {
|
||||||
/// Optimize the segments by combining adjacent segments when possible.
|
|
||||||
///
|
|
||||||
/// Currently this method uses a greedy algorithm by combining segments from
|
|
||||||
/// left to right until the new segment is longer than before. This method
|
|
||||||
/// does *not* use Annex J from the ISO standard.
|
|
||||||
///
|
|
||||||
pub fn optimize(self, version: Version) -> Optimizer<Parser<'a>> {
|
pub fn optimize(self, version: Version) -> Optimizer<Parser<'a>> {
|
||||||
Optimizer::new(self, version)
|
Optimizer::new(self, version)
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,6 @@ pub enum QrError {
|
||||||
|
|
||||||
/// The provided ECI designator is invalid. A valid designator should be
|
/// The provided ECI designator is invalid. A valid designator should be
|
||||||
/// between 0 and 999999.
|
/// between 0 and 999999.
|
||||||
#[experimental]
|
|
||||||
InvalidEciDesignator,
|
InvalidEciDesignator,
|
||||||
|
|
||||||
/// A character not belonging to the character set is found.
|
/// A character not belonging to the character set is found.
|
||||||
|
@ -72,10 +71,6 @@ pub enum Version {
|
||||||
Version(i16),
|
Version(i16),
|
||||||
|
|
||||||
/// A Micro QR code version. The parameter should be between 1 and 4.
|
/// A Micro QR code version. The parameter should be between 1 and 4.
|
||||||
///
|
|
||||||
/// This crate currently does not fully support Micro QR code yet, and using
|
|
||||||
/// these versions will likely result in `Err(InvalidVersion)`.
|
|
||||||
#[experimental]
|
|
||||||
MicroVersion(i16),
|
MicroVersion(i16),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue