UNPKG

@zxing/library

Version:

TypeScript port of ZXing multi-format 1D/2D barcode image processing library.

773 lines (772 loc) 37.9 kB
/* * 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. */ // package com.google.zxing.pdf417.decoder; // import com.google.zxing.FormatException; import FormatException from '../../FormatException'; // import com.google.zxing.common.CharacterSetECI; import CharacterSetECI from '../../common/CharacterSetECI'; // import com.google.zxing.common.DecoderResult; import DecoderResult from '../../common/DecoderResult'; // import com.google.zxing.pdf417.PDF417ResultMetadata; import PDF417ResultMetadata from '../PDF417ResultMetadata'; // import java.io.ByteArrayOutputStream; // import java.math.BigInteger; // import java.nio.charset.Charset; // import java.nio.charset.StandardCharsets; // import java.util.Arrays; import Arrays from '../../util/Arrays'; import StringBuilder from '../../util/StringBuilder'; import Integer from '../../util/Integer'; import Long from '../../util/Long'; import ByteArrayOutputStream from '../../util/ByteArrayOutputStream'; import StringEncoding from '../../util/StringEncoding'; /*private*/ var Mode; (function (Mode) { Mode[Mode["ALPHA"] = 0] = "ALPHA"; Mode[Mode["LOWER"] = 1] = "LOWER"; Mode[Mode["MIXED"] = 2] = "MIXED"; Mode[Mode["PUNCT"] = 3] = "PUNCT"; Mode[Mode["ALPHA_SHIFT"] = 4] = "ALPHA_SHIFT"; Mode[Mode["PUNCT_SHIFT"] = 5] = "PUNCT_SHIFT"; })(Mode || (Mode = {})); /** * Indirectly access the global BigInt constructor, it * allows browsers that doesn't support BigInt to run * the library without breaking due to "undefined BigInt" * errors. */ function getBigIntConstructor() { if (typeof window !== 'undefined') { return window['BigInt'] || null; } if (typeof global !== 'undefined') { return global['BigInt'] || null; } if (typeof self !== 'undefined') { return self['BigInt'] || null; } throw new Error('Can\'t search globals for BigInt!'); } /** * Used to store the BigInt constructor. */ let BigInteger; /** * This function creates a bigint value. It allows browsers * that doesn't support BigInt to run the rest of the library * by not directly accessing the BigInt constructor. */ function createBigInt(num) { if (typeof BigInteger === 'undefined') { BigInteger = getBigIntConstructor(); } if (BigInteger === null) { throw new Error('BigInt is not supported!'); } return BigInteger(num); } function getEXP900() { // in Java - array with length = 16 let EXP900 = []; EXP900[0] = createBigInt(1); let nineHundred = createBigInt(900); EXP900[1] = nineHundred; // in Java - array with length = 16 for (let i /*int*/ = 2; i < 16; i++) { EXP900[i] = EXP900[i - 1] * nineHundred; } return EXP900; } /** * <p>This class contains the methods for decoding the PDF417 codewords.</p> * * @author SITA Lab (kevin.osullivan@sita.aero) * @author Guenther Grau */ export default /*final*/ class DecodedBitStreamParser { // private DecodedBitStreamParser() { // } /** * * @param codewords * @param ecLevel * * @throws FormatException */ static decode(codewords, ecLevel) { // pass encoding to result (will be used for decode symbols in byte mode) let result = new StringBuilder(''); // let encoding: Charset = StandardCharsets.ISO_8859_1; let encoding = CharacterSetECI.ISO8859_1; /** * @note the next command is specific from this TypeScript library * because TS can't properly cast some values to char and * convert it to string later correctly due to encoding * differences from Java version. As reported here: * https://github.com/zxing-js/library/pull/264/files#r382831593 */ result.enableDecoding(encoding); // Get compaction mode let codeIndex = 1; let code = codewords[codeIndex++]; let resultMetadata = new PDF417ResultMetadata(); while (codeIndex < codewords[0]) { switch (code) { case DecodedBitStreamParser.TEXT_COMPACTION_MODE_LATCH: codeIndex = DecodedBitStreamParser.textCompaction(codewords, codeIndex, result); break; case DecodedBitStreamParser.BYTE_COMPACTION_MODE_LATCH: case DecodedBitStreamParser.BYTE_COMPACTION_MODE_LATCH_6: codeIndex = DecodedBitStreamParser.byteCompaction(code, codewords, encoding, codeIndex, result); break; case DecodedBitStreamParser.MODE_SHIFT_TO_BYTE_COMPACTION_MODE: result.append(/*(char)*/ codewords[codeIndex++]); break; case DecodedBitStreamParser.NUMERIC_COMPACTION_MODE_LATCH: codeIndex = DecodedBitStreamParser.numericCompaction(codewords, codeIndex, result); break; case DecodedBitStreamParser.ECI_CHARSET: let charsetECI = CharacterSetECI.getCharacterSetECIByValue(codewords[codeIndex++]); // encoding = Charset.forName(charsetECI.getName()); break; case DecodedBitStreamParser.ECI_GENERAL_PURPOSE: // Can't do anything with generic ECI; skip its 2 characters codeIndex += 2; break; case DecodedBitStreamParser.ECI_USER_DEFINED: // Can't do anything with user ECI; skip its 1 character codeIndex++; break; case DecodedBitStreamParser.BEGIN_MACRO_PDF417_CONTROL_BLOCK: codeIndex = DecodedBitStreamParser.decodeMacroBlock(codewords, codeIndex, resultMetadata); break; case DecodedBitStreamParser.BEGIN_MACRO_PDF417_OPTIONAL_FIELD: case DecodedBitStreamParser.MACRO_PDF417_TERMINATOR: // Should not see these outside a macro block throw new FormatException(); default: // Default to text compaction. During testing numerous barcodes // appeared to be missing the starting mode. In these cases defaulting // to text compaction seems to work. codeIndex--; codeIndex = DecodedBitStreamParser.textCompaction(codewords, codeIndex, result); break; } if (codeIndex < codewords.length) { code = codewords[codeIndex++]; } else { throw FormatException.getFormatInstance(); } } if (result.length() === 0) { throw FormatException.getFormatInstance(); } let decoderResult = new DecoderResult(null, result.toString(), null, ecLevel); decoderResult.setOther(resultMetadata); return decoderResult; } /** * * @param int * @param param1 * @param codewords * @param int * @param codeIndex * @param PDF417ResultMetadata * @param resultMetadata * * @throws FormatException */ // @SuppressWarnings("deprecation") static decodeMacroBlock(codewords, codeIndex, resultMetadata) { if (codeIndex + DecodedBitStreamParser.NUMBER_OF_SEQUENCE_CODEWORDS > codewords[0]) { // we must have at least two bytes left for the segment index throw FormatException.getFormatInstance(); } let segmentIndexArray = new Int32Array(DecodedBitStreamParser.NUMBER_OF_SEQUENCE_CODEWORDS); for (let i /*int*/ = 0; i < DecodedBitStreamParser.NUMBER_OF_SEQUENCE_CODEWORDS; i++, codeIndex++) { segmentIndexArray[i] = codewords[codeIndex]; } resultMetadata.setSegmentIndex(Integer.parseInt(DecodedBitStreamParser.decodeBase900toBase10(segmentIndexArray, DecodedBitStreamParser.NUMBER_OF_SEQUENCE_CODEWORDS))); let fileId = new StringBuilder(); codeIndex = DecodedBitStreamParser.textCompaction(codewords, codeIndex, fileId); resultMetadata.setFileId(fileId.toString()); let optionalFieldsStart = -1; if (codewords[codeIndex] === DecodedBitStreamParser.BEGIN_MACRO_PDF417_OPTIONAL_FIELD) { optionalFieldsStart = codeIndex + 1; } while (codeIndex < codewords[0]) { switch (codewords[codeIndex]) { case DecodedBitStreamParser.BEGIN_MACRO_PDF417_OPTIONAL_FIELD: codeIndex++; switch (codewords[codeIndex]) { case DecodedBitStreamParser.MACRO_PDF417_OPTIONAL_FIELD_FILE_NAME: let fileName = new StringBuilder(); codeIndex = DecodedBitStreamParser.textCompaction(codewords, codeIndex + 1, fileName); resultMetadata.setFileName(fileName.toString()); break; case DecodedBitStreamParser.MACRO_PDF417_OPTIONAL_FIELD_SENDER: let sender = new StringBuilder(); codeIndex = DecodedBitStreamParser.textCompaction(codewords, codeIndex + 1, sender); resultMetadata.setSender(sender.toString()); break; case DecodedBitStreamParser.MACRO_PDF417_OPTIONAL_FIELD_ADDRESSEE: let addressee = new StringBuilder(); codeIndex = DecodedBitStreamParser.textCompaction(codewords, codeIndex + 1, addressee); resultMetadata.setAddressee(addressee.toString()); break; case DecodedBitStreamParser.MACRO_PDF417_OPTIONAL_FIELD_SEGMENT_COUNT: let segmentCount = new StringBuilder(); codeIndex = DecodedBitStreamParser.numericCompaction(codewords, codeIndex + 1, segmentCount); resultMetadata.setSegmentCount(Integer.parseInt(segmentCount.toString())); break; case DecodedBitStreamParser.MACRO_PDF417_OPTIONAL_FIELD_TIME_STAMP: let timestamp = new StringBuilder(); codeIndex = DecodedBitStreamParser.numericCompaction(codewords, codeIndex + 1, timestamp); resultMetadata.setTimestamp(Long.parseLong(timestamp.toString())); break; case DecodedBitStreamParser.MACRO_PDF417_OPTIONAL_FIELD_CHECKSUM: let checksum = new StringBuilder(); codeIndex = DecodedBitStreamParser.numericCompaction(codewords, codeIndex + 1, checksum); resultMetadata.setChecksum(Integer.parseInt(checksum.toString())); break; case DecodedBitStreamParser.MACRO_PDF417_OPTIONAL_FIELD_FILE_SIZE: let fileSize = new StringBuilder(); codeIndex = DecodedBitStreamParser.numericCompaction(codewords, codeIndex + 1, fileSize); resultMetadata.setFileSize(Long.parseLong(fileSize.toString())); break; default: throw FormatException.getFormatInstance(); } break; case DecodedBitStreamParser.MACRO_PDF417_TERMINATOR: codeIndex++; resultMetadata.setLastSegment(true); break; default: throw FormatException.getFormatInstance(); } } // copy optional fields to additional options if (optionalFieldsStart !== -1) { let optionalFieldsLength = codeIndex - optionalFieldsStart; if (resultMetadata.isLastSegment()) { // do not include terminator optionalFieldsLength--; } resultMetadata.setOptionalData(Arrays.copyOfRange(codewords, optionalFieldsStart, optionalFieldsStart + optionalFieldsLength)); } return codeIndex; } /** * Text Compaction mode (see 5.4.1.5) permits all printable ASCII characters to be * encoded, i.e. values 32 - 126 inclusive in accordance with ISO/IEC 646 (IRV), as * well as selected control characters. * * @param codewords The array of codewords (data + error) * @param codeIndex The current index into the codeword array. * @param result The decoded data is appended to the result. * @return The next index into the codeword array. */ static textCompaction(codewords, codeIndex, result) { // 2 character per codeword let textCompactionData = new Int32Array((codewords[0] - codeIndex) * 2); // Used to hold the byte compaction value if there is a mode shift let byteCompactionData = new Int32Array((codewords[0] - codeIndex) * 2); let index = 0; let end = false; while ((codeIndex < codewords[0]) && !end) { let code = codewords[codeIndex++]; if (code < DecodedBitStreamParser.TEXT_COMPACTION_MODE_LATCH) { textCompactionData[index] = code / 30; textCompactionData[index + 1] = code % 30; index += 2; } else { switch (code) { case DecodedBitStreamParser.TEXT_COMPACTION_MODE_LATCH: // reinitialize text compaction mode to alpha sub mode textCompactionData[index++] = DecodedBitStreamParser.TEXT_COMPACTION_MODE_LATCH; break; case DecodedBitStreamParser.BYTE_COMPACTION_MODE_LATCH: case DecodedBitStreamParser.BYTE_COMPACTION_MODE_LATCH_6: case DecodedBitStreamParser.NUMERIC_COMPACTION_MODE_LATCH: case DecodedBitStreamParser.BEGIN_MACRO_PDF417_CONTROL_BLOCK: case DecodedBitStreamParser.BEGIN_MACRO_PDF417_OPTIONAL_FIELD: case DecodedBitStreamParser.MACRO_PDF417_TERMINATOR: codeIndex--; end = true; break; case DecodedBitStreamParser.MODE_SHIFT_TO_BYTE_COMPACTION_MODE: // The Mode Shift codeword 913 shall cause a temporary // switch from Text Compaction mode to Byte Compaction mode. // This switch shall be in effect for only the next codeword, // after which the mode shall revert to the prevailing sub-mode // of the Text Compaction mode. Codeword 913 is only available // in Text Compaction mode; its use is described in 5.4.2.4. textCompactionData[index] = DecodedBitStreamParser.MODE_SHIFT_TO_BYTE_COMPACTION_MODE; code = codewords[codeIndex++]; byteCompactionData[index] = code; index++; break; } } } DecodedBitStreamParser.decodeTextCompaction(textCompactionData, byteCompactionData, index, result); return codeIndex; } /** * The Text Compaction mode includes all the printable ASCII characters * (i.e. values from 32 to 126) and three ASCII control characters: HT or tab * (9: e), LF or line feed (10: e), and CR or carriage * return (13: e). The Text Compaction mode also includes various latch * and shift characters which are used exclusively within the mode. The Text * Compaction mode encodes up to 2 characters per codeword. The compaction rules * for converting data into PDF417 codewords are defined in 5.4.2.2. The sub-mode * switches are defined in 5.4.2.3. * * @param textCompactionData The text compaction data. * @param byteCompactionData The byte compaction data if there * was a mode shift. * @param length The size of the text compaction and byte compaction data. * @param result The decoded data is appended to the result. */ static decodeTextCompaction(textCompactionData, byteCompactionData, length, result) { // Beginning from an initial state of the Alpha sub-mode // The default compaction mode for PDF417 in effect at the start of each symbol shall always be Text // Compaction mode Alpha sub-mode (alphabetic: uppercase). A latch codeword from another mode to the Text // Compaction mode shall always switch to the Text Compaction Alpha sub-mode. let subMode = Mode.ALPHA; let priorToShiftMode = Mode.ALPHA; let i = 0; while (i < length) { let subModeCh = textCompactionData[i]; let ch = /*char*/ ''; switch (subMode) { case Mode.ALPHA: // Alpha (alphabetic: uppercase) if (subModeCh < 26) { // Upper case Alpha Character // Note: 65 = 'A' ASCII -> there is byte code of symbol ch = /*(char)('A' + subModeCh) */ String.fromCharCode(65 + subModeCh); } else { switch (subModeCh) { case 26: ch = ' '; break; case DecodedBitStreamParser.LL: subMode = Mode.LOWER; break; case DecodedBitStreamParser.ML: subMode = Mode.MIXED; break; case DecodedBitStreamParser.PS: // Shift to punctuation priorToShiftMode = subMode; subMode = Mode.PUNCT_SHIFT; break; case DecodedBitStreamParser.MODE_SHIFT_TO_BYTE_COMPACTION_MODE: result.append(/*(char)*/ byteCompactionData[i]); break; case DecodedBitStreamParser.TEXT_COMPACTION_MODE_LATCH: subMode = Mode.ALPHA; break; } } break; case Mode.LOWER: // Lower (alphabetic: lowercase) if (subModeCh < 26) { ch = /*(char)('a' + subModeCh)*/ String.fromCharCode(97 + subModeCh); } else { switch (subModeCh) { case 26: ch = ' '; break; case DecodedBitStreamParser.AS: // Shift to alpha priorToShiftMode = subMode; subMode = Mode.ALPHA_SHIFT; break; case DecodedBitStreamParser.ML: subMode = Mode.MIXED; break; case DecodedBitStreamParser.PS: // Shift to punctuation priorToShiftMode = subMode; subMode = Mode.PUNCT_SHIFT; break; case DecodedBitStreamParser.MODE_SHIFT_TO_BYTE_COMPACTION_MODE: // TODO Does this need to use the current character encoding? See other occurrences below result.append(/*(char)*/ byteCompactionData[i]); break; case DecodedBitStreamParser.TEXT_COMPACTION_MODE_LATCH: subMode = Mode.ALPHA; break; } } break; case Mode.MIXED: // Mixed (punctuation: e) if (subModeCh < DecodedBitStreamParser.PL) { ch = DecodedBitStreamParser.MIXED_CHARS[subModeCh]; } else { switch (subModeCh) { case DecodedBitStreamParser.PL: subMode = Mode.PUNCT; break; case 26: ch = ' '; break; case DecodedBitStreamParser.LL: subMode = Mode.LOWER; break; case DecodedBitStreamParser.AL: subMode = Mode.ALPHA; break; case DecodedBitStreamParser.PS: // Shift to punctuation priorToShiftMode = subMode; subMode = Mode.PUNCT_SHIFT; break; case DecodedBitStreamParser.MODE_SHIFT_TO_BYTE_COMPACTION_MODE: result.append(/*(char)*/ byteCompactionData[i]); break; case DecodedBitStreamParser.TEXT_COMPACTION_MODE_LATCH: subMode = Mode.ALPHA; break; } } break; case Mode.PUNCT: // Punctuation if (subModeCh < DecodedBitStreamParser.PAL) { ch = DecodedBitStreamParser.PUNCT_CHARS[subModeCh]; } else { switch (subModeCh) { case DecodedBitStreamParser.PAL: subMode = Mode.ALPHA; break; case DecodedBitStreamParser.MODE_SHIFT_TO_BYTE_COMPACTION_MODE: result.append(/*(char)*/ byteCompactionData[i]); break; case DecodedBitStreamParser.TEXT_COMPACTION_MODE_LATCH: subMode = Mode.ALPHA; break; } } break; case Mode.ALPHA_SHIFT: // Restore sub-mode subMode = priorToShiftMode; if (subModeCh < 26) { ch = /*(char)('A' + subModeCh)*/ String.fromCharCode(65 + subModeCh); } else { switch (subModeCh) { case 26: ch = ' '; break; case DecodedBitStreamParser.TEXT_COMPACTION_MODE_LATCH: subMode = Mode.ALPHA; break; } } break; case Mode.PUNCT_SHIFT: // Restore sub-mode subMode = priorToShiftMode; if (subModeCh < DecodedBitStreamParser.PAL) { ch = DecodedBitStreamParser.PUNCT_CHARS[subModeCh]; } else { switch (subModeCh) { case DecodedBitStreamParser.PAL: subMode = Mode.ALPHA; break; case DecodedBitStreamParser.MODE_SHIFT_TO_BYTE_COMPACTION_MODE: // PS before Shift-to-Byte is used as a padding character, // see 5.4.2.4 of the specification result.append(/*(char)*/ byteCompactionData[i]); break; case DecodedBitStreamParser.TEXT_COMPACTION_MODE_LATCH: subMode = Mode.ALPHA; break; } } break; } // if (ch !== 0) { if (ch !== '') { // Append decoded character to result result.append(ch); } i++; } } /** * Byte Compaction mode (see 5.4.3) permits all 256 possible 8-bit byte values to be encoded. * This includes all ASCII characters value 0 to 127 inclusive and provides for international * character set support. * * @param mode The byte compaction mode i.e. 901 or 924 * @param codewords The array of codewords (data + error) * @param encoding Currently active character encoding * @param codeIndex The current index into the codeword array. * @param result The decoded data is appended to the result. * @return The next index into the codeword array. */ static /*int*/ byteCompaction(mode, codewords, encoding, codeIndex, result) { let decodedBytes = new ByteArrayOutputStream(); let count = 0; let value = /*long*/ 0; let end = false; switch (mode) { case DecodedBitStreamParser.BYTE_COMPACTION_MODE_LATCH: // Total number of Byte Compaction characters to be encoded // is not a multiple of 6 let byteCompactedCodewords = new Int32Array(6); let nextCode = codewords[codeIndex++]; while ((codeIndex < codewords[0]) && !end) { byteCompactedCodewords[count++] = nextCode; // Base 900 value = 900 * value + nextCode; nextCode = codewords[codeIndex++]; // perhaps it should be ok to check only nextCode >= TEXT_COMPACTION_MODE_LATCH switch (nextCode) { case DecodedBitStreamParser.TEXT_COMPACTION_MODE_LATCH: case DecodedBitStreamParser.BYTE_COMPACTION_MODE_LATCH: case DecodedBitStreamParser.NUMERIC_COMPACTION_MODE_LATCH: case DecodedBitStreamParser.BYTE_COMPACTION_MODE_LATCH_6: case DecodedBitStreamParser.BEGIN_MACRO_PDF417_CONTROL_BLOCK: case DecodedBitStreamParser.BEGIN_MACRO_PDF417_OPTIONAL_FIELD: case DecodedBitStreamParser.MACRO_PDF417_TERMINATOR: codeIndex--; end = true; break; default: if ((count % 5 === 0) && (count > 0)) { // Decode every 5 codewords // Convert to Base 256 for (let j /*int*/ = 0; j < 6; ++j) { /* @note * JavaScript stores numbers as 64 bits floating point numbers, but all bitwise operations are performed on 32 bits binary numbers. * So the next bitwise operation could not be done with simple numbers */ decodedBytes.write(/*(byte)*/ Number(createBigInt(value) >> createBigInt(8 * (5 - j)))); } value = 0; count = 0; } break; } } // if the end of all codewords is reached the last codeword needs to be added if (codeIndex === codewords[0] && nextCode < DecodedBitStreamParser.TEXT_COMPACTION_MODE_LATCH) { byteCompactedCodewords[count++] = nextCode; } // If Byte Compaction mode is invoked with codeword 901, // the last group of codewords is interpreted directly // as one byte per codeword, without compaction. for (let i /*int*/ = 0; i < count; i++) { decodedBytes.write(/*(byte)*/ byteCompactedCodewords[i]); } break; case DecodedBitStreamParser.BYTE_COMPACTION_MODE_LATCH_6: // Total number of Byte Compaction characters to be encoded // is an integer multiple of 6 while (codeIndex < codewords[0] && !end) { let code = codewords[codeIndex++]; if (code < DecodedBitStreamParser.TEXT_COMPACTION_MODE_LATCH) { count++; // Base 900 value = 900 * value + code; } else { switch (code) { case DecodedBitStreamParser.TEXT_COMPACTION_MODE_LATCH: case DecodedBitStreamParser.BYTE_COMPACTION_MODE_LATCH: case DecodedBitStreamParser.NUMERIC_COMPACTION_MODE_LATCH: case DecodedBitStreamParser.BYTE_COMPACTION_MODE_LATCH_6: case DecodedBitStreamParser.BEGIN_MACRO_PDF417_CONTROL_BLOCK: case DecodedBitStreamParser.BEGIN_MACRO_PDF417_OPTIONAL_FIELD: case DecodedBitStreamParser.MACRO_PDF417_TERMINATOR: codeIndex--; end = true; break; } } if ((count % 5 === 0) && (count > 0)) { // Decode every 5 codewords // Convert to Base 256 /* @note * JavaScript stores numbers as 64 bits floating point numbers, but all bitwise operations are performed on 32 bits binary numbers. * So the next bitwise operation could not be done with simple numbers */ for (let j /*int*/ = 0; j < 6; ++j) { decodedBytes.write(/*(byte)*/ Number(createBigInt(value) >> createBigInt(8 * (5 - j)))); } value = 0; count = 0; } } break; } result.append(StringEncoding.decode(decodedBytes.toByteArray(), encoding)); return codeIndex; } /** * Numeric Compaction mode (see 5.4.4) permits efficient encoding of numeric data strings. * * @param codewords The array of codewords (data + error) * @param codeIndex The current index into the codeword array. * @param result The decoded data is appended to the result. * @return The next index into the codeword array. * * @throws FormatException */ static numericCompaction(codewords, codeIndex /*int*/, result) { let count = 0; let end = false; let numericCodewords = new Int32Array(DecodedBitStreamParser.MAX_NUMERIC_CODEWORDS); while (codeIndex < codewords[0] && !end) { let code = codewords[codeIndex++]; if (codeIndex === codewords[0]) { end = true; } if (code < DecodedBitStreamParser.TEXT_COMPACTION_MODE_LATCH) { numericCodewords[count] = code; count++; } else { switch (code) { case DecodedBitStreamParser.TEXT_COMPACTION_MODE_LATCH: case DecodedBitStreamParser.BYTE_COMPACTION_MODE_LATCH: case DecodedBitStreamParser.BYTE_COMPACTION_MODE_LATCH_6: case DecodedBitStreamParser.BEGIN_MACRO_PDF417_CONTROL_BLOCK: case DecodedBitStreamParser.BEGIN_MACRO_PDF417_OPTIONAL_FIELD: case DecodedBitStreamParser.MACRO_PDF417_TERMINATOR: codeIndex--; end = true; break; } } if ((count % DecodedBitStreamParser.MAX_NUMERIC_CODEWORDS === 0 || code === DecodedBitStreamParser.NUMERIC_COMPACTION_MODE_LATCH || end) && count > 0) { // Re-invoking Numeric Compaction mode (by using codeword 902 // while in Numeric Compaction mode) serves to terminate the // current Numeric Compaction mode grouping as described in 5.4.4.2, // and then to start a new one grouping. result.append(DecodedBitStreamParser.decodeBase900toBase10(numericCodewords, count)); count = 0; } } return codeIndex; } /** * Convert a list of Numeric Compacted codewords from Base 900 to Base 10. * * @param codewords The array of codewords * @param count The number of codewords * @return The decoded string representing the Numeric data. * * EXAMPLE * Encode the fifteen digit numeric string 000213298174000 * Prefix the numeric string with a 1 and set the initial value of * t = 1 000 213 298 174 000 * Calculate codeword 0 * d0 = 1 000 213 298 174 000 mod 900 = 200 * * t = 1 000 213 298 174 000 div 900 = 1 111 348 109 082 * Calculate codeword 1 * d1 = 1 111 348 109 082 mod 900 = 282 * * t = 1 111 348 109 082 div 900 = 1 234 831 232 * Calculate codeword 2 * d2 = 1 234 831 232 mod 900 = 632 * * t = 1 234 831 232 div 900 = 1 372 034 * Calculate codeword 3 * d3 = 1 372 034 mod 900 = 434 * * t = 1 372 034 div 900 = 1 524 * Calculate codeword 4 * d4 = 1 524 mod 900 = 624 * * t = 1 524 div 900 = 1 * Calculate codeword 5 * d5 = 1 mod 900 = 1 * t = 1 div 900 = 0 * Codeword sequence is: 1, 624, 434, 632, 282, 200 * * Decode the above codewords involves * 1 x 900 power of 5 + 624 x 900 power of 4 + 434 x 900 power of 3 + * 632 x 900 power of 2 + 282 x 900 power of 1 + 200 x 900 power of 0 = 1000213298174000 * * Remove leading 1 => Result is 000213298174000 * * @throws FormatException */ static decodeBase900toBase10(codewords, count) { let result = createBigInt(0); for (let i /*int*/ = 0; i < count; i++) { result += DecodedBitStreamParser.EXP900[count - i - 1] * createBigInt(codewords[i]); } let resultString = result.toString(); if (resultString.charAt(0) !== '1') { throw new FormatException(); } return resultString.substring(1); } } DecodedBitStreamParser.TEXT_COMPACTION_MODE_LATCH = 900; DecodedBitStreamParser.BYTE_COMPACTION_MODE_LATCH = 901; DecodedBitStreamParser.NUMERIC_COMPACTION_MODE_LATCH = 902; DecodedBitStreamParser.BYTE_COMPACTION_MODE_LATCH_6 = 924; DecodedBitStreamParser.ECI_USER_DEFINED = 925; DecodedBitStreamParser.ECI_GENERAL_PURPOSE = 926; DecodedBitStreamParser.ECI_CHARSET = 927; DecodedBitStreamParser.BEGIN_MACRO_PDF417_CONTROL_BLOCK = 928; DecodedBitStreamParser.BEGIN_MACRO_PDF417_OPTIONAL_FIELD = 923; DecodedBitStreamParser.MACRO_PDF417_TERMINATOR = 922; DecodedBitStreamParser.MODE_SHIFT_TO_BYTE_COMPACTION_MODE = 913; DecodedBitStreamParser.MAX_NUMERIC_CODEWORDS = 15; DecodedBitStreamParser.MACRO_PDF417_OPTIONAL_FIELD_FILE_NAME = 0; DecodedBitStreamParser.MACRO_PDF417_OPTIONAL_FIELD_SEGMENT_COUNT = 1; DecodedBitStreamParser.MACRO_PDF417_OPTIONAL_FIELD_TIME_STAMP = 2; DecodedBitStreamParser.MACRO_PDF417_OPTIONAL_FIELD_SENDER = 3; DecodedBitStreamParser.MACRO_PDF417_OPTIONAL_FIELD_ADDRESSEE = 4; DecodedBitStreamParser.MACRO_PDF417_OPTIONAL_FIELD_FILE_SIZE = 5; DecodedBitStreamParser.MACRO_PDF417_OPTIONAL_FIELD_CHECKSUM = 6; DecodedBitStreamParser.PL = 25; DecodedBitStreamParser.LL = 27; DecodedBitStreamParser.AS = 27; DecodedBitStreamParser.ML = 28; DecodedBitStreamParser.AL = 28; DecodedBitStreamParser.PS = 29; DecodedBitStreamParser.PAL = 29; DecodedBitStreamParser.PUNCT_CHARS = ';<>@[\\]_`~!\r\t,:\n-.$/"|*()?{}\''; DecodedBitStreamParser.MIXED_CHARS = '0123456789&\r\t,:#-.$/+%*=^'; /** * Table containing values for the exponent of 900. * This is used in the numeric compaction decode algorithm. */ DecodedBitStreamParser.EXP900 = getBigIntConstructor() ? getEXP900() : []; DecodedBitStreamParser.NUMBER_OF_SEQUENCE_CODEWORDS = 2;