mirror of
https://github.com/zxing/zxing.git
synced 2024-11-09 20:44:03 -08:00
Issue 1143: enable Codabar after improved decoding avoids false positives
git-svn-id: https://zxing.googlecode.com/svn/trunk@2158 59b500cc-1b3d-0410-9834-0bbf25fbcc57
This commit is contained in:
parent
5d89cc30fc
commit
2c84200f64
2
AUTHORS
2
AUTHORS
|
@ -21,7 +21,7 @@ Dave MacLachlan (Google)
|
||||||
David Phillip Oster (Google)
|
David Phillip Oster (Google)
|
||||||
David Albert (Bug Labs)
|
David Albert (Bug Labs)
|
||||||
David Olivier
|
David Olivier
|
||||||
dawalker (Google)
|
David Walker (Google)
|
||||||
Diego Pierotto
|
Diego Pierotto
|
||||||
drejc83
|
drejc83
|
||||||
Eduardo Castillejo (University of Deusto)
|
Eduardo Castillejo (University of Deusto)
|
||||||
|
|
|
@ -43,7 +43,8 @@ final class DecodeFormatManager {
|
||||||
ONE_D_FORMATS = EnumSet.of(BarcodeFormat.CODE_39,
|
ONE_D_FORMATS = EnumSet.of(BarcodeFormat.CODE_39,
|
||||||
BarcodeFormat.CODE_93,
|
BarcodeFormat.CODE_93,
|
||||||
BarcodeFormat.CODE_128,
|
BarcodeFormat.CODE_128,
|
||||||
BarcodeFormat.ITF);
|
BarcodeFormat.ITF,
|
||||||
|
BarcodeFormat.CODABAR);
|
||||||
ONE_D_FORMATS.addAll(PRODUCT_FORMATS);
|
ONE_D_FORMATS.addAll(PRODUCT_FORMATS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -105,7 +105,7 @@ public final class MultiFormatReader implements Reader {
|
||||||
formats.contains(BarcodeFormat.UPC_E) ||
|
formats.contains(BarcodeFormat.UPC_E) ||
|
||||||
formats.contains(BarcodeFormat.EAN_13) ||
|
formats.contains(BarcodeFormat.EAN_13) ||
|
||||||
formats.contains(BarcodeFormat.EAN_8) ||
|
formats.contains(BarcodeFormat.EAN_8) ||
|
||||||
//formats.contains(BarcodeFormat.CODABAR) ||
|
formats.contains(BarcodeFormat.CODABAR) ||
|
||||||
formats.contains(BarcodeFormat.CODE_39) ||
|
formats.contains(BarcodeFormat.CODE_39) ||
|
||||||
formats.contains(BarcodeFormat.CODE_93) ||
|
formats.contains(BarcodeFormat.CODE_93) ||
|
||||||
formats.contains(BarcodeFormat.CODE_128) ||
|
formats.contains(BarcodeFormat.CODE_128) ||
|
||||||
|
|
|
@ -29,119 +29,127 @@ import java.util.Map;
|
||||||
* <p>Decodes Codabar barcodes.</p>
|
* <p>Decodes Codabar barcodes.</p>
|
||||||
*
|
*
|
||||||
* @author Bas Vijfwinkel
|
* @author Bas Vijfwinkel
|
||||||
|
* @author David Walker
|
||||||
*/
|
*/
|
||||||
public final class CodaBarReader extends OneDReader {
|
public final class CodaBarReader extends OneDReader {
|
||||||
|
|
||||||
private static final String ALPHABET_STRING = "0123456789-$:/.+ABCDTN";
|
// 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.
|
||||||
|
private static final int MAX_ACCEPTABLE = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 2.0f);
|
||||||
|
private static final int PADDING = (int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 1.5f);
|
||||||
|
|
||||||
|
private static final String ALPHABET_STRING = "0123456789-$:/.+ABCD";
|
||||||
static final char[] ALPHABET = ALPHABET_STRING.toCharArray();
|
static final char[] ALPHABET = ALPHABET_STRING.toCharArray();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* These represent the encodings of characters, as patterns of wide and narrow bars. The 7 least-significant bits of
|
* 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. NOTE
|
* each int correspond to the pattern of wide and narrow, with 1s representing "wide" and 0s representing narrow.
|
||||||
* : c is equal to the * pattern NOTE : d is equal to the e pattern
|
|
||||||
*/
|
*/
|
||||||
static final int[] CHARACTER_ENCODINGS = {
|
static final int[] CHARACTER_ENCODINGS = {
|
||||||
0x003, 0x006, 0x009, 0x060, 0x012, 0x042, 0x021, 0x024, 0x030, 0x048, // 0-9
|
0x003, 0x006, 0x009, 0x060, 0x012, 0x042, 0x021, 0x024, 0x030, 0x048, // 0-9
|
||||||
0x00c, 0x018, 0x045, 0x051, 0x054, 0x015, 0x01A, 0x029, 0x00B, 0x00E, // -$:/.+ABCD
|
0x00c, 0x018, 0x045, 0x051, 0x054, 0x015, 0x01A, 0x029, 0x00B, 0x00E, // -$:/.+ABCD
|
||||||
0x01A, 0x029 //TN
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// minimal number of characters that should be present (inclusing start and stop characters)
|
// minimal number of characters that should be present (inclusing start and stop characters)
|
||||||
// this check has been added to reduce the number of false positive on other formats
|
// under normal circumstances this should be set to 3, but can be set higher
|
||||||
// until the cause for this behaviour has been determined
|
// as a last-ditch attempt to reduce false positives.
|
||||||
// under normal circumstances this should be set to 3
|
private static final int MIN_CHARACTER_LENGTH = 3;
|
||||||
private static final int minCharacterLength = 6;
|
|
||||||
|
|
||||||
// multiple start/end patterns
|
|
||||||
// official start and end patterns
|
// official start and end patterns
|
||||||
private static final char[] STARTEND_ENCODING = {'E', '*', 'A', 'B', 'C', 'D', 'T', 'N'};
|
private static final char[] STARTEND_ENCODING = {'A', 'B', 'C', 'D'};
|
||||||
// some codabar generator allow the codabar string to be closed by every character
|
// some codabar generator allow the codabar string to be closed by every
|
||||||
//private static final char[] STARTEND_ENCODING = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '$', ':', '/', '.', '+', 'A', 'B', 'C', 'D', 'T', 'N'};
|
// character. This will cause lots of false positives!
|
||||||
|
|
||||||
// some industries use a checksum standard but this is not part of the original codabar standard
|
// some industries use a checksum standard but this is not part of the original codabar standard
|
||||||
// for more information see : http://www.mecsw.com/specs/codabar.html
|
// for more information see : http://www.mecsw.com/specs/codabar.html
|
||||||
|
|
||||||
|
// Keep some instance variables to avoid reallocations
|
||||||
|
private final StringBuilder decodeRowResult;
|
||||||
|
private int[] counters;
|
||||||
|
private int counterLength;
|
||||||
|
|
||||||
|
public CodaBarReader() {
|
||||||
|
decodeRowResult = new StringBuilder(20);
|
||||||
|
counters = new int[80];
|
||||||
|
counterLength = 0;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Result decodeRow(int rowNumber, BitArray row, Map<DecodeHintType,?> hints)
|
public Result decodeRow(int rowNumber, BitArray row, Map<DecodeHintType,?> hints) throws NotFoundException {
|
||||||
throws NotFoundException {
|
|
||||||
int[] start = findAsteriskPattern(row);
|
|
||||||
start[1] = 0; // BAS: settings this to 0 improves the recognition rate somehow?
|
|
||||||
// Read off white space
|
|
||||||
int nextStart = row.getNextSet(start[1]);
|
|
||||||
int end = row.getSize();
|
|
||||||
|
|
||||||
StringBuilder result = new StringBuilder();
|
setCounters(row);
|
||||||
int[] counters = new int[7];
|
int startOffset = findStartPattern();
|
||||||
int lastStart;
|
int nextStart = startOffset;
|
||||||
|
|
||||||
|
decodeRowResult.setLength(0);
|
||||||
do {
|
do {
|
||||||
for (int i = 0; i < counters.length; i++) {
|
int charOffset = toNarrowWidePattern(nextStart);
|
||||||
counters[i] = 0;
|
if (charOffset == -1) {
|
||||||
}
|
|
||||||
recordPattern(row, nextStart, counters);
|
|
||||||
|
|
||||||
char decodedChar = toNarrowWidePattern(counters);
|
|
||||||
if (decodedChar == '!') {
|
|
||||||
throw NotFoundException.getNotFoundInstance();
|
throw NotFoundException.getNotFoundInstance();
|
||||||
}
|
}
|
||||||
result.append(decodedChar);
|
// Hack: We store the position in the alphabet table into a
|
||||||
lastStart = nextStart;
|
// StringBuilder, so that we can access the decoded patterns in
|
||||||
for (int counter : counters) {
|
// validatePattern. We'll translate to the actual characters later.
|
||||||
nextStart += counter;
|
decodeRowResult.append((char)charOffset);
|
||||||
|
nextStart += 8;
|
||||||
|
// Stop as soon as we see the end character.
|
||||||
|
if (decodeRowResult.length() > 1 &&
|
||||||
|
arrayContains(STARTEND_ENCODING, ALPHABET[charOffset])) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
} while (nextStart < counterLength); // no fixed end pattern so keep on reading while data is available
|
||||||
// Read off white space
|
|
||||||
nextStart = row.getNextSet(nextStart);
|
|
||||||
} while (nextStart < end); // no fixed end pattern so keep on reading while data is available
|
|
||||||
|
|
||||||
// Look for whitespace after pattern:
|
// Look for whitespace after pattern:
|
||||||
|
int trailingWhitespace = counters[nextStart - 1];
|
||||||
int lastPatternSize = 0;
|
int lastPatternSize = 0;
|
||||||
for (int counter : counters) {
|
for (int i = -8; i < -1; i++) {
|
||||||
lastPatternSize += counter;
|
lastPatternSize += counters[nextStart + i];
|
||||||
}
|
}
|
||||||
|
|
||||||
int whiteSpaceAfterEnd = nextStart - lastStart - lastPatternSize;
|
// We need to see whitespace equal to 50% of the last pattern size,
|
||||||
// If 50% of last pattern size, following last pattern, is not whitespace, fail
|
// otherwise this is probably a false positive. The exception is if we are
|
||||||
// (but if it's whitespace to the very end of the image, that's OK)
|
// at the end of the row. (I.e. the barcode barely fits.)
|
||||||
if (nextStart != end && (whiteSpaceAfterEnd / 2 < lastPatternSize)) {
|
if (nextStart < counterLength && trailingWhitespace < lastPatternSize / 2) {
|
||||||
throw NotFoundException.getNotFoundInstance();
|
throw NotFoundException.getNotFoundInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
// valid result?
|
validatePattern(startOffset);
|
||||||
if (result.length() < 2) {
|
|
||||||
throw NotFoundException.getNotFoundInstance();
|
|
||||||
}
|
|
||||||
|
|
||||||
char startchar = result.charAt(0);
|
// Translate character table offsets to actual characters.
|
||||||
|
for (int i = 0; i < decodeRowResult.length(); i++) {
|
||||||
|
decodeRowResult.setCharAt(i, ALPHABET[decodeRowResult.charAt(i)]);
|
||||||
|
}
|
||||||
|
// Ensure a valid start and end character
|
||||||
|
char startchar = decodeRowResult.charAt(0);
|
||||||
if (!arrayContains(STARTEND_ENCODING, startchar)) {
|
if (!arrayContains(STARTEND_ENCODING, startchar)) {
|
||||||
// invalid start character
|
throw NotFoundException.getNotFoundInstance();
|
||||||
|
}
|
||||||
|
char endchar = decodeRowResult.charAt(decodeRowResult.length() - 1);
|
||||||
|
if (!arrayContains(STARTEND_ENCODING, endchar)) {
|
||||||
throw NotFoundException.getNotFoundInstance();
|
throw NotFoundException.getNotFoundInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
// find stop character
|
// remove stop/start characters character and check if a long enough string is contained
|
||||||
for (int k = 1; k < result.length(); k++) {
|
if (decodeRowResult.length() <= MIN_CHARACTER_LENGTH) {
|
||||||
if (result.charAt(k) == startchar) {
|
|
||||||
// found stop character -> discard rest of the string
|
|
||||||
if (k + 1 != result.length()) {
|
|
||||||
result.delete(k + 1, result.length() - 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove stop/start characters character and check if a string longer than 5 characters is contained
|
|
||||||
if (result.length() <= minCharacterLength) {
|
|
||||||
// Almost surely a false positive ( start + stop + at least 1 character)
|
// Almost surely a false positive ( start + stop + at least 1 character)
|
||||||
throw NotFoundException.getNotFoundInstance();
|
throw NotFoundException.getNotFoundInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
result.deleteCharAt(result.length() - 1);
|
decodeRowResult.deleteCharAt(decodeRowResult.length() - 1);
|
||||||
result.deleteCharAt(0);
|
decodeRowResult.deleteCharAt(0);
|
||||||
|
|
||||||
float left = (float) (start[1] + start[0]) / 2.0f;
|
int runningCount = 0;
|
||||||
float right = (float) (nextStart + lastStart) / 2.0f;
|
for (int i = 0; i < startOffset; i++) {
|
||||||
|
runningCount += counters[i];
|
||||||
|
}
|
||||||
|
float left = (float) runningCount;
|
||||||
|
for (int i = startOffset; i < nextStart - 1; i++) {
|
||||||
|
runningCount += counters[i];
|
||||||
|
}
|
||||||
|
float right = (float) runningCount;
|
||||||
return new Result(
|
return new Result(
|
||||||
result.toString(),
|
decodeRowResult.toString(),
|
||||||
null,
|
null,
|
||||||
new ResultPoint[]{
|
new ResultPoint[]{
|
||||||
new ResultPoint(left, (float) rowNumber),
|
new ResultPoint(left, (float) rowNumber),
|
||||||
|
@ -149,41 +157,118 @@ public final class CodaBarReader extends OneDReader {
|
||||||
BarcodeFormat.CODABAR);
|
BarcodeFormat.CODABAR);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int[] findAsteriskPattern(BitArray row) throws NotFoundException {
|
void validatePattern(int start) throws NotFoundException {
|
||||||
int width = row.getSize();
|
// First, sum up the total size of our four categories of stripe sizes;
|
||||||
int rowOffset = row.getNextSet(0);
|
int[] sizes = {0, 0, 0, 0};
|
||||||
|
int[] counts = {0, 0, 0, 0};
|
||||||
|
int end = decodeRowResult.length() - 1;
|
||||||
|
|
||||||
int counterPosition = 0;
|
// We break out of this loop in the middle, in order to handle
|
||||||
int[] counters = new int[7];
|
// inter-character spaces properly.
|
||||||
int patternStart = rowOffset;
|
int pos = start;
|
||||||
boolean isWhite = false;
|
for (int i = 0; true; i++) {
|
||||||
int patternLength = counters.length;
|
int pattern = CHARACTER_ENCODINGS[decodeRowResult.charAt(i)];
|
||||||
|
for (int j = 6; j >= 0; j--) {
|
||||||
|
// Even j = bars, while odd j = spaces. Categories 2 and 3 are for
|
||||||
|
// long stripes, while 0 and 1 are for short stripes.
|
||||||
|
int category = (j & 1) + (pattern & 1) * 2;
|
||||||
|
sizes[category] += counters[pos + j];
|
||||||
|
counts[category]++;
|
||||||
|
pattern >>= 1;
|
||||||
|
}
|
||||||
|
if (i >= end) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// We ignore the inter-character space - it could be of any size.
|
||||||
|
pos += 8;
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = rowOffset; i < width; i++) {
|
// Calculate our allowable size thresholds using fixed-point math.
|
||||||
if (row.get(i) ^ isWhite) {
|
int[] maxes = new int[4];
|
||||||
counters[counterPosition]++;
|
int[] mins = new int[4];
|
||||||
} else {
|
// Define the threshold of acceptability to be the midpoint between the
|
||||||
if (counterPosition == patternLength - 1) {
|
// average small stripe and the average large stripe. No stripe lengths
|
||||||
try {
|
// should be on the "wrong" side of that line.
|
||||||
if (arrayContains(STARTEND_ENCODING, toNarrowWidePattern(counters))) {
|
for (int i = 0; i < 2; i++) {
|
||||||
// Look for whitespace before start pattern, >= 50% of width of start pattern
|
mins[i] = 0; // Accept arbitrarily small "short" stripes.
|
||||||
if (row.isRange(Math.max(0, patternStart - (i - patternStart) / 2), patternStart, false)) {
|
mins[i + 2] = ((sizes[i] << INTEGER_MATH_SHIFT) / counts[i] +
|
||||||
return new int[]{patternStart, i};
|
(sizes[i + 2] << INTEGER_MATH_SHIFT) / counts[i + 2]) >> 1;
|
||||||
}
|
maxes[i] = mins[i + 2];
|
||||||
}
|
maxes[i + 2] = (sizes[i + 2] * MAX_ACCEPTABLE + PADDING) / counts[i + 2];
|
||||||
} catch (IllegalArgumentException re) {
|
}
|
||||||
// no match, continue
|
|
||||||
}
|
// Now verify that all of the stripes are within the thresholds.
|
||||||
patternStart += counters[0] + counters[1];
|
pos = start;
|
||||||
System.arraycopy(counters, 2, counters, 0, patternLength - 2);
|
for (int i = 0; true; i++) {
|
||||||
counters[patternLength - 2] = 0;
|
int pattern = CHARACTER_ENCODINGS[decodeRowResult.charAt(i)];
|
||||||
counters[patternLength - 1] = 0;
|
for (int j = 6; j >= 0; j--) {
|
||||||
counterPosition--;
|
// Even j = bars, while odd j = spaces. Categories 2 and 3 are for
|
||||||
} else {
|
// long stripes, while 0 and 1 are for short stripes.
|
||||||
counterPosition++;
|
int category = (j & 1) + (pattern & 1) * 2;
|
||||||
|
int size = counters[pos + j] << INTEGER_MATH_SHIFT;
|
||||||
|
if (size < mins[category] || size > maxes[category]) {
|
||||||
|
throw NotFoundException.getNotFoundInstance();
|
||||||
|
}
|
||||||
|
pattern >>= 1;
|
||||||
|
}
|
||||||
|
if (i >= end) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pos += 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Records the size of all runs of white and black pixels, starting with white.
|
||||||
|
* This is just like recordPattern, except it records all the counters, and
|
||||||
|
* uses our builtin "counters" member for storage.
|
||||||
|
* @param row row to count from
|
||||||
|
*/
|
||||||
|
private void setCounters(BitArray row) throws NotFoundException {
|
||||||
|
counterLength = 0;
|
||||||
|
// Start from the first white bit.
|
||||||
|
int i = row.getNextUnset(0);
|
||||||
|
int end = row.getSize();
|
||||||
|
if (i >= end) {
|
||||||
|
throw NotFoundException.getNotFoundInstance();
|
||||||
|
}
|
||||||
|
boolean isWhite = true;
|
||||||
|
int count = 0;
|
||||||
|
for (; i < end; i++) {
|
||||||
|
if (row.get(i) ^ isWhite) { // that is, exactly one is true
|
||||||
|
count++;
|
||||||
|
} else {
|
||||||
|
counterAppend(count);
|
||||||
|
count = 1;
|
||||||
|
isWhite = !isWhite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
counterAppend(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void counterAppend(int e) {
|
||||||
|
counters[counterLength] = e;
|
||||||
|
counterLength++;
|
||||||
|
if (counterLength >= counterLength) {
|
||||||
|
int[] temp = new int[counterLength * 2];
|
||||||
|
System.arraycopy(counters, 0, temp, 0, counterLength);
|
||||||
|
counters = temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int findStartPattern() throws NotFoundException {
|
||||||
|
for (int i = 1; i < counterLength; i += 2) {
|
||||||
|
int charOffset = toNarrowWidePattern(i);
|
||||||
|
if (charOffset != -1 && arrayContains(STARTEND_ENCODING, ALPHABET[charOffset])) {
|
||||||
|
// Look for whitespace before start pattern, >= 50% of width of start pattern
|
||||||
|
// We make an exception if the whitespace is the first element.
|
||||||
|
int patternSize = 0;
|
||||||
|
for (int j = i; j < i + 7; j++) {
|
||||||
|
patternSize += counters[j];
|
||||||
|
}
|
||||||
|
if (i == 1 || counters[i-1] >= patternSize / 2) {
|
||||||
|
return i;
|
||||||
}
|
}
|
||||||
counters[counterPosition] = 1;
|
|
||||||
isWhite ^= true; // isWhite = !isWhite;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw NotFoundException.getNotFoundInstance();
|
throw NotFoundException.getNotFoundInstance();
|
||||||
|
@ -200,45 +285,45 @@ public final class CodaBarReader extends OneDReader {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static char toNarrowWidePattern(int[] counters) {
|
// Assumes that counters[position] is a bar.
|
||||||
// BAS : I have changed the following part because some codabar images would fail with the original routine
|
private int toNarrowWidePattern(int position) {
|
||||||
// I took from the Code39Reader.java file
|
int end = position + 7;
|
||||||
// ----------- change start
|
if (end >= counterLength) {
|
||||||
int numCounters = counters.length;
|
return -1;
|
||||||
int maxNarrowCounter = 0;
|
}
|
||||||
|
// First element is for bars, second is for spaces.
|
||||||
|
int[] maxes = {0, 0};
|
||||||
|
int[] mins = {Integer.MAX_VALUE, Integer.MAX_VALUE};
|
||||||
|
int[] thresholds = {0, 0};
|
||||||
|
|
||||||
int minCounter = Integer.MAX_VALUE;
|
for (int i = 0; i < 2; i++) {
|
||||||
for (int i = 0; i < numCounters; i++) {
|
for (int j = position + i; j < end; j += 2) {
|
||||||
if (counters[i] < minCounter) {
|
if (counters[j] < mins[i]) {
|
||||||
minCounter = counters[i];
|
mins[i] = counters[j];
|
||||||
|
}
|
||||||
|
if (counters[j] > maxes[i]) {
|
||||||
|
maxes[i] = counters[j];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (counters[i] > maxNarrowCounter) {
|
thresholds[i] = (mins[i] + maxes[i]) / 2;
|
||||||
maxNarrowCounter = counters[i];
|
}
|
||||||
|
|
||||||
|
int bitmask = 1 << 7;
|
||||||
|
int pattern = 0;
|
||||||
|
for (int i = 0; i < 7; i++) {
|
||||||
|
int barOrSpace = i & 1;
|
||||||
|
bitmask >>= 1;
|
||||||
|
if (counters[position + i] > thresholds[barOrSpace]) {
|
||||||
|
pattern |= bitmask;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ---------- change end
|
|
||||||
|
|
||||||
|
for (int i = 0; i < CHARACTER_ENCODINGS.length; i++) {
|
||||||
do {
|
if (CHARACTER_ENCODINGS[i] == pattern) {
|
||||||
int wideCounters = 0;
|
return i;
|
||||||
int pattern = 0;
|
|
||||||
for (int i = 0; i < numCounters; i++) {
|
|
||||||
if (counters[i] > maxNarrowCounter) {
|
|
||||||
pattern |= 1 << (numCounters - 1 - i);
|
|
||||||
wideCounters++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if ((wideCounters == 2) || (wideCounters == 3)) {
|
return -1;
|
||||||
for (int i = 0; i < CHARACTER_ENCODINGS.length; i++) {
|
|
||||||
if (CHARACTER_ENCODINGS[i] == pattern) {
|
|
||||||
return ALPHABET[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
maxNarrowCounter--;
|
|
||||||
} while (maxNarrowCounter > minCounter);
|
|
||||||
return '!';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,12 +71,20 @@ public class CodaBarWriter extends OneDimensionalCodeWriter {
|
||||||
for (int index = 0; index < contents.length(); index++) {
|
for (int index = 0; index < contents.length(); index++) {
|
||||||
char c = Character.toUpperCase(contents.charAt(index));
|
char c = Character.toUpperCase(contents.charAt(index));
|
||||||
if (index == contents.length() - 1) {
|
if (index == contents.length() - 1) {
|
||||||
// Neither * nor E are in the CodaBarReader.ALPHABET.
|
// The end chars are not in the CodaBarReader.ALPHABET.
|
||||||
// * is equal to the c pattern, and e is equal to the d pattern
|
switch (c) {
|
||||||
if (c == '*') {
|
case 'T':
|
||||||
c = 'C';
|
c = 'A';
|
||||||
} else if (c == 'E') {
|
break;
|
||||||
c = 'D';
|
case 'N':
|
||||||
|
c = 'B';
|
||||||
|
break;
|
||||||
|
case '*':
|
||||||
|
c = 'C';
|
||||||
|
break;
|
||||||
|
case 'E':
|
||||||
|
c = 'D';
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
int code = 0;
|
int code = 0;
|
||||||
|
|
|
@ -76,7 +76,7 @@ public final class MultiFormatOneDReader extends OneDReader {
|
||||||
if (readers.isEmpty()) {
|
if (readers.isEmpty()) {
|
||||||
readers.add(new MultiFormatUPCEANReader(hints));
|
readers.add(new MultiFormatUPCEANReader(hints));
|
||||||
readers.add(new Code39Reader());
|
readers.add(new Code39Reader());
|
||||||
//readers.add(new CodaBarReader());
|
readers.add(new CodaBarReader());
|
||||||
readers.add(new Code93Reader());
|
readers.add(new Code93Reader());
|
||||||
readers.add(new Code128Reader());
|
readers.add(new Code128Reader());
|
||||||
readers.add(new ITFReader());
|
readers.add(new ITFReader());
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
294/685
|
294/586
|
|
@ -1 +1 @@
|
||||||
123456789012
|
31117013206375
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2008 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.MultiFormatReader;
|
||||||
|
import com.google.zxing.common.AbstractBlackBoxTestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Sean Owen
|
||||||
|
*/
|
||||||
|
public final class CodabarBlackBox1TestCase extends AbstractBlackBoxTestCase {
|
||||||
|
|
||||||
|
public CodabarBlackBox1TestCase() {
|
||||||
|
super("test/data/blackbox/codabar-1", new MultiFormatReader(), BarcodeFormat.CODABAR);
|
||||||
|
addTest(11, 11, 0.0f);
|
||||||
|
addTest(11, 11, 180.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue