#include "Code39Reader.h"
#include <zxing/oned/OneDResultPoint.h>
#include <zxing/common/Array.h>
#include <zxing/ReaderException.h>
#include <zxing/NotFoundException.h>
#include <zxing/ChecksumException.h>
#include <math.h>
#include <limits.h>
#include <algorithm>
using std::vector;
using zxing::Ref;
using zxing::Result;
using zxing::String;
using zxing::NotFoundException;
using zxing::ChecksumException;
using zxing::oned::Code39Reader;
using zxing::BitArray;
namespace {
const char ALPHABET[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. *$/+%";
const int CHARACTER_ENCODINGS_LEN = 44;
const int CHARACTER_ENCODINGS[CHARACTER_ENCODINGS_LEN] = {
0x034, 0x121, 0x061, 0x160, 0x031, 0x130, 0x070, 0x025, 0x124, 0x064,
0x109, 0x049, 0x148, 0x019, 0x118, 0x058, 0x00D, 0x10C, 0x04C, 0x01C,
0x103, 0x043, 0x142, 0x013, 0x112, 0x052, 0x007, 0x106, 0x046, 0x016,
0x181, 0x0C1, 0x1C0, 0x091, 0x190, 0x0D0, 0x085, 0x184, 0x0C4, 0x094,
0x0A8, 0x0A2, 0x08A, 0x02A
};
const int ASTERISK_ENCODING = 0x094;
const char ALPHABET_STRING[] =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. *$/+%";
const std::string alphabet_string (ALPHABET_STRING);
}
void Code39Reader::init(bool usingCheckDigit_, bool extendedMode_) {
usingCheckDigit = usingCheckDigit_;
extendedMode = extendedMode_;
decodeRowResult.reserve(20);
counters.resize(9);
}
Code39Reader::Code39Reader() {
init();
}
Code39Reader::Code39Reader(bool usingCheckDigit_) {
init(usingCheckDigit_);
}
Code39Reader::Code39Reader(bool usingCheckDigit_, bool extendedMode_) {
init(usingCheckDigit_, extendedMode_);
}
Ref<Result> Code39Reader::decodeRow(int rowNumber, Ref<BitArray> row, zxing::DecodeHints ) {
std::vector<int>& theCounters (counters);
{
int size = theCounters.size();
theCounters.resize(0);
theCounters.resize(size); }
std::string& result (decodeRowResult);
result.clear();
vector<int> start (findAsteriskPattern(row, theCounters));
int nextStart = row->getNextSet(start[1]);
int end = row->getSize();
char decodedChar;
int lastStart;
do {
recordPattern(row, nextStart, theCounters);
int pattern = toNarrowWidePattern(theCounters);
if (pattern < 0) {
throw NotFoundException();;
}
decodedChar = patternToChar(pattern);
result.append(1, decodedChar);
lastStart = nextStart;
for (int i = 0, end=theCounters.size(); i < end; i++) {
nextStart += theCounters[i];
}
nextStart = row->getNextSet(nextStart);
} while (decodedChar != '*');
result.resize(decodeRowResult.length()-1);
int lastPatternSize = 0;
for (int i = 0, e = theCounters.size(); i < e; i++) {
lastPatternSize += theCounters[i];
}
int whiteSpaceAfterEnd = nextStart - lastStart - lastPatternSize;
if (nextStart != end && (whiteSpaceAfterEnd >> 1) < lastPatternSize) {
throw NotFoundException();
}
if (usingCheckDigit) {
int max = result.length() - 1;
int total = 0;
for (int i = 0; i < max; i++) {
total += alphabet_string.find_first_of(decodeRowResult[i], 0);
}
if (result[max] != ALPHABET[total % 43]) {
throw ChecksumException();
}
result.resize(max);
}
if (result.length() == 0) {
throw NotFoundException();
}
Ref<String> resultString;
if (extendedMode) {
resultString = decodeExtended(result);
} else {
resultString = Ref<String>(new String(result));
}
float left = (float) (start[1] + start[0]) / 2.0f;
float right = lastStart + lastPatternSize / 2.0f;
ArrayRef< Ref<ResultPoint> > resultPoints (2);
resultPoints[0] =
Ref<OneDResultPoint>(new OneDResultPoint(left, (float) rowNumber));
resultPoints[1] =
Ref<OneDResultPoint>(new OneDResultPoint(right, (float) rowNumber));
return Ref<Result>(
new Result(resultString, ArrayRef<byte>(), resultPoints, BarcodeFormat::CODE_39)
);
}
vector<int> Code39Reader::findAsteriskPattern(Ref<BitArray> row, vector<int>& counters){
int width = row->getSize();
int rowOffset = row->getNextSet(0);
int counterPosition = 0;
int patternStart = rowOffset;
bool isWhite = false;
int patternLength = counters.size();
for (int i = rowOffset; i < width; i++) {
if (row->get(i) ^ isWhite) {
counters[counterPosition]++;
} else {
if (counterPosition == patternLength - 1) {
if (toNarrowWidePattern(counters) == ASTERISK_ENCODING &&
row->isRange(std::max(0, patternStart - ((i - patternStart) >> 1)), patternStart, false)) {
vector<int> resultValue (2, 0);
resultValue[0] = patternStart;
resultValue[1] = i;
return resultValue;
}
patternStart += counters[0] + counters[1];
for (int y = 2; y < patternLength; y++) {
counters[y - 2] = counters[y];
}
counters[patternLength - 2] = 0;
counters[patternLength - 1] = 0;
counterPosition--;
} else {
counterPosition++;
}
counters[counterPosition] = 1;
isWhite = !isWhite;
}
}
throw NotFoundException();
}
int Code39Reader::toNarrowWidePattern(vector<int>& counters){
int numCounters = counters.size();
int maxNarrowCounter = 0;
int wideCounters;
do {
int minCounter = INT_MAX;
for (int i = 0; i < numCounters; i++) {
int counter = counters[i];
if (counter < minCounter && counter > maxNarrowCounter) {
minCounter = counter;
}
}
maxNarrowCounter = minCounter;
wideCounters = 0;
int totalWideCountersWidth = 0;
int pattern = 0;
for (int i = 0; i < numCounters; i++) {
int counter = counters[i];
if (counters[i] > maxNarrowCounter) {
pattern |= 1 << (numCounters - 1 - i);
wideCounters++;
totalWideCountersWidth += counter;
}
}
if (wideCounters == 3) {
for (int i = 0; i < numCounters && wideCounters > 0; i++) {
int counter = counters[i];
if (counters[i] > maxNarrowCounter) {
wideCounters--;
if ((counter << 1) >= totalWideCountersWidth) {
return -1;
}
}
}
return pattern;
}
} while (wideCounters > 3);
return -1;
}
char Code39Reader::patternToChar(int pattern){
for (int i = 0; i < CHARACTER_ENCODINGS_LEN; i++) {
if (CHARACTER_ENCODINGS[i] == pattern) {
return ALPHABET[i];
}
}
throw ReaderException("");
}
Ref<String> Code39Reader::decodeExtended(std::string encoded){
int length = encoded.length();
std::string tmpDecoded;
for (int i = 0; i < length; i++) {
char c = encoded[i];
if (c == '+' || c == '$' || c == '%' || c == '/') {
char next = encoded[i + 1];
char decodedChar = '\0';
switch (c) {
case '+':
if (next >= 'A' && next <= 'Z') {
decodedChar = (byte) (next + 32);
} else {
throw ReaderException("");
}
break;
case '$':
if (next >= 'A' && next <= 'Z') {
decodedChar = (byte) (next - 64);
} else {
throw ReaderException("");
}
break;
case '%':
if (next >= 'A' && next <= 'E') {
decodedChar = (byte) (next - 38);
} else if (next >= 'F' && next <= 'W') {
decodedChar = (byte) (next - 11);
} else {
throw ReaderException("");
}
break;
case '/':
if (next >= 'A' && next <= 'O') {
decodedChar = (byte) (next - 32);
} else if (next == 'Z') {
decodedChar = ':';
} else {
throw ReaderException("");
}
break;
}
tmpDecoded.append(1, decodedChar);
i++;
} else {
tmpDecoded.append(1, c);
}
}
Ref<String> decoded(new String(tmpDecoded));
return decoded;
}