Files
rxing/src/oned/coda_bar_writer.rs

224 lines
7.4 KiB
Rust

/*
* 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 rxing_one_d_proc_derive::OneDWriter;
use crate::common::Result;
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, Default)]
pub struct CodaBarWriter;
impl OneDimensionalCodeWriter for CodaBarWriter {
fn encode_oned(&self, contents: &str) -> Result<Vec<bool>> {
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()
.next()
.ok_or(Exceptions::INDEX_OUT_OF_BOUNDS)?
.to_ascii_uppercase();
let lastChar = contents
.chars()
.nth(contents.chars().count() - 1)
.ok_or(Exceptions::INDEX_OUT_OF_BOUNDS)?
.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::illegal_argument_with(format!(
"Invalid start/end guards: {contents}"
)));
}
// else already has valid start/end
contents.to_owned()
} else if startsAlt {
if !endsAlt {
return Err(Exceptions::illegal_argument_with(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::illegal_argument_with(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 ch in contents[1..contents.chars().count() - 1].chars() {
if ch.is_ascii_digit() || ch == '-' || ch == '$' {
resultLength += 9;
} else if CodaBarReader::arrayContains(
&CHARS_WHICH_ARE_TEN_LENGTH_EACH_AFTER_DECODED,
ch,
) {
resultLength += 10;
} else {
return Err(Exceptions::illegal_argument_with(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)
.ok_or(Exceptions::INDEX_OUT_OF_BOUNDS)?
.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() {
// 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])
}
}
/**
* @author dsbnatut@gmail.com (Kazuki Nishiura)
* @author Sean Owen
*/
#[cfg(test)]
mod CodaBarWriterTestCase {
use crate::{
common::{bit_matrix_test_case, BitMatrix},
BarcodeFormat, Writer,
};
use super::CodaBarWriter;
#[test]
fn testEncode() {
doTest(
"B515-3/B",
concat!(
"00000",
"1001001011",
"0110101001",
"0101011001",
"0110101001",
"0101001101",
"0110010101",
"01101101011",
"01001001011",
"00000"
),
);
}
#[test]
fn testEncode2() {
doTest(
"T123T",
concat!(
"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, bit_matrix_test_case::matrix_to_string(&result));
}
fn encode(input: &str) -> BitMatrix {
CodaBarWriter::default()
.encode(input, &BarcodeFormat::CODABAR, 0, 0)
.expect("must encode")
}
}