use super::placement::{placement_map, render_borders};
use super::tables::{SquareSpec, smallest_square_for, square_by_size};
use super::{DataMatrixMeta, Encodation};
use crate::error::{Error, Result};
use crate::output::Encoding;
use crate::segment::{Mode, Segment};
use crate::symbol::{Symbol, SymbolMeta};
use crate::symbology::Symbology;
use crate::traits::Encode;
const PAD: u8 = 129;
const BASE256_LATCH: u8 = 231;
const UPPER_SHIFT: u8 = 235;
#[derive(Debug, Default, Clone, Copy)]
pub struct DataMatrixEncoder;
impl DataMatrixEncoder {
pub fn new() -> Self {
DataMatrixEncoder
}
pub fn build(&self, data: &[u8]) -> Result<Symbol> {
self.build_inner(data, None)
}
pub fn build_sized(&self, data: &[u8], symbol_size: usize) -> Result<Symbol> {
self.build_inner(data, Some(symbol_size))
}
pub fn build_text(&self, text: &str) -> Result<Symbol> {
self.build(text.as_bytes())
}
fn build_inner(&self, data: &[u8], forced: Option<usize>) -> Result<Symbol> {
let segs = canonical_segments(data);
let encodations: Vec<Encodation> = segs.iter().map(|(e, _)| *e).collect();
let cw = data_codewords(&segs)?;
let spec = match forced {
Some(sz) => {
let s = square_by_size(sz)
.ok_or_else(|| Error::invalid_parameter("unknown Data Matrix square size"))?;
if cw.len() > s.data_cw {
return Err(Error::capacity("data does not fit in the requested size"));
}
s
}
None => smallest_square_for(cw.len())
.ok_or_else(|| Error::capacity("data exceeds the largest square Data Matrix"))?,
};
let segments: Vec<Segment> = segs.into_iter().map(|(_, b)| Segment::byte(b)).collect();
let meta = DataMatrixMeta {
symbol_size: spec.symbol_size,
encodations,
};
Ok(Symbol::new(
Symbology::DataMatrix,
segments,
SymbolMeta::DataMatrix(meta),
))
}
}
impl Encode for DataMatrixEncoder {
fn encode(&self, symbol: &Symbol) -> Result<Encoding> {
if symbol.symbology != Symbology::DataMatrix {
return Err(Error::invalid_parameter(
"DataMatrixEncoder given a non-Data-Matrix symbol",
));
}
let meta = match &symbol.meta {
SymbolMeta::DataMatrix(m) => m,
_ => {
return Err(Error::invalid_parameter(
"Data Matrix symbol missing DataMatrixMeta",
));
}
};
if meta.encodations.len() != symbol.segments.len() {
return Err(Error::invalid_parameter(
"encodation count does not match segment count",
));
}
let spec = square_by_size(meta.symbol_size)
.ok_or_else(|| Error::invalid_parameter("unknown Data Matrix square size"))?;
let segs: Vec<(Encodation, Vec<u8>)> = symbol
.segments
.iter()
.zip(&meta.encodations)
.map(|(seg, enc)| {
if !matches!(seg.mode, Mode::Byte) {
return Err(Error::Unsupported {
what: "Data Matrix supports only Byte-mode segments",
});
}
Ok((*enc, seg.data.clone()))
})
.collect::<Result<_>>()?;
let mut cw = data_codewords(&segs)?;
if cw.len() > spec.data_cw {
return Err(Error::capacity("data does not fit in the symbol size"));
}
pad_codewords(&mut cw, spec.data_cw);
let full = add_error_correction(&cw, &spec);
let matrix = render(&spec, &full);
Ok(Encoding::Matrix(matrix))
}
}
fn canonical_segments(data: &[u8]) -> Vec<(Encodation, Vec<u8>)> {
let mut segs = Vec::new();
let mut i = 0;
while i < data.len() {
let high = data[i] >= 128;
let start = i;
while i < data.len() && (data[i] >= 128) == high {
i += 1;
}
let enc = if high {
Encodation::Base256
} else {
Encodation::Ascii
};
segs.push((enc, data[start..i].to_vec()));
}
if segs.is_empty() {
segs.push((Encodation::Ascii, Vec::new()));
}
segs
}
fn data_codewords(segs: &[(Encodation, Vec<u8>)]) -> Result<Vec<u8>> {
let mut cw = Vec::new();
for (enc, bytes) in segs {
match enc {
Encodation::Ascii => ascii_encode(&mut cw, bytes)?,
Encodation::Base256 => base256_encode(&mut cw, bytes),
}
}
Ok(cw)
}
fn ascii_encode(cw: &mut Vec<u8>, bytes: &[u8]) -> Result<()> {
let mut i = 0;
while i < bytes.len() {
let b = bytes[i];
if b.is_ascii_digit() && i + 1 < bytes.len() && bytes[i + 1].is_ascii_digit() {
let val = (b - b'0') * 10 + (bytes[i + 1] - b'0');
cw.push(130 + val);
i += 2;
} else if b < 128 {
cw.push(b + 1);
i += 1;
} else {
cw.push(UPPER_SHIFT);
cw.push((b - 128) + 1);
i += 1;
}
}
Ok(())
}
fn base256_encode(cw: &mut Vec<u8>, bytes: &[u8]) {
cw.push(BASE256_LATCH);
let count = bytes.len();
let mut field = Vec::new();
if count <= 249 {
field.push(count as u8);
} else {
field.push((count / 250 + 249) as u8);
field.push((count % 250) as u8);
}
for &b in field.iter().chain(bytes.iter()) {
let pos = cw.len() + 1;
cw.push(randomize_255(b, pos));
}
}
fn randomize_255(value: u8, position: usize) -> u8 {
let pseudo = ((149 * position) % 255) + 1;
let t = value as usize + pseudo;
(if t <= 255 { t } else { t - 256 }) as u8
}
fn pad_codewords(cw: &mut Vec<u8>, capacity: usize) {
if cw.len() < capacity {
cw.push(PAD);
}
while cw.len() < capacity {
let pos = cw.len() + 1;
let pseudo = ((149 * pos) % 253) + 1;
let mut t = 129 + pseudo;
if t > 254 {
t -= 254;
}
cw.push(t as u8);
}
}
fn add_error_correction(data: &[u8], spec: &SquareSpec) -> Vec<u8> {
let bc = spec.blocks;
let epb = spec.ec_per_block();
let mut ec_stream = vec![0u8; spec.ec_cw];
for b in 0..bc {
let block_data: Vec<u8> = data.iter().skip(b).step_by(bc).copied().collect();
let ecc = super::gf::encode(&block_data, epb);
for (e, &val) in ecc.iter().enumerate() {
ec_stream[b + e * bc] = val;
}
}
let mut full = data.to_vec();
full.extend_from_slice(&ec_stream);
full
}
fn render(spec: &SquareSpec, full: &[u8]) -> crate::output::BitMatrix {
let ms = spec.mapping_size();
let pm = placement_map(ms, ms);
debug_assert_eq!(full.len(), spec.total_cw());
debug_assert_eq!(full.len(), pm.codewords.len());
let mut mapping = vec![false; ms * ms];
for (c, positions) in pm.codewords.iter().enumerate() {
let byte = full[c];
for (bit, &(r, col)) in positions.iter().enumerate() {
mapping[r * ms + col] = (byte >> (7 - bit)) & 1 != 0;
}
}
for &(r, col) in &pm.fixed_dark {
mapping[r * ms + col] = true;
}
render_borders(spec, &mapping)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn iso_example_data_codewords() {
let segs = canonical_segments(b"123456");
let cw = data_codewords(&segs).unwrap();
assert_eq!(cw, vec![142, 164, 186]);
}
#[test]
fn base256_length_field_and_latch() {
let cw = data_codewords(&[(Encodation::Base256, vec![0x80, 0x81])]).unwrap();
assert_eq!(cw.len(), 4);
assert_eq!(cw[0], BASE256_LATCH);
}
}