use super::Code128Meta;
use super::encode::checksum;
use super::tables::{CODE_A, CODE_B, CODE_C, CodeSet, FNC1, SHIFT, STOP_VALUE, value_for_widths};
use crate::error::{Error, Result};
use crate::output::{Encoding, LinearPattern};
use crate::segment::Segment;
use crate::symbol::{Symbol, SymbolMeta};
use crate::symbology::Symbology;
use crate::traits::Decode;
const GS: u8 = 0x1D;
#[derive(Debug, Default, Clone, Copy)]
pub struct Code128Decoder;
impl Code128Decoder {
pub fn new() -> Self {
Code128Decoder
}
pub fn decode_linear(&self, pattern: &LinearPattern) -> Result<Symbol> {
let runs = run_lengths(&pattern.modules)?;
if runs.len() < 6 + 7 {
return Err(Error::undecodable("Code 128 pattern too short"));
}
let (value_runs, stop_runs) = runs.split_at(runs.len() - 7);
let stop_expected = [2usize, 3, 3, 1, 1, 1, 2];
if stop_runs != stop_expected {
return Err(Error::undecodable("Code 128 Stop pattern not found"));
}
if !value_runs.len().is_multiple_of(6) {
return Err(Error::undecodable(
"Code 128 module count is not a whole number of characters",
));
}
let mut values = Vec::with_capacity(value_runs.len() / 6);
for group in value_runs.chunks(6) {
let v = value_for_widths(group)
.ok_or_else(|| Error::undecodable("unknown Code 128 symbol pattern"))?;
values.push(v);
}
let check = values
.pop()
.ok_or_else(|| Error::undecodable("missing Code 128 check character"))?;
if values.is_empty() {
return Err(Error::undecodable("Code 128 has no Start character"));
}
if CodeSet::from_start(values[0]).is_none() {
return Err(Error::undecodable("Code 128 does not begin with a Start"));
}
if values.iter().any(|&v| v >= STOP_VALUE) {
return Err(Error::undecodable(
"Code 128 data contains a reserved value",
));
}
if checksum(&values) != check {
return Err(Error::undecodable("Code 128 check character mismatch"));
}
let (segments, gs1) = reconstruct_segments(&values)?;
let symbology = if gs1 {
Symbology::Gs1_128
} else {
Symbology::Code128
};
let meta = Code128Meta {
gs1,
symbols: values,
};
Ok(Symbol::new(symbology, segments, SymbolMeta::Code128(meta)))
}
}
impl Decode for Code128Decoder {
fn decode(&self, encoding: &Encoding) -> Result<Symbol> {
match encoding {
Encoding::Linear(p) => self.decode_linear(p),
Encoding::Matrix(_) => Err(Error::Unsupported {
what: "Code 128 decode of a matrix",
}),
}
}
}
fn run_lengths(modules: &[bool]) -> Result<Vec<usize>> {
if modules.is_empty() {
return Err(Error::undecodable("empty Code 128 module row"));
}
if !modules[0] {
return Err(Error::undecodable("Code 128 row must start with a bar"));
}
let mut runs = Vec::new();
let mut cur = modules[0];
let mut count = 0usize;
for &m in modules {
if m == cur {
count += 1;
} else {
runs.push(count);
cur = m;
count = 1;
}
}
runs.push(count);
Ok(runs)
}
pub(crate) fn reconstruct_segments(values: &[u8]) -> Result<(Vec<Segment>, bool)> {
let mut set = CodeSet::from_start(values[0])
.ok_or_else(|| Error::undecodable("Code 128 sequence has no Start"))?;
let gs1 = values.get(1) == Some(&FNC1);
let mut bytes = Vec::new();
let mut shift: Option<CodeSet> = None;
let mut first_data = true;
for &v in &values[1..] {
let active = shift.take().unwrap_or(set);
let is_leading = first_data;
first_data = false;
match active {
CodeSet::C => match v {
0..=99 => {
bytes.push(b'0' + v / 10);
bytes.push(b'0' + v % 10);
}
CODE_B => set = CodeSet::B,
CODE_A => set = CodeSet::A,
FNC1 => push_fnc1(&mut bytes, is_leading),
_ => return Err(Error::undecodable("invalid symbol in Code C")),
},
CodeSet::A => match v {
0..=95 => bytes.push(if v < 64 { v + 32 } else { v - 64 }),
96 | 97 => {} SHIFT => shift = Some(CodeSet::B),
CODE_C => set = CodeSet::C,
CODE_B => set = CodeSet::B,
101 => {} FNC1 => push_fnc1(&mut bytes, is_leading),
_ => return Err(Error::undecodable("invalid symbol in Code A")),
},
CodeSet::B => match v {
0..=95 => bytes.push(v + 32),
96 | 97 => {} SHIFT => shift = Some(CodeSet::A),
CODE_C => set = CodeSet::C,
100 => {} CODE_A => set = CodeSet::A,
FNC1 => push_fnc1(&mut bytes, is_leading),
_ => return Err(Error::undecodable("invalid symbol in Code B")),
},
}
}
let segments = if bytes.is_empty() {
Vec::new()
} else {
vec![Segment::byte(bytes)]
};
Ok((segments, gs1))
}
fn push_fnc1(bytes: &mut Vec<u8>, is_leading: bool) {
if !is_leading {
bytes.push(GS);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn run_lengths_basic() {
let modules = vec![true, true, false, true];
assert_eq!(run_lengths(&modules).unwrap(), vec![2, 1, 1]);
}
#[test]
fn reconstruct_rejects_non_start() {
assert!(reconstruct_segments(&[0, 1]).is_err());
}
}