use rxing_one_d_proc_derive::OneDWriter;
use crate::common::Result;
use crate::BarcodeFormat;
use super::{Code39Reader, OneDimensionalCodeWriter};
#[derive(OneDWriter, Default)]
pub struct Code39Writer;
impl OneDimensionalCodeWriter for Code39Writer {
fn encode_oned(&self, contents: &str) -> Result<Vec<bool>> {
let mut contents = contents.to_owned();
let mut length = contents.chars().count();
if length > 80 {
return Err(Exceptions::illegal_argument_with(format!(
"Requested contents should be less than 80 digits long, but got {length}"
)));
}
let mut i = 0;
while i < length {
if Code39Reader::ALPHABET_STRING
.find(
contents
.chars()
.nth(i)
.ok_or(Exceptions::INDEX_OUT_OF_BOUNDS)?,
)
.is_none()
{
contents = Self::tryToConvertToExtendedMode(&contents)?;
length = contents.chars().count();
if length > 80 {
return Err(Exceptions::illegal_argument_with(format!("Requested contents should be less than 80 digits long, but got {length} (extended full ASCII mode)")));
}
break;
}
i += 1;
}
let mut widths = [0_usize; 9]; let codeWidth = 24 + 1 + (13 * length);
let mut result = vec![false; codeWidth];
Self::toIntArray(Code39Reader::ASTERISK_ENCODING, &mut widths);
let mut pos = Self::appendPattern(&mut result, 0, &widths, true);
let narrowWhite = [1_usize];
pos += Self::appendPattern(&mut result, pos as usize, &narrowWhite, false);
for i in 0..length {
let Some(indexInString) = Code39Reader::ALPHABET_STRING.find(
contents
.chars()
.nth(i)
.ok_or(Exceptions::INDEX_OUT_OF_BOUNDS)?,
) else {
continue;
};
Self::toIntArray(
Code39Reader::CHARACTER_ENCODINGS[indexInString],
&mut widths,
);
pos += Self::appendPattern(&mut result, pos as usize, &widths, true);
pos += Self::appendPattern(&mut result, pos as usize, &narrowWhite, false);
}
Self::toIntArray(Code39Reader::ASTERISK_ENCODING, &mut widths);
Self::appendPattern(&mut result, pos as usize, &widths, true);
Ok(result)
}
fn getSupportedWriteFormats(&self) -> Option<Vec<crate::BarcodeFormat>> {
Some(vec![BarcodeFormat::CODE_39])
}
}
impl Code39Writer {
fn toIntArray(a: u32, toReturn: &mut [usize; 9]) {
for (i, val) in toReturn.iter_mut().enumerate().take(9) {
let temp = a & (1 << (8 - i));
*val = if temp == 0 { 1 } else { 2 };
}
}
fn tryToConvertToExtendedMode(contents: &str) -> Result<String> {
let mut extendedContent = String::new(); for character in contents.chars() {
match character {
'\u{0000}' => extendedContent.push_str("%U"),
' ' | '-' | '.' => extendedContent.push(character),
'@' => extendedContent.push_str("%V"),
'`' => extendedContent.push_str("%W"),
_ => {
if (character as u32) <= 26 {
extendedContent.push('$');
extendedContent.push(
char::from_u32('A' as u32 + (character as u32 - 1))
.ok_or(Exceptions::PARSE)?,
);
} else if character < ' ' {
extendedContent.push('%');
extendedContent.push(
char::from_u32('A' as u32 + (character as u32 - 27))
.ok_or(Exceptions::PARSE)?,
);
} else if character <= ',' || character == '/' || character == ':' {
extendedContent.push('/');
extendedContent.push(
char::from_u32('A' as u32 + (character as u32 - 33))
.ok_or(Exceptions::PARSE)?,
);
} else if character <= '9' {
extendedContent.push(
char::from_u32('0' as u32 + (character as u32 - 48))
.ok_or(Exceptions::PARSE)?,
);
} else if character <= '?' {
extendedContent.push('%');
extendedContent.push(
char::from_u32('F' as u32 + (character as u32 - 59))
.ok_or(Exceptions::PARSE)?,
);
} else if character <= 'Z' {
extendedContent.push(
char::from_u32('A' as u32 + (character as u32 - 65))
.ok_or(Exceptions::PARSE)?,
);
} else if character <= '_' {
extendedContent.push('%');
extendedContent.push(
char::from_u32('K' as u32 + (character as u32 - 91))
.ok_or(Exceptions::PARSE)?,
);
} else if character <= 'z' {
extendedContent.push('+');
extendedContent.push(
char::from_u32('A' as u32 + (character as u32 - 97))
.ok_or(Exceptions::PARSE)?,
);
} else if character as u32 <= 127 {
extendedContent.push('%');
extendedContent.push(
char::from_u32('P' as u32 + (character as u32 - 123))
.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 Code39WriterTestCase {
use crate::{common::bit_matrix_test_case, oned::Code39Writer, BarcodeFormat, Writer};
#[test]
fn testEncode() {
doTest(
"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
"000001001011011010110101001011010110100101101101101001010101011001011011010110010101\
011011001010101010011011011010100110101011010011010101011001101011010101001101011010\
100110110110101001010101101001101101011010010101101101001010101011001101101010110010\
101101011001010101101100101100101010110100110101011011001101010101001011010110110010\
110101010011011010101010011011010110100101011010110010101101101100101010101001101011\
01101001101010101100110101010100101101101101001011010101100101101010010110110100000",
);
doTest("\u{0000}\u{0001}\u{0002}\u{0003}\u{0004}\u{0005}\u{0006}\u{0007}\u{008}\t\n\u{000b}\u{000c}\r\u{000e}\u{000f}\u{0010}\u{0011}\u{0012}\u{0013}\u{0014}\u{0015}\u{0016}\u{0017}\u{0018}\u{0019}\u{001a}\u{001b}\u{001c}\u{001d}\u{001e}\u{001f}",
"000001001011011010101001001001011001010101101001001001010110101001011010010010010101\
011010010110100100100101011011010010101001001001010101011001011010010010010101101011\
001010100100100101010110110010101001001001010101010011011010010010010101101010011010\
100100100101010110100110101001001001010101011001101010010010010101101010100110100100\
100101010110101001101001001001010110110101001010010010010101010110100110100100100101\
011010110100101001001001010101101101001010010010010101010101100110100100100101011010\
101100101001001001010101101011001010010010010101010110110010100100100101011001010101\
101001001001010100110101011010010010010101100110101010100100100101010010110101101001\
001001010110010110101010010010010101001101101010101001001001011010100101101010010010\
010101101001011010100100100101101101001010101001001001010101100101101010010010010110\
101100101010010110110100000");
doTest(
" !\"#$%&'()*+,-./0123456789:;<=>?",
"000001001011011010100110101101010010010100101101010010110100100101001010110100101101\
001001010010110110100101010010010100101010110010110100100101001011010110010101001001\
010010101101100101010010010100101010100110110100100101001011010100110101001001010010\
101101001101010010010100101010110011010100100101001011010101001101001001010010101101\
010011010010101101101100101011010100100101001011010110100101010011011010110100101011\
010110010101101101100101010101001101011011010011010101011001101010101001011011011010\
010110101011001011010100100101001010011011010101010010010010101101100101010100100100\
101010100110110101001001001011010100110101010010010010101101001101010100100100101010\
11001101010010110110100000",
);
doTest(
"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_",
"0000010010110110101010010010010100110101011011010100101101011010010110110110100101010\
101100101101101011001010101101100101010101001101101101010011010101101001101010101100\
110101101010100110101101010011011011010100101010110100110110101101001010110110100101\
010101100110110101011001010110101100101010110110010110010101011010011010101101100110\
101010100101101011011001011010101001101101010101001001001011010101001101010010010010\
101101010011010100100100101101101010010101001001001010101101001101010010010010110101\
101001010010110110100000",
);
doTest(
"`abcdefghijklmnopqrstuvwxyz{|}~",
"000001001011011010101001001001011001101010101001010010010110101001011010010100100101\
011010010110100101001001011011010010101001010010010101011001011010010100100101101011\
001010100101001001010110110010101001010010010101010011011010010100100101101010011010\
100101001001010110100110101001010010010101011001101010010100100101101010100110100101\
001001010110101001101001010010010110110101001010010100100101010110100110100101001001\
011010110100101001010010010101101101001010010100100101010101100110100101001001011010\
101100101001010010010101101011001010010100100101010110110010100101001001011001010101\
101001010010010100110101011010010100100101100110101010100101001001010010110101101001\
010010010110010110101010010100100101001101101010101001001001010110110100101010010010\
010101010110011010100100100101101010110010101001001001010110101100101010010010010101\
011011001010010110110100000",
);
}
fn doTest(input: &str, expected: &str) {
let result = Code39Writer
.encode(input, &BarcodeFormat::CODE_39, 0, 0)
.expect("must encode");
assert_eq!(
expected,
bit_matrix_test_case::matrix_to_string(&result),
"{input}"
);
}
}