Continued adding functionality to the incomplete Rust language implementation.

pull/16/head
Project Nayuki 7 years ago
parent 358fa7af57
commit 1950574bda

@ -52,6 +52,82 @@ pub struct QrCode {
impl QrCode {
pub fn encode_text(text: &[char], ecl: &'static QrCodeEcc) -> QrCode {
let segs: Vec<QrSegment> = QrSegment::make_segments(text);
QrCode::encode_segments(&segs, ecl)
}
pub fn encode_binary(data: &[u8], ecl: &'static QrCodeEcc) -> QrCode {
let segs: Vec<QrSegment> = vec![QrSegment::make_bytes(data)];
QrCode::encode_segments(&segs, ecl)
}
pub fn encode_segments(segs: &[QrSegment], ecl: &'static QrCodeEcc) -> QrCode {
QrCode::encode_segments_advanced(segs, ecl, 1, 40, -1, true)
}
pub fn encode_segments_advanced(segs: &[QrSegment], mut ecl: &'static QrCodeEcc,
minversion: u8, maxversion: u8, mask: i8, boostecl: bool) -> QrCode {
assert!(1 <= minversion && minversion <= maxversion && maxversion <= 40 && -1 <= mask && mask <= 7, "Invalid value");
// Find the minimal version number to use
let mut version: u8 = minversion;
let mut datausedbits: usize;
loop {
let datacapacitybits: usize = QrCode::get_num_data_codewords(version, ecl) * 8; // Number of data bits available
if let Some(n) = QrSegment::get_total_bits(segs, version) {
if n <= datacapacitybits {
datausedbits = n;
break; // This version number is found to be suitable
}
}
if version >= maxversion { // All versions in the range could not fit the given data
panic!("Data too long");
}
version += 1;
}
// Increase the error correction level while the data still fits in the current version number
if boostecl {
if datausedbits <= QrCode::get_num_data_codewords(version, &QrCodeEcc_MEDIUM ) * 8 { ecl = &QrCodeEcc_MEDIUM ; }
if datausedbits <= QrCode::get_num_data_codewords(version, &QrCodeEcc_QUARTILE) * 8 { ecl = &QrCodeEcc_QUARTILE; }
if datausedbits <= QrCode::get_num_data_codewords(version, &QrCodeEcc_HIGH ) * 8 { ecl = &QrCodeEcc_HIGH ; }
}
// Create the data bit string by concatenating all segments
let datacapacitybits: usize = QrCode::get_num_data_codewords(version, ecl) * 8;
let mut bb: Vec<bool> = Vec::new();
for seg in segs {
append_bits(&mut bb, seg.mode.modebits as u32, 4);
append_bits(&mut bb, seg.numchars as u32, seg.mode.num_char_count_bits(version));
bb.extend_from_slice(&seg.data);
}
// Add terminator and pad up to a byte if applicable
let numzerobits = std::cmp::min(4, datacapacitybits - bb.len()) + (bb.len().wrapping_neg() & 7);
append_bits(&mut bb, 0, numzerobits as u8);
// Pad with alternate bytes until data capacity is reached
let mut padbyte: u32 = 0xEC;
while bb.len() < datacapacitybits {
append_bits(&mut bb, padbyte, 8);
padbyte ^= 0xEC ^ 0x11;
}
assert_eq!(bb.len() % 8, 0, "Assertion error");
let mut bytes: Vec<u8> = vec![0; (bb.len() + 7) / 8];
for (i, bit) in bb.iter().enumerate() {
bytes[i >> 3] |= (*bit as u8) << (7 - (i & 7));
}
// Create the QR Code symbol
QrCode::encode_codewords(version, ecl, &bytes, mask)
}
pub fn encode_codewords(ver: u8, ecl: &'static QrCodeEcc, datacodewords: &[u8], mask: i8) -> QrCode {
// Check arguments
assert!(1 <= ver && ver <= 40 && -1 <= mask && mask <= 7, "Value out of range");
@ -682,6 +758,8 @@ pub struct QrSegment {
impl QrSegment {
/*---- Static factory functions ----*/
pub fn make_bytes(data: &[u8]) -> QrSegment {
let mut bb: Vec<bool> = Vec::with_capacity(data.len() * 8);
for b in data {
@ -696,9 +774,129 @@ impl QrSegment {
}
}
pub fn make_numeric(text: &[char]) -> QrSegment {
let mut bb: Vec<bool> = Vec::with_capacity(text.len() * 3 + (text.len() + 2) / 3);
let mut accumdata: u32 = 0;
let mut accumcount: u32 = 0;
for c in text {
assert!('0' <= *c && *c <= '9', "String contains non-numeric characters");
accumdata = accumdata * 10 + ((*c as u32) - ('0' as u32));
accumcount += 1;
if accumcount == 3 {
append_bits(&mut bb, accumdata, 10);
accumdata = 0;
accumcount = 0;
}
}
if accumcount > 0 { // 1 or 2 digits remaining
append_bits(&mut bb, accumdata, (accumcount as u8) * 3 + 1);
}
QrSegment {
mode: &QrSegmentMode_NUMERIC,
numchars: text.len(),
data: bb,
}
}
pub fn make_alphanumeric(text: &[char]) -> QrSegment {
let mut bb: Vec<bool> = Vec::with_capacity(text.len() * 5 + (text.len() + 1) / 2);
let mut accumdata: u32 = 0;
let mut accumcount: u32 = 0;
for c in text {
let i = match QrSegment_ALPHANUMERIC_CHARSET.iter().position(|x| *x == *c) {
None => panic!("String contains unencodable characters in alphanumeric mode"),
Some(j) => j,
};
accumdata = accumdata * 45 + (i as u32);
accumcount += 1;
if accumcount == 2 {
append_bits(&mut bb, accumdata, 11);
accumdata = 0;
accumcount = 0;
}
}
if accumcount > 0 { // 1 character remaining
append_bits(&mut bb, accumdata, 6);
}
QrSegment {
mode: &QrSegmentMode_ALPHANUMERIC,
numchars: text.len(),
data: bb,
}
}
pub fn make_segments(text: &[char]) -> Vec<QrSegment> {
if text.is_empty() {
vec![]
} else if QrSegment::is_numeric(text) {
vec![QrSegment::make_numeric(text)]
} else if QrSegment::is_alphanumeric(text) {
vec![QrSegment::make_alphanumeric(text)]
} else {
let s: String = text.iter().cloned().collect();
vec![QrSegment::make_bytes(s.as_bytes())]
}
}
pub fn make_eci(assignval: u32) -> QrSegment {
let mut bb: Vec<bool> = Vec::with_capacity(24);
if assignval < (1 << 7) {
append_bits(&mut bb, assignval, 8);
} else if assignval < (1 << 14) {
append_bits(&mut bb, 2, 2);
append_bits(&mut bb, assignval, 14);
} else if assignval < 1_000_000 {
append_bits(&mut bb, 6, 3);
append_bits(&mut bb, assignval, 21);
} else {
panic!("ECI assignment value out of range");
}
QrSegment {
mode: &QrSegmentMode_ECI,
numchars: 0,
data: bb,
}
}
fn get_total_bits(segs: &[QrSegment], version: u8) -> Option<usize> {
assert!(1 <= version && version <= 40, "Version number out of range");
let mut result: usize = 0;
for seg in segs {
let ccbits = seg.mode.num_char_count_bits(version);
if seg.numchars >= 1 << ccbits {
return None;
}
match result.checked_add(4 + (ccbits as usize) + seg.data.len()) {
None => return None,
Some(val) => result = val,
}
}
Some(result)
}
fn is_alphanumeric(text: &[char]) -> bool {
text.iter().all(|c| QrSegment_ALPHANUMERIC_CHARSET.contains(c))
}
fn is_numeric(text: &[char]) -> bool {
text.iter().all(|c| '0' <= *c && *c <= '9')
}
}
static QrSegment_ALPHANUMERIC_CHARSET: [char; 45] = ['0','1','2','3','4','5','6','7','8','9',
'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
' ','$','%','*','+','-','.','/',':'];
/*---- QrSegmentMode functionality ----*/

Loading…
Cancel
Save