use super::Code16kMeta;
use super::encode::{ROW_WIDTH, checksum};
use super::tables::{
CODE_A, CODE_B, CODE_C, FNC1, PAD, SHIFT, START_STOP, START_VALUES, STOP_VALUES,
value_for_widths,
};
use crate::error::{Error, Result};
use crate::output::{BitMatrix, Encoding};
use crate::segment::Segment;
use crate::symbol::{Symbol, SymbolMeta};
use crate::symbology::Symbology;
use crate::traits::Decode;
#[derive(Debug, Default, Clone, Copy)]
pub struct Code16kDecoder;
impl Code16kDecoder {
pub fn new() -> Self {
Code16kDecoder
}
pub fn decode_matrix(&self, matrix: &BitMatrix) -> Result<Symbol> {
let rows = matrix.height();
if !(2..=16).contains(&rows) {
return Err(Error::undecodable("Code 16K row count out of range"));
}
if matrix.width() != ROW_WIDTH {
return Err(Error::undecodable("Code 16K row width is not 70 modules"));
}
let mut full = Vec::with_capacity(rows * 5);
for row in 0..rows {
let runs = row_runs(matrix, row)?;
if runs.len() != 39 {
return Err(Error::undecodable("Code 16K row has wrong element count"));
}
if runs[0..4] != START_STOP[START_VALUES[row]] {
return Err(Error::undecodable("Code 16K start delimiter mismatch"));
}
if runs[35..39] != START_STOP[STOP_VALUES[row]] {
return Err(Error::undecodable("Code 16K stop delimiter mismatch"));
}
if runs[4] != 1 {
return Err(Error::undecodable("Code 16K guard bar mismatch"));
}
for i in 0..5 {
let widths = &runs[5 + i * 6..5 + i * 6 + 6];
let v = value_for_widths(widths)
.ok_or_else(|| Error::undecodable("unknown Code 16K symbol pattern"))?;
full.push(v);
}
}
let check2 = full.pop().unwrap();
let check1 = full.pop().unwrap();
let (c1, c2) = checksum(&full);
if (c1, c2) != (check1, check2) {
return Err(Error::undecodable("Code 16K check character mismatch"));
}
let mode = full[0];
if mode / 7 + 2 != rows as u8 {
return Err(Error::undecodable("Code 16K mode/row-count mismatch"));
}
let meta = Code16kMeta { rows, values: full };
let segments = reconstruct_segments(&meta.values)?;
Ok(Symbol::new(
Symbology::Code16k,
segments,
SymbolMeta::Code16k(meta),
))
}
}
impl Decode for Code16kDecoder {
fn decode(&self, encoding: &Encoding) -> Result<Symbol> {
match encoding {
Encoding::Matrix(m) => self.decode_matrix(m),
Encoding::Linear(_) => Err(Error::Unsupported {
what: "Code 16K decode of a linear pattern",
}),
}
}
}
fn row_runs(matrix: &BitMatrix, row: usize) -> Result<[u8; 39]> {
if !matrix.get(0, row) {
return Err(Error::undecodable("Code 16K row must start with a bar"));
}
let mut runs = [0u8; 39];
let mut idx = 0usize;
let mut cur = true;
let mut count = 0u16;
for x in 0..ROW_WIDTH {
let m = matrix.get(x, row);
if m == cur {
count += 1;
} else {
if idx >= 39 {
return Err(Error::undecodable("Code 16K row has too many elements"));
}
runs[idx] = count as u8;
idx += 1;
cur = m;
count = 1;
}
}
if idx != 38 {
return Err(Error::undecodable("Code 16K row has too few elements"));
}
runs[38] = count as u8;
Ok(runs)
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum Set {
A,
B,
C,
}
pub(crate) fn reconstruct_segments(values: &[u8]) -> Result<Vec<Segment>> {
if values.is_empty() {
return Ok(Vec::new());
}
let m = values[0] % 7;
let mut set = match m {
0 => Set::A,
2 | 4 => Set::C,
_ => Set::B,
};
let mut bytes = Vec::new();
let mut shift: Option<Set> = None;
for &v in &values[1..] {
if v == PAD {
continue;
}
let active = shift.take().unwrap_or(set);
match active {
Set::C => match v {
0..=99 => {
bytes.push(b'0' + v / 10);
bytes.push(b'0' + v % 10);
}
CODE_B => set = Set::B,
CODE_A => set = Set::A,
FNC1 => {}
_ => return Err(Error::undecodable("invalid symbol in Code 16K set C")),
},
Set::A => match v {
0..=95 => bytes.push(if v < 64 { v + 32 } else { v - 64 }),
96 | 97 => {} SHIFT => shift = Some(Set::B),
CODE_C => set = Set::C,
CODE_B => set = Set::B,
101 => {} FNC1 => {}
_ => return Err(Error::undecodable("invalid symbol in Code 16K set A")),
},
Set::B => match v {
0..=95 => bytes.push(v + 32),
96 | 97 => {} SHIFT => shift = Some(Set::A),
CODE_C => set = Set::C,
100 => {} CODE_A => set = Set::A,
FNC1 => {}
_ => return Err(Error::undecodable("invalid symbol in Code 16K set B")),
},
}
}
Ok(if bytes.is_empty() {
Vec::new()
} else {
vec![Segment::byte(bytes)]
})
}