| // 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 "xfa/fxbarcode/pdf417/BC_PDF417DecodedBitStreamParser.h" |
| |
| #include <stdlib.h> |
| |
| #include "third_party/bigint/BigIntegerLibrary.hh" |
| #include "xfa/fxbarcode/BC_DecoderResult.h" |
| #include "xfa/fxbarcode/common/BC_CommonDecoderResult.h" |
| #include "xfa/fxbarcode/pdf417/BC_PDF417ResultMetadata.h" |
| #include "xfa/fxbarcode/utils.h" |
| |
| #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; |
| uint16_t* decodedData = FX_Alloc(uint16_t, 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] = (uint16_t)(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)(uint16_t)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)) { |
| uint16_t* decodedData = FX_Alloc(uint16_t, 6); |
| int32_t j = 0; |
| for (; j < 6; ++j) { |
| decodedData[5 - j] = (uint16_t)(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); |
| } |