use rxing_one_d_proc_derive::OneDWriter;
use crate::common::Result;
use crate::BarcodeFormat;
use super::{Code93Reader, OneDimensionalCodeWriter};
#[derive(OneDWriter, Default)]
pub struct Code93Writer;
impl OneDimensionalCodeWriter for Code93Writer {
fn encode_oned(&self, contents: &str) -> Result<Vec<bool>> {
let mut contents = Self::convertToExtended(contents)?;
let length = contents.chars().count();
if length > 80 {
return Err(Exceptions::illegal_argument_with(format!("Requested contents should be less than 80 digits long after converting to extended encoding, but got {length}" )));
}
let codeWidth = (contents.chars().count() + 2 + 2) * 9 + 1;
let mut result = vec![false; codeWidth];
let mut pos = Self::appendPattern(&mut result, 0, Code93Reader::ASTERISK_ENCODING as u32);
for i in 0..length {
let Some(indexInString) = Code93Reader::ALPHABET_STRING.find(
contents
.chars()
.nth(i)
.ok_or(Exceptions::INDEX_OUT_OF_BOUNDS)?,
) else {
panic!("alphabet")
};
pos += Self::appendPattern(
&mut result,
pos,
Code93Reader::CHARACTER_ENCODINGS[indexInString],
);
}
let check1 = Self::computeChecksumIndex(&contents, 20);
pos += Self::appendPattern(&mut result, pos, Code93Reader::CHARACTER_ENCODINGS[check1]);
contents.push(
Code93Reader::ALPHABET_STRING
.chars()
.nth(check1)
.ok_or(Exceptions::INDEX_OUT_OF_BOUNDS)?,
);
let check2 = Self::computeChecksumIndex(&contents, 15);
pos += Self::appendPattern(&mut result, pos, Code93Reader::CHARACTER_ENCODINGS[check2]);
pos += Self::appendPattern(&mut result, pos, Code93Reader::ASTERISK_ENCODING as u32);
result[pos] = true;
Ok(result)
}
fn getSupportedWriteFormats(&self) -> Option<Vec<crate::BarcodeFormat>> {
Some(vec![BarcodeFormat::CODE_93])
}
}
impl Code93Writer {
#[allow(dead_code)]
#[deprecated]
fn appendPatternWithPatternStart(
target: &mut [bool],
pos: usize,
pattern: &[usize],
_startColor: bool,
) -> u32 {
let mut pos = pos;
for bit in pattern {
target[pos] = *bit != 0;
pos += 1;
}
9
}
fn appendPattern(target: &mut [bool], pos: usize, a: u32) -> usize {
for i in 0..9 {
let temp = a & (1 << (8 - i));
target[pos + i] = temp != 0;
}
9
}
fn computeChecksumIndex(contents: &str, maxWeight: u32) -> usize {
let mut weight = 1_u32;
let mut total = 0_u32;
for i in (0..contents.chars().count()).rev() {
let Some(indexInString) =
Code93Reader::ALPHABET_STRING.find(contents.chars().nth(i).unwrap())
else {
panic!("not in the alphabet");
};
total += indexInString as u32 * weight;
weight += 1;
if weight > maxWeight {
weight = 1;
}
}
total as usize % 47
}
fn convertToExtended(contents: &str) -> Result<String> {
let length = contents.chars().count();
let mut extendedContent = String::with_capacity(length * 2);
for character in contents.chars() {
if character as u32 == 0 {
extendedContent.push_str("bU");
} else if character as u32 <= 26 {
extendedContent.push('a');
extendedContent.push(
char::from_u32('A' as u32 + character as u32 - 1).ok_or(Exceptions::PARSE)?,
);
} else if character as u32 <= 31 {
extendedContent.push('b');
extendedContent.push(
char::from_u32('A' as u32 + character as u32 - 27).ok_or(Exceptions::PARSE)?,
);
} else if character == ' ' || character == '$' || character == '%' || character == '+' {
extendedContent.push(character);
} else if character <= ',' {
extendedContent.push('c');
extendedContent.push(
char::from_u32('A' as u32 + character as u32 - '!' as u32)
.ok_or(Exceptions::PARSE)?,
);
} else if character <= '9' {
extendedContent.push(character);
} else if character == ':' {
extendedContent.push_str("cZ");
} else if character <= '?' {
extendedContent.push('b');
extendedContent.push(
char::from_u32('F' as u32 + character as u32 - ';' as u32)
.ok_or(Exceptions::PARSE)?,
);
} else if character == '@' {
extendedContent.push_str("bV");
} else if character <= 'Z' {
extendedContent.push(character);
} else if character <= '_' {
extendedContent.push('b');
extendedContent.push(
char::from_u32('K' as u32 + character as u32 - '[' as u32)
.ok_or(Exceptions::PARSE)?,
);
} else if character == '`' {
extendedContent.push_str("bW");
} else if character <= 'z' {
extendedContent.push('d');
extendedContent.push(
char::from_u32('A' as u32 + character as u32 - 'a' as u32)
.ok_or(Exceptions::PARSE)?,
);
} else if character as u32 <= 127 {
extendedContent.push('b');
extendedContent.push(
char::from_u32('P' as u32 + character as u32 - '{' as u32)
.ok_or(Exceptions::PARSE)?,
);
} else {
return Err(Exceptions::illegal_argument_with(format!(
"Requested content contains a non-encodable character: '{character}'"
)));
}
}
Ok(extendedContent)
}
}
#[cfg(test)]
mod Code93WriterTestCase {
use crate::{common::bit_matrix_test_case, oned::Code93Writer, BarcodeFormat, Writer};
#[test]
fn testEncode() {
doTest(
"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
"000001010111101101010001101001001101000101100101001100100101100010101011010001011001\
001011000101001101001000110101010110001010011001010001101001011001000101101101101001\
101100101101011001101001101100101101100110101011011001011001101001101101001110101000\
101001010010001010001001010000101001010001001001001001000101010100001000100101000010\
10100111010101000010101011110100000",
);
doTest("\u{0000}\u{0001}\u{001a}\u{001b}\u{001f} $%+!,09:;@AZ[_`az{\u{007f}",
&format!("{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}",
"00000" , "101011110" ,
"111011010" , "110010110" , "100100110" , "110101000" , "100100110" , "100111010" , "111011010" , "110101000" , "111011010" , "110010010" , "111010010" , "111001010" , "110101110" , "101110110" , "111010110" , "110101000" , "111010110" , "101011000" , "100010100" , "100001010" , "111010110" , "100111010" , "111011010" , "110001010" , "111011010" , "110011010" , "110101000" , "100111010" , "111011010" , "100011010" , "111011010" , "100101100" , "111011010" , "101101100" , "100110010" , "110101000" , "100110010" , "100111010" , "111011010" , "100010110" , "111011010" , "110100110" , "110100010" , "110101100" , "101011110" , "100000"));
}
fn doTest(input: &str, expected: &str) {
let result = Code93Writer
.encode(input, &BarcodeFormat::CODE_93, 0, 0)
.expect("encode");
assert_eq!(expected, bit_matrix_test_case::matrix_to_string(&result));
}
#[test]
fn testConvertToExtended() {
let src = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%";
let dst = Code93Writer::convertToExtended(src).expect("convert");
assert_eq!(src, dst);
}
}