UNPKG

@zxing/library

Version:

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

706 lines (705 loc) 35.1 kB
"use strict"; /* * Copyright 2013 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. */ var __values = (this && this.__values) || function(o) { var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; if (m) return m.call(o); if (o && typeof o.length === "number") return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); }; Object.defineProperty(exports, "__esModule", { value: true }); // package com.google.zxing.pdf417.decoder; // import com.google.zxing.ChecksumException; var ChecksumException_1 = require("../../ChecksumException"); // import com.google.zxing.FormatException; var FormatException_1 = require("../../FormatException"); // import com.google.zxing.NotFoundException; var NotFoundException_1 = require("../../NotFoundException"); // import com.google.zxing.common.detector.MathUtils; var MathUtils_1 = require("../../common/detector/MathUtils"); // import com.google.zxing.pdf417.PDF417Common; var PDF417Common_1 = require("../PDF417Common"); // import com.google.zxing.pdf417.decoder.ec.ErrorCorrection; var ErrorCorrection_1 = require("./ec/ErrorCorrection"); // local var BoundingBox_1 = require("./BoundingBox"); var DetectionResultRowIndicatorColumn_1 = require("./DetectionResultRowIndicatorColumn"); var DetectionResult_1 = require("./DetectionResult"); var DetectionResultColumn_1 = require("./DetectionResultColumn"); var Codeword_1 = require("./Codeword"); var BarcodeValue_1 = require("./BarcodeValue"); var PDF417CodewordDecoder_1 = require("./PDF417CodewordDecoder"); var DecodedBitStreamParser_1 = require("./DecodedBitStreamParser"); // utils var Formatter_1 = require("../../util/Formatter"); // import java.util.ArrayList; // import java.util.Collection; // import java.util.Formatter; // import java.util.List; /** * @author Guenther Grau */ var PDF417ScanningDecoder = /** @class */ (function () { function PDF417ScanningDecoder() { } /** * @TODO don't pass in minCodewordWidth and maxCodewordWidth, pass in barcode columns for start and stop pattern * * columns. That way width can be deducted from the pattern column. * This approach also allows to detect more details about the barcode, e.g. if a bar type (white or black) is wider * than it should be. This can happen if the scanner used a bad blackpoint. * * @param BitMatrix * @param image * @param ResultPoint * @param imageTopLeft * @param ResultPoint * @param imageBottomLeft * @param ResultPoint * @param imageTopRight * @param ResultPoint * @param imageBottomRight * @param int * @param minCodewordWidth * @param int * @param maxCodewordWidth * * @throws NotFoundException * @throws FormatException * @throws ChecksumException */ PDF417ScanningDecoder.decode = function (image, imageTopLeft, imageBottomLeft, imageTopRight, imageBottomRight, minCodewordWidth, maxCodewordWidth) { var boundingBox = new BoundingBox_1.default(image, imageTopLeft, imageBottomLeft, imageTopRight, imageBottomRight); var leftRowIndicatorColumn = null; var rightRowIndicatorColumn = null; var detectionResult; for (var firstPass /*boolean*/ = true;; firstPass = false) { if (imageTopLeft != null) { leftRowIndicatorColumn = PDF417ScanningDecoder.getRowIndicatorColumn(image, boundingBox, imageTopLeft, true, minCodewordWidth, maxCodewordWidth); } if (imageTopRight != null) { rightRowIndicatorColumn = PDF417ScanningDecoder.getRowIndicatorColumn(image, boundingBox, imageTopRight, false, minCodewordWidth, maxCodewordWidth); } detectionResult = PDF417ScanningDecoder.merge(leftRowIndicatorColumn, rightRowIndicatorColumn); if (detectionResult == null) { throw NotFoundException_1.default.getNotFoundInstance(); } var resultBox = detectionResult.getBoundingBox(); if (firstPass && resultBox != null && (resultBox.getMinY() < boundingBox.getMinY() || resultBox.getMaxY() > boundingBox.getMaxY())) { boundingBox = resultBox; } else { break; } } detectionResult.setBoundingBox(boundingBox); var maxBarcodeColumn = detectionResult.getBarcodeColumnCount() + 1; detectionResult.setDetectionResultColumn(0, leftRowIndicatorColumn); detectionResult.setDetectionResultColumn(maxBarcodeColumn, rightRowIndicatorColumn); var leftToRight = leftRowIndicatorColumn != null; for (var barcodeColumnCount /*int*/ = 1; barcodeColumnCount <= maxBarcodeColumn; barcodeColumnCount++) { var barcodeColumn = leftToRight ? barcodeColumnCount : maxBarcodeColumn - barcodeColumnCount; if (detectionResult.getDetectionResultColumn(barcodeColumn) !== /* null */ undefined) { // This will be the case for the opposite row indicator column, which doesn't need to be decoded again. continue; } var detectionResultColumn = void 0; if (barcodeColumn === 0 || barcodeColumn === maxBarcodeColumn) { detectionResultColumn = new DetectionResultRowIndicatorColumn_1.default(boundingBox, barcodeColumn === 0); } else { detectionResultColumn = new DetectionResultColumn_1.default(boundingBox); } detectionResult.setDetectionResultColumn(barcodeColumn, detectionResultColumn); var startColumn = -1; var previousStartColumn = startColumn; // TODO start at a row for which we know the start position, then detect upwards and downwards from there. for (var imageRow /*int*/ = boundingBox.getMinY(); imageRow <= boundingBox.getMaxY(); imageRow++) { startColumn = PDF417ScanningDecoder.getStartColumn(detectionResult, barcodeColumn, imageRow, leftToRight); if (startColumn < 0 || startColumn > boundingBox.getMaxX()) { if (previousStartColumn === -1) { continue; } startColumn = previousStartColumn; } var codeword = PDF417ScanningDecoder.detectCodeword(image, boundingBox.getMinX(), boundingBox.getMaxX(), leftToRight, startColumn, imageRow, minCodewordWidth, maxCodewordWidth); if (codeword != null) { detectionResultColumn.setCodeword(imageRow, codeword); previousStartColumn = startColumn; minCodewordWidth = Math.min(minCodewordWidth, codeword.getWidth()); maxCodewordWidth = Math.max(maxCodewordWidth, codeword.getWidth()); } } } return PDF417ScanningDecoder.createDecoderResult(detectionResult); }; /** * * @param leftRowIndicatorColumn * @param rightRowIndicatorColumn * * @throws NotFoundException */ PDF417ScanningDecoder.merge = function (leftRowIndicatorColumn, rightRowIndicatorColumn) { if (leftRowIndicatorColumn == null && rightRowIndicatorColumn == null) { return null; } var barcodeMetadata = PDF417ScanningDecoder.getBarcodeMetadata(leftRowIndicatorColumn, rightRowIndicatorColumn); if (barcodeMetadata == null) { return null; } var boundingBox = BoundingBox_1.default.merge(PDF417ScanningDecoder.adjustBoundingBox(leftRowIndicatorColumn), PDF417ScanningDecoder.adjustBoundingBox(rightRowIndicatorColumn)); return new DetectionResult_1.default(barcodeMetadata, boundingBox); }; /** * * @param rowIndicatorColumn * * @throws NotFoundException */ PDF417ScanningDecoder.adjustBoundingBox = function (rowIndicatorColumn) { var e_1, _a; if (rowIndicatorColumn == null) { return null; } var rowHeights = rowIndicatorColumn.getRowHeights(); if (rowHeights == null) { return null; } var maxRowHeight = PDF417ScanningDecoder.getMax(rowHeights); var missingStartRows = 0; try { for (var rowHeights_1 = __values(rowHeights), rowHeights_1_1 = rowHeights_1.next(); !rowHeights_1_1.done; rowHeights_1_1 = rowHeights_1.next()) { var rowHeight = rowHeights_1_1.value /*int*/; missingStartRows += maxRowHeight - rowHeight; if (rowHeight > 0) { break; } } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (rowHeights_1_1 && !rowHeights_1_1.done && (_a = rowHeights_1.return)) _a.call(rowHeights_1); } finally { if (e_1) throw e_1.error; } } var codewords = rowIndicatorColumn.getCodewords(); for (var row /*int*/ = 0; missingStartRows > 0 && codewords[row] == null; row++) { missingStartRows--; } var missingEndRows = 0; for (var row /*int*/ = rowHeights.length - 1; row >= 0; row--) { missingEndRows += maxRowHeight - rowHeights[row]; if (rowHeights[row] > 0) { break; } } for (var row /*int*/ = codewords.length - 1; missingEndRows > 0 && codewords[row] == null; row--) { missingEndRows--; } return rowIndicatorColumn.getBoundingBox().addMissingRows(missingStartRows, missingEndRows, rowIndicatorColumn.isLeft()); }; PDF417ScanningDecoder.getMax = function (values) { var e_2, _a; var maxValue = -1; try { for (var values_1 = __values(values), values_1_1 = values_1.next(); !values_1_1.done; values_1_1 = values_1.next()) { var value = values_1_1.value /*int*/; maxValue = Math.max(maxValue, value); } } catch (e_2_1) { e_2 = { error: e_2_1 }; } finally { try { if (values_1_1 && !values_1_1.done && (_a = values_1.return)) _a.call(values_1); } finally { if (e_2) throw e_2.error; } } return maxValue; }; PDF417ScanningDecoder.getBarcodeMetadata = function (leftRowIndicatorColumn, rightRowIndicatorColumn) { var leftBarcodeMetadata; if (leftRowIndicatorColumn == null || (leftBarcodeMetadata = leftRowIndicatorColumn.getBarcodeMetadata()) == null) { return rightRowIndicatorColumn == null ? null : rightRowIndicatorColumn.getBarcodeMetadata(); } var rightBarcodeMetadata; if (rightRowIndicatorColumn == null || (rightBarcodeMetadata = rightRowIndicatorColumn.getBarcodeMetadata()) == null) { return leftBarcodeMetadata; } if (leftBarcodeMetadata.getColumnCount() !== rightBarcodeMetadata.getColumnCount() && leftBarcodeMetadata.getErrorCorrectionLevel() !== rightBarcodeMetadata.getErrorCorrectionLevel() && leftBarcodeMetadata.getRowCount() !== rightBarcodeMetadata.getRowCount()) { return null; } return leftBarcodeMetadata; }; PDF417ScanningDecoder.getRowIndicatorColumn = function (image, boundingBox, startPoint, leftToRight, minCodewordWidth, maxCodewordWidth) { var rowIndicatorColumn = new DetectionResultRowIndicatorColumn_1.default(boundingBox, leftToRight); for (var i /*int*/ = 0; i < 2; i++) { var increment = i === 0 ? 1 : -1; var startColumn = Math.trunc(Math.trunc(startPoint.getX())); for (var imageRow /*int*/ = Math.trunc(Math.trunc(startPoint.getY())); imageRow <= boundingBox.getMaxY() && imageRow >= boundingBox.getMinY(); imageRow += increment) { var codeword = PDF417ScanningDecoder.detectCodeword(image, 0, image.getWidth(), leftToRight, startColumn, imageRow, minCodewordWidth, maxCodewordWidth); if (codeword != null) { rowIndicatorColumn.setCodeword(imageRow, codeword); if (leftToRight) { startColumn = codeword.getStartX(); } else { startColumn = codeword.getEndX(); } } } } return rowIndicatorColumn; }; /** * * @param detectionResult * @param BarcodeValue * @param param2 * @param param3 * @param barcodeMatrix * * @throws NotFoundException */ PDF417ScanningDecoder.adjustCodewordCount = function (detectionResult, barcodeMatrix) { var barcodeMatrix01 = barcodeMatrix[0][1]; var numberOfCodewords = barcodeMatrix01.getValue(); var calculatedNumberOfCodewords = detectionResult.getBarcodeColumnCount() * detectionResult.getBarcodeRowCount() - PDF417ScanningDecoder.getNumberOfECCodeWords(detectionResult.getBarcodeECLevel()); if (numberOfCodewords.length === 0) { if (calculatedNumberOfCodewords < 1 || calculatedNumberOfCodewords > PDF417Common_1.default.MAX_CODEWORDS_IN_BARCODE) { throw NotFoundException_1.default.getNotFoundInstance(); } barcodeMatrix01.setValue(calculatedNumberOfCodewords); } else if (numberOfCodewords[0] !== calculatedNumberOfCodewords) { // The calculated one is more reliable as it is derived from the row indicator columns barcodeMatrix01.setValue(calculatedNumberOfCodewords); } }; /** * * @param detectionResult * * @throws FormatException * @throws ChecksumException * @throws NotFoundException */ PDF417ScanningDecoder.createDecoderResult = function (detectionResult) { var barcodeMatrix = PDF417ScanningDecoder.createBarcodeMatrix(detectionResult); PDF417ScanningDecoder.adjustCodewordCount(detectionResult, barcodeMatrix); var erasures /*Collection<Integer>*/ = new Array(); var codewords = new Int32Array(detectionResult.getBarcodeRowCount() * detectionResult.getBarcodeColumnCount()); var ambiguousIndexValuesList = /*List<int[]>*/ []; var ambiguousIndexesList = /*Collection<Integer>*/ new Array(); for (var row /*int*/ = 0; row < detectionResult.getBarcodeRowCount(); row++) { for (var column /*int*/ = 0; column < detectionResult.getBarcodeColumnCount(); column++) { var values = barcodeMatrix[row][column + 1].getValue(); var codewordIndex = row * detectionResult.getBarcodeColumnCount() + column; if (values.length === 0) { erasures.push(codewordIndex); } else if (values.length === 1) { codewords[codewordIndex] = values[0]; } else { ambiguousIndexesList.push(codewordIndex); ambiguousIndexValuesList.push(values); } } } var ambiguousIndexValues = new Array(ambiguousIndexValuesList.length); for (var i /*int*/ = 0; i < ambiguousIndexValues.length; i++) { ambiguousIndexValues[i] = ambiguousIndexValuesList[i]; } return PDF417ScanningDecoder.createDecoderResultFromAmbiguousValues(detectionResult.getBarcodeECLevel(), codewords, PDF417Common_1.default.toIntArray(erasures), PDF417Common_1.default.toIntArray(ambiguousIndexesList), ambiguousIndexValues); }; /** * This method deals with the fact, that the decoding process doesn't always yield a single most likely value. The * current error correction implementation doesn't deal with erasures very well, so it's better to provide a value * for these ambiguous codewords instead of treating it as an erasure. The problem is that we don't know which of * the ambiguous values to choose. We try decode using the first value, and if that fails, we use another of the * ambiguous values and try to decode again. This usually only happens on very hard to read and decode barcodes, * so decoding the normal barcodes is not affected by this. * * @param erasureArray contains the indexes of erasures * @param ambiguousIndexes array with the indexes that have more than one most likely value * @param ambiguousIndexValues two dimensional array that contains the ambiguous values. The first dimension must * be the same length as the ambiguousIndexes array * * @throws FormatException * @throws ChecksumException */ PDF417ScanningDecoder.createDecoderResultFromAmbiguousValues = function (ecLevel, codewords, erasureArray, ambiguousIndexes, ambiguousIndexValues) { var ambiguousIndexCount = new Int32Array(ambiguousIndexes.length); var tries = 100; while (tries-- > 0) { for (var i /*int*/ = 0; i < ambiguousIndexCount.length; i++) { codewords[ambiguousIndexes[i]] = ambiguousIndexValues[i][ambiguousIndexCount[i]]; } try { return PDF417ScanningDecoder.decodeCodewords(codewords, ecLevel, erasureArray); } catch (err) { var ignored = err instanceof ChecksumException_1.default; if (!ignored) { throw err; } } if (ambiguousIndexCount.length === 0) { throw ChecksumException_1.default.getChecksumInstance(); } for (var i /*int*/ = 0; i < ambiguousIndexCount.length; i++) { if (ambiguousIndexCount[i] < ambiguousIndexValues[i].length - 1) { ambiguousIndexCount[i]++; break; } else { ambiguousIndexCount[i] = 0; if (i === ambiguousIndexCount.length - 1) { throw ChecksumException_1.default.getChecksumInstance(); } } } } throw ChecksumException_1.default.getChecksumInstance(); }; PDF417ScanningDecoder.createBarcodeMatrix = function (detectionResult) { var e_3, _a, e_4, _b; // let barcodeMatrix: BarcodeValue[][] = // new BarcodeValue[detectionResult.getBarcodeRowCount()][detectionResult.getBarcodeColumnCount() + 2]; var barcodeMatrix = Array.from({ length: detectionResult.getBarcodeRowCount() }, function () { return new Array(detectionResult.getBarcodeColumnCount() + 2); }); for (var row /*int*/ = 0; row < barcodeMatrix.length; row++) { for (var column_1 /*int*/ = 0; column_1 < barcodeMatrix[row].length; column_1++) { barcodeMatrix[row][column_1] = new BarcodeValue_1.default(); } } var column = 0; try { for (var _c = __values(detectionResult.getDetectionResultColumns()), _d = _c.next(); !_d.done; _d = _c.next()) { var detectionResultColumn = _d.value /*DetectionResultColumn*/; if (detectionResultColumn != null) { try { for (var _e = (e_4 = void 0, __values(detectionResultColumn.getCodewords())), _f = _e.next(); !_f.done; _f = _e.next()) { var codeword = _f.value /*Codeword*/; if (codeword != null) { var rowNumber = codeword.getRowNumber(); if (rowNumber >= 0) { if (rowNumber >= barcodeMatrix.length) { // We have more rows than the barcode metadata allows for, ignore them. continue; } barcodeMatrix[rowNumber][column].setValue(codeword.getValue()); } } } } catch (e_4_1) { e_4 = { error: e_4_1 }; } finally { try { if (_f && !_f.done && (_b = _e.return)) _b.call(_e); } finally { if (e_4) throw e_4.error; } } } column++; } } catch (e_3_1) { e_3 = { error: e_3_1 }; } finally { try { if (_d && !_d.done && (_a = _c.return)) _a.call(_c); } finally { if (e_3) throw e_3.error; } } return barcodeMatrix; }; PDF417ScanningDecoder.isValidBarcodeColumn = function (detectionResult, barcodeColumn) { return barcodeColumn >= 0 && barcodeColumn <= detectionResult.getBarcodeColumnCount() + 1; }; PDF417ScanningDecoder.getStartColumn = function (detectionResult, barcodeColumn, imageRow, leftToRight) { var e_5, _a; var offset = leftToRight ? 1 : -1; var codeword = null; if (PDF417ScanningDecoder.isValidBarcodeColumn(detectionResult, barcodeColumn - offset)) { codeword = detectionResult.getDetectionResultColumn(barcodeColumn - offset).getCodeword(imageRow); } if (codeword != null) { return leftToRight ? codeword.getEndX() : codeword.getStartX(); } codeword = detectionResult.getDetectionResultColumn(barcodeColumn).getCodewordNearby(imageRow); if (codeword != null) { return leftToRight ? codeword.getStartX() : codeword.getEndX(); } if (PDF417ScanningDecoder.isValidBarcodeColumn(detectionResult, barcodeColumn - offset)) { codeword = detectionResult.getDetectionResultColumn(barcodeColumn - offset).getCodewordNearby(imageRow); } if (codeword != null) { return leftToRight ? codeword.getEndX() : codeword.getStartX(); } var skippedColumns = 0; while (PDF417ScanningDecoder.isValidBarcodeColumn(detectionResult, barcodeColumn - offset)) { barcodeColumn -= offset; try { for (var _b = (e_5 = void 0, __values(detectionResult.getDetectionResultColumn(barcodeColumn).getCodewords())), _c = _b.next(); !_c.done; _c = _b.next()) { var previousRowCodeword = _c.value /*Codeword*/; if (previousRowCodeword != null) { return (leftToRight ? previousRowCodeword.getEndX() : previousRowCodeword.getStartX()) + offset * skippedColumns * (previousRowCodeword.getEndX() - previousRowCodeword.getStartX()); } } } catch (e_5_1) { e_5 = { error: e_5_1 }; } finally { try { if (_c && !_c.done && (_a = _b.return)) _a.call(_b); } finally { if (e_5) throw e_5.error; } } skippedColumns++; } return leftToRight ? detectionResult.getBoundingBox().getMinX() : detectionResult.getBoundingBox().getMaxX(); }; PDF417ScanningDecoder.detectCodeword = function (image, minColumn, maxColumn, leftToRight, startColumn, imageRow, minCodewordWidth, maxCodewordWidth) { startColumn = PDF417ScanningDecoder.adjustCodewordStartColumn(image, minColumn, maxColumn, leftToRight, startColumn, imageRow); // we usually know fairly exact now how long a codeword is. We should provide minimum and maximum expected length // and try to adjust the read pixels, e.g. remove single pixel errors or try to cut off exceeding pixels. // min and maxCodewordWidth should not be used as they are calculated for the whole barcode an can be inaccurate // for the current position var moduleBitCount = PDF417ScanningDecoder.getModuleBitCount(image, minColumn, maxColumn, leftToRight, startColumn, imageRow); if (moduleBitCount == null) { return null; } var endColumn; var codewordBitCount = MathUtils_1.default.sum(moduleBitCount); if (leftToRight) { endColumn = startColumn + codewordBitCount; } else { for (var i /*int*/ = 0; i < moduleBitCount.length / 2; i++) { var tmpCount = moduleBitCount[i]; moduleBitCount[i] = moduleBitCount[moduleBitCount.length - 1 - i]; moduleBitCount[moduleBitCount.length - 1 - i] = tmpCount; } endColumn = startColumn; startColumn = endColumn - codewordBitCount; } // TODO implement check for width and correction of black and white bars // use start (and maybe stop pattern) to determine if black bars are wider than white bars. If so, adjust. // should probably done only for codewords with a lot more than 17 bits. // The following fixes 10-1.png, which has wide black bars and small white bars // for (let i /*int*/ = 0; i < moduleBitCount.length; i++) { // if (i % 2 === 0) { // moduleBitCount[i]--; // } else { // moduleBitCount[i]++; // } // } // We could also use the width of surrounding codewords for more accurate results, but this seems // sufficient for now if (!PDF417ScanningDecoder.checkCodewordSkew(codewordBitCount, minCodewordWidth, maxCodewordWidth)) { // We could try to use the startX and endX position of the codeword in the same column in the previous row, // create the bit count from it and normalize it to 8. This would help with single pixel errors. return null; } var decodedValue = PDF417CodewordDecoder_1.default.getDecodedValue(moduleBitCount); var codeword = PDF417Common_1.default.getCodeword(decodedValue); if (codeword === -1) { return null; } return new Codeword_1.default(startColumn, endColumn, PDF417ScanningDecoder.getCodewordBucketNumber(decodedValue), codeword); }; PDF417ScanningDecoder.getModuleBitCount = function (image, minColumn, maxColumn, leftToRight, startColumn, imageRow) { var imageColumn = startColumn; var moduleBitCount = new Int32Array(8); var moduleNumber = 0; var increment = leftToRight ? 1 : -1; var previousPixelValue = leftToRight; while ((leftToRight ? imageColumn < maxColumn : imageColumn >= minColumn) && moduleNumber < moduleBitCount.length) { if (image.get(imageColumn, imageRow) === previousPixelValue) { moduleBitCount[moduleNumber]++; imageColumn += increment; } else { moduleNumber++; previousPixelValue = !previousPixelValue; } } if (moduleNumber === moduleBitCount.length || ((imageColumn === (leftToRight ? maxColumn : minColumn)) && moduleNumber === moduleBitCount.length - 1)) { return moduleBitCount; } return null; }; PDF417ScanningDecoder.getNumberOfECCodeWords = function (barcodeECLevel) { return 2 << barcodeECLevel; }; PDF417ScanningDecoder.adjustCodewordStartColumn = function (image, minColumn, maxColumn, leftToRight, codewordStartColumn, imageRow) { var correctedStartColumn = codewordStartColumn; var increment = leftToRight ? -1 : 1; // there should be no black pixels before the start column. If there are, then we need to start earlier. for (var i /*int*/ = 0; i < 2; i++) { while ((leftToRight ? correctedStartColumn >= minColumn : correctedStartColumn < maxColumn) && leftToRight === image.get(correctedStartColumn, imageRow)) { if (Math.abs(codewordStartColumn - correctedStartColumn) > PDF417ScanningDecoder.CODEWORD_SKEW_SIZE) { return codewordStartColumn; } correctedStartColumn += increment; } increment = -increment; leftToRight = !leftToRight; } return correctedStartColumn; }; PDF417ScanningDecoder.checkCodewordSkew = function (codewordSize, minCodewordWidth, maxCodewordWidth) { return minCodewordWidth - PDF417ScanningDecoder.CODEWORD_SKEW_SIZE <= codewordSize && codewordSize <= maxCodewordWidth + PDF417ScanningDecoder.CODEWORD_SKEW_SIZE; }; /** * @throws FormatException, * @throws ChecksumException */ PDF417ScanningDecoder.decodeCodewords = function (codewords, ecLevel, erasures) { if (codewords.length === 0) { throw FormatException_1.default.getFormatInstance(); } var numECCodewords = 1 << (ecLevel + 1); var correctedErrorsCount = PDF417ScanningDecoder.correctErrors(codewords, erasures, numECCodewords); PDF417ScanningDecoder.verifyCodewordCount(codewords, numECCodewords); // Decode the codewords var decoderResult = DecodedBitStreamParser_1.default.decode(codewords, '' + ecLevel); decoderResult.setErrorsCorrected(correctedErrorsCount); decoderResult.setErasures(erasures.length); return decoderResult; }; /** * <p>Given data and error-correction codewords received, possibly corrupted by errors, attempts to * correct the errors in-place.</p> * * @param codewords data and error correction codewords * @param erasures positions of any known erasures * @param numECCodewords number of error correction codewords that are available in codewords * @throws ChecksumException if error correction fails */ PDF417ScanningDecoder.correctErrors = function (codewords, erasures, numECCodewords) { if (erasures != null && erasures.length > numECCodewords / 2 + PDF417ScanningDecoder.MAX_ERRORS || numECCodewords < 0 || numECCodewords > PDF417ScanningDecoder.MAX_EC_CODEWORDS) { // Too many errors or EC Codewords is corrupted throw ChecksumException_1.default.getChecksumInstance(); } return PDF417ScanningDecoder.errorCorrection.decode(codewords, numECCodewords, erasures); }; /** * Verify that all is OK with the codeword array. * @throws FormatException */ PDF417ScanningDecoder.verifyCodewordCount = function (codewords, numECCodewords) { if (codewords.length < 4) { // Codeword array size should be at least 4 allowing for // Count CW, At least one Data CW, Error Correction CW, Error Correction CW throw FormatException_1.default.getFormatInstance(); } // The first codeword, the Symbol Length Descriptor, shall always encode the total number of data // codewords in the symbol, including the Symbol Length Descriptor itself, data codewords and pad // codewords, but excluding the number of error correction codewords. var numberOfCodewords = codewords[0]; if (numberOfCodewords > codewords.length) { throw FormatException_1.default.getFormatInstance(); } if (numberOfCodewords === 0) { // Reset to the length of the array - 8 (Allow for at least level 3 Error Correction (8 Error Codewords) if (numECCodewords < codewords.length) { codewords[0] = codewords.length - numECCodewords; } else { throw FormatException_1.default.getFormatInstance(); } } }; PDF417ScanningDecoder.getBitCountForCodeword = function (codeword) { var result = new Int32Array(8); var previousValue = 0; var i = result.length - 1; while (true) { if ((codeword & 0x1) !== previousValue) { previousValue = codeword & 0x1; i--; if (i < 0) { break; } } result[i]++; codeword >>= 1; } return result; }; PDF417ScanningDecoder.getCodewordBucketNumber = function (codeword) { if (codeword instanceof Int32Array) { return this.getCodewordBucketNumber_Int32Array(codeword); } return this.getCodewordBucketNumber_number(codeword); }; PDF417ScanningDecoder.getCodewordBucketNumber_number = function (codeword) { return PDF417ScanningDecoder.getCodewordBucketNumber(PDF417ScanningDecoder.getBitCountForCodeword(codeword)); }; PDF417ScanningDecoder.getCodewordBucketNumber_Int32Array = function (moduleBitCount) { return (moduleBitCount[0] - moduleBitCount[2] + moduleBitCount[4] - moduleBitCount[6] + 9) % 9; }; PDF417ScanningDecoder.toString = function (barcodeMatrix) { var formatter = new Formatter_1.default(); // try (let formatter = new Formatter()) { for (var row /*int*/ = 0; row < barcodeMatrix.length; row++) { formatter.format('Row %2d: ', row); for (var column /*int*/ = 0; column < barcodeMatrix[row].length; column++) { var barcodeValue = barcodeMatrix[row][column]; if (barcodeValue.getValue().length === 0) { formatter.format(' ', null); } else { formatter.format('%4d(%2d)', barcodeValue.getValue()[0], barcodeValue.getConfidence(barcodeValue.getValue()[0])); } } formatter.format('%n'); } return formatter.toString(); // } }; /*final*/ PDF417ScanningDecoder.CODEWORD_SKEW_SIZE = 2; /*final*/ PDF417ScanningDecoder.MAX_ERRORS = 3; /*final*/ PDF417ScanningDecoder.MAX_EC_CODEWORDS = 512; /*final*/ PDF417ScanningDecoder.errorCorrection = new ErrorCorrection_1.default(); return PDF417ScanningDecoder; }()); exports.default = PDF417ScanningDecoder;