Update doc. Refactor bits API. De-experimentalize everything.

This commit is contained in:
kennytm 2014-08-19 04:18:30 +08:00
parent 072cadec95
commit 6b4d8aa366
5 changed files with 117 additions and 70 deletions

View file

@ -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, mut segments_iter: I) -> QrResult<()> {
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),
}
} }
} }

View file

@ -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)],

View file

@ -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, ///
/// Use this method only if there are very special need to manipulate the
/// raw bits before encoding. Some examples are:
///
/// * Encode data using specific character set with ECI
/// * Use the FNC1 modes
/// * Avoid the optimal segmentation algorithm
///
/// See the `Bits` structure for detail.
///
/// #![allow(unused_must_use)];
///
/// use qrcode::{QrCode, Version, 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> { ec_level: ErrorCorrectionLevel) -> QrResult<QrCode> {
let (encoded_data, ec_data) = try!(ec::construct_codewords(data, version, ec_level)); 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());

View file

@ -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)
} }

View file

@ -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),
} }