mirror of
https://github.com/starovoid/rxing.git
synced 2026-07-26 04:12:34 +00:00
codabar_writer + test
This commit is contained in:
@@ -1,140 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.google.zxing.oned;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* This class renders CodaBar as {@code boolean[]}.
|
||||
*
|
||||
* @author dsbnatut@gmail.com (Kazuki Nishiura)
|
||||
*/
|
||||
public final class CodaBarWriter extends OneDimensionalCodeWriter {
|
||||
|
||||
private static final char[] START_END_CHARS = {'A', 'B', 'C', 'D'};
|
||||
private static final char[] ALT_START_END_CHARS = {'T', 'N', '*', 'E'};
|
||||
private static final char[] CHARS_WHICH_ARE_TEN_LENGTH_EACH_AFTER_DECODED = {'/', ':', '+', '.'};
|
||||
private static final char DEFAULT_GUARD = START_END_CHARS[0];
|
||||
|
||||
@Override
|
||||
protected Collection<BarcodeFormat> getSupportedWriteFormats() {
|
||||
return Collections.singleton(BarcodeFormat.CODABAR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean[] encode(String contents) {
|
||||
|
||||
if (contents.length() < 2) {
|
||||
// Can't have a start/end guard, so tentatively add default guards
|
||||
contents = DEFAULT_GUARD + contents + DEFAULT_GUARD;
|
||||
} else {
|
||||
// Verify input and calculate decoded length.
|
||||
char firstChar = Character.toUpperCase(contents.charAt(0));
|
||||
char lastChar = Character.toUpperCase(contents.charAt(contents.length() - 1));
|
||||
boolean startsNormal = CodaBarReader.arrayContains(START_END_CHARS, firstChar);
|
||||
boolean endsNormal = CodaBarReader.arrayContains(START_END_CHARS, lastChar);
|
||||
boolean startsAlt = CodaBarReader.arrayContains(ALT_START_END_CHARS, firstChar);
|
||||
boolean endsAlt = CodaBarReader.arrayContains(ALT_START_END_CHARS, lastChar);
|
||||
if (startsNormal) {
|
||||
if (!endsNormal) {
|
||||
throw new IllegalArgumentException("Invalid start/end guards: " + contents);
|
||||
}
|
||||
// else already has valid start/end
|
||||
} else if (startsAlt) {
|
||||
if (!endsAlt) {
|
||||
throw new IllegalArgumentException("Invalid start/end guards: " + contents);
|
||||
}
|
||||
// else already has valid start/end
|
||||
} else {
|
||||
// Doesn't start with a guard
|
||||
if (endsNormal || endsAlt) {
|
||||
throw new IllegalArgumentException("Invalid start/end guards: " + contents);
|
||||
}
|
||||
// else doesn't end with guard either, so add a default
|
||||
contents = DEFAULT_GUARD + contents + DEFAULT_GUARD;
|
||||
}
|
||||
}
|
||||
|
||||
// The start character and the end character are decoded to 10 length each.
|
||||
int resultLength = 20;
|
||||
for (int i = 1; i < contents.length() - 1; i++) {
|
||||
if (Character.isDigit(contents.charAt(i)) || contents.charAt(i) == '-' || contents.charAt(i) == '$') {
|
||||
resultLength += 9;
|
||||
} else if (CodaBarReader.arrayContains(CHARS_WHICH_ARE_TEN_LENGTH_EACH_AFTER_DECODED, contents.charAt(i))) {
|
||||
resultLength += 10;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Cannot encode : '" + contents.charAt(i) + '\'');
|
||||
}
|
||||
}
|
||||
// A blank is placed between each character.
|
||||
resultLength += contents.length() - 1;
|
||||
|
||||
boolean[] result = new boolean[resultLength];
|
||||
int position = 0;
|
||||
for (int index = 0; index < contents.length(); index++) {
|
||||
char c = Character.toUpperCase(contents.charAt(index));
|
||||
if (index == 0 || index == contents.length() - 1) {
|
||||
// The start/end chars are not in the CodaBarReader.ALPHABET.
|
||||
switch (c) {
|
||||
case 'T':
|
||||
c = 'A';
|
||||
break;
|
||||
case 'N':
|
||||
c = 'B';
|
||||
break;
|
||||
case '*':
|
||||
c = 'C';
|
||||
break;
|
||||
case 'E':
|
||||
c = 'D';
|
||||
break;
|
||||
}
|
||||
}
|
||||
int code = 0;
|
||||
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;
|
||||
}
|
||||
}
|
||||
boolean color = true;
|
||||
int counter = 0;
|
||||
int bit = 0;
|
||||
while (bit < 7) { // A character consists of 7 digit.
|
||||
result[position] = color;
|
||||
position++;
|
||||
if (((code >> (6 - bit)) & 1) == 0 || counter == 1) {
|
||||
color = !color; // Flip the color.
|
||||
bit++;
|
||||
counter = 0;
|
||||
} else {
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
if (index < contents.length() - 1) {
|
||||
result[position] = false;
|
||||
position++;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* @author dsbnatut@gmail.com (Kazuki Nishiura)
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public final class CodaBarWriterTestCase extends Assert {
|
||||
|
||||
@Test
|
||||
public void testEncode() {
|
||||
doTest("B515-3/B",
|
||||
"00000" +
|
||||
"1001001011" + "0110101001" + "0101011001" + "0110101001" + "0101001101" +
|
||||
"0110010101" + "01101101011" + "01001001011" +
|
||||
"00000");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncode2() {
|
||||
doTest("T123T",
|
||||
"00000" +
|
||||
"1011001001" + "0101011001" + "0101001011" + "0110010101" + "01011001001" +
|
||||
"00000");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAltStartEnd() {
|
||||
assertEquals(encode("T123456789-$T"), encode("A123456789-$A"));
|
||||
}
|
||||
|
||||
private static void doTest(String input, CharSequence expected) {
|
||||
BitMatrix result = encode(input);
|
||||
assertEquals(expected, BitMatrixTestCase.matrixToString(result));
|
||||
}
|
||||
|
||||
private static BitMatrix encode(String input) {
|
||||
return new CodaBarWriter().encode(input, BarcodeFormat.CODABAR, 0, 0);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.google.zxing.oned;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.google.zxing.EncodeHintType;
|
||||
import com.google.zxing.Writer;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* <p>Encapsulates functionality and implementation that is common to one-dimensional barcodes.</p>
|
||||
*
|
||||
* @author dsbnatut@gmail.com (Kazuki Nishiura)
|
||||
*/
|
||||
public abstract class OneDimensionalCodeWriter implements Writer {
|
||||
private static final Pattern NUMERIC = Pattern.compile("[0-9]+");
|
||||
|
||||
/**
|
||||
* Encode the contents to boolean array expression of one-dimensional barcode.
|
||||
* Start code and end code should be included in result, and side margins should not be included.
|
||||
*
|
||||
* @param contents barcode contents to encode
|
||||
* @return a {@code boolean[]} of horizontal pixels (false = white, true = black)
|
||||
*/
|
||||
public abstract boolean[] encode(String contents);
|
||||
|
||||
/**
|
||||
* Can be overwritten if the encode requires to read the hints map. Otherwise it defaults to {@code encode}.
|
||||
* @param contents barcode contents to encode
|
||||
* @param hints encoding hints
|
||||
* @return a {@code boolean[]} of horizontal pixels (false = white, true = black)
|
||||
*/
|
||||
protected boolean[] encode(String contents, Map<EncodeHintType,?> hints) {
|
||||
return encode(contents);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final BitMatrix encode(String contents, BarcodeFormat format, int width, int height) {
|
||||
return encode(contents, format, width, height, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode the contents following specified format.
|
||||
* {@code width} and {@code height} are required size. This method may return bigger size
|
||||
* {@code BitMatrix} when specified size is too small. The user can set both {@code width} and
|
||||
* {@code height} to zero to get minimum size barcode. If negative value is set to {@code width}
|
||||
* or {@code height}, {@code IllegalArgumentException} is thrown.
|
||||
*/
|
||||
@Override
|
||||
public BitMatrix encode(String contents,
|
||||
BarcodeFormat format,
|
||||
int width,
|
||||
int height,
|
||||
Map<EncodeHintType,?> hints) {
|
||||
if (contents.isEmpty()) {
|
||||
throw new IllegalArgumentException("Found empty contents");
|
||||
}
|
||||
|
||||
if (width < 0 || height < 0) {
|
||||
throw new IllegalArgumentException("Negative size is not allowed. Input: "
|
||||
+ width + 'x' + height);
|
||||
}
|
||||
Collection<BarcodeFormat> supportedFormats = getSupportedWriteFormats();
|
||||
if (supportedFormats != null && !supportedFormats.contains(format)) {
|
||||
throw new IllegalArgumentException("Can only encode " + supportedFormats +
|
||||
", but got " + format);
|
||||
}
|
||||
|
||||
int sidesMargin = getDefaultMargin();
|
||||
if (hints != null && hints.containsKey(EncodeHintType.MARGIN)) {
|
||||
sidesMargin = Integer.parseInt(hints.get(EncodeHintType.MARGIN).toString());
|
||||
}
|
||||
|
||||
boolean[] code = encode(contents, hints);
|
||||
return renderRXingResult(code, width, height, sidesMargin);
|
||||
}
|
||||
|
||||
protected Collection<BarcodeFormat> getSupportedWriteFormats() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a byte array of horizontal pixels (0 = white, 1 = black)
|
||||
*/
|
||||
private static BitMatrix renderRXingResult(boolean[] code, int width, int height, int sidesMargin) {
|
||||
int inputWidth = code.length;
|
||||
// Add quiet zone on both sides.
|
||||
int fullWidth = inputWidth + sidesMargin;
|
||||
int outputWidth = Math.max(width, fullWidth);
|
||||
int outputHeight = Math.max(1, height);
|
||||
|
||||
int multiple = outputWidth / fullWidth;
|
||||
int leftPadding = (outputWidth - (inputWidth * multiple)) / 2;
|
||||
|
||||
BitMatrix output = new BitMatrix(outputWidth, outputHeight);
|
||||
for (int inputX = 0, outputX = leftPadding; inputX < inputWidth; inputX++, outputX += multiple) {
|
||||
if (code[inputX]) {
|
||||
output.setRegion(outputX, 0, multiple, outputHeight);
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param contents string to check for numeric characters
|
||||
* @throws IllegalArgumentException if input contains characters other than digits 0-9.
|
||||
*/
|
||||
protected static void checkNumeric(String contents) {
|
||||
if (!NUMERIC.matcher(contents).matches()) {
|
||||
throw new IllegalArgumentException("Input should only contain digits 0-9");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param target encode black/white pattern into this array
|
||||
* @param pos position to start encoding at in {@code target}
|
||||
* @param pattern lengths of black/white runs to encode
|
||||
* @param startColor starting color - false for white, true for black
|
||||
* @return the number of elements added to target.
|
||||
*/
|
||||
protected static int appendPattern(boolean[] target, int pos, int[] pattern, boolean startColor) {
|
||||
boolean color = startColor;
|
||||
int numAdded = 0;
|
||||
for (int len : pattern) {
|
||||
for (int j = 0; j < len; j++) {
|
||||
target[pos++] = color;
|
||||
}
|
||||
numAdded += len;
|
||||
color = !color; // flip color after each segment
|
||||
}
|
||||
return numAdded;
|
||||
}
|
||||
|
||||
public int getDefaultMargin() {
|
||||
// CodaBar spec requires a side margin to be more than ten times wider than narrow space.
|
||||
// This seems like a decent idea for a default for all formats.
|
||||
return 10;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,11 +174,11 @@ impl CodaBarReader {
|
||||
// These values are critical for determining how permissive the decoding
|
||||
// will be. All stripe sizes must be within the window these define, as
|
||||
// compared to the average stripe size.
|
||||
const MAX_ACCEPTABLE: f32 = 2.0;
|
||||
const PADDING: f32 = 1.5;
|
||||
pub const MAX_ACCEPTABLE: f32 = 2.0;
|
||||
pub const PADDING: f32 = 1.5;
|
||||
|
||||
// const ALPHABET_STRING : &str= "0123456789-$:/.+ABCD";
|
||||
const ALPHABET: [char; 20] = [
|
||||
pub const ALPHABET: [char; 20] = [
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '$', ':', '/', '.', '+', 'A', 'B',
|
||||
'C', 'D',
|
||||
];
|
||||
@@ -187,7 +187,7 @@ impl CodaBarReader {
|
||||
* These represent the encodings of characters, as patterns of wide and narrow bars. The 7 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; 20] = [
|
||||
pub const CHARACTER_ENCODINGS: [u32; 20] = [
|
||||
0x003, 0x006, 0x009, 0x060, 0x012, 0x042, 0x021, 0x024, 0x030, 0x048, // 0-9
|
||||
0x00c, 0x018, 0x045, 0x051, 0x054, 0x015, 0x01A, 0x029, 0x00B, 0x00E, // -$:/.+ABCD
|
||||
];
|
||||
@@ -195,10 +195,10 @@ impl CodaBarReader {
|
||||
// minimal number of characters that should be present (including start and stop characters)
|
||||
// under normal circumstances this should be set to 3, but can be set higher
|
||||
// as a last-ditch attempt to reduce false positives.
|
||||
const MIN_CHARACTER_LENGTH: u32 = 3;
|
||||
pub const MIN_CHARACTER_LENGTH: u32 = 3;
|
||||
|
||||
// official start and end patterns
|
||||
const STARTEND_ENCODING: [char; 4] = ['A', 'B', 'C', 'D'];
|
||||
pub const STARTEND_ENCODING: [char; 4] = ['A', 'B', 'C', 'D'];
|
||||
// some Codabar generator allow the Codabar string to be closed by every
|
||||
// character. This will cause lots of false positives!
|
||||
|
||||
|
||||
230
src/oned/coda_bar_writer.rs
Normal file
230
src/oned/coda_bar_writer.rs
Normal 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")
|
||||
}
|
||||
}
|
||||
@@ -46,3 +46,9 @@ pub use upc_a_reader::*;
|
||||
|
||||
mod upc_e_reader;
|
||||
pub use upc_e_reader::*;
|
||||
|
||||
mod one_d_code_writer;
|
||||
pub use one_d_code_writer::*;
|
||||
|
||||
mod coda_bar_writer;
|
||||
pub use coda_bar_writer::*;
|
||||
|
||||
203
src/oned/one_d_code_writer.rs
Normal file
203
src/oned/one_d_code_writer.rs
Normal file
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* 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 std::collections::HashMap;
|
||||
|
||||
use crate::{
|
||||
common::BitMatrix, BarcodeFormat, EncodeHintType, EncodeHintValue, Exceptions, Writer,
|
||||
};
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use regex::Regex;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref NUMERIC: Regex = Regex::new("[0-9]+").unwrap();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Encapsulates functionality and implementation that is common to one-dimensional barcodes.</p>
|
||||
*
|
||||
* @author dsbnatut@gmail.com (Kazuki Nishiura)
|
||||
*/
|
||||
pub trait OneDimensionalCodeWriter: Writer {
|
||||
// private static final Pattern NUMERIC = Pattern.compile("[0-9]+");
|
||||
|
||||
/**
|
||||
* Encode the contents to boolean array expression of one-dimensional barcode.
|
||||
* Start code and end code should be included in result, and side margins should not be included.
|
||||
*
|
||||
* @param contents barcode contents to encode
|
||||
* @return a {@code boolean[]} of horizontal pixels (false = white, true = black)
|
||||
*/
|
||||
fn encode_oned(&self, contents: &str) -> Result<Vec<bool>, Exceptions>;
|
||||
|
||||
/**
|
||||
* Can be overwritten if the encode requires to read the hints map. Otherwise it defaults to {@code encode}.
|
||||
* @param contents barcode contents to encode
|
||||
* @param hints encoding hints
|
||||
* @return a {@code boolean[]} of horizontal pixels (false = white, true = black)
|
||||
*/
|
||||
fn encode_oned_with_hints(
|
||||
&self,
|
||||
contents: &str,
|
||||
_hints: &crate::EncodingHintDictionary,
|
||||
) -> Result<Vec<bool>, Exceptions> {
|
||||
self.encode_oned(contents)
|
||||
}
|
||||
|
||||
fn getSupportedWriteFormats(&self) -> Option<Vec<BarcodeFormat>> {
|
||||
None
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a byte array of horizontal pixels (0 = white, 1 = black)
|
||||
*/
|
||||
fn renderRXingResult(
|
||||
code: &[bool],
|
||||
width: i32,
|
||||
height: i32,
|
||||
sidesMargin: u32,
|
||||
) -> Result<BitMatrix, Exceptions> {
|
||||
let inputWidth = code.len();
|
||||
// Add quiet zone on both sides.
|
||||
let fullWidth = inputWidth + sidesMargin as usize;
|
||||
let outputWidth = width.max(fullWidth as i32);
|
||||
let outputHeight = 1.max(height);
|
||||
|
||||
let multiple = outputWidth as usize / fullWidth;
|
||||
let leftPadding = (outputWidth as isize - (inputWidth as isize * multiple as isize)) / 2;
|
||||
|
||||
let mut output = BitMatrix::new(outputWidth as u32, outputHeight as u32)?;
|
||||
|
||||
let mut inputX = 0;
|
||||
let mut outputX = leftPadding;
|
||||
|
||||
while inputX < inputWidth {
|
||||
// for (int inputX = 0, outputX = leftPadding; inputX < inputWidth; inputX++, outputX += multiple) {
|
||||
if code[inputX] {
|
||||
output.setRegion(outputX as u32, 0, multiple as u32, outputHeight as u32)?;
|
||||
}
|
||||
|
||||
inputX += 1;
|
||||
outputX += multiple as isize;
|
||||
}
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param contents string to check for numeric characters
|
||||
* @throws IllegalArgumentException if input contains characters other than digits 0-9.
|
||||
*/
|
||||
fn checkNumeric(contents: &str) -> Result<(), Exceptions> {
|
||||
if !NUMERIC.is_match(contents) {
|
||||
Err(Exceptions::IllegalArgumentException(
|
||||
"Input should only contain digits 0-9".to_owned(),
|
||||
))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param target encode black/white pattern into this array
|
||||
* @param pos position to start encoding at in {@code target}
|
||||
* @param pattern lengths of black/white runs to encode
|
||||
* @param startColor starting color - false for white, true for black
|
||||
* @return the number of elements added to target.
|
||||
*/
|
||||
fn appendPattern(target: &mut [bool], pos: usize, pattern: &[usize], startColor: bool) -> u32 {
|
||||
let mut color = startColor;
|
||||
let mut numAdded = 0;
|
||||
let mut pos = pos;
|
||||
for len in pattern {
|
||||
// for (int len : pattern) {
|
||||
for _j in 0..*len {
|
||||
// for (int j = 0; j < len; j++) {
|
||||
target[pos] = color;
|
||||
pos += 1;
|
||||
}
|
||||
numAdded += len;
|
||||
color = !color; // flip color after each segment
|
||||
}
|
||||
numAdded as u32
|
||||
}
|
||||
|
||||
fn getDefaultMargin(&self) -> u32 {
|
||||
// CodaBar spec requires a side margin to be more than ten times wider than narrow space.
|
||||
// This seems like a decent idea for a default for all formats.
|
||||
10
|
||||
}
|
||||
}
|
||||
|
||||
struct L;
|
||||
impl Writer for L {
|
||||
fn encode(
|
||||
&self,
|
||||
contents: &str,
|
||||
format: &crate::BarcodeFormat,
|
||||
width: i32,
|
||||
height: i32,
|
||||
) -> Result<crate::common::BitMatrix, crate::Exceptions> {
|
||||
self.encode_with_hints(contents, format, width, height, &HashMap::new())
|
||||
}
|
||||
|
||||
fn encode_with_hints(
|
||||
&self,
|
||||
contents: &str,
|
||||
format: &crate::BarcodeFormat,
|
||||
width: i32,
|
||||
height: i32,
|
||||
hints: &crate::EncodingHintDictionary,
|
||||
) -> Result<crate::common::BitMatrix, crate::Exceptions> {
|
||||
if contents.is_empty() {
|
||||
return Err(Exceptions::IllegalArgumentException(
|
||||
"Found empty contents".to_owned(),
|
||||
));
|
||||
}
|
||||
|
||||
if width < 0 || height < 0 {
|
||||
return Err(Exceptions::IllegalArgumentException(format!(
|
||||
"Negative size is not allowed. Input: {}x{}",
|
||||
width, height
|
||||
)));
|
||||
}
|
||||
if let Some(supportedFormats) = self.getSupportedWriteFormats() {
|
||||
if !supportedFormats.contains(format) {
|
||||
return Err(Exceptions::IllegalArgumentException(format!(
|
||||
"Can only encode {:?}, but got {:?}",
|
||||
supportedFormats, format
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
let mut sidesMargin = self.getDefaultMargin();
|
||||
if let Some(EncodeHintValue::Margin(margin)) = hints.get(&EncodeHintType::MARGIN) {
|
||||
sidesMargin = margin.parse::<u32>().unwrap();
|
||||
}
|
||||
// if hints.contains_key(&EncodeHintType::MARGIN) {
|
||||
// sidesMargin = Integer.parseInt(hints.get(EncodeHintType.MARGIN).toString());
|
||||
// }
|
||||
|
||||
let code = self.encode_oned_with_hints(contents, hints)?;
|
||||
|
||||
Self::renderRXingResult(&code, width, height, sidesMargin)
|
||||
}
|
||||
}
|
||||
impl OneDimensionalCodeWriter for L {
|
||||
fn encode_oned(&self, contents: &str) -> Result<Vec<bool>, Exceptions> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user