use super::tables::{
LTD_CHECK_WEIGHT, LTD_FINDER, LTD_G_SUM, LTD_MODULES, LTD_T_EVEN, LTD_WIDEST, OMN_CHECK_WEIGHT,
OMN_FINDER, OMN_G_SUM, OMN_MODULES, OMN_T_EVEN_ODD, OMN_WIDEST, gtin_check_digit,
};
use super::widths::get_value;
use super::{DataBarMeta, DataBarVariant};
use crate::error::{Error, Result};
use crate::output::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 DataBarDecoder;
impl DataBarDecoder {
pub fn new() -> Self {
DataBarDecoder
}
}
impl Decode for DataBarDecoder {
fn decode(&self, encoding: &Encoding) -> Result<Symbol> {
let pattern = match encoding {
Encoding::Linear(p) => p,
Encoding::Matrix(_) => {
return Err(Error::Unsupported {
what: "GS1 DataBar stacked decoding",
});
}
};
let widths = run_lengths(&pattern.modules)?;
match widths.len() {
46 => decode_omni(&widths).or_else(|_| super::expanded::decode(&widths)),
47 => decode_limited(&widths),
n if super::expanded::is_expanded_width(n) => super::expanded::decode(&widths),
n => Err(Error::undecodable(format!(
"unexpected DataBar element count {n} (expected 46, 47, or an Expanded width)"
))),
}
}
}
fn run_lengths(modules: &[bool]) -> Result<Vec<i32>> {
if modules.is_empty() {
return Err(Error::undecodable("empty DataBar module row"));
}
if modules[0] {
return Err(Error::undecodable(
"DataBar row must start with a light element",
));
}
let mut widths = Vec::new();
let mut current = modules[0];
let mut run = 0i32;
for &m in modules {
if m == current {
run += 1;
} else {
widths.push(run);
current = m;
run = 1;
}
}
widths.push(run);
Ok(widths)
}
fn format_gtin(val: u64) -> [u8; 14] {
let mut body = [b'0'; 13];
let mut v = val;
for slot in body.iter_mut().rev() {
*slot = b'0' + (v % 10) as u8;
v /= 10;
}
let check = gtin_check_digit(&body);
let mut out = [0u8; 14];
out[..13].copy_from_slice(&body);
out[13] = check;
out
}
fn omn_group_from_n_odd(n_odd: i32, outside: bool) -> Option<usize> {
let range = if outside { 0..5 } else { 5..9 };
range.into_iter().find(|&g| OMN_MODULES[g] == n_odd)
}
fn omn_char_value(dw: &[i32; 8], outside: bool) -> Result<i32> {
let odd = [dw[0], dw[2], dw[4], dw[6]];
let even = [dw[1], dw[3], dw[5], dw[7]];
let n_odd: i32 = odd.iter().sum();
let group = omn_group_from_n_odd(n_odd, outside)
.ok_or_else(|| Error::undecodable("invalid DataBar data character (bad module sum)"))?;
let max_width = OMN_WIDEST[group];
let no_narrow = !outside;
let v_odd = get_value(&odd, max_width, no_narrow);
let v_even = get_value(&even, 9 - max_width, !no_narrow);
let t = OMN_T_EVEN_ODD[group] as i64;
let v = if outside {
v_odd * t + v_even
} else {
v_even * t + v_odd
};
Ok(OMN_G_SUM[group] + v as i32)
}
fn match_omn_finder(pat: &[i32; 5]) -> Option<usize> {
OMN_FINDER
.iter()
.position(|f| f.iter().zip(pat).all(|(&a, &b)| a as i32 == b))
}
fn decode_omni(tw: &[i32]) -> Result<Symbol> {
let mut dw = [[0i32; 8]; 4];
for i in 0..8 {
dw[0][i] = tw[i + 2];
dw[1][7 - i] = tw[i + 15];
dw[3][i] = tw[i + 23];
dw[2][7 - i] = tw[i + 36];
}
let finder_left: [i32; 5] = tw[10..15].try_into().unwrap();
let mut finder_right: [i32; 5] = tw[31..36].try_into().unwrap();
finder_right.reverse();
let c_left = match_omn_finder(&finder_left)
.ok_or_else(|| Error::undecodable("unrecognized DataBar left finder pattern"))?;
let c_right = match_omn_finder(&finder_right)
.ok_or_else(|| Error::undecodable("unrecognized DataBar right finder pattern"))?;
let dc = [
omn_char_value(&dw[0], true)?,
omn_char_value(&dw[1], false)?,
omn_char_value(&dw[2], true)?,
omn_char_value(&dw[3], false)?,
];
let mut checksum = 0i32;
for (i, dw_i) in dw.iter().enumerate() {
for (j, &w) in dw_i.iter().enumerate() {
checksum += OMN_CHECK_WEIGHT[i][j] * w;
}
}
checksum %= 79;
if checksum >= 8 {
checksum += 1;
}
if checksum >= 72 {
checksum += 1;
}
if (checksum / 9) as usize != c_left || (checksum % 9) as usize != c_right {
return Err(Error::undecodable(
"DataBar checksum does not match finder patterns",
));
}
let left_pair = dc[0] as u64 * 1597 + dc[1] as u64;
let right_pair = dc[2] as u64 * 1597 + dc[3] as u64;
let val = left_pair * 4_537_077 + right_pair;
Ok(gtin_symbol(
Symbology::DataBarOmni,
DataBarVariant::Omni,
val,
))
}
fn ltd_pair_value(pw: &[i32; 14]) -> Result<i64> {
let odd = [pw[0], pw[2], pw[4], pw[6], pw[8], pw[10], pw[12]];
let even = [pw[1], pw[3], pw[5], pw[7], pw[9], pw[11], pw[13]];
let n_odd: i32 = odd.iter().sum();
let group = LTD_MODULES
.iter()
.position(|&m| m == n_odd)
.ok_or_else(|| Error::undecodable("invalid DataBar Limited character (bad module sum)"))?;
let max_width = LTD_WIDEST[group];
let odd_val = get_value(&odd, max_width, false);
let even_val = get_value(&even, 9 - max_width, true);
Ok(LTD_G_SUM[group] as i64 + odd_val * LTD_T_EVEN[group] as i64 + even_val)
}
fn decode_limited(tw: &[i32]) -> Result<Symbol> {
let pw0: [i32; 14] = tw[2..16].try_into().unwrap();
let finder: [i32; 14] = tw[16..30].try_into().unwrap();
let pw1: [i32; 14] = tw[30..44].try_into().unwrap();
let checksum_index = LTD_FINDER
.iter()
.position(|f| f.iter().zip(&finder).all(|(&a, &b)| a as i32 == b))
.ok_or_else(|| Error::undecodable("unrecognized DataBar Limited finder pattern"))?;
let pair0 = ltd_pair_value(&pw0)?;
let pair1 = ltd_pair_value(&pw1)?;
let mut checksum = 0i32;
for i in 0..14 {
checksum += LTD_CHECK_WEIGHT[0][i] * pw0[i];
checksum += LTD_CHECK_WEIGHT[1][i] * pw1[i];
}
checksum %= 89;
if checksum as usize != checksum_index {
return Err(Error::undecodable(
"DataBar Limited checksum does not match finder pattern",
));
}
let val = pair0 as u64 * 2_013_571 + pair1 as u64;
Ok(gtin_symbol(
Symbology::DataBarLimited,
DataBarVariant::Limited,
val,
))
}
fn gtin_symbol(symbology: Symbology, variant: DataBarVariant, val: u64) -> Symbol {
let gtin = format_gtin(val);
Symbol::new(
symbology,
vec![Segment::numeric(gtin.to_vec())],
SymbolMeta::DataBar(DataBarMeta::new(variant)),
)
}