#include <zxing/ZXing.h>
#include <zxing/oned/CodaBarReader.h>
#include <zxing/oned/OneDResultPoint.h>
#include <zxing/common/Array.h>
#include <zxing/ReaderException.h>
#include <zxing/NotFoundException.h>
#include <zxing/FormatException.h>
#include <zxing/ChecksumException.h>
#include <math.h>
#include <sstream>
using std::vector;
using std::string;
using zxing::NotFoundException;
using zxing::FormatException;
using zxing::ChecksumException;
using zxing::Ref;
using zxing::Result;
using zxing::oned::CodaBarReader;
using zxing::BitArray;
namespace {
char const ALPHABET_STRING[] = "0123456789-$:/.+ABCD";
char const* const ALPHABET = ALPHABET_STRING;
const int CHARACTER_ENCODINGS[] = {
0x003, 0x006, 0x009, 0x060, 0x012, 0x042, 0x021, 0x024, 0x030, 0x048,
0x00c, 0x018, 0x045, 0x051, 0x054, 0x015, 0x01A, 0x029, 0x00B, 0x00E,
};
const int MIN_CHARACTER_LENGTH = 3;
const char STARTEND_ENCODING[] = {'A', 'B', 'C', 'D', 0};
}
const int CodaBarReader::MAX_ACCEPTABLE =
(int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 2.0f);
const int CodaBarReader::PADDING =
(int) (PATTERN_MATCH_RESULT_SCALE_FACTOR * 1.5f);
CodaBarReader::CodaBarReader()
: counters(80, 0), counterLength(0) {}
Ref<Result> CodaBarReader::decodeRow(int rowNumber, Ref<BitArray> row, zxing::DecodeHints ) {
{
int size = counters.size();
counters.resize(0);
counters.resize(size); }
setCounters(row);
int startOffset = findStartPattern();
int nextStart = startOffset;
decodeRowResult.clear();
do {
int charOffset = toNarrowWidePattern(nextStart);
if (charOffset == -1) {
throw NotFoundException();
}
decodeRowResult.append(1, (byte)charOffset);
nextStart += 8;
if (decodeRowResult.length() > 1 &&
arrayContains(STARTEND_ENCODING, ALPHABET[charOffset])) {
break;
}
} while (nextStart < counterLength);
int trailingWhitespace = counters[nextStart - 1];
int lastPatternSize = 0;
for (int i = -8; i < -1; i++) {
lastPatternSize += counters[nextStart + i];
}
if (nextStart < counterLength && trailingWhitespace < lastPatternSize / 2) {
throw NotFoundException();
}
validatePattern(startOffset);
for (int i = 0; i < (int)decodeRowResult.length(); i++) {
decodeRowResult[i] = ALPHABET[(int)decodeRowResult[i]];
}
char startchar = decodeRowResult[0];
if (!arrayContains(STARTEND_ENCODING, startchar)) {
throw NotFoundException();
}
char endchar = decodeRowResult[decodeRowResult.length() - 1];
if (!arrayContains(STARTEND_ENCODING, endchar)) {
throw NotFoundException();
}
if ((int)decodeRowResult.length() <= MIN_CHARACTER_LENGTH) {
throw NotFoundException();
}
decodeRowResult.erase(decodeRowResult.length() - 1, 1);
decodeRowResult.erase(0, 1);
int runningCount = 0;
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;
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(Ref<String>(new String(decodeRowResult)),
ArrayRef<byte>(),
resultPoints,
BarcodeFormat::CODABAR));
}
void CodaBarReader::validatePattern(int start) {
vector<int> sizes (4, 0);
vector<int> counts (4, 0);
int end = decodeRowResult.length() - 1;
int pos = start;
for (int i = 0; true; i++) {
int pattern = CHARACTER_ENCODINGS[(int)decodeRowResult[i]];
for (int j = 6; j >= 0; j--) {
int category = (j & 1) + (pattern & 1) * 2;
sizes[category] += counters[pos + j];
counts[category]++;
pattern >>= 1;
}
if (i >= end) {
break;
}
pos += 8;
}
vector<int> maxes (4, 0);
vector<int> mins (4, 0);
for (int i = 0; i < 2; i++) {
mins[i] = 0;
mins[i + 2] = ((sizes[i] << INTEGER_MATH_SHIFT) / counts[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];
}
pos = start;
for (int i = 0; true; i++) {
int pattern = CHARACTER_ENCODINGS[(int)decodeRowResult[i]];
for (int j = 6; j >= 0; j--) {
int category = (j & 1) + (pattern & 1) * 2;
int size = counters[pos + j] << INTEGER_MATH_SHIFT;
if (size < mins[category] || size > maxes[category]) {
throw NotFoundException();
}
pattern >>= 1;
}
if (i >= end) {
break;
}
pos += 8;
}
}
void CodaBarReader::setCounters(Ref<BitArray> row) {
counterLength = 0;
int i = row->getNextUnset(0);
int end = row->getSize();
if (i >= end) {
throw NotFoundException();
}
bool isWhite = true;
int count = 0;
for (; i < end; i++) {
if (row->get(i) ^ isWhite) {
count++;
} else {
counterAppend(count);
count = 1;
isWhite = !isWhite;
}
}
counterAppend(count);
}
void CodaBarReader::counterAppend(int e) {
if (counterLength < (int)counters.size()) {
counters[counterLength] = e;
} else {
counters.push_back(e);
}
counterLength++;
}
int CodaBarReader::findStartPattern() {
for (int i = 1; i < counterLength; i += 2) {
int charOffset = toNarrowWidePattern(i);
if (charOffset != -1 && arrayContains(STARTEND_ENCODING, ALPHABET[charOffset])) {
int patternSize = 0;
for (int j = i; j < i + 7; j++) {
patternSize += counters[j];
}
if (i == 1 || counters[i-1] >= patternSize / 2) {
return i;
}
}
}
throw NotFoundException();
}
bool CodaBarReader::arrayContains(char const array[], char key) {
return strchr(array, key) != 0;
}
int CodaBarReader::toNarrowWidePattern(int position) {
int end = position + 7;
if (end >= counterLength) {
return -1;
}
vector<int>& theCounters = counters;
int maxBar = 0;
int minBar = std::numeric_limits<int>::max();
for (int j = position; j < end; j += 2) {
int currentCounter = theCounters[j];
if (currentCounter < minBar) {
minBar = currentCounter;
}
if (currentCounter > maxBar) {
maxBar = currentCounter;
}
}
int thresholdBar = (minBar + maxBar) / 2;
int maxSpace = 0;
int minSpace = std::numeric_limits<int>::max();
for (int j = position + 1; j < end; j += 2) {
int currentCounter = theCounters[j];
if (currentCounter < minSpace) {
minSpace = currentCounter;
}
if (currentCounter > maxSpace) {
maxSpace = currentCounter;
}
}
int thresholdSpace = (minSpace + maxSpace) / 2;
int bitmask = 1 << 7;
int pattern = 0;
for (int i = 0; i < 7; i++) {
int threshold = (i & 1) == 0 ? thresholdBar : thresholdSpace;
bitmask >>= 1;
if (theCounters[position + i] > threshold) {
pattern |= bitmask;
}
}
for (int i = 0; i < ZXING_ARRAY_LEN(CHARACTER_ENCODINGS); i++) {
if (CHARACTER_ENCODINGS[i] == pattern) {
return i;
}
}
return -1;
}