code 39 writer + tests

This commit is contained in:
Henry Schimke
2022-12-10 17:43:47 -06:00
parent f13d8bd479
commit 20f8d15065
4 changed files with 260 additions and 239 deletions

View File

@@ -1,141 +0,0 @@
/*
* Copyright 2010 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.
*/
package com.google.zxing.oned;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.common.BitMatrix;
import java.util.Collection;
import java.util.Collections;
/**
* This object renders a CODE39 code as a {@link BitMatrix}.
*
* @author erik.barbara@gmail.com (Erik Barbara)
*/
public final class Code39Writer extends OneDimensionalCodeWriter {
@Override
protected Collection<BarcodeFormat> getSupportedWriteFormats() {
return Collections.singleton(BarcodeFormat.CODE_39);
}
@Override
public boolean[] encode(String contents) {
int length = contents.length();
if (length > 80) {
throw new IllegalArgumentException(
"Requested contents should be less than 80 digits long, but got " + length);
}
for (int i = 0; i < length; i++) {
int indexInString = Code39Reader.ALPHABET_STRING.indexOf(contents.charAt(i));
if (indexInString < 0) {
contents = tryToConvertToExtendedMode(contents);
length = contents.length();
if (length > 80) {
throw new IllegalArgumentException("Requested contents should be less than 80 digits long, but got " +
length + " (extended full ASCII mode)");
}
break;
}
}
int[] widths = new int[9];
int codeWidth = 24 + 1 + (13 * length);
boolean[] result = new boolean[codeWidth];
toIntArray(Code39Reader.ASTERISK_ENCODING, widths);
int pos = appendPattern(result, 0, widths, true);
int[] narrowWhite = {1};
pos += appendPattern(result, pos, narrowWhite, false);
//append next character to byte matrix
for (int i = 0; i < length; i++) {
int indexInString = Code39Reader.ALPHABET_STRING.indexOf(contents.charAt(i));
toIntArray(Code39Reader.CHARACTER_ENCODINGS[indexInString], widths);
pos += appendPattern(result, pos, widths, true);
pos += appendPattern(result, pos, narrowWhite, false);
}
toIntArray(Code39Reader.ASTERISK_ENCODING, widths);
appendPattern(result, pos, widths, true);
return result;
}
private static void toIntArray(int a, int[] toReturn) {
for (int i = 0; i < 9; i++) {
int temp = a & (1 << (8 - i));
toReturn[i] = temp == 0 ? 1 : 2;
}
}
private static String tryToConvertToExtendedMode(String contents) {
int length = contents.length();
StringBuilder extendedContent = new StringBuilder();
for (int i = 0; i < length; i++) {
char character = contents.charAt(i);
switch (character) {
case '\u0000':
extendedContent.append("%U");
break;
case ' ':
case '-':
case '.':
extendedContent.append(character);
break;
case '@':
extendedContent.append("%V");
break;
case '`':
extendedContent.append("%W");
break;
default:
if (character <= 26) {
extendedContent.append('$');
extendedContent.append((char) ('A' + (character - 1)));
} else if (character < ' ') {
extendedContent.append('%');
extendedContent.append((char) ('A' + (character - 27)));
} else if (character <= ',' || character == '/' || character == ':') {
extendedContent.append('/');
extendedContent.append((char) ('A' + (character - 33)));
} else if (character <= '9') {
extendedContent.append((char) ('0' + (character - 48)));
} else if (character <= '?') {
extendedContent.append('%');
extendedContent.append((char) ('F' + (character - 59)));
} else if (character <= 'Z') {
extendedContent.append((char) ('A' + (character - 65)));
} else if (character <= '_') {
extendedContent.append('%');
extendedContent.append((char) ('K' + (character - 91)));
} else if (character <= 'z') {
extendedContent.append('+');
extendedContent.append((char) ('A' + (character - 97)));
} else if (character <= 127) {
extendedContent.append('%');
extendedContent.append((char) ('P' + (character - 123)));
} else {
throw new IllegalArgumentException(
"Requested content contains a non-encodable character: '" + contents.charAt(i) + "'");
}
break;
}
}
return extendedContent.toString();
}
}

View File

@@ -1,94 +0,0 @@
/*
* Copyright 2016 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.
*/
package com.google.zxing.oned;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.BitMatrixTestCase;
import org.junit.Assert;
import org.junit.Test;
/**
* Tests {@link Code39Writer}.
*/
public final class Code39WriterTestCase extends Assert {
@SuppressWarnings("checkstyle:lineLength")
@Test
public void testEncode() {
doTest("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
"000001001011011010110101001011010110100101101101101001010101011001011011010110010101" +
"011011001010101010011011011010100110101011010011010101011001101011010101001101011010" +
"100110110110101001010101101001101101011010010101101101001010101011001101101010110010" +
"101101011001010101101100101100101010110100110101011011001101010101001011010110110010" +
"110101010011011010101010011011010110100101011010110010101101101100101010101001101011" +
"01101001101010101100110101010100101101101101001011010101100101101010010110110100000");
// extended mode blocks
doTest("\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f",
"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");
}
private static void doTest(String input, CharSequence expected) {
BitMatrix result = new Code39Writer().encode(input, BarcodeFormat.CODE_39, 0, 0);
assertEquals(input, expected, BitMatrixTestCase.matrixToString(result));
}
}

View File

@@ -42,7 +42,7 @@ impl OneDReader for Code39Reader {
_hints: &DecodingHintDictionary,
) -> Result<crate::RXingResult, Exceptions> {
// let theCounters = self.counters;
let mut counters = [0_u32;9];
let mut counters = [0_u32; 9];
// self.counters.fill(0);
// let result = self.decodeRowRXingResult;
// result.setLength(0);
@@ -145,14 +145,14 @@ impl OneDReader for Code39Reader {
}
}
impl Code39Reader {
const ALPHABET_STRING: &str = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%";
pub const ALPHABET_STRING: &str = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%";
/**
* These represent the encodings of characters, as patterns of wide and narrow bars.
* The 9 least-significant bits of each int correspond to the pattern of wide and narrow,
* with 1s representing "wide" and 0s representing narrow.
*/
const CHARACTER_ENCODINGS: [u32; 43] = [
pub const CHARACTER_ENCODINGS: [u32; 43] = [
0x034, 0x121, 0x061, 0x160, 0x031, 0x130, 0x070, 0x025, 0x124, 0x064, // 0-9
0x109, 0x049, 0x148, 0x019, 0x118, 0x058, 0x00D, 0x10C, 0x04C, 0x01C, // A-J
0x103, 0x043, 0x142, 0x013, 0x112, 0x052, 0x007, 0x106, 0x046, 0x016, // K-T
@@ -160,7 +160,7 @@ impl Code39Reader {
0x0A2, 0x08A, 0x02A, // /-%
];
const ASTERISK_ENCODING: u32 = 0x094;
pub const ASTERISK_ENCODING: u32 = 0x094;
/**
* Creates a reader that assumes all encoded data is data, and does not treat the final

256
src/oned/code_39_writer.rs Normal file
View File

@@ -0,0 +1,256 @@
/*
* Copyright 2010 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::{Code39Reader, OneDimensionalCodeWriter};
/**
* This object renders a CODE39 code as a {@link BitMatrix}.
*
* @author erik.barbara@gmail.com (Erik Barbara)
*/
#[derive(OneDWriter)]
pub struct Code39Writer;
impl Default for Code39Writer {
fn default() -> Self {
Self {}
}
}
impl OneDimensionalCodeWriter for Code39Writer {
fn encode_oned(&self, contents: &str) -> Result<Vec<bool>, Exceptions> {
let mut contents = contents.to_owned();
let mut length = contents.chars().count();
if length > 80 {
return Err(Exceptions::IllegalArgumentException(format!(
"Requested contents should be less than 80 digits long, but got {}",
length
)));
}
for i in 0..length {
// for (int i = 0; i < length; i++) {
if let None = Code39Reader::ALPHABET_STRING.find(contents.chars().nth(i).unwrap()) {
contents = Self::tryToConvertToExtendedMode(&contents)?;
length = contents.chars().count();
if length > 80 {
return Err(Exceptions::IllegalArgumentException(format!("Requested contents should be less than 80 digits long, but got {} (extended full ASCII mode)",length)));
}
break;
}
//let indexInString = Code39Reader::ALPHABET_STRING.indexOf(contents.charAt(i));
// if indexInString < 0 {
// contents = Self::tryToConvertToExtendedMode(&contents);
// length = contents.chars().count();
// if length > 80 {
// return Err(Exceptions::IllegalArgumentException(format!("Requested contents should be less than 80 digits long, but got {} (extended full ASCII mode)",length)));
// }
// break;
// }
}
let mut widths = [0_usize; 9]; //new int[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);
//append next character to byte matrix
for i in 0..length {
// for (int i = 0; i < length; i++) {
let Some(indexInString) = Code39Reader::ALPHABET_STRING.find(contents.chars().nth(i).unwrap()) else {
continue;
};
// let indexInString = Code39Reader::ALPHABET_STRING.indexOf(contents.charAt(i));
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 in 0..9 {
// for (int i = 0; i < 9; i++) {
let temp = a & (1 << (8 - i));
toReturn[i] = if temp == 0 { 1 } else { 2 };
}
}
fn tryToConvertToExtendedMode(contents: &str) -> Result<String, Exceptions> {
// let length = contents.chars().count();
let mut extendedContent = String::new(); //new StringBuilder();
for character in contents.chars() {
// for (int i = 0; i < length; i++) {
// char character = contents.charAt(i);
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)).unwrap());
} else if character < ' ' {
extendedContent.push('%');
extendedContent
.push(char::from_u32('A' as u32 + (character as u32 - 27)).unwrap());
} else if character <= ',' || character == '/' || character == ':' {
extendedContent.push('/');
extendedContent
.push(char::from_u32('A' as u32 + (character as u32 - 33)).unwrap());
} else if character <= '9' {
extendedContent
.push(char::from_u32('0' as u32 + (character as u32 - 48)).unwrap());
} else if character <= '?' {
extendedContent.push('%');
extendedContent
.push(char::from_u32('F' as u32 + (character as u32 - 59)).unwrap());
} else if character <= 'Z' {
extendedContent
.push(char::from_u32('A' as u32 + (character as u32 - 65)).unwrap());
} else if character <= '_' {
extendedContent.push('%');
extendedContent
.push(char::from_u32('K' as u32 + (character as u32 - 91)).unwrap());
} else if character <= 'z' {
extendedContent.push('+');
extendedContent
.push(char::from_u32('A' as u32 + (character as u32 - 97)).unwrap());
} else if character as u32 <= 127 {
extendedContent.push('%');
extendedContent
.push(char::from_u32('P' as u32 + (character as u32 - 123)).unwrap());
} else {
return Err(Exceptions::IllegalArgumentException(format!(
"Requested content contains a non-encodable character: '{}'",
character
)));
}
}
}
}
Ok(extendedContent)
}
}
#[cfg(test)]
/**
* Tests {@link Code39Writer}.
*/
mod Code39WriterTestCase {
use crate::{common::BitMatrixTestCase, oned::Code39Writer, BarcodeFormat, Writer};
#[test]
fn testEncode() {
doTest(
"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
"000001001011011010110101001011010110100101101101101001010101011001011011010110010101\
011011001010101010011011011010100110101011010011010101011001101011010101001101011010\
100110110110101001010101101001101101011010010101101101001010101011001101101010110010\
101101011001010101101100101100101010110100110101011011001101010101001011010110110010\
110101010011011010101010011011010110100101011010110010101101101100101010101001101011\
01101001101010101100110101010100101101101101001011010101100101101010010110110100000",
);
// extended mode blocks
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::default()
.encode(input, &BarcodeFormat::CODE_39, 0, 0)
.expect("must encode");
assert_eq!(
expected,
BitMatrixTestCase::matrix_to_string(&result),
"{}",
input
);
}
}