#include <stdint.h>
#include <bigint/BigIntegerUtils.hh>
#include <zxing/FormatException.h>
#include <zxing/pdf417/decoder/DecodedBitStreamParser.h>
#include <zxing/common/DecoderResult.h>
using std::string;
using zxing::pdf417::DecodedBitStreamParser;
using zxing::ArrayRef;
using zxing::Ref;
using zxing::DecoderResult;
using zxing::String;
namespace zxing {
namespace pdf417 {
const int DecodedBitStreamParser::TEXT_COMPACTION_MODE_LATCH = 900;
const int DecodedBitStreamParser::BYTE_COMPACTION_MODE_LATCH = 901;
const int DecodedBitStreamParser::NUMERIC_COMPACTION_MODE_LATCH = 902;
const int DecodedBitStreamParser::BYTE_COMPACTION_MODE_LATCH_6 = 924;
const int DecodedBitStreamParser::BEGIN_MACRO_PDF417_CONTROL_BLOCK = 928;
const int DecodedBitStreamParser::BEGIN_MACRO_PDF417_OPTIONAL_FIELD = 923;
const int DecodedBitStreamParser::MACRO_PDF417_TERMINATOR = 922;
const int DecodedBitStreamParser::MODE_SHIFT_TO_BYTE_COMPACTION_MODE = 913;
const int DecodedBitStreamParser::MAX_NUMERIC_CODEWORDS = 15;
const int DecodedBitStreamParser::PL = 25;
const int DecodedBitStreamParser::LL = 27;
const int DecodedBitStreamParser::AS = 27;
const int DecodedBitStreamParser::ML = 28;
const int DecodedBitStreamParser::AL = 28;
const int DecodedBitStreamParser::PS = 29;
const int DecodedBitStreamParser::PAL = 29;
const int DecodedBitStreamParser::EXP900_SIZE = 16;
const char DecodedBitStreamParser::PUNCT_CHARS[] = {
';', '<', '>', '@', '[', '\\', '}', '_', '`', '~', '!',
'\r', '\t', ',', ':', '\n', '-', '.', '$', '/', '"', '|', '*',
'(', ')', '?', '{', '}', '\''};
const char DecodedBitStreamParser::MIXED_CHARS[] = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '&',
'\r', '\t', ',', ':', '#', '-', '.', '$', '/', '+', '%', '*',
'=', '^'};
ArrayRef<BigInteger> DecodedBitStreamParser::initEXP900() {
ArrayRef<BigInteger> EXP900 (16);
EXP900[0] = BigInteger(1);
BigInteger nineHundred (900);
EXP900[1] = nineHundred;
for (int i = 2; i < EXP900->size(); i++) {
EXP900[i] = EXP900[i - 1] * nineHundred;
}
return EXP900;
}
ArrayRef<BigInteger> DecodedBitStreamParser::EXP900 = initEXP900();
DecodedBitStreamParser::DecodedBitStreamParser(){}
Ref<DecoderResult> DecodedBitStreamParser::decode(ArrayRef<int> codewords)
{
Ref<String> result (new String(100));
int codeIndex = 1;
int code = codewords[codeIndex++];
while (codeIndex < codewords[0]) {
switch (code) {
case TEXT_COMPACTION_MODE_LATCH:
codeIndex = textCompaction(codewords, codeIndex, result);
break;
case BYTE_COMPACTION_MODE_LATCH:
codeIndex = byteCompaction(code, codewords, codeIndex, result);
break;
case NUMERIC_COMPACTION_MODE_LATCH:
codeIndex = numericCompaction(codewords, codeIndex, result);
break;
case MODE_SHIFT_TO_BYTE_COMPACTION_MODE:
codeIndex = byteCompaction(code, codewords, codeIndex, result);
break;
case BYTE_COMPACTION_MODE_LATCH_6:
codeIndex = byteCompaction(code, codewords, codeIndex, result);
break;
default:
codeIndex--;
codeIndex = textCompaction(codewords, codeIndex, result);
break;
}
if (codeIndex < codewords->size()) {
code = codewords[codeIndex++];
} else {
throw FormatException();
}
}
return Ref<DecoderResult>(new DecoderResult(ArrayRef<byte>(), result));
}
int DecodedBitStreamParser::textCompaction(ArrayRef<int> codewords,
int codeIndex,
Ref<String> result) {
ArrayRef<int> textCompactionData (codewords[0] << 1);
ArrayRef<int> byteCompactionData (codewords[0] << 1);
int index = 0;
bool end = false;
while ((codeIndex < codewords[0]) && !end) {
int code = codewords[codeIndex++];
if (code < TEXT_COMPACTION_MODE_LATCH) {
textCompactionData[index] = code / 30;
textCompactionData[index + 1] = code % 30;
index += 2;
} else {
switch (code) {
case TEXT_COMPACTION_MODE_LATCH:
textCompactionData[index++] = TEXT_COMPACTION_MODE_LATCH;
break;
case BYTE_COMPACTION_MODE_LATCH:
codeIndex--;
end = true;
break;
case NUMERIC_COMPACTION_MODE_LATCH:
codeIndex--;
end = true;
break;
case MODE_SHIFT_TO_BYTE_COMPACTION_MODE:
textCompactionData[index] = MODE_SHIFT_TO_BYTE_COMPACTION_MODE;
code = codewords[codeIndex++];
byteCompactionData[index] = code;
index++;
break;
case BYTE_COMPACTION_MODE_LATCH_6:
codeIndex--;
end = true;
break;
}
}
}
decodeTextCompaction(textCompactionData, byteCompactionData, index, result);
return codeIndex;
}
void DecodedBitStreamParser::decodeTextCompaction(ArrayRef<int> textCompactionData,
ArrayRef<int> byteCompactionData,
int length,
Ref<String> result)
{
Mode subMode = ALPHA;
Mode priorToShiftMode = ALPHA;
int i = 0;
while (i < length) {
int subModeCh = textCompactionData[i];
char ch = 0;
switch (subMode) {
case ALPHA:
if (subModeCh < 26) {
ch = (byte) ('A' + subModeCh);
} else {
if (subModeCh == 26) {
ch = ' ';
} else if (subModeCh == LL) {
subMode = LOWER;
} else if (subModeCh == ML) {
subMode = MIXED;
} else if (subModeCh == PS) {
priorToShiftMode = subMode;
subMode = PUNCT_SHIFT;
} else if (subModeCh == MODE_SHIFT_TO_BYTE_COMPACTION_MODE) {
result->append((byte) byteCompactionData[i]);
} else if (subModeCh == TEXT_COMPACTION_MODE_LATCH) {
subMode = ALPHA;
}
}
break;
case LOWER:
if (subModeCh < 26) {
ch = (byte) ('a' + subModeCh);
} else {
if (subModeCh == 26) {
ch = ' ';
} else if (subModeCh == AS) {
priorToShiftMode = subMode;
subMode = ALPHA_SHIFT;
} else if (subModeCh == ML) {
subMode = MIXED;
} else if (subModeCh == PS) {
priorToShiftMode = subMode;
subMode = PUNCT_SHIFT;
} else if (subModeCh == MODE_SHIFT_TO_BYTE_COMPACTION_MODE) {
result->append((byte) byteCompactionData[i]);
} else if (subModeCh == TEXT_COMPACTION_MODE_LATCH) {
subMode = ALPHA;
}
}
break;
case MIXED:
if (subModeCh < PL) {
ch = MIXED_CHARS[subModeCh];
} else {
if (subModeCh == PL) {
subMode = PUNCT;
} else if (subModeCh == 26) {
ch = ' ';
} else if (subModeCh == LL) {
subMode = LOWER;
} else if (subModeCh == AL) {
subMode = ALPHA;
} else if (subModeCh == PS) {
priorToShiftMode = subMode;
subMode = PUNCT_SHIFT;
} else if (subModeCh == MODE_SHIFT_TO_BYTE_COMPACTION_MODE) {
result->append((byte) byteCompactionData[i]);
} else if (subModeCh == TEXT_COMPACTION_MODE_LATCH) {
subMode = ALPHA;
}
}
break;
case PUNCT:
if (subModeCh < PAL) {
ch = PUNCT_CHARS[subModeCh];
} else {
if (subModeCh == PAL) {
subMode = ALPHA;
} else if (subModeCh == MODE_SHIFT_TO_BYTE_COMPACTION_MODE) {
result->append((byte) byteCompactionData[i]);
} else if (subModeCh == TEXT_COMPACTION_MODE_LATCH) {
subMode = ALPHA;
}
}
break;
case ALPHA_SHIFT:
subMode = priorToShiftMode;
if (subModeCh < 26) {
ch = (byte) ('A' + subModeCh);
} else {
if (subModeCh == 26) {
ch = ' ';
} else {
if (subModeCh == 26) {
ch = ' ';
} else if (subModeCh == TEXT_COMPACTION_MODE_LATCH) {
subMode = ALPHA;
}
}
}
break;
case PUNCT_SHIFT:
subMode = priorToShiftMode;
if (subModeCh < PAL) {
ch = PUNCT_CHARS[subModeCh];
} else {
if (subModeCh == PAL) {
subMode = ALPHA;
} else if (subModeCh == MODE_SHIFT_TO_BYTE_COMPACTION_MODE) {
result->append((byte) byteCompactionData[i]);
} else if (subModeCh == TEXT_COMPACTION_MODE_LATCH) {
subMode = ALPHA;
}
}
break;
}
if (ch != 0) {
result->append(ch);
}
i++;
}
}
int DecodedBitStreamParser::byteCompaction(int mode,
ArrayRef<int> codewords,
int codeIndex, Ref<String> result) {
if (mode == BYTE_COMPACTION_MODE_LATCH) {
int count = 0;
int64_t value = 0;
ArrayRef<byte> decodedData = new Array<byte>(6);
ArrayRef<int> byteCompactedCodewords = new Array<int>(6);
bool end = false;
int nextCode = codewords[codeIndex++];
while ((codeIndex < codewords[0]) && !end) {
byteCompactedCodewords[count++] = nextCode;
value = 900 * value + nextCode;
nextCode = codewords[codeIndex++];
if (nextCode == TEXT_COMPACTION_MODE_LATCH ||
nextCode == BYTE_COMPACTION_MODE_LATCH ||
nextCode == NUMERIC_COMPACTION_MODE_LATCH ||
nextCode == BYTE_COMPACTION_MODE_LATCH_6 ||
nextCode == BEGIN_MACRO_PDF417_CONTROL_BLOCK ||
nextCode == BEGIN_MACRO_PDF417_OPTIONAL_FIELD ||
nextCode == MACRO_PDF417_TERMINATOR)
{
end = true;
}
else
{
if ((count%5 == 0) && (count > 0))
{
for (int j = 0; j < 6; ++j)
{
decodedData[5 - j] = (byte) (value%256);
value >>= 8;
}
result->append(string((char*)&(decodedData->values()[0]), decodedData->values().size()));
count = 0;
}
}
}
if (codeIndex == codewords[0] && nextCode < TEXT_COMPACTION_MODE_LATCH)
byteCompactedCodewords[count++] = nextCode;
for (int i = 0; i < count; i++)
{
result->append((byte)byteCompactedCodewords[i]);
}
} else if (mode == BYTE_COMPACTION_MODE_LATCH_6) {
int count = 0;
int64_t value = 0;
bool end = false;
while (codeIndex < codewords[0] && !end) {
int code = codewords[codeIndex++];
if (code < TEXT_COMPACTION_MODE_LATCH) {
count++;
value = 900 * value + code;
} else {
if (code == TEXT_COMPACTION_MODE_LATCH ||
code == BYTE_COMPACTION_MODE_LATCH ||
code == NUMERIC_COMPACTION_MODE_LATCH ||
code == BYTE_COMPACTION_MODE_LATCH_6 ||
code == BEGIN_MACRO_PDF417_CONTROL_BLOCK ||
code == BEGIN_MACRO_PDF417_OPTIONAL_FIELD ||
code == MACRO_PDF417_TERMINATOR) {
codeIndex--;
end = true;
}
}
if ((count % 5 == 0) && (count > 0)) {
ArrayRef<byte> decodedData = new Array<byte>(6);
for (int j = 0; j < 6; ++j) {
decodedData[5 - j] = (byte) (value & 0xFF);
value >>= 8;
}
result->append(string((char*)&decodedData[0],6));
count = 0;
}
}
}
return codeIndex;
}
int DecodedBitStreamParser::numericCompaction(ArrayRef<int> codewords,
int codeIndex,
Ref<String> result) {
int count = 0;
bool end = false;
ArrayRef<int> numericCodewords = new Array<int>(MAX_NUMERIC_CODEWORDS);
while (codeIndex < codewords[0] && !end) {
int code = codewords[codeIndex++];
if (codeIndex == codewords[0]) {
end = true;
}
if (code < TEXT_COMPACTION_MODE_LATCH) {
numericCodewords[count] = code;
count++;
} else {
if (code == TEXT_COMPACTION_MODE_LATCH ||
code == BYTE_COMPACTION_MODE_LATCH ||
code == BYTE_COMPACTION_MODE_LATCH_6 ||
code == BEGIN_MACRO_PDF417_CONTROL_BLOCK ||
code == BEGIN_MACRO_PDF417_OPTIONAL_FIELD ||
code == MACRO_PDF417_TERMINATOR) {
codeIndex--;
end = true;
}
}
if (count % MAX_NUMERIC_CODEWORDS == 0 ||
code == NUMERIC_COMPACTION_MODE_LATCH ||
end) {
Ref<String> s = decodeBase900toBase10(numericCodewords, count);
result->append(s->getText());
count = 0;
}
}
return codeIndex;
}
Ref<String> DecodedBitStreamParser::decodeBase900toBase10(ArrayRef<int> codewords, int count)
{
BigInteger result = BigInteger(0);
for (int i = 0; i < count; i++) {
result = result + (EXP900[count - i - 1] * BigInteger(codewords[i]));
}
string resultString = bigIntegerToString(result);
if (resultString[0] != '1') {
throw FormatException("DecodedBitStreamParser::decodeBase900toBase10: String does not begin with 1");
}
string resultString2;
resultString2.assign(resultString.begin()+1,resultString.end());
Ref<String> res (new String(resultString2));
return res;
}
}
}