use crate::{
common::{
reedsolomon::{
get_predefined_genericgf, GenericGFRef, PredefinedGenericGF, ReedSolomonDecoder,
},
BitMatrix, CharacterSet, DecoderRXingResult, DetectorRXingResult, Eci, Result,
},
exceptions::Exceptions,
};
use super::aztec_detector_result::AztecDetectorRXingResult;
#[derive(PartialEq, Eq, Copy, Clone)]
enum Table {
Upper,
Lower,
Mixed,
Digit,
Punct,
Binary,
}
const UPPER_TABLE: [&str; 32] = [
"CTRL_PS", " ", "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", "CTRL_LL", "CTRL_ML", "CTRL_DL", "CTRL_BS",
];
const LOWER_TABLE: [&str; 32] = [
"CTRL_PS", " ", "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", "CTRL_US", "CTRL_ML", "CTRL_DL", "CTRL_BS",
];
const MIXED_TABLE: [&str; 32] = [
"CTRL_PS", " ", "\u{1}", "\u{2}", "\u{3}", "\u{4}", "\u{5}", "\u{6}", "\u{7}", "\u{8}", "\t",
"\n", "\u{000b}", "\u{000c}", "\r", "\u{001b}", "\u{001c}", "\u{001d}", "\u{001e}", "\u{001f}",
"@", "\\", "^", "_", "`", "|", "~", "\u{007f}", "CTRL_LL", "CTRL_UL", "CTRL_PL", "CTRL_BS",
];
const PUNCT_TABLE: [&str; 32] = [
"FLG(n)", "\r", "\r\n", ". ", ", ", ": ", "!", "\"", "#", "$", "%", "&", "'", "(", ")", "*",
"+", ",", "-", ".", "/", ":", ";", "<", "=", ">", "?", "[", "]", "{", "}", "CTRL_UL",
];
const DIGIT_TABLE: [&str; 16] = [
"CTRL_PS", " ", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ",", ".", "CTRL_UL",
"CTRL_US",
];
pub fn decode(detectorRXingResult: &AztecDetectorRXingResult) -> Result<DecoderRXingResult> {
let matrix = detectorRXingResult.getBits();
let rawbits = extract_bits(detectorRXingResult, matrix);
let corrected_bits = correct_bits(detectorRXingResult, &rawbits)?;
let raw_bytes = convertBoolArrayToByteArray(&corrected_bits.correct_bits);
let result = get_encoded_data(&corrected_bits.correct_bits);
let mut decoder_rxing_result = DecoderRXingResult::new(
raw_bytes,
result?,
Vec::new(),
format!("{}%", corrected_bits.ec_level),
);
decoder_rxing_result.setNumBits(corrected_bits.correct_bits.len());
Ok(decoder_rxing_result)
}
pub fn highLevelDecode(correctedBits: &[bool]) -> Result<String> {
get_encoded_data(correctedBits)
}
fn get_encoded_data(corrected_bits: &[bool]) -> Result<String> {
let end_index = corrected_bits.len();
let mut latch_table = Table::Upper; let mut shift_table = Table::Upper;
let mut result = String::with_capacity((corrected_bits.len() - 5) / 4);
let mut decoded_bytes: Vec<u8> = Vec::new();
let mut encdr: CharacterSet = CharacterSet::ISO8859_1;
let mut index = 0;
'main: while index < end_index {
if shift_table == Table::Binary {
if end_index - index < 5 {
break;
}
let mut length = read_code(corrected_bits, index, 5);
index += 5;
if length == 0 {
if end_index - index < 11 {
break;
}
length = read_code(corrected_bits, index, 11) + 31;
index += 11;
}
for _char_count in 0..length {
if end_index - index < 8 {
break 'main;
}
let code = read_code(corrected_bits, index, 8);
decoded_bytes.push(code as u8);
index += 8;
}
shift_table = latch_table;
} else {
let size = if shift_table == Table::Digit { 4 } else { 5 };
if end_index - index < size {
break;
}
let code = read_code(corrected_bits, index, size);
index += size;
let str = get_character(shift_table, code)?;
if "FLG(n)" == str {
if end_index - index < 3 {
break;
}
let mut n = read_code(corrected_bits, index, 3);
index += 3;
result.push_str(&encdr.decode(&decoded_bytes)?);
decoded_bytes.clear();
match n {
0 => result.push(29 as char), 7 => return Err(Exceptions::format_with("FLG(7) is reserved and illegal")), _ => {
let mut eci = 0;
if end_index - index < 4 * (n as usize) {
break;
}
while n > 0 {
let next_digit = read_code(corrected_bits, index, 4);
index += 4;
if !(2..=11).contains(&next_digit) {
return Err(Exceptions::format_with("Not a decimal digit"));
}
eci = eci * 10 + (next_digit - 2);
n -= 1;
}
let charset_eci: Eci = eci.into();
if charset_eci == Eci::Unknown {
return Err(Exceptions::format_with("Charset must exist"));
}
encdr = charset_eci.into();
}
}
shift_table = latch_table;
} else if str.starts_with("CTRL_") {
latch_table = shift_table; shift_table = getTable(str.chars().nth(5).ok_or(Exceptions::INDEX_OUT_OF_BOUNDS)?);
if str.chars().nth(6).ok_or(Exceptions::INDEX_OUT_OF_BOUNDS)? == 'L' {
latch_table = shift_table;
}
} else {
let b = str.as_bytes();
for bt in b {
decoded_bytes.push(*bt);
}
shift_table = latch_table;
}
}
}
if let Ok(str) = encdr.decode(&decoded_bytes) {
result.push_str(&str);
} else {
return Err(Exceptions::illegal_state_with("bad encoding"));
}
Ok(result)
}
fn getTable(t: char) -> Table {
match t {
'L' => Table::Lower,
'P' => Table::Punct,
'M' => Table::Mixed,
'D' => Table::Digit,
'B' => Table::Binary,
_ => Table::Upper,
}
}
fn get_character(table: Table, code: u32) -> Result<&'static str> {
match table {
Table::Upper => Ok(UPPER_TABLE[code as usize]),
Table::Lower => Ok(LOWER_TABLE[code as usize]),
Table::Mixed => Ok(MIXED_TABLE[code as usize]),
Table::Digit => Ok(DIGIT_TABLE[code as usize]),
Table::Punct => Ok(PUNCT_TABLE[code as usize]),
_ => Err(Exceptions::illegal_state_with("Bad table")),
}
}
struct CorrectedBitsRXingResult {
correct_bits: Vec<bool>,
ec_level: u32,
}
impl CorrectedBitsRXingResult {
pub fn new(correct_bits: Vec<bool>, ec_level: u32) -> Self {
Self {
correct_bits,
ec_level,
}
}
}
fn correct_bits(
ddata: &AztecDetectorRXingResult,
rawbits: &[bool],
) -> Result<CorrectedBitsRXingResult> {
let gf: GenericGFRef;
let codeword_size;
if ddata.getNbLayers() <= 2 {
codeword_size = 6;
gf = get_predefined_genericgf(PredefinedGenericGF::AztecData6); } else if ddata.getNbLayers() <= 8 {
codeword_size = 8;
gf = get_predefined_genericgf(PredefinedGenericGF::AztecData8); } else if ddata.getNbLayers() <= 22 {
codeword_size = 10;
gf = get_predefined_genericgf(PredefinedGenericGF::AztecData10); } else {
codeword_size = 12;
gf = get_predefined_genericgf(PredefinedGenericGF::AztecData12); }
let num_data_codewords = ddata.getNbDatablocks();
let num_codewords = rawbits.len() / codeword_size;
if num_codewords < num_data_codewords as usize {
return Err(Exceptions::format_with(format!(
"numCodewords {num_codewords}< numDataCodewords{num_data_codewords}"
)));
}
let mut offset = rawbits.len() % codeword_size;
let mut data_words = vec![0; num_codewords];
for word in data_words.iter_mut().take(num_codewords) {
*word = read_code(rawbits, offset, codeword_size) as i32;
offset += codeword_size;
}
let rs_decoder = ReedSolomonDecoder::new(gf);
rs_decoder.decode(
&mut data_words,
(num_codewords - num_data_codewords as usize) as i32,
)?;
let mask = (1 << codeword_size) - 1;
let mut stuffed_bits = 0;
for data_word in data_words.iter().take(num_data_codewords as usize) {
if data_word == &0 || data_word == &mask {
return Err(Exceptions::FORMAT);
} else if data_word == &1 || data_word == &(mask - 1) {
stuffed_bits += 1;
}
}
let mut corrected_bits =
vec![false; (num_data_codewords * codeword_size as u32 - stuffed_bits) as usize];
let mut index = 0;
for data_word in data_words.iter().take(num_data_codewords as usize) {
if *data_word == 1 || *data_word == mask - 1 {
corrected_bits.splice(
index..index + codeword_size - 1,
vec![*data_word > 1; codeword_size - 1],
);
index += codeword_size - 1;
} else {
for bit in (0..codeword_size).rev() {
corrected_bits[index] = (*data_word & (1 << bit)) != 0;
index += 1;
}
}
}
Ok(CorrectedBitsRXingResult::new(
corrected_bits,
(100 * (num_codewords - num_data_codewords as usize) / num_codewords) as u32,
))
}
fn extract_bits(ddata: &AztecDetectorRXingResult, matrix: &BitMatrix) -> Vec<bool> {
let compact = ddata.isCompact();
let layers = ddata.getNbLayers();
let base_matrix_size = ((if compact { 11 } else { 14 }) + layers * 4) as usize; let mut alignment_map = vec![0u32; base_matrix_size];
let mut rawbits = vec![false; total_bits_in_layer(layers as usize, compact)];
if compact {
for (i, am) in alignment_map.iter_mut().enumerate() {
*am = i as u32;
}
} else {
let matrix_size = base_matrix_size + 1 + 2 * ((base_matrix_size / 2 - 1) / 15);
let orig_center = base_matrix_size / 2;
let center = matrix_size / 2;
for i in 0..orig_center {
let new_offset = i + i / 15;
alignment_map[orig_center - i - 1] = (center - new_offset - 1) as u32;
alignment_map[orig_center + i] = (center + new_offset + 1) as u32;
}
}
let mut row_offset = 0;
for i in 0..layers {
let row_size = (layers - i) * 4 + (if compact { 9 } else { 12 });
let low = i * 2;
let high = base_matrix_size as u32 - 1 - low;
for j in 0..row_size {
let column_offset = j * 2;
for k in 0..2 {
rawbits[(row_offset + column_offset + k) as usize] = matrix.get(
alignment_map[(low + k) as usize],
alignment_map[(low + j) as usize],
);
rawbits[(row_offset + 2 * row_size + column_offset + k) as usize] = matrix.get(
alignment_map[(low + j) as usize],
alignment_map[(high - k) as usize],
);
rawbits[(row_offset + 4 * row_size + column_offset + k) as usize] = matrix.get(
alignment_map[(high - k) as usize],
alignment_map[(high - j) as usize],
);
rawbits[(row_offset + 6 * row_size + column_offset + k) as usize] = matrix.get(
alignment_map[(high - j) as usize],
alignment_map[(low + k) as usize],
);
}
}
row_offset += row_size * 8;
}
rawbits
}
fn read_code(rawbits: &[bool], start_index: usize, length: usize) -> u32 {
let mut res = 0;
for bit in rawbits.iter().skip(start_index).take(length) {
res <<= 1;
if *bit {
res |= 0x01;
}
}
res
}
fn read_byte(rawbits: &[bool], start_index: usize) -> u8 {
let n = rawbits.len() - start_index;
if n >= 8 {
return read_code(rawbits, start_index, 8) as u8;
}
(read_code(rawbits, start_index, n) << (8 - n)) as u8
}
pub fn convertBoolArrayToByteArray(bool_arr: &[bool]) -> Vec<u8> {
let mut byte_arr = vec![0u8; (bool_arr.len() + 7) / 8];
for (i, byte) in byte_arr.iter_mut().enumerate() {
*byte = read_byte(bool_arr, 8 * i);
}
byte_arr
}
fn total_bits_in_layer(layers: usize, compact: bool) -> usize {
(if compact { 88 } else { 112 } + 16 * layers) * layers
}