#![no_std]
extern crate alloc;
mod decodation;
mod encodation;
pub mod errorcode;
pub mod placement;
mod symbol_size;
pub mod data;
pub use encodation::EncodationType;
pub use symbol_size::{SymbolList, SymbolSize};
use alloc::vec::Vec;
use flagset::FlagSet;
use encodation::DataEncodingError;
use placement::{Bitmap, MatrixMap};
#[cfg(test)]
use pretty_assertions::assert_eq;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DataMatrix {
pub size: SymbolSize,
codewords: Vec<u8>,
num_data_codewords: usize,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DecodingError {
PixelConversion(placement::BitmapConversionError),
ErrorCorrection(errorcode::ErrorDecodingError),
DataDecoding(decodation::DataDecodingError),
}
impl DataMatrix {
pub fn decode(pixels: &[bool], width: usize) -> Result<Vec<u8>, DecodingError> {
let (matrix_map, size) =
MatrixMap::try_from_bits(pixels, width).map_err(DecodingError::PixelConversion)?;
let mut codewords = matrix_map.codewords();
errorcode::decode_error(&mut codewords, size).map_err(DecodingError::ErrorCorrection)?;
decodation::decode_data(&codewords[..size.num_data_codewords()])
.map_err(DecodingError::DataDecoding)
}
pub fn codewords(&self) -> &[u8] {
&self.codewords
}
pub fn data_codewords(&self) -> &[u8] {
&self.codewords[..self.num_data_codewords]
}
pub fn bitmap(&self) -> Bitmap<bool> {
MatrixMap::new_with_codewords(&self.codewords, self.size).bitmap()
}
pub fn encode<I: Into<SymbolList>>(
data: &[u8],
symbol_list: I,
) -> Result<DataMatrix, DataEncodingError> {
DataMatrixBuilder::new()
.with_symbol_list(symbol_list)
.encode(data)
}
pub fn encode_str<I: Into<SymbolList>>(
text: &str,
symbol_list: I,
) -> Result<DataMatrix, DataEncodingError> {
DataMatrixBuilder::new()
.with_symbol_list(symbol_list)
.encode_str(text)
}
pub fn encode_gs1<I: Into<SymbolList>>(
data: &[u8],
symbol_list: I,
) -> Result<DataMatrix, DataEncodingError> {
DataMatrixBuilder::new()
.with_symbol_list(symbol_list)
.with_fnc1_start(true)
.encode(data)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DataMatrixBuilder {
encodation_types: FlagSet<EncodationType>,
symbol_list: SymbolList,
use_macros: bool,
fnc1_start: bool,
}
impl DataMatrixBuilder {
pub fn new() -> Self {
Self {
encodation_types: EncodationType::all(),
symbol_list: SymbolList::default(),
use_macros: true,
fnc1_start: false,
}
}
pub fn with_encodation_types(self, types: impl Into<FlagSet<EncodationType>>) -> Self {
Self {
encodation_types: types.into(),
..self
}
}
pub fn with_fnc1_start(self, fnc1_start: bool) -> Self {
Self { fnc1_start, ..self }
}
pub fn with_macros(self, use_macros: bool) -> Self {
Self { use_macros, ..self }
}
pub fn with_symbol_list<I: Into<SymbolList>>(self, symbol_list: I) -> Self {
Self {
symbol_list: symbol_list.into(),
..self
}
}
pub fn encode(self, data: &[u8]) -> Result<DataMatrix, DataEncodingError> {
self.encode_eci(data, None)
}
pub fn encode_str(self, text: &str) -> Result<DataMatrix, DataEncodingError> {
if let Some(data) = data::utf8_to_latin1(text) {
self.encode_eci(&data, None)
} else {
self.encode_eci(text.as_bytes(), Some(decodation::ECI_UTF8))
}
}
#[doc(hidden)]
pub fn encode_eci(
self,
data: &[u8],
eci: Option<u32>,
) -> Result<DataMatrix, DataEncodingError> {
let (mut codewords, size) = data::encode_data_internal(
data,
&self.symbol_list,
eci,
self.encodation_types,
self.use_macros,
self.fnc1_start,
)?;
let ecc = errorcode::encode_error(&codewords, size);
let num_data_codewords = codewords.len();
codewords.extend_from_slice(&ecc);
Ok(DataMatrix {
size,
codewords,
num_data_codewords,
})
}
}
impl Default for DataMatrixBuilder {
fn default() -> Self {
Self::new()
}
}
#[test]
fn utf8_eci_test() {
let data = "🥸";
let code = DataMatrix::encode_str(data, SymbolList::default()).unwrap();
let decoded = data::decode_str(code.data_codewords()).unwrap();
assert_eq!(decoded, data);
}
#[test]
fn test_tile_placement_forth_and_back() {
let mut rnd_data = test::random_data();
for size in SymbolList::all() {
let data = rnd_data(size.num_codewords());
let map = MatrixMap::new_with_codewords(&data, size);
assert_eq!(map.codewords(), data);
let bitmap = map.bitmap();
let (matrix_map, _size) = MatrixMap::try_from_bits(bitmap.bits(), bitmap.width()).unwrap();
assert_eq!(matrix_map.codewords(), data);
}
}
#[test]
fn test_macro_str() {
let data = "[)>\x1E05\x1D🤘\x1E\x04";
let map = DataMatrix::encode_str(data, SymbolList::default()).unwrap();
let codewords = map.data_codewords();
assert_eq!(
codewords,
&[
encodation::MACRO05,
encodation::ascii::ECI,
decodation::ECI_UTF8 as u8 + 1,
231,
240,
114,
183,
81,
219,
129,
]
);
let out = data::decode_str(codewords).unwrap();
assert_eq!(data, out);
}
#[test]
fn test_too_much_data() {
let mut rnd_data = test::random_data();
let data = rnd_data(5000);
let result = DataMatrix::encode(&data, SymbolList::default());
assert_eq!(result, Err(DataEncodingError::TooMuchOrIllegalData));
}
#[cfg(test)]
mod test {
use crate::placement::MatrixMap;
use crate::symbol_size::SymbolSize;
use alloc::vec::Vec;
pub fn random_maps() -> impl FnMut(SymbolSize) -> MatrixMap<bool> {
let mut rnd = random_bytes();
move |size| {
let mut map = MatrixMap::new(size);
map.traverse_mut(|_, bits| {
for bit in bits {
*bit = rnd() > 127;
}
});
map.write_padding();
map
}
}
pub fn random_bytes() -> impl FnMut() -> u8 {
let mut seed = 0;
move || {
let modulus = 2u64.pow(31);
let a = 1103515245u64;
let c = 12345u64;
seed = (a * seed + c) % modulus;
(seed % 256) as u8
}
}
pub fn random_data() -> impl FnMut(usize) -> Vec<u8> {
let mut rnd = random_bytes();
move |len| {
let mut v = Vec::with_capacity(len);
for _ in 0..len {
v.push(rnd());
}
v
}
}
}
#[test]
fn test_simple_gs1() {
let data = b"01034531200000111719112510ABCD1234\x1D2110";
let result = DataMatrix::encode_gs1(data, SymbolList::default()).unwrap();
let codewords = result.codewords();
assert_eq!(codewords[0], crate::encodation::ascii::FNC1);
let decoded = data::decode_data(result.data_codewords()).unwrap();
assert_eq!(decoded, data);
}