codabar_writer + test

This commit is contained in:
Henry Schimke
2022-12-10 12:46:03 -06:00
parent da16a67c04
commit b366e59e28
10 changed files with 521 additions and 368 deletions

230
src/oned/coda_bar_writer.rs Normal file
View File

@@ -0,0 +1,230 @@
/*
* Copyright 2011 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use one_d_reader_derive::OneDWriter;
use crate::BarcodeFormat;
use super::{CodaBarReader, OneDimensionalCodeWriter};
const START_END_CHARS: [char; 4] = ['A', 'B', 'C', 'D'];
const ALT_START_END_CHARS: [char; 4] = ['T', 'N', '*', 'E'];
const CHARS_WHICH_ARE_TEN_LENGTH_EACH_AFTER_DECODED: [char; 4] = ['/', ':', '+', '.'];
const DEFAULT_GUARD: char = START_END_CHARS[0];
/**
* This class renders CodaBar as {@code boolean[]}.
*
* @author dsbnatut@gmail.com (Kazuki Nishiura)
*/
#[derive(OneDWriter)]
pub struct CodaBarWriter;
impl OneDimensionalCodeWriter for CodaBarWriter {
fn encode_oned(&self, contents: &str) -> Result<Vec<bool>, crate::Exceptions> {
let contents = if contents.chars().count() < 2 {
// Can't have a start/end guard, so tentatively add default guards
format!("{}{}{}", DEFAULT_GUARD, contents, DEFAULT_GUARD)
} else {
// Verify input and calculate decoded length.
let firstChar = contents.chars().nth(0).unwrap().to_ascii_uppercase();
let lastChar = contents
.chars()
.nth(contents.chars().count() - 1)
.unwrap()
.to_ascii_uppercase();
let startsNormal = CodaBarReader::arrayContains(&START_END_CHARS, firstChar);
let endsNormal = CodaBarReader::arrayContains(&START_END_CHARS, lastChar);
let startsAlt = CodaBarReader::arrayContains(&ALT_START_END_CHARS, firstChar);
let endsAlt = CodaBarReader::arrayContains(&ALT_START_END_CHARS, lastChar);
if startsNormal {
if !endsNormal {
return Err(Exceptions::IllegalArgumentException(format!(
"Invalid start/end guards: {}",
contents
)));
}
// else already has valid start/end
contents.to_owned()
} else if startsAlt {
if !endsAlt {
return Err(Exceptions::IllegalArgumentException(format!(
"Invalid start/end guards: {}",
contents
)));
}
// else already has valid start/end
contents.to_owned()
} else {
// Doesn't start with a guard
if endsNormal || endsAlt {
return Err(Exceptions::IllegalArgumentException(format!(
"Invalid start/end guards: {}",
contents
)));
}
// else doesn't end with guard either, so add a default
format!("{}{}{}", DEFAULT_GUARD, contents, DEFAULT_GUARD)
}
};
// The start character and the end character are decoded to 10 length each.
let mut resultLength = 20;
//for i in 1..contents.chars().count() {
for ch in contents[1..contents.chars().count() - 1].chars() {
// for (int i = 1; i < contents.length() - 1; i++) {
if ch.is_digit(10) || ch == '-' || ch == '$' {
resultLength += 9;
} else if CodaBarReader::arrayContains(
&CHARS_WHICH_ARE_TEN_LENGTH_EACH_AFTER_DECODED,
ch,
) {
resultLength += 10;
} else {
return Err(Exceptions::IllegalArgumentException(format!(
"Cannot encode : '{}'",
ch
)));
}
}
// A blank is placed between each character.
resultLength += contents.chars().count() - 1;
let mut result = vec![false; resultLength];
let mut position = 0;
for index in 0..contents.chars().count() {
// for (int index = 0; index < contents.length(); index++) {
let mut c = contents.chars().nth(index).unwrap().to_ascii_uppercase();
if index == 0 || index == contents.chars().count() - 1 {
// The start/end chars are not in the CodaBarReader.ALPHABET.
c = match c {
'T' => 'A',
'N' => 'B',
'*' => 'C',
'E' => 'D',
_ => c,
}
}
let mut code = 0;
for i in 0..CodaBarReader::ALPHABET.len() {
// for (int i = 0; i < CodaBarReader::ALPHABET.length; i++) {
// Found any, because I checked above.
if c == CodaBarReader::ALPHABET[i] {
code = CodaBarReader::CHARACTER_ENCODINGS[i];
break;
}
}
let mut color = true;
let mut counter = 0;
let mut bit = 0;
while bit < 7 {
// A character consists of 7 digit.
result[position] = color;
position += 1;
if ((code >> (6 - bit)) & 1) == 0 || counter == 1 {
color = !color; // Flip the color.
bit += 1;
counter = 0;
} else {
counter += 1;
}
}
if index < contents.chars().count() - 1 {
result[position] = false;
position += 1;
}
}
Ok(result)
}
fn getSupportedWriteFormats(&self) -> Option<Vec<crate::BarcodeFormat>> {
Some(vec![BarcodeFormat::CODABAR])
}
}
impl Default for CodaBarWriter {
fn default() -> Self {
Self {}
}
}
/**
* @author dsbnatut@gmail.com (Kazuki Nishiura)
* @author Sean Owen
*/
#[cfg(test)]
mod CodaBarWriterTestCase {
use crate::{
common::{BitMatrix, BitMatrixTestCase},
oned::OneDimensionalCodeWriter,
BarcodeFormat, Writer,
};
use super::CodaBarWriter;
#[test]
fn testEncode() {
doTest(
"B515-3/B",
&format!(
"{}{}{}{}{}{}{}{}{}{}",
"00000",
"1001001011",
"0110101001",
"0101011001",
"0110101001",
"0101001101",
"0110010101",
"01101101011",
"01001001011",
"00000"
),
);
}
#[test]
fn testEncode2() {
doTest(
"T123T",
&format!(
"{}{}{}{}{}{}{}",
"00000",
"1011001001",
"0101011001",
"0101001011",
"0110010101",
"01011001001",
"00000"
),
);
}
#[test]
fn testAltStartEnd() {
assert_eq!(encode("T123456789-$T"), encode("A123456789-$A"));
}
fn doTest(input: &str, expected: &str) {
let result = encode(input);
assert_eq!(expected, BitMatrixTestCase::matrix_to_string(&result));
}
fn encode(input: &str) -> BitMatrix {
CodaBarWriter::default()
.encode(input, &BarcodeFormat::CODABAR, 0, 0)
.expect("must encode")
}
}