UNPKG

@zxing/library

Version:

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

354 lines (353 loc) 14.6 kB
"use strict"; /* * Copyright 2010 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. */ Object.defineProperty(exports, "__esModule", { value: true }); var DecoderResult_1 = require("../../common/DecoderResult"); var GenericGF_1 = require("../../common/reedsolomon/GenericGF"); var ReedSolomonDecoder_1 = require("../../common/reedsolomon/ReedSolomonDecoder"); var IllegalStateException_1 = require("../../IllegalStateException"); var FormatException_1 = require("../../FormatException"); var StringUtils_1 = require("../../common/StringUtils"); var Integer_1 = require("../../util/Integer"); // import java.util.Arrays; var Table; (function (Table) { Table[Table["UPPER"] = 0] = "UPPER"; Table[Table["LOWER"] = 1] = "LOWER"; Table[Table["MIXED"] = 2] = "MIXED"; Table[Table["DIGIT"] = 3] = "DIGIT"; Table[Table["PUNCT"] = 4] = "PUNCT"; Table[Table["BINARY"] = 5] = "BINARY"; })(Table || (Table = {})); /** * <p>The main class which implements Aztec Code decoding -- as opposed to locating and extracting * the Aztec Code from an image.</p> * * @author David Olivier */ var Decoder = /** @class */ (function () { function Decoder() { } Decoder.prototype.decode = function (detectorResult) { this.ddata = detectorResult; var matrix = detectorResult.getBits(); var rawbits = this.extractBits(matrix); var correctedBits = this.correctBits(rawbits); var rawBytes = Decoder.convertBoolArrayToByteArray(correctedBits); var result = Decoder.getEncodedData(correctedBits); var decoderResult = new DecoderResult_1.default(rawBytes, result, null, null); decoderResult.setNumBits(correctedBits.length); return decoderResult; }; // This method is used for testing the high-level encoder Decoder.highLevelDecode = function (correctedBits) { return this.getEncodedData(correctedBits); }; /** * Gets the string encoded in the aztec code bits * * @return the decoded string */ Decoder.getEncodedData = function (correctedBits) { var endIndex = correctedBits.length; var latchTable = Table.UPPER; // table most recently latched to var shiftTable = Table.UPPER; // table to use for the next read var result = ''; var index = 0; while (index < endIndex) { if (shiftTable === Table.BINARY) { if (endIndex - index < 5) { break; } var length_1 = Decoder.readCode(correctedBits, index, 5); index += 5; if (length_1 === 0) { if (endIndex - index < 11) { break; } length_1 = Decoder.readCode(correctedBits, index, 11) + 31; index += 11; } for (var charCount = 0; charCount < length_1; charCount++) { if (endIndex - index < 8) { index = endIndex; // Force outer loop to exit break; } var code = Decoder.readCode(correctedBits, index, 8); result += /*(char)*/ StringUtils_1.default.castAsNonUtf8Char(code); index += 8; } // Go back to whatever mode we had been in shiftTable = latchTable; } else { var size = shiftTable === Table.DIGIT ? 4 : 5; if (endIndex - index < size) { break; } var code = Decoder.readCode(correctedBits, index, size); index += size; var str = Decoder.getCharacter(shiftTable, code); if (str.startsWith('CTRL_')) { // Table changes // ISO/IEC 24778:2008 prescribes ending a shift sequence in the mode from which it was invoked. // That's including when that mode is a shift. // Our test case dlusbs.png for issue #642 exercises that. latchTable = shiftTable; // Latch the current mode, so as to return to Upper after U/S B/S shiftTable = Decoder.getTable(str.charAt(5)); if (str.charAt(6) === 'L') { latchTable = shiftTable; } } else { result += str; // Go back to whatever mode we had been in shiftTable = latchTable; } } } return result; }; /** * gets the table corresponding to the char passed */ Decoder.getTable = function (t) { switch (t) { case 'L': return Table.LOWER; case 'P': return Table.PUNCT; case 'M': return Table.MIXED; case 'D': return Table.DIGIT; case 'B': return Table.BINARY; case 'U': default: return Table.UPPER; } }; /** * Gets the character (or string) corresponding to the passed code in the given table * * @param table the table used * @param code the code of the character */ Decoder.getCharacter = function (table, code) { switch (table) { case Table.UPPER: return Decoder.UPPER_TABLE[code]; case Table.LOWER: return Decoder.LOWER_TABLE[code]; case Table.MIXED: return Decoder.MIXED_TABLE[code]; case Table.PUNCT: return Decoder.PUNCT_TABLE[code]; case Table.DIGIT: return Decoder.DIGIT_TABLE[code]; default: // Should not reach here. throw new IllegalStateException_1.default('Bad table'); } }; /** * <p>Performs RS error correction on an array of bits.</p> * * @return the corrected array * @throws FormatException if the input contains too many errors */ Decoder.prototype.correctBits = function (rawbits) { var gf; var codewordSize; if (this.ddata.getNbLayers() <= 2) { codewordSize = 6; gf = GenericGF_1.default.AZTEC_DATA_6; } else if (this.ddata.getNbLayers() <= 8) { codewordSize = 8; gf = GenericGF_1.default.AZTEC_DATA_8; } else if (this.ddata.getNbLayers() <= 22) { codewordSize = 10; gf = GenericGF_1.default.AZTEC_DATA_10; } else { codewordSize = 12; gf = GenericGF_1.default.AZTEC_DATA_12; } var numDataCodewords = this.ddata.getNbDatablocks(); var numCodewords = rawbits.length / codewordSize; if (numCodewords < numDataCodewords) { throw new FormatException_1.default(); } var offset = rawbits.length % codewordSize; var dataWords = new Int32Array(numCodewords); for (var i = 0; i < numCodewords; i++, offset += codewordSize) { dataWords[i] = Decoder.readCode(rawbits, offset, codewordSize); } try { var rsDecoder = new ReedSolomonDecoder_1.default(gf); rsDecoder.decode(dataWords, numCodewords - numDataCodewords); } catch (ex) { throw new FormatException_1.default(ex); } // Now perform the unstuffing operation. // First, count how many bits are going to be thrown out as stuffing var mask = (1 << codewordSize) - 1; var stuffedBits = 0; for (var i = 0; i < numDataCodewords; i++) { var dataWord = dataWords[i]; if (dataWord === 0 || dataWord === mask) { throw new FormatException_1.default(); } else if (dataWord === 1 || dataWord === mask - 1) { stuffedBits++; } } // Now, actually unpack the bits and remove the stuffing var correctedBits = new Array(numDataCodewords * codewordSize - stuffedBits); var index = 0; for (var i = 0; i < numDataCodewords; i++) { var dataWord = dataWords[i]; if (dataWord === 1 || dataWord === mask - 1) { // next codewordSize-1 bits are all zeros or all ones correctedBits.fill(dataWord > 1, index, index + codewordSize - 1); // Arrays.fill(correctedBits, index, index + codewordSize - 1, dataWord > 1); index += codewordSize - 1; } else { for (var bit = codewordSize - 1; bit >= 0; --bit) { correctedBits[index++] = (dataWord & (1 << bit)) !== 0; } } } return correctedBits; }; /** * Gets the array of bits from an Aztec Code matrix * * @return the array of bits */ Decoder.prototype.extractBits = function (matrix) { var compact = this.ddata.isCompact(); var layers = this.ddata.getNbLayers(); var baseMatrixSize = (compact ? 11 : 14) + layers * 4; // not including alignment lines var alignmentMap = new Int32Array(baseMatrixSize); var rawbits = new Array(this.totalBitsInLayer(layers, compact)); if (compact) { for (var i = 0; i < alignmentMap.length; i++) { alignmentMap[i] = i; } } else { var matrixSize = baseMatrixSize + 1 + 2 * Integer_1.default.truncDivision((Integer_1.default.truncDivision(baseMatrixSize, 2) - 1), 15); var origCenter = baseMatrixSize / 2; var center = Integer_1.default.truncDivision(matrixSize, 2); for (var i = 0; i < origCenter; i++) { var newOffset = i + Integer_1.default.truncDivision(i, 15); alignmentMap[origCenter - i - 1] = center - newOffset - 1; alignmentMap[origCenter + i] = center + newOffset + 1; } } for (var i = 0, rowOffset = 0; i < layers; i++) { var rowSize = (layers - i) * 4 + (compact ? 9 : 12); // The top-left most point of this layer is <low, low> (not including alignment lines) var low = i * 2; // The bottom-right most point of this layer is <high, high> (not including alignment lines) var high = baseMatrixSize - 1 - low; // We pull bits from the two 2 x rowSize columns and two rowSize x 2 rows for (var j = 0; j < rowSize; j++) { var columnOffset = j * 2; for (var k = 0; k < 2; k++) { // left column rawbits[rowOffset + columnOffset + k] = matrix.get(alignmentMap[low + k], alignmentMap[low + j]); // bottom row rawbits[rowOffset + 2 * rowSize + columnOffset + k] = matrix.get(alignmentMap[low + j], alignmentMap[high - k]); // right column rawbits[rowOffset + 4 * rowSize + columnOffset + k] = matrix.get(alignmentMap[high - k], alignmentMap[high - j]); // top row rawbits[rowOffset + 6 * rowSize + columnOffset + k] = matrix.get(alignmentMap[high - j], alignmentMap[low + k]); } } rowOffset += rowSize * 8; } return rawbits; }; /** * Reads a code of given length and at given index in an array of bits */ Decoder.readCode = function (rawbits, startIndex, length) { var res = 0; for (var i = startIndex; i < startIndex + length; i++) { res <<= 1; if (rawbits[i]) { res |= 0x01; } } return res; }; /** * Reads a code of length 8 in an array of bits, padding with zeros */ Decoder.readByte = function (rawbits, startIndex) { var n = rawbits.length - startIndex; if (n >= 8) { return Decoder.readCode(rawbits, startIndex, 8); } return Decoder.readCode(rawbits, startIndex, n) << (8 - n); }; /** * Packs a bit array into bytes, most significant bit first */ Decoder.convertBoolArrayToByteArray = function (boolArr) { var byteArr = new Uint8Array((boolArr.length + 7) / 8); for (var i = 0; i < byteArr.length; i++) { byteArr[i] = Decoder.readByte(boolArr, 8 * i); } return byteArr; }; Decoder.prototype.totalBitsInLayer = function (layers, compact) { return ((compact ? 88 : 112) + 16 * layers) * layers; }; Decoder.UPPER_TABLE = [ 'CTRL_PS', ' ', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'CTRL_LL', 'CTRL_ML', 'CTRL_DL', 'CTRL_BS' ]; Decoder.LOWER_TABLE = [ 'CTRL_PS', ' ', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'CTRL_US', 'CTRL_ML', 'CTRL_DL', 'CTRL_BS' ]; Decoder.MIXED_TABLE = [ 'CTRL_PS', ' ', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\b', '\t', '\n', '\x0b', '\f', '\r', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f', '@', '\\', '^', '_', '`', '|', '~', '\x7f', 'CTRL_LL', 'CTRL_UL', 'CTRL_PL', 'CTRL_BS' ]; Decoder.PUNCT_TABLE = [ '', '\r', '\r\n', '. ', ', ', ': ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '[', ']', '{', '}', 'CTRL_UL' ]; Decoder.DIGIT_TABLE = [ 'CTRL_PS', ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ',', '.', 'CTRL_UL', 'CTRL_US' ]; return Decoder; }()); exports.default = Decoder;