package jjil.android;
import java.util.HashMap;
public class Ean13Barcode1D {
/**
* Description of the barcode layout.
*/
public static final int LEFT_DIGITS = 6; // number of digits in the left half of the barcode
public static final int RIGHT_DIGITS = 6; // number of digits in the right half of the barcode
public static final int LEFT_WIDTH = 3; // number of elementary bars in the left-side pattern
public static final int RIGHT_WIDTH = 3; // number of elementary bars in the right-side pattern
public static final int MID_WIDTH = 5; // number of elementary bars in the middle pattern
public static final int DIGIT_WIDTH = 7; // number of elementary bars in a digit
// the offset to the middle pattern, in elementary bars
public static final int MID_OFFSET = LEFT_WIDTH + LEFT_DIGITS*DIGIT_WIDTH;
// the offset to the right-side pattern, in elementary bars
public static final int RIGHT_OFFSET = LEFT_WIDTH +
(LEFT_DIGITS+RIGHT_DIGITS)*DIGIT_WIDTH + MID_WIDTH;
// the total number of elementary bars in a UPC barcode
public static final int TOTAL_WIDTH =
LEFT_WIDTH + DIGIT_WIDTH * LEFT_DIGITS + MID_WIDTH +
DIGIT_WIDTH * RIGHT_DIGITS + RIGHT_WIDTH;
public Ean13Barcode1D() {
initDigitCodes();
}
private void initDigitCodes()
{
/* The odd parity left (character set A) barcodes for the ten digits are:
0 = 3-2-1-1 = 0001101 = 0x0d
1 = 2-2-2-1 = 0011001 = 0x19
2 = 2-1-2-2 = 0010011 = 0x13
3 = 1-4-1-1 = 0111101 = 0x3d
4 = 1-1-3-2 = 0100011 = 0x23
5 = 1-2-3-1 = 0110001 = 0x31
6 = 1-1-1-4 = 0101111 = 0x2f
7 = 1-3-1-2 = 0111011 = 0x3b
8 = 1-2-1-3 = 0110111 = 0x37
9 = 3-1-1-2 = 0001011 = 0x0b
*/
mhOddLeft = new HashMap<Integer, Character>();
mhOddLeft.put(0x0d, '0');
mhOddLeft.put(0x19, '1');
mhOddLeft.put(0x13, '2');
mhOddLeft.put(0x3d, '3');
mhOddLeft.put(0x23, '4');
mhOddLeft.put(0x31, '5');
mhOddLeft.put(0x2f, '6');
mhOddLeft.put(0x3b, '7');
mhOddLeft.put(0x37, '8');
mhOddLeft.put(0x0b, '9');
// The R barcodes for the digits are the 1-complements
// of the L odd barcodes. But we encode the R digits by
// encoding a white bar as 1 and a black bar as 0
// so we automatically get the 1-complement without
// while using the same table
/* The even parity left (character set B) barcodes for the ten digits are:
0 = 1-1-2-3 = 0100111 = 0x27
1 = 1-2-2-2 = 0110011 = 0x33
2 = 2-2-1-2 = 0011011 = 0x1b
3 = 1-1-4-1 = 0100001 = 0x21
4 = 2-3-1-1 = 0011101 = 0x1d
5 = 1-3-2-1 = 0111001 = 0x39
6 = 4-1-1-1 = 0000101 = 0x05
7 = 2-1-3-1 = 0010001 = 0x11
8 = 3-1-2-1 = 0001001 = 0x09
9 = 2-1-1-3 = 0010111 = 0x17
*/
mhEvenLeft = new HashMap<Integer, Character>();
mhEvenLeft.put(0x27, '0');
mhEvenLeft.put(0x33, '1');
mhEvenLeft.put(0x1b, '2');
mhEvenLeft.put(0x21, '3');
mhEvenLeft.put(0x1d, '4');
mhEvenLeft.put(0x39, '5');
mhEvenLeft.put(0x05, '6');
mhEvenLeft.put(0x11, '7');
mhEvenLeft.put(0x09, '8');
mhEvenLeft.put(0x17, '9');
/**
* The first digit is implied based on
* the parity of the other digits in the
* left half. Below 1 = odd parity 0 = even
* 0 111111 = 0x3f
* 1 110100 = 0x34
* 2 110010 = 0x32
* 3 110001 = 0x31
* 4 101100 = 0x2c
* 5 100110 = 0x26
* 6 100011 = 0x23
* 7 101010 = 0x2a
* 8 101001 = 0x29
* 9 100101 = 0x25
*/
mhFirstDigit = new HashMap<Integer, Character>();
mhFirstDigit.put(0x3f, '0');
mhFirstDigit.put(0x34, '1');
mhFirstDigit.put(0x32, '2');
mhFirstDigit.put(0x31, '3');
mhFirstDigit.put(0x2c, '4');
mhFirstDigit.put(0x26, '5');
mhFirstDigit.put(0x23, '6');
mhFirstDigit.put(0x2a, '7');
mhFirstDigit.put(0x29, '8');
mhFirstDigit.put(0x25, '9');
}
/**
* Decode an EAN-13 barcode from the image values in nValues, which should be
* thresholded at the value 'nMid', starting at position nStart and ending
* at position nEnd
* @param nValues -- the image values
* @param nStart -- column to start decoding the barcode
* @param nEnd -- column to end decoding the barcode
* @return the 13-digit decoded barcode if one was found, or null if not
*/
public String decodeBarcode(byte[] bCompressed, int nStart) {
int nEnd = nStart + TOTAL_WIDTH;
// verify that the barcode starts and ends with the right patterns
if (bCompressed[nStart] != 1 || bCompressed[nStart+1] != 0 || bCompressed[nStart+2] != 1) {
return null;
}
if (bCompressed[nEnd-3] != 1 || bCompressed[nEnd-2] != 0 || bCompressed[nEnd-1] != 1) {
return null;
}
// skip past the marker on the left
int nCurr = LEFT_WIDTH + nStart;
StringBuilder sbBarcode = new StringBuilder();
int nLeftParity = 0;
// decode each digit, detecting the parity of each
for (int nDigit = 0; nDigit < LEFT_DIGITS; nDigit++) {
int nSum = 0;
// build an index into digitCodes for this pattern
for (int l = 0; l < DIGIT_WIDTH; l++) {
nSum = nSum * 2 + bCompressed[nCurr++];
}
if (nDigit == 0) {
// in EAN-13 the first digit always has odd parity
if (mhOddLeft.containsKey(nSum)) {
sbBarcode.append(mhOddLeft.get(nSum));
nLeftParity = 1;
} else {
// the first digit didn't match any of the codes
return null;
}
} else {
// determine the parity of the digit
if (mhOddLeft.containsKey(nSum)) {
sbBarcode.append(mhOddLeft.get(nSum));
nLeftParity = (nLeftParity * 2) + 1;
} else if (mhEvenLeft.containsKey(nSum)) {
sbBarcode.append(mhEvenLeft.get(nSum));
nLeftParity = nLeftParity * 2;
} else {
return sbBarcode.toString();
}
}
}
// check parity and add prefix character
if (mhFirstDigit.containsKey(nLeftParity)) {
sbBarcode.insert(0, mhFirstDigit.get(nLeftParity));
} else {
return sbBarcode.toString();
}
// now do the right side digits
nCurr += MID_WIDTH;
for (int nDigit = 0; nDigit < RIGHT_DIGITS; nDigit++) {
int nSum = 0;
// build an index into digitCodes for this pattern
for (int n = 0; n < DIGIT_WIDTH; n++) {
nSum = nSum * 2 + (1 - bCompressed[nCurr++]);
}
if (mhOddLeft.containsKey(nSum)) {
sbBarcode.append(mhOddLeft.get(nSum));
} else {
// the first digit didn't match any of the codes
return sbBarcode.toString();
}
}
return sbBarcode.toString();
}
public String searchForBarcode(int[] nValues, CrosshairOverlay co, boolean bHorizontal) {
// this is the number of pixels we look left and right to determine
// the local average.
final int LOCAL_THRESH = 32;
// compute the cumulative sum of nValues. We use this for local
// thresholding
int[] nCumulativeSum = new int[nValues.length];
nCumulativeSum[0] = nValues[0];
for (int i = 1; i < nValues.length; i++) {
nCumulativeSum[i] = nCumulativeSum[i - 1] + nValues[i];
}
for (int nPixelsPerBar = 1; nPixelsPerBar < nValues.length / TOTAL_WIDTH; nPixelsPerBar++) {
int nPixelSum = 0, nPixCount = 0, j = 0;
byte[] bCompressed = new byte[nValues.length];
for (int i = 0; i < nValues.length; i++) {
nPixelSum += nValues[i];
nPixCount++;
if (nPixCount == nPixelsPerBar) {
int nEnd = Math.min(nValues.length - 1, i + LOCAL_THRESH);
int nStart = Math.max(0, i - LOCAL_THRESH);
int nPixelValue = nPixelSum / nPixCount;
int nLocalAverage = (nCumulativeSum[nEnd] - nCumulativeSum[nStart])
/ (nEnd - nStart);
if (nPixelValue > nLocalAverage) {
bCompressed[j++] = 0;
} else {
bCompressed[j++] = 1;
}
nPixelSum = 0;
nPixCount = 0;
}
}
// EAN-13 barcodes start and end with a black-white-black
// pattern preceded by white space. This is the bit pattern
// 00101 = 0x05, encoding white bars as 0 and dark bars as 1
int nStartCode = (bCompressed[0] << 3) + (bCompressed[1] << 2)
+ (bCompressed[2] << 1) + (bCompressed[3]);
for (int i = 2; i < bCompressed.length - TOTAL_WIDTH - 2; i++) {
nStartCode = ((nStartCode & 0x0f) << 1) + bCompressed[i + 2];
if (nStartCode == 5) {
// found the left code, the right code will code as
// 10100 = 0x14 = 20
int nEndCode = (bCompressed[i + TOTAL_WIDTH - 3] << 4)
+ (bCompressed[i + TOTAL_WIDTH - 2] << 3)
+ (bCompressed[i + TOTAL_WIDTH - 1] << 2)
+ (bCompressed[i + TOTAL_WIDTH] << 1)
+ (bCompressed[i + TOTAL_WIDTH + 1]);
if (nEndCode == 20) {
if (co != null) {
if (bHorizontal) {
co.setHorizLimits(i*nPixelsPerBar, (i+TOTAL_WIDTH)*nPixelsPerBar);
} else {
co.setVertLimits(i*nPixelsPerBar, (i+TOTAL_WIDTH)*nPixelsPerBar);
}
}
String szBarcode = decodeBarcode(bCompressed, i);
if (szBarcode != null) {
return szBarcode;
}
}
}
}
}
return null;
}
/**
* Verifies the check digit in a decoded barcode string. Returns true
* if the check digit passes the verify test.
*
* @param digits the barcode to be verified.
* @return true iff the barcode passes the check digit test.
*/
public static boolean verifyCheckDigit(String digits) {
if (digits == null || digits.length() != 13) {
return false;
}
// compute check digit
// add odd digits
int nOddSum = 0;
for (int i=1; i<digits.length()-1; i+=2) {
nOddSum += Character.digit(digits.charAt(i), 10);
}
// add even digits
int nEvenSum = 0;
for (int i=0;i<digits.length()-1; i+=2) {
nEvenSum += Character.digit(digits.charAt(i), 10);
}
// compute even digit sum * 3 + odd digit sum;
int nTotal = nOddSum*3 + nEvenSum;
// check digit is this sum subtracted from the next higher multiple of 10
int checkDigit = (nTotal/10 + 1) * 10 - nTotal;
return Character.digit(
digits.charAt(digits.length()-1), 10) == checkDigit;
}
/**
* Hashmaps for the various digit patterns
*/
HashMap<Integer, Character> mhOddLeft, mhEvenLeft, mhFirstDigit;
}