// 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 #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; CBC_CommonDecoderResult* tempCd = new CBC_CommonDecoderResult(); tempCd->Init(rawBytes, result, 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); }