UNPKG

@zxing/library

Version:

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

152 lines (151 loc) 6.79 kB
/* * Copyright 2008 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. */ import BarcodeFormat from '../BarcodeFormat'; import DecodeHintType from '../DecodeHintType'; import Result from '../Result'; import ResultMetadataType from '../ResultMetadataType'; import ResultPoint from '../ResultPoint'; import UPCEANExtensionSupport from './UPCEANExtensionSupport'; import AbstractUPCEANReader from './AbstractUPCEANReader'; import NotFoundException from '../NotFoundException'; import FormatException from '../FormatException'; import ChecksumException from '../ChecksumException'; /** * <p>Encapsulates functionality and implementation that is common to UPC and EAN families * of one-dimensional barcodes.</p> * * @author dswitkin@google.com (Daniel Switkin) * @author Sean Owen * @author alasdair@google.com (Alasdair Mackintosh) */ export default class UPCEANReader extends AbstractUPCEANReader { constructor() { super(); this.decodeRowStringBuffer = ''; UPCEANReader.L_AND_G_PATTERNS = UPCEANReader.L_PATTERNS.map(arr => Int32Array.from(arr)); for (let i = 10; i < 20; i++) { let widths = UPCEANReader.L_PATTERNS[i - 10]; let reversedWidths = new Int32Array(widths.length); for (let j = 0; j < widths.length; j++) { reversedWidths[j] = widths[widths.length - j - 1]; } UPCEANReader.L_AND_G_PATTERNS[i] = reversedWidths; } } decodeRow(rowNumber, row, hints) { let startGuardRange = UPCEANReader.findStartGuardPattern(row); let resultPointCallback = hints == null ? null : hints.get(DecodeHintType.NEED_RESULT_POINT_CALLBACK); if (resultPointCallback != null) { const resultPoint = new ResultPoint((startGuardRange[0] + startGuardRange[1]) / 2.0, rowNumber); resultPointCallback.foundPossibleResultPoint(resultPoint); } let budello = this.decodeMiddle(row, startGuardRange, this.decodeRowStringBuffer); let endStart = budello.rowOffset; let result = budello.resultString; if (resultPointCallback != null) { const resultPoint = new ResultPoint(endStart, rowNumber); resultPointCallback.foundPossibleResultPoint(resultPoint); } let endRange = UPCEANReader.decodeEnd(row, endStart); if (resultPointCallback != null) { const resultPoint = new ResultPoint((endRange[0] + endRange[1]) / 2.0, rowNumber); resultPointCallback.foundPossibleResultPoint(resultPoint); } // Make sure there is a quiet zone at least as big as the end pattern after the barcode. The // spec might want more whitespace, but in practice this is the maximum we can count on. let end = endRange[1]; let quietEnd = end + (end - endRange[0]); if (quietEnd >= row.getSize() || !row.isRange(end, quietEnd, false)) { throw new NotFoundException(); } let resultString = result.toString(); // UPC/EAN should never be less than 8 chars anyway if (resultString.length < 8) { throw new FormatException(); } if (!UPCEANReader.checkChecksum(resultString)) { throw new ChecksumException(); } let left = (startGuardRange[1] + startGuardRange[0]) / 2.0; let right = (endRange[1] + endRange[0]) / 2.0; let format = this.getBarcodeFormat(); let resultPoint = [new ResultPoint(left, rowNumber), new ResultPoint(right, rowNumber)]; let decodeResult = new Result(resultString, null, 0, resultPoint, format, new Date().getTime()); let extensionLength = 0; try { let extensionResult = UPCEANExtensionSupport.decodeRow(rowNumber, row, endRange[1]); decodeResult.putMetadata(ResultMetadataType.UPC_EAN_EXTENSION, extensionResult.getText()); decodeResult.putAllMetadata(extensionResult.getResultMetadata()); decodeResult.addResultPoints(extensionResult.getResultPoints()); extensionLength = extensionResult.getText().length; } catch (err) { } let allowedExtensions = hints == null ? null : hints.get(DecodeHintType.ALLOWED_EAN_EXTENSIONS); if (allowedExtensions != null) { let valid = false; for (let length in allowedExtensions) { if (extensionLength.toString() === length) { // check me valid = true; break; } } if (!valid) { throw new NotFoundException(); } } if (format === BarcodeFormat.EAN_13 || format === BarcodeFormat.UPC_A) { // let countryID = eanManSupport.lookupContryIdentifier(resultString); todo // if (countryID != null) { // decodeResult.putMetadata(ResultMetadataType.POSSIBLE_COUNTRY, countryID); // } } return decodeResult; } static checkChecksum(s) { return UPCEANReader.checkStandardUPCEANChecksum(s); } static checkStandardUPCEANChecksum(s) { let length = s.length; if (length === 0) return false; let check = parseInt(s.charAt(length - 1), 10); return UPCEANReader.getStandardUPCEANChecksum(s.substring(0, length - 1)) === check; } static getStandardUPCEANChecksum(s) { let length = s.length; let sum = 0; for (let i = length - 1; i >= 0; i -= 2) { let digit = s.charAt(i).charCodeAt(0) - '0'.charCodeAt(0); if (digit < 0 || digit > 9) { throw new FormatException(); } sum += digit; } sum *= 3; for (let i = length - 2; i >= 0; i -= 2) { let digit = s.charAt(i).charCodeAt(0) - '0'.charCodeAt(0); if (digit < 0 || digit > 9) { throw new FormatException(); } sum += digit; } return (1000 - sum) % 10; } static decodeEnd(row, endStart) { return UPCEANReader.findGuardPattern(row, endStart, false, UPCEANReader.START_END_PATTERN, new Int32Array(UPCEANReader.START_END_PATTERN.length).fill(0)); } }