// Copyright 2014 PDFium Authors. All rights reserved. | |
// Use of this source code is governed by a BSD-style license that can be | |
// found in the LICENSE file. | |
// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com | |
// Original code is licensed as follows: | |
/* | |
* Copyright 2009 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. | |
*/ | |
#include "BC_PDF417DecodedBitStreamParser.h" | |
#include <stdlib.h> | |
#include "xfa/src/fxbarcode/BC_DecoderResult.h" | |
#include "xfa/src/fxbarcode/barcode.h" | |
#include "xfa/src/fxbarcode/common/BC_CommonDecoderResult.h" | |
#include "BC_PDF417ResultMetadata.h" | |
#include "third_party/bigint/BigIntegerLibrary.hh" | |
#define TEXT_COMPACTION_MODE_LATCH 900 | |
#define BYTE_COMPACTION_MODE_LATCH 901 | |
#define NUMERIC_COMPACTION_MODE_LATCH 902 | |
#define BYTE_COMPACTION_MODE_LATCH_6 924 | |
#define BEGIN_MACRO_PDF417_CONTROL_BLOCK 928 | |
#define BEGIN_MACRO_PDF417_OPTIONAL_FIELD 923 | |
#define MACRO_PDF417_TERMINATOR 922 | |
#define MODE_SHIFT_TO_BYTE_COMPACTION_MODE 913 | |
int32_t CBC_DecodedBitStreamPaser::MAX_NUMERIC_CODEWORDS = 15; | |
int32_t CBC_DecodedBitStreamPaser::NUMBER_OF_SEQUENCE_CODEWORDS = 2; | |
int32_t CBC_DecodedBitStreamPaser::PL = 25; | |
int32_t CBC_DecodedBitStreamPaser::LL = 27; | |
int32_t CBC_DecodedBitStreamPaser::AS = 27; | |
int32_t CBC_DecodedBitStreamPaser::ML = 28; | |
int32_t CBC_DecodedBitStreamPaser::AL = 28; | |
int32_t CBC_DecodedBitStreamPaser::PS = 29; | |
int32_t CBC_DecodedBitStreamPaser::PAL = 29; | |
FX_CHAR CBC_DecodedBitStreamPaser::PUNCT_CHARS[29] = { | |
';', '<', '>', '@', '[', '\\', '}', '_', '`', '~', | |
'!', '\r', '\t', ',', ':', '\n', '-', '.', '$', '/', | |
'"', '|', '*', '(', ')', '?', '{', '}', '\''}; | |
FX_CHAR CBC_DecodedBitStreamPaser::MIXED_CHARS[30] = { | |
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '&', '\r', '\t', | |
',', ':', '#', '-', '.', '$', '/', '+', '%', '*', '=', '^'}; | |
void CBC_DecodedBitStreamPaser::Initialize() {} | |
void CBC_DecodedBitStreamPaser::Finalize() {} | |
CBC_DecodedBitStreamPaser::CBC_DecodedBitStreamPaser() {} | |
CBC_DecodedBitStreamPaser::~CBC_DecodedBitStreamPaser() {} | |
CBC_CommonDecoderResult* CBC_DecodedBitStreamPaser::decode( | |
CFX_Int32Array& codewords, | |
CFX_ByteString ecLevel, | |
int32_t& e) { | |
CFX_ByteString result; | |
int32_t codeIndex = 1; | |
int32_t code = codewords.GetAt(codeIndex); | |
codeIndex++; | |
CBC_PDF417ResultMetadata* resultMetadata = new CBC_PDF417ResultMetadata; | |
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, e); | |
BC_EXCEPTION_CHECK_ReturnValue(e, NULL); | |
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; | |
case BEGIN_MACRO_PDF417_CONTROL_BLOCK: | |
codeIndex = decodeMacroBlock(codewords, codeIndex, resultMetadata, e); | |
if (e != BCExceptionNO) { | |
delete resultMetadata; | |
return NULL; | |
} | |
break; | |
default: | |
codeIndex--; | |
codeIndex = textCompaction(codewords, codeIndex, result); | |
break; | |
} | |
if (codeIndex < codewords.GetSize()) { | |
code = codewords[codeIndex++]; | |
} else { | |
e = BCExceptionFormatInstance; | |
delete resultMetadata; | |
return NULL; | |
} | |
} | |
if (result.GetLength() == 0) { | |
e = BCExceptionFormatInstance; | |
delete resultMetadata; | |
return NULL; | |
} | |
CFX_ByteArray rawBytes; | |
CFX_PtrArray byteSegments; | |
CBC_CommonDecoderResult* tempCd = new CBC_CommonDecoderResult(); | |
tempCd->Init(rawBytes, result, byteSegments, ecLevel, e); | |
if (e != BCExceptionNO) { | |
delete resultMetadata; | |
return NULL; | |
} | |
tempCd->setOther(resultMetadata); | |
return tempCd; | |
} | |
int32_t CBC_DecodedBitStreamPaser::decodeMacroBlock( | |
CFX_Int32Array& codewords, | |
int32_t codeIndex, | |
CBC_PDF417ResultMetadata* resultMetadata, | |
int32_t& e) { | |
if (codeIndex + NUMBER_OF_SEQUENCE_CODEWORDS > codewords[0]) { | |
e = BCExceptionFormatInstance; | |
return -1; | |
} | |
CFX_Int32Array segmentIndexArray; | |
segmentIndexArray.SetSize(NUMBER_OF_SEQUENCE_CODEWORDS); | |
for (int32_t i = 0; i < NUMBER_OF_SEQUENCE_CODEWORDS; i++, codeIndex++) { | |
segmentIndexArray.SetAt(i, codewords[codeIndex]); | |
} | |
CFX_ByteString str = | |
decodeBase900toBase10(segmentIndexArray, NUMBER_OF_SEQUENCE_CODEWORDS, e); | |
BC_EXCEPTION_CHECK_ReturnValue(e, -1); | |
resultMetadata->setSegmentIndex(atoi(str.GetBuffer(str.GetLength()))); | |
CFX_ByteString fileId; | |
codeIndex = textCompaction(codewords, codeIndex, fileId); | |
resultMetadata->setFileId(fileId); | |
if (codewords[codeIndex] == BEGIN_MACRO_PDF417_OPTIONAL_FIELD) { | |
codeIndex++; | |
CFX_Int32Array additionalOptionCodeWords; | |
additionalOptionCodeWords.SetSize(codewords[0] - codeIndex); | |
int32_t additionalOptionCodeWordsIndex = 0; | |
FX_BOOL end = FALSE; | |
while ((codeIndex < codewords[0]) && !end) { | |
int32_t code = codewords[codeIndex++]; | |
if (code < TEXT_COMPACTION_MODE_LATCH) { | |
additionalOptionCodeWords[additionalOptionCodeWordsIndex++] = code; | |
} else { | |
switch (code) { | |
case MACRO_PDF417_TERMINATOR: | |
resultMetadata->setLastSegment(TRUE); | |
codeIndex++; | |
end = TRUE; | |
break; | |
default: | |
e = BCExceptionFormatInstance; | |
return -1; | |
} | |
} | |
} | |
CFX_Int32Array array; | |
array.SetSize(additionalOptionCodeWordsIndex); | |
array.Copy(additionalOptionCodeWords); | |
resultMetadata->setOptionalData(array); | |
} else if (codewords[codeIndex] == MACRO_PDF417_TERMINATOR) { | |
resultMetadata->setLastSegment(TRUE); | |
codeIndex++; | |
} | |
return codeIndex; | |
} | |
int32_t CBC_DecodedBitStreamPaser::textCompaction(CFX_Int32Array& codewords, | |
int32_t codeIndex, | |
CFX_ByteString& result) { | |
CFX_Int32Array textCompactionData; | |
textCompactionData.SetSize((codewords[0] - codeIndex) << 1); | |
CFX_Int32Array byteCompactionData; | |
byteCompactionData.SetSize((codewords[0] - codeIndex) << 1); | |
int32_t index = 0; | |
FX_BOOL end = FALSE; | |
while ((codeIndex < codewords[0]) && !end) { | |
int32_t 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 BEGIN_MACRO_PDF417_CONTROL_BLOCK: | |
codeIndex--; | |
end = TRUE; | |
break; | |
case BEGIN_MACRO_PDF417_OPTIONAL_FIELD: | |
codeIndex--; | |
end = TRUE; | |
break; | |
case MACRO_PDF417_TERMINATOR: | |
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 CBC_DecodedBitStreamPaser::decodeTextCompaction( | |
CFX_Int32Array& textCompactionData, | |
CFX_Int32Array& byteCompactionData, | |
int32_t length, | |
CFX_ByteString& result) { | |
Mode subMode = ALPHA; | |
Mode priorToShiftMode = ALPHA; | |
int32_t i = 0; | |
while (i < length) { | |
int32_t subModeCh = textCompactionData[i]; | |
FX_CHAR ch = 0; | |
switch (subMode) { | |
case ALPHA: | |
if (subModeCh < 26) { | |
ch = (FX_CHAR)('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 += (FX_CHAR)byteCompactionData[i]; | |
} else if (subModeCh == TEXT_COMPACTION_MODE_LATCH) { | |
subMode = ALPHA; | |
} | |
} | |
break; | |
case LOWER: | |
if (subModeCh < 26) { | |
ch = (FX_CHAR)('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 += (FX_CHAR)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 += (FX_CHAR)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 += (FX_CHAR)byteCompactionData[i]; | |
} else if (subModeCh == TEXT_COMPACTION_MODE_LATCH) { | |
subMode = ALPHA; | |
} | |
} | |
break; | |
case ALPHA_SHIFT: | |
subMode = priorToShiftMode; | |
if (subModeCh < 26) { | |
ch = (FX_CHAR)('A' + subModeCh); | |
} 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 += (FX_CHAR)byteCompactionData[i]; | |
} else if (subModeCh == TEXT_COMPACTION_MODE_LATCH) { | |
subMode = ALPHA; | |
} | |
} | |
break; | |
} | |
if (ch != 0) { | |
result += ch; | |
} | |
i++; | |
} | |
} | |
int32_t CBC_DecodedBitStreamPaser::byteCompaction(int32_t mode, | |
CFX_Int32Array& codewords, | |
int32_t codeIndex, | |
CFX_ByteString& result) { | |
if (mode == BYTE_COMPACTION_MODE_LATCH) { | |
int32_t count = 0; | |
int64_t value = 0; | |
FX_WORD* decodedData = FX_Alloc(FX_WORD, 6); | |
CFX_Int32Array byteCompactedCodewords; | |
byteCompactedCodewords.SetSize(6); | |
FX_BOOL end = FALSE; | |
int32_t 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) { | |
codeIndex--; | |
end = TRUE; | |
} else { | |
if ((count % 5 == 0) && (count > 0)) { | |
int32_t j = 0; | |
for (; j < 6; ++j) { | |
decodedData[5 - j] = (FX_WORD)(value % 256); | |
value >>= 8; | |
} | |
for (j = 0; j < 6; ++j) { | |
result += (FX_CHAR)decodedData[j]; | |
} | |
count = 0; | |
} | |
} | |
} | |
FX_Free(decodedData); | |
if (codeIndex == codewords[0] && nextCode < TEXT_COMPACTION_MODE_LATCH) { | |
byteCompactedCodewords[count++] = nextCode; | |
} | |
for (int32_t i = 0; i < count; i++) { | |
result += (FX_CHAR)(FX_WORD)byteCompactedCodewords[i]; | |
} | |
} else if (mode == BYTE_COMPACTION_MODE_LATCH_6) { | |
int32_t count = 0; | |
int64_t value = 0; | |
FX_BOOL end = FALSE; | |
while (codeIndex < codewords[0] && !end) { | |
int32_t 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)) { | |
FX_WORD* decodedData = FX_Alloc(FX_WORD, 6); | |
int32_t j = 0; | |
for (; j < 6; ++j) { | |
decodedData[5 - j] = (FX_WORD)(value & 0xFF); | |
value >>= 8; | |
} | |
for (j = 0; j < 6; ++j) { | |
result += (FX_CHAR)decodedData[j]; | |
} | |
count = 0; | |
FX_Free(decodedData); | |
} | |
} | |
} | |
return codeIndex; | |
} | |
int32_t CBC_DecodedBitStreamPaser::numericCompaction(CFX_Int32Array& codewords, | |
int32_t codeIndex, | |
CFX_ByteString& result, | |
int32_t& e) { | |
int32_t count = 0; | |
FX_BOOL end = FALSE; | |
CFX_Int32Array numericCodewords; | |
numericCodewords.SetSize(MAX_NUMERIC_CODEWORDS); | |
while (codeIndex < codewords[0] && !end) { | |
int32_t 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) { | |
CFX_ByteString s = decodeBase900toBase10(numericCodewords, count, e); | |
BC_EXCEPTION_CHECK_ReturnValue(e, -1); | |
result += s; | |
count = 0; | |
} | |
} | |
return codeIndex; | |
} | |
CFX_ByteString CBC_DecodedBitStreamPaser::decodeBase900toBase10( | |
CFX_Int32Array& codewords, | |
int32_t count, | |
int32_t& e) { | |
BigInteger result = 0; | |
BigInteger nineHundred(900); | |
for (int32_t i = 0; i < count; i++) { | |
result = result * nineHundred + BigInteger(codewords[i]); | |
} | |
CFX_ByteString resultString(bigIntegerToString(result).c_str()); | |
if (resultString.GetAt(0) != '1') { | |
e = BCExceptionFormatInstance; | |
return ' '; | |
} | |
return resultString.Mid(1, resultString.GetLength() - 1); | |
} |