diff --git a/changelog.md b/changelog.md index 1e59744..99a74ab 100644 --- a/changelog.md +++ b/changelog.md @@ -1,4 +1,6 @@ -### Version 2.2.5 +### Version 2.2.5 & Version 2.2.6 + +> Small minification fix in Version 2.2.6. #### Added support for turning `torch` On and Off in `Html5QrcodeScanner`. On supported devices + browsers. diff --git a/minified/html5-qrcode.min.js b/minified/html5-qrcode.min.js index 6191e37..bf63d1c 100644 --- a/minified/html5-qrcode.min.js +++ b/minified/html5-qrcode.min.js @@ -1,27141 +1,5 @@ -var __Html5QrcodeLibrary__; -/******/ (() => { // webpackBootstrap -/******/ var __webpack_modules__ = ({ - -/***/ 449: -/***/ (function(__unused_webpack_module, exports, __webpack_require__) { - -(function (global, factory) { - true ? factory(exports) : - 0; -}(this, (function (exports) { 'use strict'; - - /*! ***************************************************************************** - Copyright (c) Microsoft Corporation. All rights reserved. - 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 - - THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED - WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, - MERCHANTABLITY OR NON-INFRINGEMENT. - - See the Apache Version 2.0 License for specific language governing permissions - and limitations under the License. - ***************************************************************************** */ - /* global Reflect, Promise */ - - var extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - - function __extends(d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - } - - function fixProto(target, prototype) { - var setPrototypeOf = Object.setPrototypeOf; - setPrototypeOf ? setPrototypeOf(target, prototype) : (target.__proto__ = prototype); - } - - function fixStack(target, fn) { - if (fn === void 0) { - fn = target.constructor; - } - var captureStackTrace = Error.captureStackTrace; - captureStackTrace && captureStackTrace(target, fn); - } - - - - - - var CustomError = (function (_super) { - __extends(CustomError, _super); - function CustomError(message) { - var _newTarget = this.constructor; - var _this = _super.call(this, message) || this; - Object.defineProperty(_this, 'name', { - value: _newTarget.name, - enumerable: false - }); - fixProto(_this, _newTarget.prototype); - fixStack(_this); - return _this; - } - - return CustomError; - })(Error); - - /** - * Custom Error class of type Exception. - */ - class Exception extends CustomError { - /** - * Allows Exception to be constructed directly - * with some message and prototype definition. - */ - constructor(message = undefined) { - super(message); - this.message = message; - } - getKind() { - const ex = this.constructor; - return ex.kind; - } - } - /** - * It's typed as string so it can be extended and overriden. - */ - Exception.kind = 'Exception'; - - /** - * Custom Error class of type Exception. - */ - class ArgumentException extends Exception { - } - ArgumentException.kind = 'ArgumentException'; - - /** - * Custom Error class of type Exception. - */ - class IllegalArgumentException extends Exception { - } - IllegalArgumentException.kind = 'IllegalArgumentException'; - - /* - * 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. - */ - class BinaryBitmap { - constructor(binarizer) { - this.binarizer = binarizer; - if (binarizer === null) { - throw new IllegalArgumentException('Binarizer must be non-null.'); - } - } - /** - * @return The width of the bitmap. - */ - getWidth() { - return this.binarizer.getWidth(); - } - /** - * @return The height of the bitmap. - */ - getHeight() { - return this.binarizer.getHeight(); - } - /** - * Converts one row of luminance data to 1 bit data. May actually do the conversion, or return - * cached data. Callers should assume this method is expensive and call it as seldom as possible. - * This method is intended for decoding 1D barcodes and may choose to apply sharpening. - * - * @param y The row to fetch, which must be in [0, bitmap height) - * @param row An optional preallocated array. If null or too small, it will be ignored. - * If used, the Binarizer will call BitArray.clear(). Always use the returned object. - * @return The array of bits for this row (true means black). - * @throws NotFoundException if row can't be binarized - */ - getBlackRow(y /*int*/, row) { - return this.binarizer.getBlackRow(y, row); - } - /** - * Converts a 2D array of luminance data to 1 bit. As above, assume this method is expensive - * and do not call it repeatedly. This method is intended for decoding 2D barcodes and may or - * may not apply sharpening. Therefore, a row from this matrix may not be identical to one - * fetched using getBlackRow(), so don't mix and match between them. - * - * @return The 2D array of bits for the image (true means black). - * @throws NotFoundException if image can't be binarized to make a matrix - */ - getBlackMatrix() { - // The matrix is created on demand the first time it is requested, then cached. There are two - // reasons for this: - // 1. This work will never be done if the caller only installs 1D Reader objects, or if a - // 1D Reader finds a barcode before the 2D Readers run. - // 2. This work will only be done once even if the caller installs multiple 2D Readers. - if (this.matrix === null || this.matrix === undefined) { - this.matrix = this.binarizer.getBlackMatrix(); - } - return this.matrix; - } - /** - * @return Whether this bitmap can be cropped. - */ - isCropSupported() { - return this.binarizer.getLuminanceSource().isCropSupported(); - } - /** - * Returns a new object with cropped image data. Implementations may keep a reference to the - * original data rather than a copy. Only callable if isCropSupported() is true. - * - * @param left The left coordinate, which must be in [0,getWidth()) - * @param top The top coordinate, which must be in [0,getHeight()) - * @param width The width of the rectangle to crop. - * @param height The height of the rectangle to crop. - * @return A cropped version of this object. - */ - crop(left /*int*/, top /*int*/, width /*int*/, height /*int*/) { - const newSource = this.binarizer.getLuminanceSource().crop(left, top, width, height); - return new BinaryBitmap(this.binarizer.createBinarizer(newSource)); - } - /** - * @return Whether this bitmap supports counter-clockwise rotation. - */ - isRotateSupported() { - return this.binarizer.getLuminanceSource().isRotateSupported(); - } - /** - * Returns a new object with rotated image data by 90 degrees counterclockwise. - * Only callable if {@link #isRotateSupported()} is true. - * - * @return A rotated version of this object. - */ - rotateCounterClockwise() { - const newSource = this.binarizer.getLuminanceSource().rotateCounterClockwise(); - return new BinaryBitmap(this.binarizer.createBinarizer(newSource)); - } - /** - * Returns a new object with rotated image data by 45 degrees counterclockwise. - * Only callable if {@link #isRotateSupported()} is true. - * - * @return A rotated version of this object. - */ - rotateCounterClockwise45() { - const newSource = this.binarizer.getLuminanceSource().rotateCounterClockwise45(); - return new BinaryBitmap(this.binarizer.createBinarizer(newSource)); - } - /*@Override*/ - toString() { - try { - return this.getBlackMatrix().toString(); - } - catch (e /*: NotFoundException*/) { - return ''; - } - } - } - - /** - * Custom Error class of type Exception. - */ - class ChecksumException extends Exception { - static getChecksumInstance() { - return new ChecksumException(); - } - } - ChecksumException.kind = 'ChecksumException'; - - /* - * 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. - */ - /** - * This class hierarchy provides a set of methods to convert luminance data to 1 bit data. - * It allows the algorithm to vary polymorphically, for example allowing a very expensive - * thresholding technique for servers and a fast one for mobile. It also permits the implementation - * to vary, e.g. a JNI version for Android and a Java fallback version for other platforms. - * - * @author dswitkin@google.com (Daniel Switkin) - */ - class Binarizer { - constructor(source) { - this.source = source; - } - getLuminanceSource() { - return this.source; - } - getWidth() { - return this.source.getWidth(); - } - getHeight() { - return this.source.getHeight(); - } - } - - class System { - // public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length) - /** - * Makes a copy of a array. - */ - static arraycopy(src, srcPos, dest, destPos, length) { - // TODO: better use split or set? - while (length--) { - dest[destPos++] = src[srcPos++]; - } - } - /** - * Returns the current time in milliseconds. - */ - static currentTimeMillis() { - return Date.now(); - } - } - - /** - * Custom Error class of type Exception. - */ - class IndexOutOfBoundsException extends Exception { - } - IndexOutOfBoundsException.kind = 'IndexOutOfBoundsException'; - - /** - * Custom Error class of type Exception. - */ - class ArrayIndexOutOfBoundsException extends IndexOutOfBoundsException { - constructor(index = undefined, message = undefined) { - super(message); - this.index = index; - this.message = message; - } - } - ArrayIndexOutOfBoundsException.kind = 'ArrayIndexOutOfBoundsException'; - - class Arrays { - /** - * Assigns the specified int value to each element of the specified array - * of ints. - * - * @param a the array to be filled - * @param val the value to be stored in all elements of the array - */ - static fill(a, val) { - for (let i = 0, len = a.length; i < len; i++) - a[i] = val; - } - /** - * Assigns the specified int value to each element of the specified - * range of the specified array of ints. The range to be filled - * extends from index {@code fromIndex}, inclusive, to index - * {@code toIndex}, exclusive. (If {@code fromIndex==toIndex}, the - * range to be filled is empty.) - * - * @param a the array to be filled - * @param fromIndex the index of the first element (inclusive) to be - * filled with the specified value - * @param toIndex the index of the last element (exclusive) to be - * filled with the specified value - * @param val the value to be stored in all elements of the array - * @throws IllegalArgumentException if {@code fromIndex > toIndex} - * @throws ArrayIndexOutOfBoundsException if {@code fromIndex < 0} or - * {@code toIndex > a.length} - */ - static fillWithin(a, fromIndex, toIndex, val) { - Arrays.rangeCheck(a.length, fromIndex, toIndex); - for (let i = fromIndex; i < toIndex; i++) - a[i] = val; - } - /** - * Checks that {@code fromIndex} and {@code toIndex} are in - * the range and throws an exception if they aren't. - */ - static rangeCheck(arrayLength, fromIndex, toIndex) { - if (fromIndex > toIndex) { - throw new IllegalArgumentException('fromIndex(' + fromIndex + ') > toIndex(' + toIndex + ')'); - } - if (fromIndex < 0) { - throw new ArrayIndexOutOfBoundsException(fromIndex); - } - if (toIndex > arrayLength) { - throw new ArrayIndexOutOfBoundsException(toIndex); - } - } - static asList(...args) { - return args; - } - static create(rows, cols, value) { - let arr = Array.from({ length: rows }); - return arr.map(x => Array.from({ length: cols }).fill(value)); - } - static createInt32Array(rows, cols, value) { - let arr = Array.from({ length: rows }); - return arr.map(x => Int32Array.from({ length: cols }).fill(value)); - } - static equals(first, second) { - if (!first) { - return false; - } - if (!second) { - return false; - } - if (!first.length) { - return false; - } - if (!second.length) { - return false; - } - if (first.length !== second.length) { - return false; - } - for (let i = 0, length = first.length; i < length; i++) { - if (first[i] !== second[i]) { - return false; - } - } - return true; - } - static hashCode(a) { - if (a === null) { - return 0; - } - let result = 1; - for (const element of a) { - result = 31 * result + element; - } - return result; - } - static fillUint8Array(a, value) { - for (let i = 0; i !== a.length; i++) { - a[i] = value; - } - } - static copyOf(original, newLength) { - return original.slice(0, newLength); - } - static copyOfUint8Array(original, newLength) { - if (original.length <= newLength) { - const newArray = new Uint8Array(newLength); - newArray.set(original); - return newArray; - } - return original.slice(0, newLength); - } - static copyOfRange(original, from, to) { - const newLength = to - from; - const copy = new Int32Array(newLength); - System.arraycopy(original, from, copy, 0, newLength); - return copy; - } - /* - * Returns the index of of the element in a sorted array or (-n-1) where n is the insertion point - * for the new element. - * Parameters: - * ar - A sorted array - * el - An element to search for - * comparator - A comparator function. The function takes two arguments: (a, b) and returns: - * a negative number if a is less than b; - * 0 if a is equal to b; - * a positive number of a is greater than b. - * The array may contain duplicate elements. If there are more than one equal elements in the array, - * the returned value can be the index of any one of the equal elements. - * - * http://jsfiddle.net/aryzhov/pkfst550/ - */ - static binarySearch(ar, el, comparator) { - if (undefined === comparator) { - comparator = Arrays.numberComparator; - } - let m = 0; - let n = ar.length - 1; - while (m <= n) { - const k = (n + m) >> 1; - const cmp = comparator(el, ar[k]); - if (cmp > 0) { - m = k + 1; - } - else if (cmp < 0) { - n = k - 1; - } - else { - return k; - } - } - return -m - 1; - } - static numberComparator(a, b) { - return a - b; - } - } - - /** - * Ponyfill for Java's Integer class. - */ - class Integer { - static numberOfTrailingZeros(i) { - let y; - if (i === 0) - return 32; - let n = 31; - y = i << 16; - if (y !== 0) { - n -= 16; - i = y; - } - y = i << 8; - if (y !== 0) { - n -= 8; - i = y; - } - y = i << 4; - if (y !== 0) { - n -= 4; - i = y; - } - y = i << 2; - if (y !== 0) { - n -= 2; - i = y; - } - return n - ((i << 1) >>> 31); - } - static numberOfLeadingZeros(i) { - // HD, Figure 5-6 - if (i === 0) { - return 32; - } - let n = 1; - if (i >>> 16 === 0) { - n += 16; - i <<= 16; - } - if (i >>> 24 === 0) { - n += 8; - i <<= 8; - } - if (i >>> 28 === 0) { - n += 4; - i <<= 4; - } - if (i >>> 30 === 0) { - n += 2; - i <<= 2; - } - n -= i >>> 31; - return n; - } - static toHexString(i) { - return i.toString(16); - } - static toBinaryString(intNumber) { - return String(parseInt(String(intNumber), 2)); - } - // Returns the number of one-bits in the two's complement binary representation of the specified int value. This function is sometimes referred to as the population count. - // Returns: - // the number of one-bits in the two's complement binary representation of the specified int value. - static bitCount(i) { - // HD, Figure 5-2 - i = i - ((i >>> 1) & 0x55555555); - i = (i & 0x33333333) + ((i >>> 2) & 0x33333333); - i = (i + (i >>> 4)) & 0x0f0f0f0f; - i = i + (i >>> 8); - i = i + (i >>> 16); - return i & 0x3f; - } - static truncDivision(dividend, divisor) { - return Math.trunc(dividend / divisor); - } - /** - * Converts A string to an integer. - * @param s A string to convert into a number. - * @param radix A value between 2 and 36 that specifies the base of the number in numString. If this argument is not supplied, strings with a prefix of '0x' are considered hexadecimal. All other strings are considered decimal. - */ - static parseInt(num, radix = undefined) { - return parseInt(num, radix); - } - } - Integer.MIN_VALUE_32_BITS = -2147483648; - Integer.MAX_VALUE = Number.MAX_SAFE_INTEGER; - - /* - * Copyright 2007 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. - */ - /** - *
A simple, fast array of bits, represented compactly by an array of ints internally.
- * - * @author Sean Owen - */ - class BitArray /*implements Cloneable*/ { - // public constructor() { - // this.size = 0 - // this.bits = new Int32Array(1) - // } - // public constructor(size?: number /*int*/) { - // if (undefined === size) { - // this.size = 0 - // } else { - // this.size = size - // } - // this.bits = this.makeArray(size) - // } - // For testing only - constructor(size /*int*/, bits) { - if (undefined === size) { - this.size = 0; - this.bits = new Int32Array(1); - } - else { - this.size = size; - if (undefined === bits || null === bits) { - this.bits = BitArray.makeArray(size); - } - else { - this.bits = bits; - } - } - } - getSize() { - return this.size; - } - getSizeInBytes() { - return Math.floor((this.size + 7) / 8); - } - ensureCapacity(size /*int*/) { - if (size > this.bits.length * 32) { - const newBits = BitArray.makeArray(size); - System.arraycopy(this.bits, 0, newBits, 0, this.bits.length); - this.bits = newBits; - } - } - /** - * @param i bit to get - * @return true iff bit i is set - */ - get(i /*int*/) { - return (this.bits[Math.floor(i / 32)] & (1 << (i & 0x1F))) !== 0; - } - /** - * Sets bit i. - * - * @param i bit to set - */ - set(i /*int*/) { - this.bits[Math.floor(i / 32)] |= 1 << (i & 0x1F); - } - /** - * Flips bit i. - * - * @param i bit to set - */ - flip(i /*int*/) { - this.bits[Math.floor(i / 32)] ^= 1 << (i & 0x1F); - } - /** - * @param from first bit to check - * @return index of first bit that is set, starting from the given index, or size if none are set - * at or beyond this given index - * @see #getNextUnset(int) - */ - getNextSet(from /*int*/) { - const size = this.size; - if (from >= size) { - return size; - } - const bits = this.bits; - let bitsOffset = Math.floor(from / 32); - let currentBits = bits[bitsOffset]; - // mask off lesser bits first - currentBits &= ~((1 << (from & 0x1F)) - 1); - const length = bits.length; - while (currentBits === 0) { - if (++bitsOffset === length) { - return size; - } - currentBits = bits[bitsOffset]; - } - const result = (bitsOffset * 32) + Integer.numberOfTrailingZeros(currentBits); - return result > size ? size : result; - } - /** - * @param from index to start looking for unset bit - * @return index of next unset bit, or {@code size} if none are unset until the end - * @see #getNextSet(int) - */ - getNextUnset(from /*int*/) { - const size = this.size; - if (from >= size) { - return size; - } - const bits = this.bits; - let bitsOffset = Math.floor(from / 32); - let currentBits = ~bits[bitsOffset]; - // mask off lesser bits first - currentBits &= ~((1 << (from & 0x1F)) - 1); - const length = bits.length; - while (currentBits === 0) { - if (++bitsOffset === length) { - return size; - } - currentBits = ~bits[bitsOffset]; - } - const result = (bitsOffset * 32) + Integer.numberOfTrailingZeros(currentBits); - return result > size ? size : result; - } - /** - * Sets a block of 32 bits, starting at bit i. - * - * @param i first bit to set - * @param newBits the new value of the next 32 bits. Note again that the least-significant bit - * corresponds to bit i, the next-least-significant to i+1, and so on. - */ - setBulk(i /*int*/, newBits /*int*/) { - this.bits[Math.floor(i / 32)] = newBits; - } - /** - * Sets a range of bits. - * - * @param start start of range, inclusive. - * @param end end of range, exclusive - */ - setRange(start /*int*/, end /*int*/) { - if (end < start || start < 0 || end > this.size) { - throw new IllegalArgumentException(); - } - if (end === start) { - return; - } - end--; // will be easier to treat this as the last actually set bit -- inclusive - const firstInt = Math.floor(start / 32); - const lastInt = Math.floor(end / 32); - const bits = this.bits; - for (let i = firstInt; i <= lastInt; i++) { - const firstBit = i > firstInt ? 0 : start & 0x1F; - const lastBit = i < lastInt ? 31 : end & 0x1F; - // Ones from firstBit to lastBit, inclusive - const mask = (2 << lastBit) - (1 << firstBit); - bits[i] |= mask; - } - } - /** - * Clears all bits (sets to false). - */ - clear() { - const max = this.bits.length; - const bits = this.bits; - for (let i = 0; i < max; i++) { - bits[i] = 0; - } - } - /** - * Efficient method to check if a range of bits is set, or not set. - * - * @param start start of range, inclusive. - * @param end end of range, exclusive - * @param value if true, checks that bits in range are set, otherwise checks that they are not set - * @return true iff all bits are set or not set in range, according to value argument - * @throws IllegalArgumentException if end is less than start or the range is not contained in the array - */ - isRange(start /*int*/, end /*int*/, value) { - if (end < start || start < 0 || end > this.size) { - throw new IllegalArgumentException(); - } - if (end === start) { - return true; // empty range matches - } - end--; // will be easier to treat this as the last actually set bit -- inclusive - const firstInt = Math.floor(start / 32); - const lastInt = Math.floor(end / 32); - const bits = this.bits; - for (let i = firstInt; i <= lastInt; i++) { - const firstBit = i > firstInt ? 0 : start & 0x1F; - const lastBit = i < lastInt ? 31 : end & 0x1F; - // Ones from firstBit to lastBit, inclusive - const mask = (2 << lastBit) - (1 << firstBit) & 0xFFFFFFFF; - // TYPESCRIPTPORT: & 0xFFFFFFFF added to discard anything after 32 bits, as ES has 53 bits - // Return false if we're looking for 1s and the masked bits[i] isn't all 1s (is: that, - // equals the mask, or we're looking for 0s and the masked portion is not all 0s - if ((bits[i] & mask) !== (value ? mask : 0)) { - return false; - } - } - return true; - } - appendBit(bit) { - this.ensureCapacity(this.size + 1); - if (bit) { - this.bits[Math.floor(this.size / 32)] |= 1 << (this.size & 0x1F); - } - this.size++; - } - /** - * Appends the least-significant bits, from value, in order from most-significant to - * least-significant. For example, appending 6 bits from 0x000001E will append the bits - * 0, 1, 1, 1, 1, 0 in that order. - * - * @param value {@code int} containing bits to append - * @param numBits bits from value to append - */ - appendBits(value /*int*/, numBits /*int*/) { - if (numBits < 0 || numBits > 32) { - throw new IllegalArgumentException('Num bits must be between 0 and 32'); - } - this.ensureCapacity(this.size + numBits); - // const appendBit = this.appendBit; - for (let numBitsLeft = numBits; numBitsLeft > 0; numBitsLeft--) { - this.appendBit(((value >> (numBitsLeft - 1)) & 0x01) === 1); - } - } - appendBitArray(other) { - const otherSize = other.size; - this.ensureCapacity(this.size + otherSize); - // const appendBit = this.appendBit; - for (let i = 0; i < otherSize; i++) { - this.appendBit(other.get(i)); - } - } - xor(other) { - if (this.size !== other.size) { - throw new IllegalArgumentException('Sizes don\'t match'); - } - const bits = this.bits; - for (let i = 0, length = bits.length; i < length; i++) { - // The last int could be incomplete (i.e. not have 32 bits in - // it) but there is no problem since 0 XOR 0 == 0. - bits[i] ^= other.bits[i]; - } - } - /** - * - * @param bitOffset first bit to start writing - * @param array array to write into. Bytes are written most-significant byte first. This is the opposite - * of the internal representation, which is exposed by {@link #getBitArray()} - * @param offset position in array to start writing - * @param numBytes how many bytes to write - */ - toBytes(bitOffset /*int*/, array, offset /*int*/, numBytes /*int*/) { - for (let i = 0; i < numBytes; i++) { - let theByte = 0; - for (let j = 0; j < 8; j++) { - if (this.get(bitOffset)) { - theByte |= 1 << (7 - j); - } - bitOffset++; - } - array[offset + i] = /*(byte)*/ theByte; - } - } - /** - * @return underlying array of ints. The first element holds the first 32 bits, and the least - * significant bit is bit 0. - */ - getBitArray() { - return this.bits; - } - /** - * Reverses all bits in the array. - */ - reverse() { - const newBits = new Int32Array(this.bits.length); - // reverse all int's first - const len = Math.floor((this.size - 1) / 32); - const oldBitsLen = len + 1; - const bits = this.bits; - for (let i = 0; i < oldBitsLen; i++) { - let x = bits[i]; - x = ((x >> 1) & 0x55555555) | ((x & 0x55555555) << 1); - x = ((x >> 2) & 0x33333333) | ((x & 0x33333333) << 2); - x = ((x >> 4) & 0x0f0f0f0f) | ((x & 0x0f0f0f0f) << 4); - x = ((x >> 8) & 0x00ff00ff) | ((x & 0x00ff00ff) << 8); - x = ((x >> 16) & 0x0000ffff) | ((x & 0x0000ffff) << 16); - newBits[len - i] = /*(int)*/ x; - } - // now correct the int's if the bit size isn't a multiple of 32 - if (this.size !== oldBitsLen * 32) { - const leftOffset = oldBitsLen * 32 - this.size; - let currentInt = newBits[0] >>> leftOffset; - for (let i = 1; i < oldBitsLen; i++) { - const nextInt = newBits[i]; - currentInt |= nextInt << (32 - leftOffset); - newBits[i - 1] = currentInt; - currentInt = nextInt >>> leftOffset; - } - newBits[oldBitsLen - 1] = currentInt; - } - this.bits = newBits; - } - static makeArray(size /*int*/) { - return new Int32Array(Math.floor((size + 31) / 32)); - } - /*@Override*/ - equals(o) { - if (!(o instanceof BitArray)) { - return false; - } - const other = o; - return this.size === other.size && Arrays.equals(this.bits, other.bits); - } - /*@Override*/ - hashCode() { - return 31 * this.size + Arrays.hashCode(this.bits); - } - /*@Override*/ - toString() { - let result = ''; - for (let i = 0, size = this.size; i < size; i++) { - if ((i & 0x07) === 0) { - result += ' '; - } - result += this.get(i) ? 'X' : '.'; - } - return result; - } - /*@Override*/ - clone() { - return new BitArray(this.size, this.bits.slice()); - } - } - - /* - * 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. - */ - /*namespace com.google.zxing {*/ - /** - * Encapsulates a type of hint that a caller may pass to a barcode reader to help it - * more quickly or accurately decode it. It is up to implementations to decide what, - * if anything, to do with the information that is supplied. - * - * @author Sean Owen - * @author dswitkin@google.com (Daniel Switkin) - * @see Reader#decode(BinaryBitmap,java.util.Map) - */ - var DecodeHintType; - (function (DecodeHintType) { - /** - * Unspecified, application-specific hint. Maps to an unspecified {@link Object}. - */ - DecodeHintType[DecodeHintType["OTHER"] = 0] = "OTHER"; /*(Object.class)*/ - /** - * Image is a pure monochrome image of a barcode. Doesn't matter what it maps to; - * use {@link Boolean#TRUE}. - */ - DecodeHintType[DecodeHintType["PURE_BARCODE"] = 1] = "PURE_BARCODE"; /*(Void.class)*/ - /** - * Image is known to be of one of a few possible formats. - * Maps to a {@link List} of {@link BarcodeFormat}s. - */ - DecodeHintType[DecodeHintType["POSSIBLE_FORMATS"] = 2] = "POSSIBLE_FORMATS"; /*(List.class)*/ - /** - * Spend more time to try to find a barcode; optimize for accuracy, not speed. - * Doesn't matter what it maps to; use {@link Boolean#TRUE}. - */ - DecodeHintType[DecodeHintType["TRY_HARDER"] = 3] = "TRY_HARDER"; /*(Void.class)*/ - /** - * Specifies what character encoding to use when decoding, where applicable (type String) - */ - DecodeHintType[DecodeHintType["CHARACTER_SET"] = 4] = "CHARACTER_SET"; /*(String.class)*/ - /** - * Allowed lengths of encoded data -- reject anything else. Maps to an {@code Int32Array}. - */ - DecodeHintType[DecodeHintType["ALLOWED_LENGTHS"] = 5] = "ALLOWED_LENGTHS"; /*(Int32Array.class)*/ - /** - * Assume Code 39 codes employ a check digit. Doesn't matter what it maps to; - * use {@link Boolean#TRUE}. - */ - DecodeHintType[DecodeHintType["ASSUME_CODE_39_CHECK_DIGIT"] = 6] = "ASSUME_CODE_39_CHECK_DIGIT"; /*(Void.class)*/ - /** - * Assume the barcode is being processed as a GS1 barcode, and modify behavior as needed. - * For example this affects FNC1 handling for Code 128 (aka GS1-128). Doesn't matter what it maps to; - * use {@link Boolean#TRUE}. - */ - DecodeHintType[DecodeHintType["ASSUME_GS1"] = 7] = "ASSUME_GS1"; /*(Void.class)*/ - /** - * If true, return the start and end digits in a Codabar barcode instead of stripping them. They - * are alpha, whereas the rest are numeric. By default, they are stripped, but this causes them - * to not be. Doesn't matter what it maps to; use {@link Boolean#TRUE}. - */ - DecodeHintType[DecodeHintType["RETURN_CODABAR_START_END"] = 8] = "RETURN_CODABAR_START_END"; /*(Void.class)*/ - /** - * The caller needs to be notified via callback when a possible {@link ResultPoint} - * is found. Maps to a {@link ResultPointCallback}. - */ - DecodeHintType[DecodeHintType["NEED_RESULT_POINT_CALLBACK"] = 9] = "NEED_RESULT_POINT_CALLBACK"; /*(ResultPointCallback.class)*/ - /** - * Allowed extension lengths for EAN or UPC barcodes. Other formats will ignore this. - * Maps to an {@code Int32Array} of the allowed extension lengths, for example [2], [5], or [2, 5]. - * If it is optional to have an extension, do not set this hint. If this is set, - * and a UPC or EAN barcode is found but an extension is not, then no result will be returned - * at all. - */ - DecodeHintType[DecodeHintType["ALLOWED_EAN_EXTENSIONS"] = 10] = "ALLOWED_EAN_EXTENSIONS"; /*(Int32Array.class)*/ - // End of enumeration values. - /** - * Data type the hint is expecting. - * Among the possible values the {@link Void} stands out as being used for - * hints that do not expect a value to be supplied (flag hints). Such hints - * will possibly have their value ignored, or replaced by a - * {@link Boolean#TRUE}. Hint suppliers should probably use - * {@link Boolean#TRUE} as directed by the actual hint documentation. - */ - // private valueType: Class> - // DecodeHintType(valueType: Class>) { - // this.valueType = valueType - // } - // public getValueType(): Class> { - // return valueType - // } - })(DecodeHintType || (DecodeHintType = {})); - var DecodeHintType$1 = DecodeHintType; - - /** - * Custom Error class of type Exception. - */ - class FormatException extends Exception { - static getFormatInstance() { - return new FormatException(); - } - } - FormatException.kind = 'FormatException'; - - /* - * 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 java.util.HashMap;*/ - /*import java.util.Map;*/ - var CharacterSetValueIdentifiers; - (function (CharacterSetValueIdentifiers) { - CharacterSetValueIdentifiers[CharacterSetValueIdentifiers["Cp437"] = 0] = "Cp437"; - CharacterSetValueIdentifiers[CharacterSetValueIdentifiers["ISO8859_1"] = 1] = "ISO8859_1"; - CharacterSetValueIdentifiers[CharacterSetValueIdentifiers["ISO8859_2"] = 2] = "ISO8859_2"; - CharacterSetValueIdentifiers[CharacterSetValueIdentifiers["ISO8859_3"] = 3] = "ISO8859_3"; - CharacterSetValueIdentifiers[CharacterSetValueIdentifiers["ISO8859_4"] = 4] = "ISO8859_4"; - CharacterSetValueIdentifiers[CharacterSetValueIdentifiers["ISO8859_5"] = 5] = "ISO8859_5"; - CharacterSetValueIdentifiers[CharacterSetValueIdentifiers["ISO8859_6"] = 6] = "ISO8859_6"; - CharacterSetValueIdentifiers[CharacterSetValueIdentifiers["ISO8859_7"] = 7] = "ISO8859_7"; - CharacterSetValueIdentifiers[CharacterSetValueIdentifiers["ISO8859_8"] = 8] = "ISO8859_8"; - CharacterSetValueIdentifiers[CharacterSetValueIdentifiers["ISO8859_9"] = 9] = "ISO8859_9"; - CharacterSetValueIdentifiers[CharacterSetValueIdentifiers["ISO8859_10"] = 10] = "ISO8859_10"; - CharacterSetValueIdentifiers[CharacterSetValueIdentifiers["ISO8859_11"] = 11] = "ISO8859_11"; - CharacterSetValueIdentifiers[CharacterSetValueIdentifiers["ISO8859_13"] = 12] = "ISO8859_13"; - CharacterSetValueIdentifiers[CharacterSetValueIdentifiers["ISO8859_14"] = 13] = "ISO8859_14"; - CharacterSetValueIdentifiers[CharacterSetValueIdentifiers["ISO8859_15"] = 14] = "ISO8859_15"; - CharacterSetValueIdentifiers[CharacterSetValueIdentifiers["ISO8859_16"] = 15] = "ISO8859_16"; - CharacterSetValueIdentifiers[CharacterSetValueIdentifiers["SJIS"] = 16] = "SJIS"; - CharacterSetValueIdentifiers[CharacterSetValueIdentifiers["Cp1250"] = 17] = "Cp1250"; - CharacterSetValueIdentifiers[CharacterSetValueIdentifiers["Cp1251"] = 18] = "Cp1251"; - CharacterSetValueIdentifiers[CharacterSetValueIdentifiers["Cp1252"] = 19] = "Cp1252"; - CharacterSetValueIdentifiers[CharacterSetValueIdentifiers["Cp1256"] = 20] = "Cp1256"; - CharacterSetValueIdentifiers[CharacterSetValueIdentifiers["UnicodeBigUnmarked"] = 21] = "UnicodeBigUnmarked"; - CharacterSetValueIdentifiers[CharacterSetValueIdentifiers["UTF8"] = 22] = "UTF8"; - CharacterSetValueIdentifiers[CharacterSetValueIdentifiers["ASCII"] = 23] = "ASCII"; - CharacterSetValueIdentifiers[CharacterSetValueIdentifiers["Big5"] = 24] = "Big5"; - CharacterSetValueIdentifiers[CharacterSetValueIdentifiers["GB18030"] = 25] = "GB18030"; - CharacterSetValueIdentifiers[CharacterSetValueIdentifiers["EUC_KR"] = 26] = "EUC_KR"; - })(CharacterSetValueIdentifiers || (CharacterSetValueIdentifiers = {})); - /** - * Encapsulates a Character Set ECI, according to "Extended Channel Interpretations" 5.3.1.1 - * of ISO 18004. - * - * @author Sean Owen - */ - class CharacterSetECI { - constructor(valueIdentifier, valuesParam, name, ...otherEncodingNames) { - this.valueIdentifier = valueIdentifier; - this.name = name; - if (typeof valuesParam === 'number') { - this.values = Int32Array.from([valuesParam]); - } - else { - this.values = valuesParam; - } - this.otherEncodingNames = otherEncodingNames; - CharacterSetECI.VALUE_IDENTIFIER_TO_ECI.set(valueIdentifier, this); - CharacterSetECI.NAME_TO_ECI.set(name, this); - const values = this.values; - for (let i = 0, length = values.length; i !== length; i++) { - const v = values[i]; - CharacterSetECI.VALUES_TO_ECI.set(v, this); - } - for (const otherName of otherEncodingNames) { - CharacterSetECI.NAME_TO_ECI.set(otherName, this); - } - } - // CharacterSetECI(value: number /*int*/) { - // this(new Int32Array {value}) - // } - // CharacterSetECI(value: number /*int*/, String... otherEncodingNames) { - // this.values = new Int32Array {value} - // this.otherEncodingNames = otherEncodingNames - // } - // CharacterSetECI(values: Int32Array, String... otherEncodingNames) { - // this.values = values - // this.otherEncodingNames = otherEncodingNames - // } - getValueIdentifier() { - return this.valueIdentifier; - } - getName() { - return this.name; - } - getValue() { - return this.values[0]; - } - /** - * @param value character set ECI value - * @return {@code CharacterSetECI} representing ECI of given value, or null if it is legal but - * unsupported - * @throws FormatException if ECI value is invalid - */ - static getCharacterSetECIByValue(value /*int*/) { - if (value < 0 || value >= 900) { - throw new FormatException('incorect value'); - } - const characterSet = CharacterSetECI.VALUES_TO_ECI.get(value); - if (undefined === characterSet) { - throw new FormatException('incorect value'); - } - return characterSet; - } - /** - * @param name character set ECI encoding name - * @return CharacterSetECI representing ECI for character encoding, or null if it is legal - * but unsupported - */ - static getCharacterSetECIByName(name) { - const characterSet = CharacterSetECI.NAME_TO_ECI.get(name); - if (undefined === characterSet) { - throw new FormatException('incorect value'); - } - return characterSet; - } - equals(o) { - if (!(o instanceof CharacterSetECI)) { - return false; - } - const other = o; - return this.getName() === other.getName(); - } - } - CharacterSetECI.VALUE_IDENTIFIER_TO_ECI = new Map(); - CharacterSetECI.VALUES_TO_ECI = new Map(); - CharacterSetECI.NAME_TO_ECI = new Map(); - // Enum name is a Java encoding valid for java.lang and java.io - // TYPESCRIPTPORT: changed the main label for ISO as the TextEncoder did not recognized them in the form from java - // (eg ISO8859_1 must be ISO88591 or ISO8859-1 or ISO-8859-1) - // later on: well, except 16 wich does not work with ISO885916 so used ISO-8859-1 form for default - CharacterSetECI.Cp437 = new CharacterSetECI(CharacterSetValueIdentifiers.Cp437, Int32Array.from([0, 2]), 'Cp437'); - CharacterSetECI.ISO8859_1 = new CharacterSetECI(CharacterSetValueIdentifiers.ISO8859_1, Int32Array.from([1, 3]), 'ISO-8859-1', 'ISO88591', 'ISO8859_1'); - CharacterSetECI.ISO8859_2 = new CharacterSetECI(CharacterSetValueIdentifiers.ISO8859_2, 4, 'ISO-8859-2', 'ISO88592', 'ISO8859_2'); - CharacterSetECI.ISO8859_3 = new CharacterSetECI(CharacterSetValueIdentifiers.ISO8859_3, 5, 'ISO-8859-3', 'ISO88593', 'ISO8859_3'); - CharacterSetECI.ISO8859_4 = new CharacterSetECI(CharacterSetValueIdentifiers.ISO8859_4, 6, 'ISO-8859-4', 'ISO88594', 'ISO8859_4'); - CharacterSetECI.ISO8859_5 = new CharacterSetECI(CharacterSetValueIdentifiers.ISO8859_5, 7, 'ISO-8859-5', 'ISO88595', 'ISO8859_5'); - CharacterSetECI.ISO8859_6 = new CharacterSetECI(CharacterSetValueIdentifiers.ISO8859_6, 8, 'ISO-8859-6', 'ISO88596', 'ISO8859_6'); - CharacterSetECI.ISO8859_7 = new CharacterSetECI(CharacterSetValueIdentifiers.ISO8859_7, 9, 'ISO-8859-7', 'ISO88597', 'ISO8859_7'); - CharacterSetECI.ISO8859_8 = new CharacterSetECI(CharacterSetValueIdentifiers.ISO8859_8, 10, 'ISO-8859-8', 'ISO88598', 'ISO8859_8'); - CharacterSetECI.ISO8859_9 = new CharacterSetECI(CharacterSetValueIdentifiers.ISO8859_9, 11, 'ISO-8859-9', 'ISO88599', 'ISO8859_9'); - CharacterSetECI.ISO8859_10 = new CharacterSetECI(CharacterSetValueIdentifiers.ISO8859_10, 12, 'ISO-8859-10', 'ISO885910', 'ISO8859_10'); - CharacterSetECI.ISO8859_11 = new CharacterSetECI(CharacterSetValueIdentifiers.ISO8859_11, 13, 'ISO-8859-11', 'ISO885911', 'ISO8859_11'); - CharacterSetECI.ISO8859_13 = new CharacterSetECI(CharacterSetValueIdentifiers.ISO8859_13, 15, 'ISO-8859-13', 'ISO885913', 'ISO8859_13'); - CharacterSetECI.ISO8859_14 = new CharacterSetECI(CharacterSetValueIdentifiers.ISO8859_14, 16, 'ISO-8859-14', 'ISO885914', 'ISO8859_14'); - CharacterSetECI.ISO8859_15 = new CharacterSetECI(CharacterSetValueIdentifiers.ISO8859_15, 17, 'ISO-8859-15', 'ISO885915', 'ISO8859_15'); - CharacterSetECI.ISO8859_16 = new CharacterSetECI(CharacterSetValueIdentifiers.ISO8859_16, 18, 'ISO-8859-16', 'ISO885916', 'ISO8859_16'); - CharacterSetECI.SJIS = new CharacterSetECI(CharacterSetValueIdentifiers.SJIS, 20, 'SJIS', 'Shift_JIS'); - CharacterSetECI.Cp1250 = new CharacterSetECI(CharacterSetValueIdentifiers.Cp1250, 21, 'Cp1250', 'windows-1250'); - CharacterSetECI.Cp1251 = new CharacterSetECI(CharacterSetValueIdentifiers.Cp1251, 22, 'Cp1251', 'windows-1251'); - CharacterSetECI.Cp1252 = new CharacterSetECI(CharacterSetValueIdentifiers.Cp1252, 23, 'Cp1252', 'windows-1252'); - CharacterSetECI.Cp1256 = new CharacterSetECI(CharacterSetValueIdentifiers.Cp1256, 24, 'Cp1256', 'windows-1256'); - CharacterSetECI.UnicodeBigUnmarked = new CharacterSetECI(CharacterSetValueIdentifiers.UnicodeBigUnmarked, 25, 'UnicodeBigUnmarked', 'UTF-16BE', 'UnicodeBig'); - CharacterSetECI.UTF8 = new CharacterSetECI(CharacterSetValueIdentifiers.UTF8, 26, 'UTF8', 'UTF-8'); - CharacterSetECI.ASCII = new CharacterSetECI(CharacterSetValueIdentifiers.ASCII, Int32Array.from([27, 170]), 'ASCII', 'US-ASCII'); - CharacterSetECI.Big5 = new CharacterSetECI(CharacterSetValueIdentifiers.Big5, 28, 'Big5'); - CharacterSetECI.GB18030 = new CharacterSetECI(CharacterSetValueIdentifiers.GB18030, 29, 'GB18030', 'GB2312', 'EUC_CN', 'GBK'); - CharacterSetECI.EUC_KR = new CharacterSetECI(CharacterSetValueIdentifiers.EUC_KR, 30, 'EUC_KR', 'EUC-KR'); - - /** - * Custom Error class of type Exception. - */ - class UnsupportedOperationException extends Exception { - } - UnsupportedOperationException.kind = 'UnsupportedOperationException'; - - /** - * Responsible for en/decoding strings. - */ - class StringEncoding { - /** - * Decodes some Uint8Array to a string format. - */ - static decode(bytes, encoding) { - const encodingName = this.encodingName(encoding); - if (this.customDecoder) { - return this.customDecoder(bytes, encodingName); - } - // Increases browser support. - if (typeof TextDecoder === 'undefined' || this.shouldDecodeOnFallback(encodingName)) { - return this.decodeFallback(bytes, encodingName); - } - return new TextDecoder(encodingName).decode(bytes); - } - /** - * Checks if the decoding method should use the fallback for decoding - * once Node TextDecoder doesn't support all encoding formats. - * - * @param encodingName - */ - static shouldDecodeOnFallback(encodingName) { - return !StringEncoding.isBrowser() && encodingName === 'ISO-8859-1'; - } - /** - * Encodes some string into a Uint8Array. - */ - static encode(s, encoding) { - const encodingName = this.encodingName(encoding); - if (this.customEncoder) { - return this.customEncoder(s, encodingName); - } - // Increases browser support. - if (typeof TextEncoder === 'undefined') { - return this.encodeFallback(s); - } - // TextEncoder only encodes to UTF8 by default as specified by encoding.spec.whatwg.org - return new TextEncoder().encode(s); - } - static isBrowser() { - return (typeof window !== 'undefined' && {}.toString.call(window) === '[object Window]'); - } - /** - * Returns the string value from some encoding character set. - */ - static encodingName(encoding) { - return typeof encoding === 'string' - ? encoding - : encoding.getName(); - } - /** - * Returns character set from some encoding character set. - */ - static encodingCharacterSet(encoding) { - if (encoding instanceof CharacterSetECI) { - return encoding; - } - return CharacterSetECI.getCharacterSetECIByName(encoding); - } - /** - * Runs a fallback for the native decoding funcion. - */ - static decodeFallback(bytes, encoding) { - const characterSet = this.encodingCharacterSet(encoding); - if (StringEncoding.isDecodeFallbackSupported(characterSet)) { - let s = ''; - for (let i = 0, length = bytes.length; i < length; i++) { - let h = bytes[i].toString(16); - if (h.length < 2) { - h = '0' + h; - } - s += '%' + h; - } - return decodeURIComponent(s); - } - if (characterSet.equals(CharacterSetECI.UnicodeBigUnmarked)) { - return String.fromCharCode.apply(null, new Uint16Array(bytes.buffer)); - } - throw new UnsupportedOperationException(`Encoding ${this.encodingName(encoding)} not supported by fallback.`); - } - static isDecodeFallbackSupported(characterSet) { - return characterSet.equals(CharacterSetECI.UTF8) || - characterSet.equals(CharacterSetECI.ISO8859_1) || - characterSet.equals(CharacterSetECI.ASCII); - } - /** - * Runs a fallback for the native encoding funcion. - * - * @see https://stackoverflow.com/a/17192845/4367683 - */ - static encodeFallback(s) { - const encodedURIstring = btoa(unescape(encodeURIComponent(s))); - const charList = encodedURIstring.split(''); - const uintArray = []; - for (let i = 0; i < charList.length; i++) { - uintArray.push(charList[i].charCodeAt(0)); - } - return new Uint8Array(uintArray); - } - } - - /* - * Copyright (C) 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. - */ - /** - * Common string-related functions. - * - * @author Sean Owen - * @author Alex Dupre - */ - class StringUtils { - // SHIFT_JIS.equalsIgnoreCase(PLATFORM_DEFAULT_ENCODING) || - // EUC_JP.equalsIgnoreCase(PLATFORM_DEFAULT_ENCODING); - static castAsNonUtf8Char(code, encoding = null) { - // ISO 8859-1 is the Java default as UTF-8 is JavaScripts - // you can see this method as a Java version of String.fromCharCode - const e = encoding ? encoding.getName() : this.ISO88591; - // use passed format (fromCharCode will return UTF8 encoding) - return StringEncoding.decode(new Uint8Array([code]), e); - } - /** - * @param bytes bytes encoding a string, whose encoding should be guessed - * @param hints decode hints if applicable - * @return name of guessed encoding; at the moment will only guess one of: - * {@link #SHIFT_JIS}, {@link #UTF8}, {@link #ISO88591}, or the platform - * default encoding if none of these can possibly be correct - */ - static guessEncoding(bytes, hints) { - if (hints !== null && hints !== undefined && undefined !== hints.get(DecodeHintType$1.CHARACTER_SET)) { - return hints.get(DecodeHintType$1.CHARACTER_SET).toString(); - } - // For now, merely tries to distinguish ISO-8859-1, UTF-8 and Shift_JIS, - // which should be by far the most common encodings. - const length = bytes.length; - let canBeISO88591 = true; - let canBeShiftJIS = true; - let canBeUTF8 = true; - let utf8BytesLeft = 0; - // int utf8LowChars = 0 - let utf2BytesChars = 0; - let utf3BytesChars = 0; - let utf4BytesChars = 0; - let sjisBytesLeft = 0; - // int sjisLowChars = 0 - let sjisKatakanaChars = 0; - // int sjisDoubleBytesChars = 0 - let sjisCurKatakanaWordLength = 0; - let sjisCurDoubleBytesWordLength = 0; - let sjisMaxKatakanaWordLength = 0; - let sjisMaxDoubleBytesWordLength = 0; - // int isoLowChars = 0 - // int isoHighChars = 0 - let isoHighOther = 0; - const utf8bom = bytes.length > 3 && - bytes[0] === /*(byte) */ 0xEF && - bytes[1] === /*(byte) */ 0xBB && - bytes[2] === /*(byte) */ 0xBF; - for (let i = 0; i < length && (canBeISO88591 || canBeShiftJIS || canBeUTF8); i++) { - const value = bytes[i] & 0xFF; - // UTF-8 stuff - if (canBeUTF8) { - if (utf8BytesLeft > 0) { - if ((value & 0x80) === 0) { - canBeUTF8 = false; - } - else { - utf8BytesLeft--; - } - } - else if ((value & 0x80) !== 0) { - if ((value & 0x40) === 0) { - canBeUTF8 = false; - } - else { - utf8BytesLeft++; - if ((value & 0x20) === 0) { - utf2BytesChars++; - } - else { - utf8BytesLeft++; - if ((value & 0x10) === 0) { - utf3BytesChars++; - } - else { - utf8BytesLeft++; - if ((value & 0x08) === 0) { - utf4BytesChars++; - } - else { - canBeUTF8 = false; - } - } - } - } - } // else { - // utf8LowChars++ - // } - } - // ISO-8859-1 stuff - if (canBeISO88591) { - if (value > 0x7F && value < 0xA0) { - canBeISO88591 = false; - } - else if (value > 0x9F) { - if (value < 0xC0 || value === 0xD7 || value === 0xF7) { - isoHighOther++; - } // else { - // isoHighChars++ - // } - } // else { - // isoLowChars++ - // } - } - // Shift_JIS stuff - if (canBeShiftJIS) { - if (sjisBytesLeft > 0) { - if (value < 0x40 || value === 0x7F || value > 0xFC) { - canBeShiftJIS = false; - } - else { - sjisBytesLeft--; - } - } - else if (value === 0x80 || value === 0xA0 || value > 0xEF) { - canBeShiftJIS = false; - } - else if (value > 0xA0 && value < 0xE0) { - sjisKatakanaChars++; - sjisCurDoubleBytesWordLength = 0; - sjisCurKatakanaWordLength++; - if (sjisCurKatakanaWordLength > sjisMaxKatakanaWordLength) { - sjisMaxKatakanaWordLength = sjisCurKatakanaWordLength; - } - } - else if (value > 0x7F) { - sjisBytesLeft++; - // sjisDoubleBytesChars++ - sjisCurKatakanaWordLength = 0; - sjisCurDoubleBytesWordLength++; - if (sjisCurDoubleBytesWordLength > sjisMaxDoubleBytesWordLength) { - sjisMaxDoubleBytesWordLength = sjisCurDoubleBytesWordLength; - } - } - else { - // sjisLowChars++ - sjisCurKatakanaWordLength = 0; - sjisCurDoubleBytesWordLength = 0; - } - } - } - if (canBeUTF8 && utf8BytesLeft > 0) { - canBeUTF8 = false; - } - if (canBeShiftJIS && sjisBytesLeft > 0) { - canBeShiftJIS = false; - } - // Easy -- if there is BOM or at least 1 valid not-single byte character (and no evidence it can't be UTF-8), done - if (canBeUTF8 && (utf8bom || utf2BytesChars + utf3BytesChars + utf4BytesChars > 0)) { - return StringUtils.UTF8; - } - // Easy -- if assuming Shift_JIS or at least 3 valid consecutive not-ascii characters (and no evidence it can't be), done - if (canBeShiftJIS && (StringUtils.ASSUME_SHIFT_JIS || sjisMaxKatakanaWordLength >= 3 || sjisMaxDoubleBytesWordLength >= 3)) { - return StringUtils.SHIFT_JIS; - } - // Distinguishing Shift_JIS and ISO-8859-1 can be a little tough for short words. The crude heuristic is: - // - If we saw - // - only two consecutive katakana chars in the whole text, or - // - at least 10% of bytes that could be "upper" not-alphanumeric Latin1, - // - then we conclude Shift_JIS, else ISO-8859-1 - if (canBeISO88591 && canBeShiftJIS) { - return (sjisMaxKatakanaWordLength === 2 && sjisKatakanaChars === 2) || isoHighOther * 10 >= length - ? StringUtils.SHIFT_JIS : StringUtils.ISO88591; - } - // Otherwise, try in order ISO-8859-1, Shift JIS, UTF-8 and fall back to default platform encoding - if (canBeISO88591) { - return StringUtils.ISO88591; - } - if (canBeShiftJIS) { - return StringUtils.SHIFT_JIS; - } - if (canBeUTF8) { - return StringUtils.UTF8; - } - // Otherwise, we take a wild guess with platform encoding - return StringUtils.PLATFORM_DEFAULT_ENCODING; - } - /** - * - * @see https://stackoverflow.com/a/13439711/4367683 - * - * @param append The new string to append. - * @param args Argumets values to be formated. - */ - static format(append, ...args) { - let i = -1; - function callback(exp, p0, p1, p2, p3, p4) { - if (exp === '%%') - return '%'; - if (args[++i] === undefined) - return undefined; - exp = p2 ? parseInt(p2.substr(1)) : undefined; - let base = p3 ? parseInt(p3.substr(1)) : undefined; - let val; - switch (p4) { - case 's': - val = args[i]; - break; - case 'c': - val = args[i][0]; - break; - case 'f': - val = parseFloat(args[i]).toFixed(exp); - break; - case 'p': - val = parseFloat(args[i]).toPrecision(exp); - break; - case 'e': - val = parseFloat(args[i]).toExponential(exp); - break; - case 'x': - val = parseInt(args[i]).toString(base ? base : 16); - break; - case 'd': - val = parseFloat(parseInt(args[i], base ? base : 10).toPrecision(exp)).toFixed(0); - break; - } - val = typeof val === 'object' ? JSON.stringify(val) : (+val).toString(base); - let size = parseInt(p1); /* padding size */ - let ch = p1 && (p1[0] + '') === '0' ? '0' : ' '; /* isnull? */ - while (val.length < size) - val = p0 !== undefined ? val + ch : ch + val; /* isminus? */ - return val; - } - let regex = /%(-)?(0?[0-9]+)?([.][0-9]+)?([#][0-9]+)?([scfpexd%])/g; - return append.replace(regex, callback); - } - /** - * - */ - static getBytes(str, encoding) { - return StringEncoding.encode(str, encoding); - } - /** - * Returns the charcode at the specified index or at index zero. - */ - static getCharCode(str, index = 0) { - return str.charCodeAt(index); - } - /** - * Returns char for given charcode - */ - static getCharAt(charCode) { - return String.fromCharCode(charCode); - } - } - StringUtils.SHIFT_JIS = CharacterSetECI.SJIS.getName(); // "SJIS" - StringUtils.GB2312 = 'GB2312'; - StringUtils.ISO88591 = CharacterSetECI.ISO8859_1.getName(); // "ISO8859_1" - StringUtils.EUC_JP = 'EUC_JP'; - StringUtils.UTF8 = CharacterSetECI.UTF8.getName(); // "UTF8" - StringUtils.PLATFORM_DEFAULT_ENCODING = StringUtils.UTF8; // "UTF8"//Charset.defaultCharset().name() - StringUtils.ASSUME_SHIFT_JIS = false; - - class StringBuilder { - constructor(value = '') { - this.value = value; - } - enableDecoding(encoding) { - this.encoding = encoding; - return this; - } - append(s) { - if (typeof s === 'string') { - this.value += s.toString(); - } - else if (this.encoding) { - // use passed format (fromCharCode will return UTF8 encoding) - this.value += StringUtils.castAsNonUtf8Char(s, this.encoding); - } - else { - // correctly converts from UTF-8, but not other encodings - this.value += String.fromCharCode(s); - } - return this; - } - appendChars(str, offset, len) { - for (let i = offset; offset < offset + len; i++) { - this.append(str[i]); - } - return this; - } - length() { - return this.value.length; - } - charAt(n) { - return this.value.charAt(n); - } - deleteCharAt(n) { - this.value = this.value.substr(0, n) + this.value.substring(n + 1); - } - setCharAt(n, c) { - this.value = this.value.substr(0, n) + c + this.value.substr(n + 1); - } - substring(start, end) { - return this.value.substring(start, end); - } - /** - * @note helper method for RSS Expanded - */ - setLengthToZero() { - this.value = ''; - } - toString() { - return this.value; - } - insert(n, c) { - this.value = this.value.substr(0, n) + c + this.value.substr(n + c.length); - } - } - - /* - * Copyright 2007 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. - */ - /** - *Represents a 2D matrix of bits. In function arguments below, and throughout the common - * module, x is the column position, and y is the row position. The ordering is always x, y. - * The origin is at the top-left.
- * - *Internally the bits are represented in a 1-D array of 32-bit ints. However, each row begins - * with a new int. This is done intentionally so that we can copy out a row into a BitArray very - * efficiently.
- * - *The ordering of bits is row-major. Within each int, the least significant bits are used first, - * meaning they represent lower x values. This is compatible with BitArray's implementation.
- * - * @author Sean Owen - * @author dswitkin@google.com (Daniel Switkin) - */ - class BitMatrix /*implements Cloneable*/ { - /** - * Creates an empty square {@link BitMatrix}. - * - * @param dimension height and width - */ - // public constructor(dimension: number /*int*/) { - // this(dimension, dimension) - // } - /** - * Creates an empty {@link BitMatrix}. - * - * @param width bit matrix width - * @param height bit matrix height - */ - // public constructor(width: number /*int*/, height: number /*int*/) { - // if (width < 1 || height < 1) { - // throw new IllegalArgumentException("Both dimensions must be greater than 0") - // } - // this.width = width - // this.height = height - // this.rowSize = (width + 31) / 32 - // bits = new int[rowSize * height]; - // } - constructor(width /*int*/, height /*int*/, rowSize /*int*/, bits) { - this.width = width; - this.height = height; - this.rowSize = rowSize; - this.bits = bits; - if (undefined === height || null === height) { - height = width; - } - this.height = height; - if (width < 1 || height < 1) { - throw new IllegalArgumentException('Both dimensions must be greater than 0'); - } - if (undefined === rowSize || null === rowSize) { - rowSize = Math.floor((width + 31) / 32); - } - this.rowSize = rowSize; - if (undefined === bits || null === bits) { - this.bits = new Int32Array(this.rowSize * this.height); - } - } - /** - * Interprets a 2D array of booleans as a {@link BitMatrix}, where "true" means an "on" bit. - * - * @function parse - * @param image bits of the image, as a row-major 2D array. Elements are arrays representing rows - * @return {@link BitMatrix} representation of image - */ - static parseFromBooleanArray(image) { - const height = image.length; - const width = image[0].length; - const bits = new BitMatrix(width, height); - for (let i = 0; i < height; i++) { - const imageI = image[i]; - for (let j = 0; j < width; j++) { - if (imageI[j]) { - bits.set(j, i); - } - } - } - return bits; - } - /** - * - * @function parse - * @param stringRepresentation - * @param setString - * @param unsetString - */ - static parseFromString(stringRepresentation, setString, unsetString) { - if (stringRepresentation === null) { - throw new IllegalArgumentException('stringRepresentation cannot be null'); - } - const bits = new Array(stringRepresentation.length); - let bitsPos = 0; - let rowStartPos = 0; - let rowLength = -1; - let nRows = 0; - let pos = 0; - while (pos < stringRepresentation.length) { - if (stringRepresentation.charAt(pos) === '\n' || - stringRepresentation.charAt(pos) === '\r') { - if (bitsPos > rowStartPos) { - if (rowLength === -1) { - rowLength = bitsPos - rowStartPos; - } - else if (bitsPos - rowStartPos !== rowLength) { - throw new IllegalArgumentException('row lengths do not match'); - } - rowStartPos = bitsPos; - nRows++; - } - pos++; - } - else if (stringRepresentation.substring(pos, pos + setString.length) === setString) { - pos += setString.length; - bits[bitsPos] = true; - bitsPos++; - } - else if (stringRepresentation.substring(pos, pos + unsetString.length) === unsetString) { - pos += unsetString.length; - bits[bitsPos] = false; - bitsPos++; - } - else { - throw new IllegalArgumentException('illegal character encountered: ' + stringRepresentation.substring(pos)); - } - } - // no EOL at end? - if (bitsPos > rowStartPos) { - if (rowLength === -1) { - rowLength = bitsPos - rowStartPos; - } - else if (bitsPos - rowStartPos !== rowLength) { - throw new IllegalArgumentException('row lengths do not match'); - } - nRows++; - } - const matrix = new BitMatrix(rowLength, nRows); - for (let i = 0; i < bitsPos; i++) { - if (bits[i]) { - matrix.set(Math.floor(i % rowLength), Math.floor(i / rowLength)); - } - } - return matrix; - } - /** - *Gets the requested bit, where true means black.
- * - * @param x The horizontal component (i.e. which column) - * @param y The vertical component (i.e. which row) - * @return value of given bit in matrix - */ - get(x /*int*/, y /*int*/) { - const offset = y * this.rowSize + Math.floor(x / 32); - return ((this.bits[offset] >>> (x & 0x1f)) & 1) !== 0; - } - /** - *Sets the given bit to true.
- * - * @param x The horizontal component (i.e. which column) - * @param y The vertical component (i.e. which row) - */ - set(x /*int*/, y /*int*/) { - const offset = y * this.rowSize + Math.floor(x / 32); - this.bits[offset] |= (1 << (x & 0x1f)) & 0xFFFFFFFF; - } - unset(x /*int*/, y /*int*/) { - const offset = y * this.rowSize + Math.floor(x / 32); - this.bits[offset] &= ~((1 << (x & 0x1f)) & 0xFFFFFFFF); - } - /** - *Flips the given bit.
- * - * @param x The horizontal component (i.e. which column) - * @param y The vertical component (i.e. which row) - */ - flip(x /*int*/, y /*int*/) { - const offset = y * this.rowSize + Math.floor(x / 32); - this.bits[offset] ^= ((1 << (x & 0x1f)) & 0xFFFFFFFF); - } - /** - * Exclusive-or (XOR): Flip the bit in this {@code BitMatrix} if the corresponding - * mask bit is set. - * - * @param mask XOR mask - */ - xor(mask) { - if (this.width !== mask.getWidth() || this.height !== mask.getHeight() - || this.rowSize !== mask.getRowSize()) { - throw new IllegalArgumentException('input matrix dimensions do not match'); - } - const rowArray = new BitArray(Math.floor(this.width / 32) + 1); - const rowSize = this.rowSize; - const bits = this.bits; - for (let y = 0, height = this.height; y < height; y++) { - const offset = y * rowSize; - const row = mask.getRow(y, rowArray).getBitArray(); - for (let x = 0; x < rowSize; x++) { - bits[offset + x] ^= row[x]; - } - } - } - /** - * Clears all bits (sets to false). - */ - clear() { - const bits = this.bits; - const max = bits.length; - for (let i = 0; i < max; i++) { - bits[i] = 0; - } - } - /** - *Sets a square region of the bit matrix to true.
- * - * @param left The horizontal position to begin at (inclusive) - * @param top The vertical position to begin at (inclusive) - * @param width The width of the region - * @param height The height of the region - */ - setRegion(left /*int*/, top /*int*/, width /*int*/, height /*int*/) { - if (top < 0 || left < 0) { - throw new IllegalArgumentException('Left and top must be nonnegative'); - } - if (height < 1 || width < 1) { - throw new IllegalArgumentException('Height and width must be at least 1'); - } - const right = left + width; - const bottom = top + height; - if (bottom > this.height || right > this.width) { - throw new IllegalArgumentException('The region must fit inside the matrix'); - } - const rowSize = this.rowSize; - const bits = this.bits; - for (let y = top; y < bottom; y++) { - const offset = y * rowSize; - for (let x = left; x < right; x++) { - bits[offset + Math.floor(x / 32)] |= ((1 << (x & 0x1f)) & 0xFFFFFFFF); - } - } - } - /** - * A fast method to retrieve one row of data from the matrix as a BitArray. - * - * @param y The row to retrieve - * @param row An optional caller-allocated BitArray, will be allocated if null or too small - * @return The resulting BitArray - this reference should always be used even when passing - * your own row - */ - getRow(y /*int*/, row) { - if (row === null || row === undefined || row.getSize() < this.width) { - row = new BitArray(this.width); - } - else { - row.clear(); - } - const rowSize = this.rowSize; - const bits = this.bits; - const offset = y * rowSize; - for (let x = 0; x < rowSize; x++) { - row.setBulk(x * 32, bits[offset + x]); - } - return row; - } - /** - * @param y row to set - * @param row {@link BitArray} to copy from - */ - setRow(y /*int*/, row) { - System.arraycopy(row.getBitArray(), 0, this.bits, y * this.rowSize, this.rowSize); - } - /** - * Modifies this {@code BitMatrix} to represent the same but rotated 180 degrees - */ - rotate180() { - const width = this.getWidth(); - const height = this.getHeight(); - let topRow = new BitArray(width); - let bottomRow = new BitArray(width); - for (let i = 0, length = Math.floor((height + 1) / 2); i < length; i++) { - topRow = this.getRow(i, topRow); - bottomRow = this.getRow(height - 1 - i, bottomRow); - topRow.reverse(); - bottomRow.reverse(); - this.setRow(i, bottomRow); - this.setRow(height - 1 - i, topRow); - } - } - /** - * This is useful in detecting the enclosing rectangle of a 'pure' barcode. - * - * @return {@code left,top,width,height} enclosing rectangle of all 1 bits, or null if it is all white - */ - getEnclosingRectangle() { - const width = this.width; - const height = this.height; - const rowSize = this.rowSize; - const bits = this.bits; - let left = width; - let top = height; - let right = -1; - let bottom = -1; - for (let y = 0; y < height; y++) { - for (let x32 = 0; x32 < rowSize; x32++) { - const theBits = bits[y * rowSize + x32]; - if (theBits !== 0) { - if (y < top) { - top = y; - } - if (y > bottom) { - bottom = y; - } - if (x32 * 32 < left) { - let bit = 0; - while (((theBits << (31 - bit)) & 0xFFFFFFFF) === 0) { - bit++; - } - if ((x32 * 32 + bit) < left) { - left = x32 * 32 + bit; - } - } - if (x32 * 32 + 31 > right) { - let bit = 31; - while ((theBits >>> bit) === 0) { - bit--; - } - if ((x32 * 32 + bit) > right) { - right = x32 * 32 + bit; - } - } - } - } - } - if (right < left || bottom < top) { - return null; - } - return Int32Array.from([left, top, right - left + 1, bottom - top + 1]); - } - /** - * This is useful in detecting a corner of a 'pure' barcode. - * - * @return {@code x,y} coordinate of top-left-most 1 bit, or null if it is all white - */ - getTopLeftOnBit() { - const rowSize = this.rowSize; - const bits = this.bits; - let bitsOffset = 0; - while (bitsOffset < bits.length && bits[bitsOffset] === 0) { - bitsOffset++; - } - if (bitsOffset === bits.length) { - return null; - } - const y = bitsOffset / rowSize; - let x = (bitsOffset % rowSize) * 32; - const theBits = bits[bitsOffset]; - let bit = 0; - while (((theBits << (31 - bit)) & 0xFFFFFFFF) === 0) { - bit++; - } - x += bit; - return Int32Array.from([x, y]); - } - getBottomRightOnBit() { - const rowSize = this.rowSize; - const bits = this.bits; - let bitsOffset = bits.length - 1; - while (bitsOffset >= 0 && bits[bitsOffset] === 0) { - bitsOffset--; - } - if (bitsOffset < 0) { - return null; - } - const y = Math.floor(bitsOffset / rowSize); - let x = Math.floor(bitsOffset % rowSize) * 32; - const theBits = bits[bitsOffset]; - let bit = 31; - while ((theBits >>> bit) === 0) { - bit--; - } - x += bit; - return Int32Array.from([x, y]); - } - /** - * @return The width of the matrix - */ - getWidth() { - return this.width; - } - /** - * @return The height of the matrix - */ - getHeight() { - return this.height; - } - /** - * @return The row size of the matrix - */ - getRowSize() { - return this.rowSize; - } - /*@Override*/ - equals(o) { - if (!(o instanceof BitMatrix)) { - return false; - } - const other = o; - return this.width === other.width && this.height === other.height && this.rowSize === other.rowSize && - Arrays.equals(this.bits, other.bits); - } - /*@Override*/ - hashCode() { - let hash = this.width; - hash = 31 * hash + this.width; - hash = 31 * hash + this.height; - hash = 31 * hash + this.rowSize; - hash = 31 * hash + Arrays.hashCode(this.bits); - return hash; - } - /** - * @return string representation using "X" for set and " " for unset bits - */ - /*@Override*/ - // public toString(): string { - // return toString(": "X, " ") - // } - /** - * @param setString representation of a set bit - * @param unsetString representation of an unset bit - * @return string representation of entire matrix utilizing given strings - */ - // public toString(setString: string = "X ", unsetString: string = " "): string { - // return this.buildToString(setString, unsetString, "\n") - // } - /** - * @param setString representation of a set bit - * @param unsetString representation of an unset bit - * @param lineSeparator newline character in string representation - * @return string representation of entire matrix utilizing given strings and line separator - * @deprecated call {@link #toString(String,String)} only, which uses \n line separator always - */ - // @Deprecated - toString(setString = 'X ', unsetString = ' ', lineSeparator = '\n') { - return this.buildToString(setString, unsetString, lineSeparator); - } - buildToString(setString, unsetString, lineSeparator) { - let result = new StringBuilder(); - // result.append(lineSeparator); - for (let y = 0, height = this.height; y < height; y++) { - for (let x = 0, width = this.width; x < width; x++) { - result.append(this.get(x, y) ? setString : unsetString); - } - result.append(lineSeparator); - } - return result.toString(); - } - /*@Override*/ - clone() { - return new BitMatrix(this.width, this.height, this.rowSize, this.bits.slice()); - } - } - - /** - * Custom Error class of type Exception. - */ - class NotFoundException extends Exception { - static getNotFoundInstance() { - return new NotFoundException(); - } - } - NotFoundException.kind = 'NotFoundException'; - - /* - * 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. - */ - /** - * This Binarizer implementation uses the old ZXing global histogram approach. It is suitable - * for low-end mobile devices which don't have enough CPU or memory to use a local thresholding - * algorithm. However, because it picks a global black point, it cannot handle difficult shadows - * and gradients. - * - * Faster mobile devices and all desktop applications should probably use HybridBinarizer instead. - * - * @author dswitkin@google.com (Daniel Switkin) - * @author Sean Owen - */ - class GlobalHistogramBinarizer extends Binarizer { - constructor(source) { - super(source); - this.luminances = GlobalHistogramBinarizer.EMPTY; - this.buckets = new Int32Array(GlobalHistogramBinarizer.LUMINANCE_BUCKETS); - } - // Applies simple sharpening to the row data to improve performance of the 1D Readers. - /*@Override*/ - getBlackRow(y /*int*/, row) { - const source = this.getLuminanceSource(); - const width = source.getWidth(); - if (row === undefined || row === null || row.getSize() < width) { - row = new BitArray(width); - } - else { - row.clear(); - } - this.initArrays(width); - const localLuminances = source.getRow(y, this.luminances); - const localBuckets = this.buckets; - for (let x = 0; x < width; x++) { - localBuckets[(localLuminances[x] & 0xff) >> GlobalHistogramBinarizer.LUMINANCE_SHIFT]++; - } - const blackPoint = GlobalHistogramBinarizer.estimateBlackPoint(localBuckets); - if (width < 3) { - // Special case for very small images - for (let x = 0; x < width; x++) { - if ((localLuminances[x] & 0xff) < blackPoint) { - row.set(x); - } - } - } - else { - let left = localLuminances[0] & 0xff; - let center = localLuminances[1] & 0xff; - for (let x = 1; x < width - 1; x++) { - const right = localLuminances[x + 1] & 0xff; - // A simple -1 4 -1 box filter with a weight of 2. - if (((center * 4) - left - right) / 2 < blackPoint) { - row.set(x); - } - left = center; - center = right; - } - } - return row; - } - // Does not sharpen the data, as this call is intended to only be used by 2D Readers. - /*@Override*/ - getBlackMatrix() { - const source = this.getLuminanceSource(); - const width = source.getWidth(); - const height = source.getHeight(); - const matrix = new BitMatrix(width, height); - // Quickly calculates the histogram by sampling four rows from the image. This proved to be - // more robust on the blackbox tests than sampling a diagonal as we used to do. - this.initArrays(width); - const localBuckets = this.buckets; - for (let y = 1; y < 5; y++) { - const row = Math.floor((height * y) / 5); - const localLuminances = source.getRow(row, this.luminances); - const right = Math.floor((width * 4) / 5); - for (let x = Math.floor(width / 5); x < right; x++) { - const pixel = localLuminances[x] & 0xff; - localBuckets[pixel >> GlobalHistogramBinarizer.LUMINANCE_SHIFT]++; - } - } - const blackPoint = GlobalHistogramBinarizer.estimateBlackPoint(localBuckets); - // We delay reading the entire image luminance until the black point estimation succeeds. - // Although we end up reading four rows twice, it is consistent with our motto of - // "fail quickly" which is necessary for continuous scanning. - const localLuminances = source.getMatrix(); - for (let y = 0; y < height; y++) { - const offset = y * width; - for (let x = 0; x < width; x++) { - const pixel = localLuminances[offset + x] & 0xff; - if (pixel < blackPoint) { - matrix.set(x, y); - } - } - } - return matrix; - } - /*@Override*/ - createBinarizer(source) { - return new GlobalHistogramBinarizer(source); - } - initArrays(luminanceSize /*int*/) { - if (this.luminances.length < luminanceSize) { - this.luminances = new Uint8ClampedArray(luminanceSize); - } - const buckets = this.buckets; - for (let x = 0; x < GlobalHistogramBinarizer.LUMINANCE_BUCKETS; x++) { - buckets[x] = 0; - } - } - static estimateBlackPoint(buckets) { - // Find the tallest peak in the histogram. - const numBuckets = buckets.length; - let maxBucketCount = 0; - let firstPeak = 0; - let firstPeakSize = 0; - for (let x = 0; x < numBuckets; x++) { - if (buckets[x] > firstPeakSize) { - firstPeak = x; - firstPeakSize = buckets[x]; - } - if (buckets[x] > maxBucketCount) { - maxBucketCount = buckets[x]; - } - } - // Find the second-tallest peak which is somewhat far from the tallest peak. - let secondPeak = 0; - let secondPeakScore = 0; - for (let x = 0; x < numBuckets; x++) { - const distanceToBiggest = x - firstPeak; - // Encourage more distant second peaks by multiplying by square of distance. - const score = buckets[x] * distanceToBiggest * distanceToBiggest; - if (score > secondPeakScore) { - secondPeak = x; - secondPeakScore = score; - } - } - // Make sure firstPeak corresponds to the black peak. - if (firstPeak > secondPeak) { - const temp = firstPeak; - firstPeak = secondPeak; - secondPeak = temp; - } - // If there is too little contrast in the image to pick a meaningful black point, throw rather - // than waste time trying to decode the image, and risk false positives. - if (secondPeak - firstPeak <= numBuckets / 16) { - throw new NotFoundException(); - } - // Find a valley between them that is low and closer to the white peak. - let bestValley = secondPeak - 1; - let bestValleyScore = -1; - for (let x = secondPeak - 1; x > firstPeak; x--) { - const fromFirst = x - firstPeak; - const score = fromFirst * fromFirst * (secondPeak - x) * (maxBucketCount - buckets[x]); - if (score > bestValleyScore) { - bestValley = x; - bestValleyScore = score; - } - } - return bestValley << GlobalHistogramBinarizer.LUMINANCE_SHIFT; - } - } - GlobalHistogramBinarizer.LUMINANCE_BITS = 5; - GlobalHistogramBinarizer.LUMINANCE_SHIFT = 8 - GlobalHistogramBinarizer.LUMINANCE_BITS; - GlobalHistogramBinarizer.LUMINANCE_BUCKETS = 1 << GlobalHistogramBinarizer.LUMINANCE_BITS; - GlobalHistogramBinarizer.EMPTY = Uint8ClampedArray.from([0]); - - /* - * 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. - */ - /** - * This class implements a local thresholding algorithm, which while slower than the - * GlobalHistogramBinarizer, is fairly efficient for what it does. It is designed for - * high frequency images of barcodes with black data on white backgrounds. For this application, - * it does a much better job than a global blackpoint with severe shadows and gradients. - * However it tends to produce artifacts on lower frequency images and is therefore not - * a good general purpose binarizer for uses outside ZXing. - * - * This class extends GlobalHistogramBinarizer, using the older histogram approach for 1D readers, - * and the newer local approach for 2D readers. 1D decoding using a per-row histogram is already - * inherently local, and only fails for horizontal gradients. We can revisit that problem later, - * but for now it was not a win to use local blocks for 1D. - * - * This Binarizer is the default for the unit tests and the recommended class for library users. - * - * @author dswitkin@google.com (Daniel Switkin) - */ - class HybridBinarizer extends GlobalHistogramBinarizer { - constructor(source) { - super(source); - this.matrix = null; - } - /** - * Calculates the final BitMatrix once for all requests. This could be called once from the - * constructor instead, but there are some advantages to doing it lazily, such as making - * profiling easier, and not doing heavy lifting when callers don't expect it. - */ - /*@Override*/ - getBlackMatrix() { - if (this.matrix !== null) { - return this.matrix; - } - const source = this.getLuminanceSource(); - const width = source.getWidth(); - const height = source.getHeight(); - if (width >= HybridBinarizer.MINIMUM_DIMENSION && height >= HybridBinarizer.MINIMUM_DIMENSION) { - const luminances = source.getMatrix(); - let subWidth = width >> HybridBinarizer.BLOCK_SIZE_POWER; - if ((width & HybridBinarizer.BLOCK_SIZE_MASK) !== 0) { - subWidth++; - } - let subHeight = height >> HybridBinarizer.BLOCK_SIZE_POWER; - if ((height & HybridBinarizer.BLOCK_SIZE_MASK) !== 0) { - subHeight++; - } - const blackPoints = HybridBinarizer.calculateBlackPoints(luminances, subWidth, subHeight, width, height); - const newMatrix = new BitMatrix(width, height); - HybridBinarizer.calculateThresholdForBlock(luminances, subWidth, subHeight, width, height, blackPoints, newMatrix); - this.matrix = newMatrix; - } - else { - // If the image is too small, fall back to the global histogram approach. - this.matrix = super.getBlackMatrix(); - } - return this.matrix; - } - /*@Override*/ - createBinarizer(source) { - return new HybridBinarizer(source); - } - /** - * For each block in the image, calculate the average black point using a 5x5 grid - * of the blocks around it. Also handles the corner cases (fractional blocks are computed based - * on the last pixels in the row/column which are also used in the previous block). - */ - static calculateThresholdForBlock(luminances, subWidth /*int*/, subHeight /*int*/, width /*int*/, height /*int*/, blackPoints, matrix) { - const maxYOffset = height - HybridBinarizer.BLOCK_SIZE; - const maxXOffset = width - HybridBinarizer.BLOCK_SIZE; - for (let y = 0; y < subHeight; y++) { - let yoffset = y << HybridBinarizer.BLOCK_SIZE_POWER; - if (yoffset > maxYOffset) { - yoffset = maxYOffset; - } - const top = HybridBinarizer.cap(y, 2, subHeight - 3); - for (let x = 0; x < subWidth; x++) { - let xoffset = x << HybridBinarizer.BLOCK_SIZE_POWER; - if (xoffset > maxXOffset) { - xoffset = maxXOffset; - } - const left = HybridBinarizer.cap(x, 2, subWidth - 3); - let sum = 0; - for (let z = -2; z <= 2; z++) { - const blackRow = blackPoints[top + z]; - sum += blackRow[left - 2] + blackRow[left - 1] + blackRow[left] + blackRow[left + 1] + blackRow[left + 2]; - } - const average = sum / 25; - HybridBinarizer.thresholdBlock(luminances, xoffset, yoffset, average, width, matrix); - } - } - } - static cap(value /*int*/, min /*int*/, max /*int*/) { - return value < min ? min : value > max ? max : value; - } - /** - * Applies a single threshold to a block of pixels. - */ - static thresholdBlock(luminances, xoffset /*int*/, yoffset /*int*/, threshold /*int*/, stride /*int*/, matrix) { - for (let y = 0, offset = yoffset * stride + xoffset; y < HybridBinarizer.BLOCK_SIZE; y++, offset += stride) { - for (let x = 0; x < HybridBinarizer.BLOCK_SIZE; x++) { - // Comparison needs to be <= so that black == 0 pixels are black even if the threshold is 0. - if ((luminances[offset + x] & 0xFF) <= threshold) { - matrix.set(xoffset + x, yoffset + y); - } - } - } - } - /** - * Calculates a single black point for each block of pixels and saves it away. - * See the following thread for a discussion of this algorithm: - * http://groups.google.com/group/zxing/browse_thread/thread/d06efa2c35a7ddc0 - */ - static calculateBlackPoints(luminances, subWidth /*int*/, subHeight /*int*/, width /*int*/, height /*int*/) { - const maxYOffset = height - HybridBinarizer.BLOCK_SIZE; - const maxXOffset = width - HybridBinarizer.BLOCK_SIZE; - // tslint:disable-next-line:whitespace - const blackPoints = new Array(subHeight); // subWidth - for (let y = 0; y < subHeight; y++) { - blackPoints[y] = new Int32Array(subWidth); - let yoffset = y << HybridBinarizer.BLOCK_SIZE_POWER; - if (yoffset > maxYOffset) { - yoffset = maxYOffset; - } - for (let x = 0; x < subWidth; x++) { - let xoffset = x << HybridBinarizer.BLOCK_SIZE_POWER; - if (xoffset > maxXOffset) { - xoffset = maxXOffset; - } - let sum = 0; - let min = 0xFF; - let max = 0; - for (let yy = 0, offset = yoffset * width + xoffset; yy < HybridBinarizer.BLOCK_SIZE; yy++, offset += width) { - for (let xx = 0; xx < HybridBinarizer.BLOCK_SIZE; xx++) { - const pixel = luminances[offset + xx] & 0xFF; - sum += pixel; - // still looking for good contrast - if (pixel < min) { - min = pixel; - } - if (pixel > max) { - max = pixel; - } - } - // short-circuit min/max tests once dynamic range is met - if (max - min > HybridBinarizer.MIN_DYNAMIC_RANGE) { - // finish the rest of the rows quickly - for (yy++, offset += width; yy < HybridBinarizer.BLOCK_SIZE; yy++, offset += width) { - for (let xx = 0; xx < HybridBinarizer.BLOCK_SIZE; xx++) { - sum += luminances[offset + xx] & 0xFF; - } - } - } - } - // The default estimate is the average of the values in the block. - let average = sum >> (HybridBinarizer.BLOCK_SIZE_POWER * 2); - if (max - min <= HybridBinarizer.MIN_DYNAMIC_RANGE) { - // If variation within the block is low, assume this is a block with only light or only - // dark pixels. In that case we do not want to use the average, as it would divide this - // low contrast area into black and white pixels, essentially creating data out of noise. - // - // The default assumption is that the block is light/background. Since no estimate for - // the level of dark pixels exists locally, use half the min for the block. - average = min / 2; - if (y > 0 && x > 0) { - // Correct the "white background" assumption for blocks that have neighbors by comparing - // the pixels in this block to the previously calculated black points. This is based on - // the fact that dark barcode symbology is always surrounded by some amount of light - // background for which reasonable black point estimates were made. The bp estimated at - // the boundaries is used for the interior. - // The (min < bp) is arbitrary but works better than other heuristics that were tried. - const averageNeighborBlackPoint = (blackPoints[y - 1][x] + (2 * blackPoints[y][x - 1]) + blackPoints[y - 1][x - 1]) / 4; - if (min < averageNeighborBlackPoint) { - average = averageNeighborBlackPoint; - } - } - } - blackPoints[y][x] = average; - } - } - return blackPoints; - } - } - // This class uses 5x5 blocks to compute local luminance, where each block is 8x8 pixels. - // So this is the smallest dimension in each axis we can accept. - HybridBinarizer.BLOCK_SIZE_POWER = 3; - HybridBinarizer.BLOCK_SIZE = 1 << HybridBinarizer.BLOCK_SIZE_POWER; // ...0100...00 - HybridBinarizer.BLOCK_SIZE_MASK = HybridBinarizer.BLOCK_SIZE - 1; // ...0011...11 - HybridBinarizer.MINIMUM_DIMENSION = HybridBinarizer.BLOCK_SIZE * 5; - HybridBinarizer.MIN_DYNAMIC_RANGE = 24; - - /* - * 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. - */ - /*namespace com.google.zxing {*/ - /** - * The purpose of this class hierarchy is to abstract different bitmap implementations across - * platforms into a standard interface for requesting greyscale luminance values. The interface - * only provides immutable methods; therefore crop and rotation create copies. This is to ensure - * that one Reader does not modify the original luminance source and leave it in an unknown state - * for other Readers in the chain. - * - * @author dswitkin@google.com (Daniel Switkin) - */ - class LuminanceSource { - constructor(width /*int*/, height /*int*/) { - this.width = width; - this.height = height; - } - /** - * @return The width of the bitmap. - */ - getWidth() { - return this.width; - } - /** - * @return The height of the bitmap. - */ - getHeight() { - return this.height; - } - /** - * @return Whether this subclass supports cropping. - */ - isCropSupported() { - return false; - } - /** - * Returns a new object with cropped image data. Implementations may keep a reference to the - * original data rather than a copy. Only callable if isCropSupported() is true. - * - * @param left The left coordinate, which must be in [0,getWidth()) - * @param top The top coordinate, which must be in [0,getHeight()) - * @param width The width of the rectangle to crop. - * @param height The height of the rectangle to crop. - * @return A cropped version of this object. - */ - crop(left /*int*/, top /*int*/, width /*int*/, height /*int*/) { - throw new UnsupportedOperationException('This luminance source does not support cropping.'); - } - /** - * @return Whether this subclass supports counter-clockwise rotation. - */ - isRotateSupported() { - return false; - } - /** - * Returns a new object with rotated image data by 90 degrees counterclockwise. - * Only callable if {@link #isRotateSupported()} is true. - * - * @return A rotated version of this object. - */ - rotateCounterClockwise() { - throw new UnsupportedOperationException('This luminance source does not support rotation by 90 degrees.'); - } - /** - * Returns a new object with rotated image data by 45 degrees counterclockwise. - * Only callable if {@link #isRotateSupported()} is true. - * - * @return A rotated version of this object. - */ - rotateCounterClockwise45() { - throw new UnsupportedOperationException('This luminance source does not support rotation by 45 degrees.'); - } - /*@Override*/ - toString() { - const row = new Uint8ClampedArray(this.width); - let result = new StringBuilder(); - for (let y = 0; y < this.height; y++) { - const sourceRow = this.getRow(y, row); - for (let x = 0; x < this.width; x++) { - const luminance = sourceRow[x] & 0xFF; - let c; - if (luminance < 0x40) { - c = '#'; - } - else if (luminance < 0x80) { - c = '+'; - } - else if (luminance < 0xC0) { - c = '.'; - } - else { - c = ' '; - } - result.append(c); - } - result.append('\n'); - } - return result.toString(); - } - } - - /* - * 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. - */ - /*namespace com.google.zxing {*/ - /** - * A wrapper implementation of {@link LuminanceSource} which inverts the luminances it returns -- black becomes - * white and vice versa, and each value becomes (255-value). - * - * @author Sean Owen - */ - class InvertedLuminanceSource extends LuminanceSource { - constructor(delegate) { - super(delegate.getWidth(), delegate.getHeight()); - this.delegate = delegate; - } - /*@Override*/ - getRow(y /*int*/, row) { - const sourceRow = this.delegate.getRow(y, row); - const width = this.getWidth(); - for (let i = 0; i < width; i++) { - sourceRow[i] = /*(byte)*/ (255 - (sourceRow[i] & 0xFF)); - } - return sourceRow; - } - /*@Override*/ - getMatrix() { - const matrix = this.delegate.getMatrix(); - const length = this.getWidth() * this.getHeight(); - const invertedMatrix = new Uint8ClampedArray(length); - for (let i = 0; i < length; i++) { - invertedMatrix[i] = /*(byte)*/ (255 - (matrix[i] & 0xFF)); - } - return invertedMatrix; - } - /*@Override*/ - isCropSupported() { - return this.delegate.isCropSupported(); - } - /*@Override*/ - crop(left /*int*/, top /*int*/, width /*int*/, height /*int*/) { - return new InvertedLuminanceSource(this.delegate.crop(left, top, width, height)); - } - /*@Override*/ - isRotateSupported() { - return this.delegate.isRotateSupported(); - } - /** - * @return original delegate {@link LuminanceSource} since invert undoes itself - */ - /*@Override*/ - invert() { - return this.delegate; - } - /*@Override*/ - rotateCounterClockwise() { - return new InvertedLuminanceSource(this.delegate.rotateCounterClockwise()); - } - /*@Override*/ - rotateCounterClockwise45() { - return new InvertedLuminanceSource(this.delegate.rotateCounterClockwise45()); - } - } - - /** - * @deprecated Moving to @zxing/browser - */ - class HTMLCanvasElementLuminanceSource extends LuminanceSource { - constructor(canvas) { - super(canvas.width, canvas.height); - this.canvas = canvas; - this.tempCanvasElement = null; - this.buffer = HTMLCanvasElementLuminanceSource.makeBufferFromCanvasImageData(canvas); - } - static makeBufferFromCanvasImageData(canvas) { - const imageData = canvas.getContext('2d').getImageData(0, 0, canvas.width, canvas.height); - return HTMLCanvasElementLuminanceSource.toGrayscaleBuffer(imageData.data, canvas.width, canvas.height); - } - static toGrayscaleBuffer(imageBuffer, width, height) { - const grayscaleBuffer = new Uint8ClampedArray(width * height); - for (let i = 0, j = 0, length = imageBuffer.length; i < length; i += 4, j++) { - let gray; - const alpha = imageBuffer[i + 3]; - // The color of fully-transparent pixels is irrelevant. They are often, technically, fully-transparent - // black (0 alpha, and then 0 RGB). They are often used, of course as the "white" area in a - // barcode image. Force any such pixel to be white: - if (alpha === 0) { - gray = 0xFF; - } - else { - const pixelR = imageBuffer[i]; - const pixelG = imageBuffer[i + 1]; - const pixelB = imageBuffer[i + 2]; - // .299R + 0.587G + 0.114B (YUV/YIQ for PAL and NTSC), - // (306*R) >> 10 is approximately equal to R*0.299, and so on. - // 0x200 >> 10 is 0.5, it implements rounding. - gray = (306 * pixelR + - 601 * pixelG + - 117 * pixelB + - 0x200) >> 10; - } - grayscaleBuffer[j] = gray; - } - return grayscaleBuffer; - } - getRow(y /*int*/, row) { - if (y < 0 || y >= this.getHeight()) { - throw new IllegalArgumentException('Requested row is outside the image: ' + y); - } - const width = this.getWidth(); - const start = y * width; - if (row === null) { - row = this.buffer.slice(start, start + width); - } - else { - if (row.length < width) { - row = new Uint8ClampedArray(width); - } - // The underlying raster of image consists of bytes with the luminance values - // TODO: can avoid set/slice? - row.set(this.buffer.slice(start, start + width)); - } - return row; - } - getMatrix() { - return this.buffer; - } - isCropSupported() { - return true; - } - crop(left /*int*/, top /*int*/, width /*int*/, height /*int*/) { - super.crop(left, top, width, height); - return this; - } - /** - * This is always true, since the image is a gray-scale image. - * - * @return true - */ - isRotateSupported() { - return true; - } - rotateCounterClockwise() { - this.rotate(-90); - return this; - } - rotateCounterClockwise45() { - this.rotate(-45); - return this; - } - getTempCanvasElement() { - if (null === this.tempCanvasElement) { - const tempCanvasElement = this.canvas.ownerDocument.createElement('canvas'); - tempCanvasElement.width = this.canvas.width; - tempCanvasElement.height = this.canvas.height; - this.tempCanvasElement = tempCanvasElement; - } - return this.tempCanvasElement; - } - rotate(angle) { - const tempCanvasElement = this.getTempCanvasElement(); - const tempContext = tempCanvasElement.getContext('2d'); - const angleRadians = angle * HTMLCanvasElementLuminanceSource.DEGREE_TO_RADIANS; - // Calculate and set new dimensions for temp canvas - const width = this.canvas.width; - const height = this.canvas.height; - const newWidth = Math.ceil(Math.abs(Math.cos(angleRadians)) * width + Math.abs(Math.sin(angleRadians)) * height); - const newHeight = Math.ceil(Math.abs(Math.sin(angleRadians)) * width + Math.abs(Math.cos(angleRadians)) * height); - tempCanvasElement.width = newWidth; - tempCanvasElement.height = newHeight; - // Draw at center of temp canvas to prevent clipping of image data - tempContext.translate(newWidth / 2, newHeight / 2); - tempContext.rotate(angleRadians); - tempContext.drawImage(this.canvas, width / -2, height / -2); - this.buffer = HTMLCanvasElementLuminanceSource.makeBufferFromCanvasImageData(tempCanvasElement); - return this; - } - invert() { - return new InvertedLuminanceSource(this); - } - } - HTMLCanvasElementLuminanceSource.DEGREE_TO_RADIANS = Math.PI / 180; - - /** - * @deprecated Moving to @zxing/browser - * - * Video input device metadata containing the id and label of the device if available. - */ - class VideoInputDevice { - /** - * Creates an instance of VideoInputDevice. - * - * @param {string} deviceId the video input device id - * @param {string} label the label of the device if available - */ - constructor(deviceId, label, groupId) { - this.deviceId = deviceId; - this.label = label; - /** @inheritdoc */ - this.kind = 'videoinput'; - this.groupId = groupId || undefined; - } - /** @inheritdoc */ - toJSON() { - return { - kind: this.kind, - groupId: this.groupId, - deviceId: this.deviceId, - label: this.label, - }; - } - } - - var __awaiter = ((globalThis || __webpack_require__.g || self || window || undefined) && (globalThis || __webpack_require__.g || self || window || undefined).__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); - }; - /** - * @deprecated Moving to @zxing/browser - * - * Base class for browser code reader. - */ - class BrowserCodeReader { - /** - * Creates an instance of BrowserCodeReader. - * @param {Reader} reader The reader instance to decode the barcode - * @param {number} [timeBetweenScansMillis=500] the time delay between subsequent successful decode tries - * - * @memberOf BrowserCodeReader - */ - constructor(reader, timeBetweenScansMillis = 500, _hints) { - this.reader = reader; - this.timeBetweenScansMillis = timeBetweenScansMillis; - this._hints = _hints; - /** - * This will break the loop. - */ - this._stopContinuousDecode = false; - /** - * This will break the loop. - */ - this._stopAsyncDecode = false; - /** - * Delay time between decode attempts made by the scanner. - */ - this._timeBetweenDecodingAttempts = 0; - } - /** - * If navigator is present. - */ - get hasNavigator() { - return typeof navigator !== 'undefined'; - } - /** - * If mediaDevices under navigator is supported. - */ - get isMediaDevicesSuported() { - return this.hasNavigator && !!navigator.mediaDevices; - } - /** - * If enumerateDevices under navigator is supported. - */ - get canEnumerateDevices() { - return !!(this.isMediaDevicesSuported && navigator.mediaDevices.enumerateDevices); - } - /** Time between two decoding tries in milli seconds. */ - get timeBetweenDecodingAttempts() { - return this._timeBetweenDecodingAttempts; - } - /** - * Change the time span the decoder waits between two decoding tries. - * - * @param {number} millis Time between two decoding tries in milli seconds. - */ - set timeBetweenDecodingAttempts(millis) { - this._timeBetweenDecodingAttempts = millis < 0 ? 0 : millis; - } - /** - * Sets the hints. - */ - set hints(hints) { - this._hints = hints || null; - } - /** - * Sets the hints. - */ - get hints() { - return this._hints; - } - /** - * Lists all the available video input devices. - */ - listVideoInputDevices() { - return __awaiter(this, void 0, void 0, function* () { - if (!this.hasNavigator) { - throw new Error('Can\'t enumerate devices, navigator is not present.'); - } - if (!this.canEnumerateDevices) { - throw new Error('Can\'t enumerate devices, method not supported.'); - } - const devices = yield navigator.mediaDevices.enumerateDevices(); - const videoDevices = []; - for (const device of devices) { - const kind = device.kind === 'video' ? 'videoinput' : device.kind; - if (kind !== 'videoinput') { - continue; - } - const deviceId = device.deviceId || device.id; - const label = device.label || `Video device ${videoDevices.length + 1}`; - const groupId = device.groupId; - const videoDevice = { deviceId, label, kind, groupId }; - videoDevices.push(videoDevice); - } - return videoDevices; - }); - } - /** - * Obtain the list of available devices with type 'videoinput'. - * - * @returns {PromiseEncapsulates the result of decoding a barcode within an image.
- * - * @author Sean Owen - */ - class Result { - // public constructor(private text: string, - // Uint8Array rawBytes, - // ResultPoconst resultPoints: Int32Array, - // BarcodeFormat format) { - // this(text, rawBytes, resultPoints, format, System.currentTimeMillis()) - // } - // public constructor(text: string, - // Uint8Array rawBytes, - // ResultPoconst resultPoints: Int32Array, - // BarcodeFormat format, - // long timestamp) { - // this(text, rawBytes, rawBytes == null ? 0 : 8 * rawBytes.length, - // resultPoints, format, timestamp) - // } - constructor(text, rawBytes, numBits = rawBytes == null ? 0 : 8 * rawBytes.length, resultPoints, format, timestamp = System.currentTimeMillis()) { - this.text = text; - this.rawBytes = rawBytes; - this.numBits = numBits; - this.resultPoints = resultPoints; - this.format = format; - this.timestamp = timestamp; - this.text = text; - this.rawBytes = rawBytes; - if (undefined === numBits || null === numBits) { - this.numBits = (rawBytes === null || rawBytes === undefined) ? 0 : 8 * rawBytes.length; - } - else { - this.numBits = numBits; - } - this.resultPoints = resultPoints; - this.format = format; - this.resultMetadata = null; - if (undefined === timestamp || null === timestamp) { - this.timestamp = System.currentTimeMillis(); - } - else { - this.timestamp = timestamp; - } - } - /** - * @return raw text encoded by the barcode - */ - getText() { - return this.text; - } - /** - * @return raw bytes encoded by the barcode, if applicable, otherwise {@code null} - */ - getRawBytes() { - return this.rawBytes; - } - /** - * @return how many bits of {@link #getRawBytes()} are valid; typically 8 times its length - * @since 3.3.0 - */ - getNumBits() { - return this.numBits; - } - /** - * @return points related to the barcode in the image. These are typically points - * identifying finder patterns or the corners of the barcode. The exact meaning is - * specific to the type of barcode that was decoded. - */ - getResultPoints() { - return this.resultPoints; - } - /** - * @return {@link BarcodeFormat} representing the format of the barcode that was decoded - */ - getBarcodeFormat() { - return this.format; - } - /** - * @return {@link Map} mapping {@link ResultMetadataType} keys to values. May be - * {@code null}. This contains optional metadata about what was detected about the barcode, - * like orientation. - */ - getResultMetadata() { - return this.resultMetadata; - } - putMetadata(type, value) { - if (this.resultMetadata === null) { - this.resultMetadata = new Map(); - } - this.resultMetadata.set(type, value); - } - putAllMetadata(metadata) { - if (metadata !== null) { - if (this.resultMetadata === null) { - this.resultMetadata = metadata; - } - else { - this.resultMetadata = new Map(metadata); - } - } - } - addResultPoints(newPoints) { - const oldPoints = this.resultPoints; - if (oldPoints === null) { - this.resultPoints = newPoints; - } - else if (newPoints !== null && newPoints.length > 0) { - const allPoints = new Array(oldPoints.length + newPoints.length); - System.arraycopy(oldPoints, 0, allPoints, 0, oldPoints.length); - System.arraycopy(newPoints, 0, allPoints, oldPoints.length, newPoints.length); - this.resultPoints = allPoints; - } - } - getTimestamp() { - return this.timestamp; - } - /*@Override*/ - toString() { - return this.text; - } - } - - /* - * Direct port to TypeScript of ZXing by Adrian Toșcă - */ - /* - * 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. - */ - /*namespace com.google.zxing {*/ - /** - * Enumerates barcode formats known to this package. Please keep alphabetized. - * - * @author Sean Owen - */ - var BarcodeFormat; - (function (BarcodeFormat) { - /** Aztec 2D barcode format. */ - BarcodeFormat[BarcodeFormat["AZTEC"] = 0] = "AZTEC"; - /** CODABAR 1D format. */ - BarcodeFormat[BarcodeFormat["CODABAR"] = 1] = "CODABAR"; - /** Code 39 1D format. */ - BarcodeFormat[BarcodeFormat["CODE_39"] = 2] = "CODE_39"; - /** Code 93 1D format. */ - BarcodeFormat[BarcodeFormat["CODE_93"] = 3] = "CODE_93"; - /** Code 128 1D format. */ - BarcodeFormat[BarcodeFormat["CODE_128"] = 4] = "CODE_128"; - /** Data Matrix 2D barcode format. */ - BarcodeFormat[BarcodeFormat["DATA_MATRIX"] = 5] = "DATA_MATRIX"; - /** EAN-8 1D format. */ - BarcodeFormat[BarcodeFormat["EAN_8"] = 6] = "EAN_8"; - /** EAN-13 1D format. */ - BarcodeFormat[BarcodeFormat["EAN_13"] = 7] = "EAN_13"; - /** ITF (Interleaved Two of Five) 1D format. */ - BarcodeFormat[BarcodeFormat["ITF"] = 8] = "ITF"; - /** MaxiCode 2D barcode format. */ - BarcodeFormat[BarcodeFormat["MAXICODE"] = 9] = "MAXICODE"; - /** PDF417 format. */ - BarcodeFormat[BarcodeFormat["PDF_417"] = 10] = "PDF_417"; - /** QR Code 2D barcode format. */ - BarcodeFormat[BarcodeFormat["QR_CODE"] = 11] = "QR_CODE"; - /** RSS 14 */ - BarcodeFormat[BarcodeFormat["RSS_14"] = 12] = "RSS_14"; - /** RSS EXPANDED */ - BarcodeFormat[BarcodeFormat["RSS_EXPANDED"] = 13] = "RSS_EXPANDED"; - /** UPC-A 1D format. */ - BarcodeFormat[BarcodeFormat["UPC_A"] = 14] = "UPC_A"; - /** UPC-E 1D format. */ - BarcodeFormat[BarcodeFormat["UPC_E"] = 15] = "UPC_E"; - /** UPC/EAN extension format. Not a stand-alone format. */ - BarcodeFormat[BarcodeFormat["UPC_EAN_EXTENSION"] = 16] = "UPC_EAN_EXTENSION"; - })(BarcodeFormat || (BarcodeFormat = {})); - var BarcodeFormat$1 = BarcodeFormat; - - /* - * 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. - */ - /*namespace com.google.zxing {*/ - /** - * Represents some type of metadata about the result of the decoding that the decoder - * wishes to communicate back to the caller. - * - * @author Sean Owen - */ - var ResultMetadataType; - (function (ResultMetadataType) { - /** - * Unspecified, application-specific metadata. Maps to an unspecified {@link Object}. - */ - ResultMetadataType[ResultMetadataType["OTHER"] = 0] = "OTHER"; - /** - * Denotes the likely approximate orientation of the barcode in the image. This value - * is given as degrees rotated clockwise from the normal, upright orientation. - * For example a 1D barcode which was found by reading top-to-bottom would be - * said to have orientation "90". This key maps to an {@link Integer} whose - * value is in the range [0,360). - */ - ResultMetadataType[ResultMetadataType["ORIENTATION"] = 1] = "ORIENTATION"; - /** - *2D barcode formats typically encode text, but allow for a sort of 'byte mode' - * which is sometimes used to encode binary data. While {@link Result} makes available - * the complete raw bytes in the barcode for these formats, it does not offer the bytes - * from the byte segments alone.
- * - *This maps to a {@link java.util.List} of byte arrays corresponding to the - * raw bytes in the byte segments in the barcode, in order.
- */ - ResultMetadataType[ResultMetadataType["BYTE_SEGMENTS"] = 2] = "BYTE_SEGMENTS"; - /** - * Error correction level used, if applicable. The value type depends on the - * format, but is typically a String. - */ - ResultMetadataType[ResultMetadataType["ERROR_CORRECTION_LEVEL"] = 3] = "ERROR_CORRECTION_LEVEL"; - /** - * For some periodicals, indicates the issue number as an {@link Integer}. - */ - ResultMetadataType[ResultMetadataType["ISSUE_NUMBER"] = 4] = "ISSUE_NUMBER"; - /** - * For some products, indicates the suggested retail price in the barcode as a - * formatted {@link String}. - */ - ResultMetadataType[ResultMetadataType["SUGGESTED_PRICE"] = 5] = "SUGGESTED_PRICE"; - /** - * For some products, the possible country of manufacture as a {@link String} denoting the - * ISO country code. Some map to multiple possible countries, like "US/CA". - */ - ResultMetadataType[ResultMetadataType["POSSIBLE_COUNTRY"] = 6] = "POSSIBLE_COUNTRY"; - /** - * For some products, the extension text - */ - ResultMetadataType[ResultMetadataType["UPC_EAN_EXTENSION"] = 7] = "UPC_EAN_EXTENSION"; - /** - * PDF417-specific metadata - */ - ResultMetadataType[ResultMetadataType["PDF417_EXTRA_METADATA"] = 8] = "PDF417_EXTRA_METADATA"; - /** - * If the code format supports structured append and the current scanned code is part of one then the - * sequence number is given with it. - */ - ResultMetadataType[ResultMetadataType["STRUCTURED_APPEND_SEQUENCE"] = 9] = "STRUCTURED_APPEND_SEQUENCE"; - /** - * If the code format supports structured append and the current scanned code is part of one then the - * parity is given with it. - */ - ResultMetadataType[ResultMetadataType["STRUCTURED_APPEND_PARITY"] = 10] = "STRUCTURED_APPEND_PARITY"; - })(ResultMetadataType || (ResultMetadataType = {})); - var ResultMetadataType$1 = ResultMetadataType; - - /* - * Copyright 2007 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. - */ - /*namespace com.google.zxing.common {*/ - /*import java.util.List;*/ - /** - *Encapsulates the result of decoding a matrix of bits. This typically - * applies to 2D barcode formats. For now it contains the raw bytes obtained, - * as well as a String interpretation of those bytes, if applicable.
- * - * @author Sean Owen - */ - class DecoderResult { - // public constructor(rawBytes: Uint8Array, - // text: string, - // ListThis class contains utility methods for performing mathematical operations over - * the Galois Fields. Operations use a given primitive polynomial in calculations.
- * - *Throughout this package, elements of the GF are represented as an {@code int} - * for convenience and speed (but at the cost of memory). - *
- * - * @author Sean Owen - * @author David Olivier - */ - class AbstractGenericGF { - /** - * @return 2 to the power of a in GF(size) - */ - exp(a) { - return this.expTable[a]; - } - /** - * @return base 2 log of a in GF(size) - */ - log(a /*int*/) { - if (a === 0) { - throw new IllegalArgumentException(); - } - return this.logTable[a]; - } - /** - * Implements both addition and subtraction -- they are the same in GF(size). - * - * @return sum/difference of a and b - */ - static addOrSubtract(a /*int*/, b /*int*/) { - return a ^ b; - } - } - - /* - * Copyright 2007 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. - */ - /** - *Represents a polynomial whose coefficients are elements of a GF. - * Instances of this class are immutable.
- * - *Much credit is due to William Rucklidge since portions of this code are an indirect - * port of his C++ Reed-Solomon implementation.
- * - * @author Sean Owen - */ - class GenericGFPoly { - /** - * @param field the {@link GenericGF} instance representing the field to use - * to perform computations - * @param coefficients coefficients as ints representing elements of GF(size), arranged - * from most significant (highest-power term) coefficient to least significant - * @throws IllegalArgumentException if argument is null or empty, - * or if leading coefficient is 0 and this is not a - * constant polynomial (that is, it is not the monomial "0") - */ - constructor(field, coefficients) { - if (coefficients.length === 0) { - throw new IllegalArgumentException(); - } - this.field = field; - const coefficientsLength = coefficients.length; - if (coefficientsLength > 1 && coefficients[0] === 0) { - // Leading term must be non-zero for anything except the constant polynomial "0" - let firstNonZero = 1; - while (firstNonZero < coefficientsLength && coefficients[firstNonZero] === 0) { - firstNonZero++; - } - if (firstNonZero === coefficientsLength) { - this.coefficients = Int32Array.from([0]); - } - else { - this.coefficients = new Int32Array(coefficientsLength - firstNonZero); - System.arraycopy(coefficients, firstNonZero, this.coefficients, 0, this.coefficients.length); - } - } - else { - this.coefficients = coefficients; - } - } - getCoefficients() { - return this.coefficients; - } - /** - * @return degree of this polynomial - */ - getDegree() { - return this.coefficients.length - 1; - } - /** - * @return true iff this polynomial is the monomial "0" - */ - isZero() { - return this.coefficients[0] === 0; - } - /** - * @return coefficient of x^degree term in this polynomial - */ - getCoefficient(degree /*int*/) { - return this.coefficients[this.coefficients.length - 1 - degree]; - } - /** - * @return evaluation of this polynomial at a given point - */ - evaluateAt(a /*int*/) { - if (a === 0) { - // Just return the x^0 coefficient - return this.getCoefficient(0); - } - const coefficients = this.coefficients; - let result; - if (a === 1) { - // Just the sum of the coefficients - result = 0; - for (let i = 0, length = coefficients.length; i !== length; i++) { - const coefficient = coefficients[i]; - result = AbstractGenericGF.addOrSubtract(result, coefficient); - } - return result; - } - result = coefficients[0]; - const size = coefficients.length; - const field = this.field; - for (let i = 1; i < size; i++) { - result = AbstractGenericGF.addOrSubtract(field.multiply(a, result), coefficients[i]); - } - return result; - } - addOrSubtract(other) { - if (!this.field.equals(other.field)) { - throw new IllegalArgumentException('GenericGFPolys do not have same GenericGF field'); - } - if (this.isZero()) { - return other; - } - if (other.isZero()) { - return this; - } - let smallerCoefficients = this.coefficients; - let largerCoefficients = other.coefficients; - if (smallerCoefficients.length > largerCoefficients.length) { - const temp = smallerCoefficients; - smallerCoefficients = largerCoefficients; - largerCoefficients = temp; - } - let sumDiff = new Int32Array(largerCoefficients.length); - const lengthDiff = largerCoefficients.length - smallerCoefficients.length; - // Copy high-order terms only found in higher-degree polynomial's coefficients - System.arraycopy(largerCoefficients, 0, sumDiff, 0, lengthDiff); - for (let i = lengthDiff; i < largerCoefficients.length; i++) { - sumDiff[i] = AbstractGenericGF.addOrSubtract(smallerCoefficients[i - lengthDiff], largerCoefficients[i]); - } - return new GenericGFPoly(this.field, sumDiff); - } - multiply(other) { - if (!this.field.equals(other.field)) { - throw new IllegalArgumentException('GenericGFPolys do not have same GenericGF field'); - } - if (this.isZero() || other.isZero()) { - return this.field.getZero(); - } - const aCoefficients = this.coefficients; - const aLength = aCoefficients.length; - const bCoefficients = other.coefficients; - const bLength = bCoefficients.length; - const product = new Int32Array(aLength + bLength - 1); - const field = this.field; - for (let i = 0; i < aLength; i++) { - const aCoeff = aCoefficients[i]; - for (let j = 0; j < bLength; j++) { - product[i + j] = AbstractGenericGF.addOrSubtract(product[i + j], field.multiply(aCoeff, bCoefficients[j])); - } - } - return new GenericGFPoly(field, product); - } - multiplyScalar(scalar /*int*/) { - if (scalar === 0) { - return this.field.getZero(); - } - if (scalar === 1) { - return this; - } - const size = this.coefficients.length; - const field = this.field; - const product = new Int32Array(size); - const coefficients = this.coefficients; - for (let i = 0; i < size; i++) { - product[i] = field.multiply(coefficients[i], scalar); - } - return new GenericGFPoly(field, product); - } - multiplyByMonomial(degree /*int*/, coefficient /*int*/) { - if (degree < 0) { - throw new IllegalArgumentException(); - } - if (coefficient === 0) { - return this.field.getZero(); - } - const coefficients = this.coefficients; - const size = coefficients.length; - const product = new Int32Array(size + degree); - const field = this.field; - for (let i = 0; i < size; i++) { - product[i] = field.multiply(coefficients[i], coefficient); - } - return new GenericGFPoly(field, product); - } - divide(other) { - if (!this.field.equals(other.field)) { - throw new IllegalArgumentException('GenericGFPolys do not have same GenericGF field'); - } - if (other.isZero()) { - throw new IllegalArgumentException('Divide by 0'); - } - const field = this.field; - let quotient = field.getZero(); - let remainder = this; - const denominatorLeadingTerm = other.getCoefficient(other.getDegree()); - const inverseDenominatorLeadingTerm = field.inverse(denominatorLeadingTerm); - while (remainder.getDegree() >= other.getDegree() && !remainder.isZero()) { - const degreeDifference = remainder.getDegree() - other.getDegree(); - const scale = field.multiply(remainder.getCoefficient(remainder.getDegree()), inverseDenominatorLeadingTerm); - const term = other.multiplyByMonomial(degreeDifference, scale); - const iterationQuotient = field.buildMonomial(degreeDifference, scale); - quotient = quotient.addOrSubtract(iterationQuotient); - remainder = remainder.addOrSubtract(term); - } - return [quotient, remainder]; - } - /*@Override*/ - toString() { - let result = ''; - for (let degree = this.getDegree(); degree >= 0; degree--) { - let coefficient = this.getCoefficient(degree); - if (coefficient !== 0) { - if (coefficient < 0) { - result += ' - '; - coefficient = -coefficient; - } - else { - if (result.length > 0) { - result += ' + '; - } - } - if (degree === 0 || coefficient !== 1) { - const alphaPower = this.field.log(coefficient); - if (alphaPower === 0) { - result += '1'; - } - else if (alphaPower === 1) { - result += 'a'; - } - else { - result += 'a^'; - result += alphaPower; - } - } - if (degree !== 0) { - if (degree === 1) { - result += 'x'; - } - else { - result += 'x^'; - result += degree; - } - } - } - } - return result; - } - } - - /** - * Custom Error class of type Exception. - */ - class ArithmeticException extends Exception { - } - ArithmeticException.kind = 'ArithmeticException'; - - /* - * Copyright 2007 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. - */ - /** - *This class contains utility methods for performing mathematical operations over - * the Galois Fields. Operations use a given primitive polynomial in calculations.
- * - *Throughout this package, elements of the GF are represented as an {@code int} - * for convenience and speed (but at the cost of memory). - *
- * - * @author Sean Owen - * @author David Olivier - */ - class GenericGF extends AbstractGenericGF { - /** - * Create a representation of GF(size) using the given primitive polynomial. - * - * @param primitive irreducible polynomial whose coefficients are represented by - * the bits of an int, where the least-significant bit represents the constant - * coefficient - * @param size the size of the field - * @param b the factor b in the generator polynomial can be 0- or 1-based - * (g(x) = (x+a^b)(x+a^(b+1))...(x+a^(b+2t-1))). - * In most cases it should be 1, but for QR code it is 0. - */ - constructor(primitive /*int*/, size /*int*/, generatorBase /*int*/) { - super(); - this.primitive = primitive; - this.size = size; - this.generatorBase = generatorBase; - const expTable = new Int32Array(size); - let x = 1; - for (let i = 0; i < size; i++) { - expTable[i] = x; - x *= 2; // we're assuming the generator alpha is 2 - if (x >= size) { - x ^= primitive; - x &= size - 1; - } - } - this.expTable = expTable; - const logTable = new Int32Array(size); - for (let i = 0; i < size - 1; i++) { - logTable[expTable[i]] = i; - } - this.logTable = logTable; - // logTable[0] == 0 but this should never be used - this.zero = new GenericGFPoly(this, Int32Array.from([0])); - this.one = new GenericGFPoly(this, Int32Array.from([1])); - } - getZero() { - return this.zero; - } - getOne() { - return this.one; - } - /** - * @return the monomial representing coefficient * x^degree - */ - buildMonomial(degree /*int*/, coefficient /*int*/) { - if (degree < 0) { - throw new IllegalArgumentException(); - } - if (coefficient === 0) { - return this.zero; - } - const coefficients = new Int32Array(degree + 1); - coefficients[0] = coefficient; - return new GenericGFPoly(this, coefficients); - } - /** - * @return multiplicative inverse of a - */ - inverse(a /*int*/) { - if (a === 0) { - throw new ArithmeticException(); - } - return this.expTable[this.size - this.logTable[a] - 1]; - } - /** - * @return product of a and b in GF(size) - */ - multiply(a /*int*/, b /*int*/) { - if (a === 0 || b === 0) { - return 0; - } - return this.expTable[(this.logTable[a] + this.logTable[b]) % (this.size - 1)]; - } - getSize() { - return this.size; - } - getGeneratorBase() { - return this.generatorBase; - } - /*@Override*/ - toString() { - return ('GF(0x' + Integer.toHexString(this.primitive) + ',' + this.size + ')'); - } - equals(o) { - return o === this; - } - } - GenericGF.AZTEC_DATA_12 = new GenericGF(0x1069, 4096, 1); // x^12 + x^6 + x^5 + x^3 + 1 - GenericGF.AZTEC_DATA_10 = new GenericGF(0x409, 1024, 1); // x^10 + x^3 + 1 - GenericGF.AZTEC_DATA_6 = new GenericGF(0x43, 64, 1); // x^6 + x + 1 - GenericGF.AZTEC_PARAM = new GenericGF(0x13, 16, 1); // x^4 + x + 1 - GenericGF.QR_CODE_FIELD_256 = new GenericGF(0x011d, 256, 0); // x^8 + x^4 + x^3 + x^2 + 1 - GenericGF.DATA_MATRIX_FIELD_256 = new GenericGF(0x012d, 256, 1); // x^8 + x^5 + x^3 + x^2 + 1 - GenericGF.AZTEC_DATA_8 = GenericGF.DATA_MATRIX_FIELD_256; - GenericGF.MAXICODE_FIELD_64 = GenericGF.AZTEC_DATA_6; - - /** - * Custom Error class of type Exception. - */ - class ReedSolomonException extends Exception { - } - ReedSolomonException.kind = 'ReedSolomonException'; - - /** - * Custom Error class of type Exception. - */ - class IllegalStateException extends Exception { - } - IllegalStateException.kind = 'IllegalStateException'; - - /* - * Copyright 2007 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. - */ - /** - *Implements Reed-Solomon decoding, as the name implies.
- * - *The algorithm will not be explained here, but the following references were helpful - * in creating this implementation:
- * - *Much credit is due to William Rucklidge since portions of this code are an indirect - * port of his C++ Reed-Solomon implementation.
- * - * @author Sean Owen - * @author William Rucklidge - * @author sanfordsquires - */ - class ReedSolomonDecoder { - constructor(field) { - this.field = field; - } - /** - *Decodes given set of received codewords, which include both data and error-correction - * codewords. Really, this means it uses Reed-Solomon to detect and correct errors, in-place, - * in the input.
- * - * @param received data and error-correction codewords - * @param twoS number of error-correction codewords available - * @throws ReedSolomonException if decoding fails for any reason - */ - decode(received, twoS /*int*/) { - const field = this.field; - const poly = new GenericGFPoly(field, received); - const syndromeCoefficients = new Int32Array(twoS); - let noError = true; - for (let i = 0; i < twoS; i++) { - const evalResult = poly.evaluateAt(field.exp(i + field.getGeneratorBase())); - syndromeCoefficients[syndromeCoefficients.length - 1 - i] = evalResult; - if (evalResult !== 0) { - noError = false; - } - } - if (noError) { - return; - } - const syndrome = new GenericGFPoly(field, syndromeCoefficients); - const sigmaOmega = this.runEuclideanAlgorithm(field.buildMonomial(twoS, 1), syndrome, twoS); - const sigma = sigmaOmega[0]; - const omega = sigmaOmega[1]; - const errorLocations = this.findErrorLocations(sigma); - const errorMagnitudes = this.findErrorMagnitudes(omega, errorLocations); - for (let i = 0; i < errorLocations.length; i++) { - const position = received.length - 1 - field.log(errorLocations[i]); - if (position < 0) { - throw new ReedSolomonException('Bad error location'); - } - received[position] = GenericGF.addOrSubtract(received[position], errorMagnitudes[i]); - } - } - runEuclideanAlgorithm(a, b, R /*int*/) { - // Assume a's degree is >= b's - if (a.getDegree() < b.getDegree()) { - const temp = a; - a = b; - b = temp; - } - const field = this.field; - let rLast = a; - let r = b; - let tLast = field.getZero(); - let t = field.getOne(); - // Run Euclidean algorithm until r's degree is less than R/2 - while (r.getDegree() >= (R / 2 | 0)) { - let rLastLast = rLast; - let tLastLast = tLast; - rLast = r; - tLast = t; - // Divide rLastLast by rLast, with quotient in q and remainder in r - if (rLast.isZero()) { - // Oops, Euclidean algorithm already terminated? - throw new ReedSolomonException('r_{i-1} was zero'); - } - r = rLastLast; - let q = field.getZero(); - const denominatorLeadingTerm = rLast.getCoefficient(rLast.getDegree()); - const dltInverse = field.inverse(denominatorLeadingTerm); - while (r.getDegree() >= rLast.getDegree() && !r.isZero()) { - const degreeDiff = r.getDegree() - rLast.getDegree(); - const scale = field.multiply(r.getCoefficient(r.getDegree()), dltInverse); - q = q.addOrSubtract(field.buildMonomial(degreeDiff, scale)); - r = r.addOrSubtract(rLast.multiplyByMonomial(degreeDiff, scale)); - } - t = q.multiply(tLast).addOrSubtract(tLastLast); - if (r.getDegree() >= rLast.getDegree()) { - throw new IllegalStateException('Division algorithm failed to reduce polynomial?'); - } - } - const sigmaTildeAtZero = t.getCoefficient(0); - if (sigmaTildeAtZero === 0) { - throw new ReedSolomonException('sigmaTilde(0) was zero'); - } - const inverse = field.inverse(sigmaTildeAtZero); - const sigma = t.multiplyScalar(inverse); - const omega = r.multiplyScalar(inverse); - return [sigma, omega]; - } - findErrorLocations(errorLocator) { - // This is a direct application of Chien's search - const numErrors = errorLocator.getDegree(); - if (numErrors === 1) { // shortcut - return Int32Array.from([errorLocator.getCoefficient(1)]); - } - const result = new Int32Array(numErrors); - let e = 0; - const field = this.field; - for (let i = 1; i < field.getSize() && e < numErrors; i++) { - if (errorLocator.evaluateAt(i) === 0) { - result[e] = field.inverse(i); - e++; - } - } - if (e !== numErrors) { - throw new ReedSolomonException('Error locator degree does not match number of roots'); - } - return result; - } - findErrorMagnitudes(errorEvaluator, errorLocations) { - // This is directly applying Forney's Formula - const s = errorLocations.length; - const result = new Int32Array(s); - const field = this.field; - for (let i = 0; i < s; i++) { - const xiInverse = field.inverse(errorLocations[i]); - let denominator = 1; - for (let j = 0; j < s; j++) { - if (i !== j) { - // denominator = field.multiply(denominator, - // GenericGF.addOrSubtract(1, field.multiply(errorLocations[j], xiInverse))) - // Above should work but fails on some Apple and Linux JDKs due to a Hotspot bug. - // Below is a funny-looking workaround from Steven Parkes - const term = field.multiply(errorLocations[j], xiInverse); - const termPlus1 = (term & 0x1) === 0 ? term | 1 : term & ~1; - denominator = field.multiply(denominator, termPlus1); - } - } - result[i] = field.multiply(errorEvaluator.evaluateAt(xiInverse), field.inverse(denominator)); - if (field.getGeneratorBase() !== 0) { - result[i] = field.multiply(result[i], xiInverse); - } - } - return result; - } - } - - /* - * 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. - */ - // 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 = {})); - /** - *The main class which implements Aztec Code decoding -- as opposed to locating and extracting - * the Aztec Code from an image.
- * - * @author David Olivier - */ - class Decoder { - decode(detectorResult) { - this.ddata = detectorResult; - let matrix = detectorResult.getBits(); - let rawbits = this.extractBits(matrix); - let correctedBits = this.correctBits(rawbits); - let rawBytes = Decoder.convertBoolArrayToByteArray(correctedBits); - let result = Decoder.getEncodedData(correctedBits); - let decoderResult = new DecoderResult(rawBytes, result, null, null); - decoderResult.setNumBits(correctedBits.length); - return decoderResult; - } - // This method is used for testing the high-level encoder - static highLevelDecode(correctedBits) { - return this.getEncodedData(correctedBits); - } - /** - * Gets the string encoded in the aztec code bits - * - * @return the decoded string - */ - static getEncodedData(correctedBits) { - let endIndex = correctedBits.length; - let latchTable = Table.UPPER; // table most recently latched to - let shiftTable = Table.UPPER; // table to use for the next read - let result = ''; - let index = 0; - while (index < endIndex) { - if (shiftTable === Table.BINARY) { - if (endIndex - index < 5) { - break; - } - let length = Decoder.readCode(correctedBits, index, 5); - index += 5; - if (length === 0) { - if (endIndex - index < 11) { - break; - } - length = Decoder.readCode(correctedBits, index, 11) + 31; - index += 11; - } - for (let charCount = 0; charCount < length; charCount++) { - if (endIndex - index < 8) { - index = endIndex; // Force outer loop to exit - break; - } - const code = Decoder.readCode(correctedBits, index, 8); - result += /*(char)*/ StringUtils.castAsNonUtf8Char(code); - index += 8; - } - // Go back to whatever mode we had been in - shiftTable = latchTable; - } - else { - let size = shiftTable === Table.DIGIT ? 4 : 5; - if (endIndex - index < size) { - break; - } - let code = Decoder.readCode(correctedBits, index, size); - index += size; - let 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 - */ - static getTable(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 - */ - static getCharacter(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('Bad table'); - } - } - /** - *Performs RS error correction on an array of bits.
- * - * @return the corrected array - * @throws FormatException if the input contains too many errors - */ - correctBits(rawbits) { - let gf; - let codewordSize; - if (this.ddata.getNbLayers() <= 2) { - codewordSize = 6; - gf = GenericGF.AZTEC_DATA_6; - } - else if (this.ddata.getNbLayers() <= 8) { - codewordSize = 8; - gf = GenericGF.AZTEC_DATA_8; - } - else if (this.ddata.getNbLayers() <= 22) { - codewordSize = 10; - gf = GenericGF.AZTEC_DATA_10; - } - else { - codewordSize = 12; - gf = GenericGF.AZTEC_DATA_12; - } - let numDataCodewords = this.ddata.getNbDatablocks(); - let numCodewords = rawbits.length / codewordSize; - if (numCodewords < numDataCodewords) { - throw new FormatException(); - } - let offset = rawbits.length % codewordSize; - let dataWords = new Int32Array(numCodewords); - for (let i = 0; i < numCodewords; i++, offset += codewordSize) { - dataWords[i] = Decoder.readCode(rawbits, offset, codewordSize); - } - try { - let rsDecoder = new ReedSolomonDecoder(gf); - rsDecoder.decode(dataWords, numCodewords - numDataCodewords); - } - catch (ex) { - throw new FormatException(ex); - } - // Now perform the unstuffing operation. - // First, count how many bits are going to be thrown out as stuffing - let mask = (1 << codewordSize) - 1; - let stuffedBits = 0; - for (let i = 0; i < numDataCodewords; i++) { - let dataWord = dataWords[i]; - if (dataWord === 0 || dataWord === mask) { - throw new FormatException(); - } - else if (dataWord === 1 || dataWord === mask - 1) { - stuffedBits++; - } - } - // Now, actually unpack the bits and remove the stuffing - let correctedBits = new Array(numDataCodewords * codewordSize - stuffedBits); - let index = 0; - for (let i = 0; i < numDataCodewords; i++) { - let 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 (let 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 - */ - extractBits(matrix) { - let compact = this.ddata.isCompact(); - let layers = this.ddata.getNbLayers(); - let baseMatrixSize = (compact ? 11 : 14) + layers * 4; // not including alignment lines - let alignmentMap = new Int32Array(baseMatrixSize); - let rawbits = new Array(this.totalBitsInLayer(layers, compact)); - if (compact) { - for (let i = 0; i < alignmentMap.length; i++) { - alignmentMap[i] = i; - } - } - else { - let matrixSize = baseMatrixSize + 1 + 2 * Integer.truncDivision((Integer.truncDivision(baseMatrixSize, 2) - 1), 15); - let origCenter = baseMatrixSize / 2; - let center = Integer.truncDivision(matrixSize, 2); - for (let i = 0; i < origCenter; i++) { - let newOffset = i + Integer.truncDivision(i, 15); - alignmentMap[origCenter - i - 1] = center - newOffset - 1; - alignmentMap[origCenter + i] = center + newOffset + 1; - } - } - for (let i = 0, rowOffset = 0; i < layers; i++) { - let rowSize = (layers - i) * 4 + (compact ? 9 : 12); - // The top-left most point of this layer isEncapsulates a point of interest in an image containing a barcode. Typically, this - * would be the location of a finder pattern or the corner of the barcode, for example.
- * - * @author Sean Owen - */ - class ResultPoint { - constructor(x, y) { - this.x = x; - this.y = y; - } - getX() { - return this.x; - } - getY() { - return this.y; - } - /*@Override*/ - equals(other) { - if (other instanceof ResultPoint) { - const otherPoint = other; - return this.x === otherPoint.x && this.y === otherPoint.y; - } - return false; - } - /*@Override*/ - hashCode() { - return 31 * Float.floatToIntBits(this.x) + Float.floatToIntBits(this.y); - } - /*@Override*/ - toString() { - return '(' + this.x + ',' + this.y + ')'; - } - /** - * Orders an array of three ResultPoints in an order [A,B,C] such that AB is less than AC - * and BC is less than AC, and the angle between BC and BA is less than 180 degrees. - * - * @param patterns array of three {@code ResultPoint} to order - */ - static orderBestPatterns(patterns) { - // Find distances between pattern centers - const zeroOneDistance = this.distance(patterns[0], patterns[1]); - const oneTwoDistance = this.distance(patterns[1], patterns[2]); - const zeroTwoDistance = this.distance(patterns[0], patterns[2]); - let pointA; - let pointB; - let pointC; - // Assume one closest to other two is B; A and C will just be guesses at first - if (oneTwoDistance >= zeroOneDistance && oneTwoDistance >= zeroTwoDistance) { - pointB = patterns[0]; - pointA = patterns[1]; - pointC = patterns[2]; - } - else if (zeroTwoDistance >= oneTwoDistance && zeroTwoDistance >= zeroOneDistance) { - pointB = patterns[1]; - pointA = patterns[0]; - pointC = patterns[2]; - } - else { - pointB = patterns[2]; - pointA = patterns[0]; - pointC = patterns[1]; - } - // Use cross product to figure out whether A and C are correct or flipped. - // This asks whether BC x BA has a positive z component, which is the arrangement - // we want for A, B, C. If it's negative, then we've got it flipped around and - // should swap A and C. - if (this.crossProductZ(pointA, pointB, pointC) < 0.0) { - const temp = pointA; - pointA = pointC; - pointC = temp; - } - patterns[0] = pointA; - patterns[1] = pointB; - patterns[2] = pointC; - } - /** - * @param pattern1 first pattern - * @param pattern2 second pattern - * @return distance between two points - */ - static distance(pattern1, pattern2) { - return MathUtils.distance(pattern1.x, pattern1.y, pattern2.x, pattern2.y); - } - /** - * Returns the z component of the cross product between vectors BC and BA. - */ - static crossProductZ(pointA, pointB, pointC) { - const bX = pointB.x; - const bY = pointB.y; - return ((pointC.x - bX) * (pointA.y - bY)) - ((pointC.y - bY) * (pointA.x - bX)); - } - } - - /* - * Copyright 2007 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. - */ - /** - *Encapsulates the result of detecting a barcode in an image. This includes the raw - * matrix of black/white pixels corresponding to the barcode, and possibly points of interest - * in the image, like the location of finder patterns or corners of the barcode in the image.
- * - * @author Sean Owen - */ - class DetectorResult { - constructor(bits, points) { - this.bits = bits; - this.points = points; - } - getBits() { - return this.bits; - } - getPoints() { - return this.points; - } - } - - /* - * 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. - */ - /** - *Extends {@link DetectorResult} with more information specific to the Aztec format, - * like the number of layers and whether it's compact.
- * - * @author Sean Owen - */ - class AztecDetectorResult extends DetectorResult { - constructor(bits, points, compact, nbDatablocks, nbLayers) { - super(bits, points); - this.compact = compact; - this.nbDatablocks = nbDatablocks; - this.nbLayers = nbLayers; - } - getNbLayers() { - return this.nbLayers; - } - getNbDatablocks() { - return this.nbDatablocks; - } - isCompact() { - return this.compact; - } - } - - /* - * 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. - */ - /** - *- * Detects a candidate barcode-like rectangular region within an image. It - * starts around the center of the image, increases the size of the candidate - * region until it finds a white rectangular region. By keeping track of the - * last black points it encountered, it determines the corners of the barcode. - *
- * - * @author David Olivier - */ - class WhiteRectangleDetector { - // public constructor(private image: BitMatrix) /*throws NotFoundException*/ { - // this(image, INIT_SIZE, image.getWidth() / 2, image.getHeight() / 2) - // } - /** - * @param image barcode image to find a rectangle in - * @param initSize initial size of search area around center - * @param x x position of search center - * @param y y position of search center - * @throws NotFoundException if image is too small to accommodate {@code initSize} - */ - constructor(image, initSize /*int*/, x /*int*/, y /*int*/) { - this.image = image; - this.height = image.getHeight(); - this.width = image.getWidth(); - if (undefined === initSize || null === initSize) { - initSize = WhiteRectangleDetector.INIT_SIZE; - } - if (undefined === x || null === x) { - x = image.getWidth() / 2 | 0; - } - if (undefined === y || null === y) { - y = image.getHeight() / 2 | 0; - } - const halfsize = initSize / 2 | 0; - this.leftInit = x - halfsize; - this.rightInit = x + halfsize; - this.upInit = y - halfsize; - this.downInit = y + halfsize; - if (this.upInit < 0 || this.leftInit < 0 || this.downInit >= this.height || this.rightInit >= this.width) { - throw new NotFoundException(); - } - } - /** - *- * Detects a candidate barcode-like rectangular region within an image. It - * starts around the center of the image, increases the size of the candidate - * region until it finds a white rectangular region. - *
- * - * @return {@link ResultPoint}[] describing the corners of the rectangular - * region. The first and last points are opposed on the diagonal, as - * are the second and third. The first point will be the topmost - * point and the last, the bottommost. The second point will be - * leftmost and the third, the rightmost - * @throws NotFoundException if no Data Matrix Code can be found - */ - detect() { - let left = this.leftInit; - let right = this.rightInit; - let up = this.upInit; - let down = this.downInit; - let sizeExceeded = false; - let aBlackPointFoundOnBorder = true; - let atLeastOneBlackPointFoundOnBorder = false; - let atLeastOneBlackPointFoundOnRight = false; - let atLeastOneBlackPointFoundOnBottom = false; - let atLeastOneBlackPointFoundOnLeft = false; - let atLeastOneBlackPointFoundOnTop = false; - const width = this.width; - const height = this.height; - while (aBlackPointFoundOnBorder) { - aBlackPointFoundOnBorder = false; - // ..... - // . | - // ..... - let rightBorderNotWhite = true; - while ((rightBorderNotWhite || !atLeastOneBlackPointFoundOnRight) && right < width) { - rightBorderNotWhite = this.containsBlackPoint(up, down, right, false); - if (rightBorderNotWhite) { - right++; - aBlackPointFoundOnBorder = true; - atLeastOneBlackPointFoundOnRight = true; - } - else if (!atLeastOneBlackPointFoundOnRight) { - right++; - } - } - if (right >= width) { - sizeExceeded = true; - break; - } - // ..... - // . . - // .___. - let bottomBorderNotWhite = true; - while ((bottomBorderNotWhite || !atLeastOneBlackPointFoundOnBottom) && down < height) { - bottomBorderNotWhite = this.containsBlackPoint(left, right, down, true); - if (bottomBorderNotWhite) { - down++; - aBlackPointFoundOnBorder = true; - atLeastOneBlackPointFoundOnBottom = true; - } - else if (!atLeastOneBlackPointFoundOnBottom) { - down++; - } - } - if (down >= height) { - sizeExceeded = true; - break; - } - // ..... - // | . - // ..... - let leftBorderNotWhite = true; - while ((leftBorderNotWhite || !atLeastOneBlackPointFoundOnLeft) && left >= 0) { - leftBorderNotWhite = this.containsBlackPoint(up, down, left, false); - if (leftBorderNotWhite) { - left--; - aBlackPointFoundOnBorder = true; - atLeastOneBlackPointFoundOnLeft = true; - } - else if (!atLeastOneBlackPointFoundOnLeft) { - left--; - } - } - if (left < 0) { - sizeExceeded = true; - break; - } - // .___. - // . . - // ..... - let topBorderNotWhite = true; - while ((topBorderNotWhite || !atLeastOneBlackPointFoundOnTop) && up >= 0) { - topBorderNotWhite = this.containsBlackPoint(left, right, up, true); - if (topBorderNotWhite) { - up--; - aBlackPointFoundOnBorder = true; - atLeastOneBlackPointFoundOnTop = true; - } - else if (!atLeastOneBlackPointFoundOnTop) { - up--; - } - } - if (up < 0) { - sizeExceeded = true; - break; - } - if (aBlackPointFoundOnBorder) { - atLeastOneBlackPointFoundOnBorder = true; - } - } - if (!sizeExceeded && atLeastOneBlackPointFoundOnBorder) { - const maxSize = right - left; - let z = null; - for (let i = 1; z === null && i < maxSize; i++) { - z = this.getBlackPointOnSegment(left, down - i, left + i, down); - } - if (z == null) { - throw new NotFoundException(); - } - let t = null; - // go down right - for (let i = 1; t === null && i < maxSize; i++) { - t = this.getBlackPointOnSegment(left, up + i, left + i, up); - } - if (t == null) { - throw new NotFoundException(); - } - let x = null; - // go down left - for (let i = 1; x === null && i < maxSize; i++) { - x = this.getBlackPointOnSegment(right, up + i, right - i, up); - } - if (x == null) { - throw new NotFoundException(); - } - let y = null; - // go up left - for (let i = 1; y === null && i < maxSize; i++) { - y = this.getBlackPointOnSegment(right, down - i, right - i, down); - } - if (y == null) { - throw new NotFoundException(); - } - return this.centerEdges(y, z, x, t); - } - else { - throw new NotFoundException(); - } - } - getBlackPointOnSegment(aX /*float*/, aY /*float*/, bX /*float*/, bY /*float*/) { - const dist = MathUtils.round(MathUtils.distance(aX, aY, bX, bY)); - const xStep = (bX - aX) / dist; - const yStep = (bY - aY) / dist; - const image = this.image; - for (let i = 0; i < dist; i++) { - const x = MathUtils.round(aX + i * xStep); - const y = MathUtils.round(aY + i * yStep); - if (image.get(x, y)) { - return new ResultPoint(x, y); - } - } - return null; - } - /** - * recenters the points of a constant distance towards the center - * - * @param y bottom most point - * @param z left most point - * @param x right most point - * @param t top most point - * @return {@link ResultPoint}[] describing the corners of the rectangular - * region. The first and last points are opposed on the diagonal, as - * are the second and third. The first point will be the topmost - * point and the last, the bottommost. The second point will be - * leftmost and the third, the rightmost - */ - centerEdges(y, z, x, t) { - // - // t t - // z x - // x OR z - // y y - // - const yi = y.getX(); - const yj = y.getY(); - const zi = z.getX(); - const zj = z.getY(); - const xi = x.getX(); - const xj = x.getY(); - const ti = t.getX(); - const tj = t.getY(); - const CORR = WhiteRectangleDetector.CORR; - if (yi < this.width / 2.0) { - return [ - new ResultPoint(ti - CORR, tj + CORR), - new ResultPoint(zi + CORR, zj + CORR), - new ResultPoint(xi - CORR, xj - CORR), - new ResultPoint(yi + CORR, yj - CORR) - ]; - } - else { - return [ - new ResultPoint(ti + CORR, tj + CORR), - new ResultPoint(zi + CORR, zj - CORR), - new ResultPoint(xi - CORR, xj + CORR), - new ResultPoint(yi - CORR, yj - CORR) - ]; - } - } - /** - * Determines whether a segment contains a black point - * - * @param a min value of the scanned coordinate - * @param b max value of the scanned coordinate - * @param fixed value of fixed coordinate - * @param horizontal set to true if scan must be horizontal, false if vertical - * @return true if a black point has been found, else false. - */ - containsBlackPoint(a /*int*/, b /*int*/, fixed /*int*/, horizontal) { - const image = this.image; - if (horizontal) { - for (let x = a; x <= b; x++) { - if (image.get(x, fixed)) { - return true; - } - } - } - else { - for (let y = a; y <= b; y++) { - if (image.get(fixed, y)) { - return true; - } - } - } - return false; - } - } - WhiteRectangleDetector.INIT_SIZE = 10; - WhiteRectangleDetector.CORR = 1; - - /* - * Copyright 2007 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. - */ - /** - * Implementations of this class can, given locations of finder patterns for a QR code in an - * image, sample the right points in the image to reconstruct the QR code, accounting for - * perspective distortion. It is abstracted since it is relatively expensive and should be allowed - * to take advantage of platform-specific optimized implementations, like Sun's Java Advanced - * Imaging library, but which may not be available in other environments such as J2ME, and vice - * versa. - * - * The implementation used can be controlled by calling {@link #setGridSampler(GridSampler)} - * with an instance of a class which implements this interface. - * - * @author Sean Owen - */ - class GridSampler { - /** - *Checks a set of points that have been transformed to sample points on an image against - * the image's dimensions to see if the point are even within the image.
- * - *This method will actually "nudge" the endpoints back onto the image if they are found to be - * barely (less than 1 pixel) off the image. This accounts for imperfect detection of finder - * patterns in an image where the QR Code runs all the way to the image border.
- * - *For efficiency, the method will check points from either end of the line until one is found - * to be within the image. Because the set of points are assumed to be linear, this is valid.
- * - * @param image image into which the points should map - * @param points actual points in x1,y1,...,xn,yn form - * @throws NotFoundException if an endpoint is lies outside the image boundaries - */ - static checkAndNudgePoints(image, points) { - const width = image.getWidth(); - const height = image.getHeight(); - // Check and nudge points from start until we see some that are OK: - let nudged = true; - for (let offset = 0; offset < points.length && nudged; offset += 2) { - const x = Math.floor(points[offset]); - const y = Math.floor(points[offset + 1]); - if (x < -1 || x > width || y < -1 || y > height) { - throw new NotFoundException(); - } - nudged = false; - if (x === -1) { - points[offset] = 0.0; - nudged = true; - } - else if (x === width) { - points[offset] = width - 1; - nudged = true; - } - if (y === -1) { - points[offset + 1] = 0.0; - nudged = true; - } - else if (y === height) { - points[offset + 1] = height - 1; - nudged = true; - } - } - // Check and nudge points from end: - nudged = true; - for (let offset = points.length - 2; offset >= 0 && nudged; offset -= 2) { - const x = Math.floor(points[offset]); - const y = Math.floor(points[offset + 1]); - if (x < -1 || x > width || y < -1 || y > height) { - throw new NotFoundException(); - } - nudged = false; - if (x === -1) { - points[offset] = 0.0; - nudged = true; - } - else if (x === width) { - points[offset] = width - 1; - nudged = true; - } - if (y === -1) { - points[offset + 1] = 0.0; - nudged = true; - } - else if (y === height) { - points[offset + 1] = height - 1; - nudged = true; - } - } - } - } - - /* - * Copyright 2007 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. - */ - /*namespace com.google.zxing.common {*/ - /** - *This class implements a perspective transform in two dimensions. Given four source and four - * destination points, it will compute the transformation implied between them. The code is based - * directly upon section 3.4.2 of George Wolberg's "Digital Image Warping"; see pages 54-56.
- * - * @author Sean Owen - */ - class PerspectiveTransform { - constructor(a11 /*float*/, a21 /*float*/, a31 /*float*/, a12 /*float*/, a22 /*float*/, a32 /*float*/, a13 /*float*/, a23 /*float*/, a33 /*float*/) { - this.a11 = a11; - this.a21 = a21; - this.a31 = a31; - this.a12 = a12; - this.a22 = a22; - this.a32 = a32; - this.a13 = a13; - this.a23 = a23; - this.a33 = a33; - } - static quadrilateralToQuadrilateral(x0 /*float*/, y0 /*float*/, x1 /*float*/, y1 /*float*/, x2 /*float*/, y2 /*float*/, x3 /*float*/, y3 /*float*/, x0p /*float*/, y0p /*float*/, x1p /*float*/, y1p /*float*/, x2p /*float*/, y2p /*float*/, x3p /*float*/, y3p /*float*/) { - const qToS = PerspectiveTransform.quadrilateralToSquare(x0, y0, x1, y1, x2, y2, x3, y3); - const sToQ = PerspectiveTransform.squareToQuadrilateral(x0p, y0p, x1p, y1p, x2p, y2p, x3p, y3p); - return sToQ.times(qToS); - } - transformPoints(points) { - const max = points.length; - const a11 = this.a11; - const a12 = this.a12; - const a13 = this.a13; - const a21 = this.a21; - const a22 = this.a22; - const a23 = this.a23; - const a31 = this.a31; - const a32 = this.a32; - const a33 = this.a33; - for (let i = 0; i < max; i += 2) { - const x = points[i]; - const y = points[i + 1]; - const denominator = a13 * x + a23 * y + a33; - points[i] = (a11 * x + a21 * y + a31) / denominator; - points[i + 1] = (a12 * x + a22 * y + a32) / denominator; - } - } - transformPointsWithValues(xValues, yValues) { - const a11 = this.a11; - const a12 = this.a12; - const a13 = this.a13; - const a21 = this.a21; - const a22 = this.a22; - const a23 = this.a23; - const a31 = this.a31; - const a32 = this.a32; - const a33 = this.a33; - const n = xValues.length; - for (let i = 0; i < n; i++) { - const x = xValues[i]; - const y = yValues[i]; - const denominator = a13 * x + a23 * y + a33; - xValues[i] = (a11 * x + a21 * y + a31) / denominator; - yValues[i] = (a12 * x + a22 * y + a32) / denominator; - } - } - static squareToQuadrilateral(x0 /*float*/, y0 /*float*/, x1 /*float*/, y1 /*float*/, x2 /*float*/, y2 /*float*/, x3 /*float*/, y3 /*float*/) { - const dx3 = x0 - x1 + x2 - x3; - const dy3 = y0 - y1 + y2 - y3; - if (dx3 === 0.0 && dy3 === 0.0) { - // Affine - return new PerspectiveTransform(x1 - x0, x2 - x1, x0, y1 - y0, y2 - y1, y0, 0.0, 0.0, 1.0); - } - else { - const dx1 = x1 - x2; - const dx2 = x3 - x2; - const dy1 = y1 - y2; - const dy2 = y3 - y2; - const denominator = dx1 * dy2 - dx2 * dy1; - const a13 = (dx3 * dy2 - dx2 * dy3) / denominator; - const a23 = (dx1 * dy3 - dx3 * dy1) / denominator; - return new PerspectiveTransform(x1 - x0 + a13 * x1, x3 - x0 + a23 * x3, x0, y1 - y0 + a13 * y1, y3 - y0 + a23 * y3, y0, a13, a23, 1.0); - } - } - static quadrilateralToSquare(x0 /*float*/, y0 /*float*/, x1 /*float*/, y1 /*float*/, x2 /*float*/, y2 /*float*/, x3 /*float*/, y3 /*float*/) { - // Here, the adjoint serves as the inverse: - return PerspectiveTransform.squareToQuadrilateral(x0, y0, x1, y1, x2, y2, x3, y3).buildAdjoint(); - } - buildAdjoint() { - // Adjoint is the transpose of the cofactor matrix: - return new PerspectiveTransform(this.a22 * this.a33 - this.a23 * this.a32, this.a23 * this.a31 - this.a21 * this.a33, this.a21 * this.a32 - this.a22 * this.a31, this.a13 * this.a32 - this.a12 * this.a33, this.a11 * this.a33 - this.a13 * this.a31, this.a12 * this.a31 - this.a11 * this.a32, this.a12 * this.a23 - this.a13 * this.a22, this.a13 * this.a21 - this.a11 * this.a23, this.a11 * this.a22 - this.a12 * this.a21); - } - times(other) { - return new PerspectiveTransform(this.a11 * other.a11 + this.a21 * other.a12 + this.a31 * other.a13, this.a11 * other.a21 + this.a21 * other.a22 + this.a31 * other.a23, this.a11 * other.a31 + this.a21 * other.a32 + this.a31 * other.a33, this.a12 * other.a11 + this.a22 * other.a12 + this.a32 * other.a13, this.a12 * other.a21 + this.a22 * other.a22 + this.a32 * other.a23, this.a12 * other.a31 + this.a22 * other.a32 + this.a32 * other.a33, this.a13 * other.a11 + this.a23 * other.a12 + this.a33 * other.a13, this.a13 * other.a21 + this.a23 * other.a22 + this.a33 * other.a23, this.a13 * other.a31 + this.a23 * other.a32 + this.a33 * other.a33); - } - } - - /* - * Copyright 2007 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. - */ - /** - * @author Sean Owen - */ - class DefaultGridSampler extends GridSampler { - /*@Override*/ - sampleGrid(image, dimensionX /*int*/, dimensionY /*int*/, p1ToX /*float*/, p1ToY /*float*/, p2ToX /*float*/, p2ToY /*float*/, p3ToX /*float*/, p3ToY /*float*/, p4ToX /*float*/, p4ToY /*float*/, p1FromX /*float*/, p1FromY /*float*/, p2FromX /*float*/, p2FromY /*float*/, p3FromX /*float*/, p3FromY /*float*/, p4FromX /*float*/, p4FromY /*float*/) { - const transform = PerspectiveTransform.quadrilateralToQuadrilateral(p1ToX, p1ToY, p2ToX, p2ToY, p3ToX, p3ToY, p4ToX, p4ToY, p1FromX, p1FromY, p2FromX, p2FromY, p3FromX, p3FromY, p4FromX, p4FromY); - return this.sampleGridWithTransform(image, dimensionX, dimensionY, transform); - } - /*@Override*/ - sampleGridWithTransform(image, dimensionX /*int*/, dimensionY /*int*/, transform) { - if (dimensionX <= 0 || dimensionY <= 0) { - throw new NotFoundException(); - } - const bits = new BitMatrix(dimensionX, dimensionY); - const points = new Float32Array(2 * dimensionX); - for (let y = 0; y < dimensionY; y++) { - const max = points.length; - const iValue = y + 0.5; - for (let x = 0; x < max; x += 2) { - points[x] = (x / 2) + 0.5; - points[x + 1] = iValue; - } - transform.transformPoints(points); - // Quick check to see if points transformed to something inside the image - // sufficient to check the endpoints - GridSampler.checkAndNudgePoints(image, points); - try { - for (let x = 0; x < max; x += 2) { - if (image.get(Math.floor(points[x]), Math.floor(points[x + 1]))) { - // Black(-ish) pixel - bits.set(x / 2, y); - } - } - } - catch (aioobe /*: ArrayIndexOutOfBoundsException*/) { - // This feels wrong, but, sometimes if the finder patterns are misidentified, the resulting - // transform gets "twisted" such that it maps a straight line of points to a set of points - // whose endpoints are in bounds, but others are not. There is probably some mathematical - // way to detect this about the transformation that I don't know yet. - // This results in an ugly runtime exception despite our clever checks above -- can't have - // that. We could check each point's coordinates but that feels duplicative. We settle for - // catching and wrapping ArrayIndexOutOfBoundsException. - throw new NotFoundException(); - } - } - return bits; - } - } - - class GridSamplerInstance { - /** - * Sets the implementation of GridSampler used by the library. One global - * instance is stored, which may sound problematic. But, the implementation provided - * ought to be appropriate for the entire platform, and all uses of this library - * in the whole lifetime of the JVM. For instance, an Android activity can swap in - * an implementation that takes advantage of native platform libraries. - * - * @param newGridSampler The platform-specific object to install. - */ - static setGridSampler(newGridSampler) { - GridSamplerInstance.gridSampler = newGridSampler; - } - /** - * @return the current implementation of GridSampler - */ - static getInstance() { - return GridSamplerInstance.gridSampler; - } - } - GridSamplerInstance.gridSampler = new DefaultGridSampler(); - - /* - * 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. - */ - class Point { - constructor(x, y) { - this.x = x; - this.y = y; - } - toResultPoint() { - return new ResultPoint(this.getX(), this.getY()); - } - getX() { - return this.x; - } - getY() { - return this.y; - } - } - /** - * Encapsulates logic that can detect an Aztec Code in an image, even if the Aztec Code - * is rotated or skewed, or partially obscured. - * - * @author David Olivier - * @author Frank Yellin - */ - class Detector { - constructor(image) { - this.EXPECTED_CORNER_BITS = new Int32Array([ - 0xee0, - 0x1dc, - 0x83b, - 0x707, - ]); - this.image = image; - } - detect() { - return this.detectMirror(false); - } - /** - * Detects an Aztec Code in an image. - * - * @param isMirror if true, image is a mirror-image of original - * @return {@link AztecDetectorResult} encapsulating results of detecting an Aztec Code - * @throws NotFoundException if no Aztec Code can be found - */ - detectMirror(isMirror) { - // 1. Get the center of the aztec matrix - let pCenter = this.getMatrixCenter(); - // 2. Get the center points of the four diagonal points just outside the bull's eye - // [topRight, bottomRight, bottomLeft, topLeft] - let bullsEyeCorners = this.getBullsEyeCorners(pCenter); - if (isMirror) { - let temp = bullsEyeCorners[0]; - bullsEyeCorners[0] = bullsEyeCorners[2]; - bullsEyeCorners[2] = temp; - } - // 3. Get the size of the matrix and other parameters from the bull's eye - this.extractParameters(bullsEyeCorners); - // 4. Sample the grid - let bits = this.sampleGrid(this.image, bullsEyeCorners[this.shift % 4], bullsEyeCorners[(this.shift + 1) % 4], bullsEyeCorners[(this.shift + 2) % 4], bullsEyeCorners[(this.shift + 3) % 4]); - // 5. Get the corners of the matrix. - let corners = this.getMatrixCornerPoints(bullsEyeCorners); - return new AztecDetectorResult(bits, corners, this.compact, this.nbDataBlocks, this.nbLayers); - } - /** - * Extracts the number of data layers and data blocks from the layer around the bull's eye. - * - * @param bullsEyeCorners the array of bull's eye corners - * @throws NotFoundException in case of too many errors or invalid parameters - */ - extractParameters(bullsEyeCorners) { - if (!this.isValidPoint(bullsEyeCorners[0]) || !this.isValidPoint(bullsEyeCorners[1]) || - !this.isValidPoint(bullsEyeCorners[2]) || !this.isValidPoint(bullsEyeCorners[3])) { - throw new NotFoundException(); - } - let length = 2 * this.nbCenterLayers; - // Get the bits around the bull's eye - let sides = new Int32Array([ - this.sampleLine(bullsEyeCorners[0], bullsEyeCorners[1], length), - this.sampleLine(bullsEyeCorners[1], bullsEyeCorners[2], length), - this.sampleLine(bullsEyeCorners[2], bullsEyeCorners[3], length), - this.sampleLine(bullsEyeCorners[3], bullsEyeCorners[0], length) // Top - ]); - // bullsEyeCorners[shift] is the corner of the bulls'eye that has three - // orientation marks. - // sides[shift] is the row/column that goes from the corner with three - // orientation marks to the corner with two. - this.shift = this.getRotation(sides, length); - // Flatten the parameter bits into a single 28- or 40-bit long - let parameterData = 0; - for (let i = 0; i < 4; i++) { - let side = sides[(this.shift + i) % 4]; - if (this.compact) { - // Each side of the form ..XXXXXXX. where Xs are parameter data - parameterData <<= 7; - parameterData += (side >> 1) & 0x7F; - } - else { - // Each side of the form ..XXXXX.XXXXX. where Xs are parameter data - parameterData <<= 10; - parameterData += ((side >> 2) & (0x1f << 5)) + ((side >> 1) & 0x1F); - } - } - // Corrects parameter data using RS. Returns just the data portion - // without the error correction. - let correctedData = this.getCorrectedParameterData(parameterData, this.compact); - if (this.compact) { - // 8 bits: 2 bits layers and 6 bits data blocks - this.nbLayers = (correctedData >> 6) + 1; - this.nbDataBlocks = (correctedData & 0x3F) + 1; - } - else { - // 16 bits: 5 bits layers and 11 bits data blocks - this.nbLayers = (correctedData >> 11) + 1; - this.nbDataBlocks = (correctedData & 0x7FF) + 1; - } - } - getRotation(sides, length) { - // In a normal pattern, we expect to See - // ** .* D A - // * * - // - // . * - // .. .. C B - // - // Grab the 3 bits from each of the sides the form the locator pattern and concatenate - // into a 12-bit integer. Start with the bit at A - let cornerBits = 0; - sides.forEach((side, idx, arr) => { - // XX......X where X's are orientation marks - let t = ((side >> (length - 2)) << 1) + (side & 1); - cornerBits = (cornerBits << 3) + t; - }); - // for (var side in sides) { - // // XX......X where X's are orientation marks - // var t = ((side >> (length - 2)) << 1) + (side & 1); - // cornerBits = (cornerBits << 3) + t; - // } - // Mov the bottom bit to the top, so that the three bits of the locator pattern at A are - // together. cornerBits is now: - // 3 orientation bits at A || 3 orientation bits at B || ... || 3 orientation bits at D - cornerBits = ((cornerBits & 1) << 11) + (cornerBits >> 1); - // The result shift indicates which element of BullsEyeCorners[] goes into the top-left - // corner. Since the four rotation values have a Hamming distance of 8, we - // can easily tolerate two errors. - for (let shift = 0; shift < 4; shift++) { - if (Integer.bitCount(cornerBits ^ this.EXPECTED_CORNER_BITS[shift]) <= 2) { - return shift; - } - } - throw new NotFoundException(); - } - /** - * Corrects the parameter bits using Reed-Solomon algorithm. - * - * @param parameterData parameter bits - * @param compact true if this is a compact Aztec code - * @throws NotFoundException if the array contains too many errors - */ - getCorrectedParameterData(parameterData, compact) { - let numCodewords; - let numDataCodewords; - if (compact) { - numCodewords = 7; - numDataCodewords = 2; - } - else { - numCodewords = 10; - numDataCodewords = 4; - } - let numECCodewords = numCodewords - numDataCodewords; - let parameterWords = new Int32Array(numCodewords); - for (let i = numCodewords - 1; i >= 0; --i) { - parameterWords[i] = parameterData & 0xF; - parameterData >>= 4; - } - try { - let rsDecoder = new ReedSolomonDecoder(GenericGF.AZTEC_PARAM); - rsDecoder.decode(parameterWords, numECCodewords); - } - catch (ignored) { - throw new NotFoundException(); - } - // Toss the error correction. Just return the data as an integer - let result = 0; - for (let i = 0; i < numDataCodewords; i++) { - result = (result << 4) + parameterWords[i]; - } - return result; - } - /** - * Finds the corners of a bull-eye centered on the passed point. - * This returns the centers of the diagonal points just outside the bull's eye - * Returns [topRight, bottomRight, bottomLeft, topLeft] - * - * @param pCenter Center point - * @return The corners of the bull-eye - * @throws NotFoundException If no valid bull-eye can be found - */ - getBullsEyeCorners(pCenter) { - let pina = pCenter; - let pinb = pCenter; - let pinc = pCenter; - let pind = pCenter; - let color = true; - for (this.nbCenterLayers = 1; this.nbCenterLayers < 9; this.nbCenterLayers++) { - let pouta = this.getFirstDifferent(pina, color, 1, -1); - let poutb = this.getFirstDifferent(pinb, color, 1, 1); - let poutc = this.getFirstDifferent(pinc, color, -1, 1); - let poutd = this.getFirstDifferent(pind, color, -1, -1); - // d a - // - // c b - if (this.nbCenterLayers > 2) { - let q = (this.distancePoint(poutd, pouta) * this.nbCenterLayers) / (this.distancePoint(pind, pina) * (this.nbCenterLayers + 2)); - if (q < 0.75 || q > 1.25 || !this.isWhiteOrBlackRectangle(pouta, poutb, poutc, poutd)) { - break; - } - } - pina = pouta; - pinb = poutb; - pinc = poutc; - pind = poutd; - color = !color; - } - if (this.nbCenterLayers !== 5 && this.nbCenterLayers !== 7) { - throw new NotFoundException(); - } - this.compact = this.nbCenterLayers === 5; - // Expand the square by .5 pixel in each direction so that we're on the border - // between the white square and the black square - let pinax = new ResultPoint(pina.getX() + 0.5, pina.getY() - 0.5); - let pinbx = new ResultPoint(pinb.getX() + 0.5, pinb.getY() + 0.5); - let pincx = new ResultPoint(pinc.getX() - 0.5, pinc.getY() + 0.5); - let pindx = new ResultPoint(pind.getX() - 0.5, pind.getY() - 0.5); - // Expand the square so that its corners are the centers of the points - // just outside the bull's eye. - return this.expandSquare([pinax, pinbx, pincx, pindx], 2 * this.nbCenterLayers - 3, 2 * this.nbCenterLayers); - } - /** - * Finds a candidate center point of an Aztec code from an image - * - * @return the center point - */ - getMatrixCenter() { - let pointA; - let pointB; - let pointC; - let pointD; - // Get a white rectangle that can be the border of the matrix in center bull's eye or - try { - let cornerPoints = new WhiteRectangleDetector(this.image).detect(); - pointA = cornerPoints[0]; - pointB = cornerPoints[1]; - pointC = cornerPoints[2]; - pointD = cornerPoints[3]; - } - catch (e) { - // This exception can be in case the initial rectangle is white - // In that case, surely in the bull's eye, we try to expand the rectangle. - let cx = this.image.getWidth() / 2; - let cy = this.image.getHeight() / 2; - pointA = this.getFirstDifferent(new Point(cx + 7, cy - 7), false, 1, -1).toResultPoint(); - pointB = this.getFirstDifferent(new Point(cx + 7, cy + 7), false, 1, 1).toResultPoint(); - pointC = this.getFirstDifferent(new Point(cx - 7, cy + 7), false, -1, 1).toResultPoint(); - pointD = this.getFirstDifferent(new Point(cx - 7, cy - 7), false, -1, -1).toResultPoint(); - } - // Compute the center of the rectangle - let cx = MathUtils.round((pointA.getX() + pointD.getX() + pointB.getX() + pointC.getX()) / 4.0); - let cy = MathUtils.round((pointA.getY() + pointD.getY() + pointB.getY() + pointC.getY()) / 4.0); - // Redetermine the white rectangle starting from previously computed center. - // This will ensure that we end up with a white rectangle in center bull's eye - // in order to compute a more accurate center. - try { - let cornerPoints = new WhiteRectangleDetector(this.image, 15, cx, cy).detect(); - pointA = cornerPoints[0]; - pointB = cornerPoints[1]; - pointC = cornerPoints[2]; - pointD = cornerPoints[3]; - } - catch (e) { - // This exception can be in case the initial rectangle is white - // In that case we try to expand the rectangle. - pointA = this.getFirstDifferent(new Point(cx + 7, cy - 7), false, 1, -1).toResultPoint(); - pointB = this.getFirstDifferent(new Point(cx + 7, cy + 7), false, 1, 1).toResultPoint(); - pointC = this.getFirstDifferent(new Point(cx - 7, cy + 7), false, -1, 1).toResultPoint(); - pointD = this.getFirstDifferent(new Point(cx - 7, cy - 7), false, -1, -1).toResultPoint(); - } - // Recompute the center of the rectangle - cx = MathUtils.round((pointA.getX() + pointD.getX() + pointB.getX() + pointC.getX()) / 4.0); - cy = MathUtils.round((pointA.getY() + pointD.getY() + pointB.getY() + pointC.getY()) / 4.0); - return new Point(cx, cy); - } - /** - * Gets the Aztec code corners from the bull's eye corners and the parameters. - * - * @param bullsEyeCorners the array of bull's eye corners - * @return the array of aztec code corners - */ - getMatrixCornerPoints(bullsEyeCorners) { - return this.expandSquare(bullsEyeCorners, 2 * this.nbCenterLayers, this.getDimension()); - } - /** - * Creates a BitMatrix by sampling the provided image. - * topLeft, topRight, bottomRight, and bottomLeft are the centers of the squares on the - * diagonal just outside the bull's eye. - */ - sampleGrid(image, topLeft, topRight, bottomRight, bottomLeft) { - let sampler = GridSamplerInstance.getInstance(); - let dimension = this.getDimension(); - let low = dimension / 2 - this.nbCenterLayers; - let high = dimension / 2 + this.nbCenterLayers; - return sampler.sampleGrid(image, dimension, dimension, low, low, // topleft - high, low, // topright - high, high, // bottomright - low, high, // bottomleft - topLeft.getX(), topLeft.getY(), topRight.getX(), topRight.getY(), bottomRight.getX(), bottomRight.getY(), bottomLeft.getX(), bottomLeft.getY()); - } - /** - * Samples a line. - * - * @param p1 start point (inclusive) - * @param p2 end point (exclusive) - * @param size number of bits - * @return the array of bits as an int (first bit is high-order bit of result) - */ - sampleLine(p1, p2, size) { - let result = 0; - let d = this.distanceResultPoint(p1, p2); - let moduleSize = d / size; - let px = p1.getX(); - let py = p1.getY(); - let dx = moduleSize * (p2.getX() - p1.getX()) / d; - let dy = moduleSize * (p2.getY() - p1.getY()) / d; - for (let i = 0; i < size; i++) { - if (this.image.get(MathUtils.round(px + i * dx), MathUtils.round(py + i * dy))) { - result |= 1 << (size - i - 1); - } - } - return result; - } - /** - * @return true if the border of the rectangle passed in parameter is compound of white points only - * or black points only - */ - isWhiteOrBlackRectangle(p1, p2, p3, p4) { - let corr = 3; - p1 = new Point(p1.getX() - corr, p1.getY() + corr); - p2 = new Point(p2.getX() - corr, p2.getY() - corr); - p3 = new Point(p3.getX() + corr, p3.getY() - corr); - p4 = new Point(p4.getX() + corr, p4.getY() + corr); - let cInit = this.getColor(p4, p1); - if (cInit === 0) { - return false; - } - let c = this.getColor(p1, p2); - if (c !== cInit) { - return false; - } - c = this.getColor(p2, p3); - if (c !== cInit) { - return false; - } - c = this.getColor(p3, p4); - return c === cInit; - } - /** - * Gets the color of a segment - * - * @return 1 if segment more than 90% black, -1 if segment is more than 90% white, 0 else - */ - getColor(p1, p2) { - let d = this.distancePoint(p1, p2); - let dx = (p2.getX() - p1.getX()) / d; - let dy = (p2.getY() - p1.getY()) / d; - let error = 0; - let px = p1.getX(); - let py = p1.getY(); - let colorModel = this.image.get(p1.getX(), p1.getY()); - let iMax = Math.ceil(d); - for (let i = 0; i < iMax; i++) { - px += dx; - py += dy; - if (this.image.get(MathUtils.round(px), MathUtils.round(py)) !== colorModel) { - error++; - } - } - let errRatio = error / d; - if (errRatio > 0.1 && errRatio < 0.9) { - return 0; - } - return (errRatio <= 0.1) === colorModel ? 1 : -1; - } - /** - * Gets the coordinate of the first point with a different color in the given direction - */ - getFirstDifferent(init, color, dx, dy) { - let x = init.getX() + dx; - let y = init.getY() + dy; - while (this.isValid(x, y) && this.image.get(x, y) === color) { - x += dx; - y += dy; - } - x -= dx; - y -= dy; - while (this.isValid(x, y) && this.image.get(x, y) === color) { - x += dx; - } - x -= dx; - while (this.isValid(x, y) && this.image.get(x, y) === color) { - y += dy; - } - y -= dy; - return new Point(x, y); - } - /** - * Expand the square represented by the corner points by pushing out equally in all directions - * - * @param cornerPoints the corners of the square, which has the bull's eye at its center - * @param oldSide the original length of the side of the square in the target bit matrix - * @param newSide the new length of the size of the square in the target bit matrix - * @return the corners of the expanded square - */ - expandSquare(cornerPoints, oldSide, newSide) { - let ratio = newSide / (2.0 * oldSide); - let dx = cornerPoints[0].getX() - cornerPoints[2].getX(); - let dy = cornerPoints[0].getY() - cornerPoints[2].getY(); - let centerx = (cornerPoints[0].getX() + cornerPoints[2].getX()) / 2.0; - let centery = (cornerPoints[0].getY() + cornerPoints[2].getY()) / 2.0; - let result0 = new ResultPoint(centerx + ratio * dx, centery + ratio * dy); - let result2 = new ResultPoint(centerx - ratio * dx, centery - ratio * dy); - dx = cornerPoints[1].getX() - cornerPoints[3].getX(); - dy = cornerPoints[1].getY() - cornerPoints[3].getY(); - centerx = (cornerPoints[1].getX() + cornerPoints[3].getX()) / 2.0; - centery = (cornerPoints[1].getY() + cornerPoints[3].getY()) / 2.0; - let result1 = new ResultPoint(centerx + ratio * dx, centery + ratio * dy); - let result3 = new ResultPoint(centerx - ratio * dx, centery - ratio * dy); - let results = [result0, result1, result2, result3]; - return results; - } - isValid(x, y) { - return x >= 0 && x < this.image.getWidth() && y > 0 && y < this.image.getHeight(); - } - isValidPoint(point) { - let x = MathUtils.round(point.getX()); - let y = MathUtils.round(point.getY()); - return this.isValid(x, y); - } - distancePoint(a, b) { - return MathUtils.distance(a.getX(), a.getY(), b.getX(), b.getY()); - } - distanceResultPoint(a, b) { - return MathUtils.distance(a.getX(), a.getY(), b.getX(), b.getY()); - } - getDimension() { - if (this.compact) { - return 4 * this.nbLayers + 11; - } - if (this.nbLayers <= 4) { - return 4 * this.nbLayers + 15; - } - return 4 * this.nbLayers + 2 * (Integer.truncDivision((this.nbLayers - 4), 8) + 1) + 15; - } - } - - /* - * 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. - */ - // import java.util.List; - // import java.util.Map; - /** - * This implementation can detect and decode Aztec codes in an image. - * - * @author David Olivier - */ - class AztecReader { - /** - * Locates and decodes a Data Matrix code in an image. - * - * @return a String representing the content encoded by the Data Matrix code - * @throws NotFoundException if a Data Matrix code cannot be found - * @throws FormatException if a Data Matrix code cannot be decoded - */ - decode(image, hints = null) { - let exception = null; - let detector = new Detector(image.getBlackMatrix()); - let points = null; - let decoderResult = null; - try { - let detectorResult = detector.detectMirror(false); - points = detectorResult.getPoints(); - this.reportFoundResultPoints(hints, points); - decoderResult = new Decoder().decode(detectorResult); - } - catch (e) { - exception = e; - } - if (decoderResult == null) { - try { - let detectorResult = detector.detectMirror(true); - points = detectorResult.getPoints(); - this.reportFoundResultPoints(hints, points); - decoderResult = new Decoder().decode(detectorResult); - } - catch (e) { - if (exception != null) { - throw exception; - } - throw e; - } - } - let result = new Result(decoderResult.getText(), decoderResult.getRawBytes(), decoderResult.getNumBits(), points, BarcodeFormat$1.AZTEC, System.currentTimeMillis()); - let byteSegments = decoderResult.getByteSegments(); - if (byteSegments != null) { - result.putMetadata(ResultMetadataType$1.BYTE_SEGMENTS, byteSegments); - } - let ecLevel = decoderResult.getECLevel(); - if (ecLevel != null) { - result.putMetadata(ResultMetadataType$1.ERROR_CORRECTION_LEVEL, ecLevel); - } - return result; - } - reportFoundResultPoints(hints, points) { - if (hints != null) { - let rpcb = hints.get(DecodeHintType$1.NEED_RESULT_POINT_CALLBACK); - if (rpcb != null) { - points.forEach((point, idx, arr) => { - rpcb.foundPossibleResultPoint(point); - }); - } - } - } - // @Override - reset() { - // do nothing - } - } - - /** - * Aztec Code reader to use from browser. - * - * @class BrowserAztecCodeReader - * @extends {BrowserCodeReader} - */ - class BrowserAztecCodeReader extends BrowserCodeReader { - /** - * Creates an instance of BrowserAztecCodeReader. - * @param {number} [timeBetweenScansMillis=500] the time delay between subsequent decode tries - * - * @memberOf BrowserAztecCodeReader - */ - constructor(timeBetweenScansMillis = 500) { - super(new AztecReader(), timeBetweenScansMillis); - } - } - - /* - * 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. - */ - /** - * Encapsulates functionality and implementation that is common to all families - * of one-dimensional barcodes. - * - * @author dswitkin@google.com (Daniel Switkin) - * @author Sean Owen - */ - class OneDReader { - /* - @Override - public Result decode(BinaryBitmap image) throws NotFoundException, FormatException { - return decode(image, null); - } - */ - // Note that we don't try rotation without the try harder flag, even if rotation was supported. - // @Override - decode(image, hints) { - try { - return this.doDecode(image, hints); - } - catch (nfe) { - const tryHarder = hints && (hints.get(DecodeHintType$1.TRY_HARDER) === true); - if (tryHarder && image.isRotateSupported()) { - const rotatedImage = image.rotateCounterClockwise(); - const result = this.doDecode(rotatedImage, hints); - // Record that we found it rotated 90 degrees CCW / 270 degrees CW - const metadata = result.getResultMetadata(); - let orientation = 270; - if (metadata !== null && (metadata.get(ResultMetadataType$1.ORIENTATION) === true)) { - // But if we found it reversed in doDecode(), add in that result here: - orientation = (orientation + metadata.get(ResultMetadataType$1.ORIENTATION) % 360); - } - result.putMetadata(ResultMetadataType$1.ORIENTATION, orientation); - // Update result points - const points = result.getResultPoints(); - if (points !== null) { - const height = rotatedImage.getHeight(); - for (let i = 0; i < points.length; i++) { - points[i] = new ResultPoint(height - points[i].getY() - 1, points[i].getX()); - } - } - return result; - } - else { - throw new NotFoundException(); - } - } - } - // @Override - reset() { - // do nothing - } - /** - * We're going to examine rows from the middle outward, searching alternately above and below the - * middle, and farther out each time. rowStep is the number of rows between each successive - * attempt above and below the middle. So we'd scan row middle, then middle - rowStep, then - * middle + rowStep, then middle - (2 * rowStep), etc. - * rowStep is bigger as the image is taller, but is always at least 1. We've somewhat arbitrarily - * decided that moving up and down by about 1/16 of the image is pretty good; we try more of the - * image if "trying harder". - * - * @param image The image to decode - * @param hints Any hints that were requested - * @return The contents of the decoded barcode - * @throws NotFoundException Any spontaneous errors which occur - */ - doDecode(image, hints) { - const width = image.getWidth(); - const height = image.getHeight(); - let row = new BitArray(width); - const tryHarder = hints && (hints.get(DecodeHintType$1.TRY_HARDER) === true); - const rowStep = Math.max(1, height >> (tryHarder ? 8 : 5)); - let maxLines; - if (tryHarder) { - maxLines = height; // Look at the whole image, not just the center - } - else { - maxLines = 15; // 15 rows spaced 1/32 apart is roughly the middle half of the image - } - const middle = Math.trunc(height / 2); - for (let x = 0; x < maxLines; x++) { - // Scanning from the middle out. Determine which row we're looking at next: - const rowStepsAboveOrBelow = Math.trunc((x + 1) / 2); - const isAbove = (x & 0x01) === 0; // i.e. is x even? - const rowNumber = middle + rowStep * (isAbove ? rowStepsAboveOrBelow : -rowStepsAboveOrBelow); - if (rowNumber < 0 || rowNumber >= height) { - // Oops, if we run off the top or bottom, stop - break; - } - // Estimate black point for this row and load it: - try { - row = image.getBlackRow(rowNumber, row); - } - catch (ignored) { - continue; - } - // While we have the image data in a BitArray, it's fairly cheap to reverse it in place to - // handle decoding upside down barcodes. - for (let attempt = 0; attempt < 2; attempt++) { - if (attempt === 1) { // trying again? - row.reverse(); // reverse the row and continue - // This means we will only ever draw result points *once* in the life of this method - // since we want to avoid drawing the wrong points after flipping the row, and, - // don't want to clutter with noise from every single row scan -- just the scans - // that start on the center line. - if (hints && (hints.get(DecodeHintType$1.NEED_RESULT_POINT_CALLBACK) === true)) { - const newHints = new Map(); - hints.forEach((hint, key) => newHints.set(key, hint)); - newHints.delete(DecodeHintType$1.NEED_RESULT_POINT_CALLBACK); - hints = newHints; - } - } - try { - // Look for a barcode - const result = this.decodeRow(rowNumber, row, hints); - // We found our barcode - if (attempt === 1) { - // But it was upside down, so note that - result.putMetadata(ResultMetadataType$1.ORIENTATION, 180); - // And remember to flip the result points horizontally. - const points = result.getResultPoints(); - if (points !== null) { - points[0] = new ResultPoint(width - points[0].getX() - 1, points[0].getY()); - points[1] = new ResultPoint(width - points[1].getX() - 1, points[1].getY()); - } - } - return result; - } - catch (re) { - // continue -- just couldn't decode this row - } - } - } - throw new NotFoundException(); - } - /** - * Records the size of successive runs of white and black pixels in a row, starting at a given point. - * The values are recorded in the given array, and the number of runs recorded is equal to the size - * of the array. If the row starts on a white pixel at the given start point, then the first count - * recorded is the run of white pixels starting from that point; likewise it is the count of a run - * of black pixels if the row begin on a black pixels at that point. - * - * @param row row to count from - * @param start offset into row to start at - * @param counters array into which to record counts - * @throws NotFoundException if counters cannot be filled entirely from row before running out - * of pixels - */ - static recordPattern(row, start, counters) { - const numCounters = counters.length; - for (let index = 0; index < numCounters; index++) - counters[index] = 0; - const end = row.getSize(); - if (start >= end) { - throw new NotFoundException(); - } - let isWhite = !row.get(start); - let counterPosition = 0; - let i = start; - while (i < end) { - if (row.get(i) !== isWhite) { - counters[counterPosition]++; - } - else { - if (++counterPosition === numCounters) { - break; - } - else { - counters[counterPosition] = 1; - isWhite = !isWhite; - } - } - i++; - } - // If we read fully the last section of pixels and filled up our counters -- or filled - // the last counter but ran off the side of the image, OK. Otherwise, a problem. - if (!(counterPosition === numCounters || (counterPosition === numCounters - 1 && i === end))) { - throw new NotFoundException(); - } - } - static recordPatternInReverse(row, start, counters) { - // This could be more efficient I guess - let numTransitionsLeft = counters.length; - let last = row.get(start); - while (start > 0 && numTransitionsLeft >= 0) { - if (row.get(--start) !== last) { - numTransitionsLeft--; - last = !last; - } - } - if (numTransitionsLeft >= 0) { - throw new NotFoundException(); - } - OneDReader.recordPattern(row, start + 1, counters); - } - /** - * Determines how closely a set of observed counts of runs of black/white values matches a given - * target pattern. This is reported as the ratio of the total variance from the expected pattern - * proportions across all pattern elements, to the length of the pattern. - * - * @param counters observed counters - * @param pattern expected pattern - * @param maxIndividualVariance The most any counter can differ before we give up - * @return ratio of total variance between counters and pattern compared to total pattern size - */ - static patternMatchVariance(counters, pattern, maxIndividualVariance) { - const numCounters = counters.length; - let total = 0; - let patternLength = 0; - for (let i = 0; i < numCounters; i++) { - total += counters[i]; - patternLength += pattern[i]; - } - if (total < patternLength) { - // If we don't even have one pixel per unit of bar width, assume this is too small - // to reliably match, so fail: - return Number.POSITIVE_INFINITY; - } - const unitBarWidth = total / patternLength; - maxIndividualVariance *= unitBarWidth; - let totalVariance = 0.0; - for (let x = 0; x < numCounters; x++) { - const counter = counters[x]; - const scaledPattern = pattern[x] * unitBarWidth; - const variance = counter > scaledPattern ? counter - scaledPattern : scaledPattern - counter; - if (variance > maxIndividualVariance) { - return Number.POSITIVE_INFINITY; - } - totalVariance += variance; - } - return totalVariance / total; - } - } - - /* - * 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. - */ - /** - *Decodes Code 128 barcodes.
- * - * @author Sean Owen - */ - class Code128Reader extends OneDReader { - static findStartPattern(row) { - const width = row.getSize(); - const rowOffset = row.getNextSet(0); - let counterPosition = 0; - let counters = Int32Array.from([0, 0, 0, 0, 0, 0]); - let patternStart = rowOffset; - let isWhite = false; - const patternLength = 6; - for (let i = rowOffset; i < width; i++) { - if (row.get(i) !== isWhite) { - counters[counterPosition]++; - } - else { - if (counterPosition === (patternLength - 1)) { - let bestVariance = Code128Reader.MAX_AVG_VARIANCE; - let bestMatch = -1; - for (let startCode = Code128Reader.CODE_START_A; startCode <= Code128Reader.CODE_START_C; startCode++) { - const variance = OneDReader.patternMatchVariance(counters, Code128Reader.CODE_PATTERNS[startCode], Code128Reader.MAX_INDIVIDUAL_VARIANCE); - if (variance < bestVariance) { - bestVariance = variance; - bestMatch = startCode; - } - } - // Look for whitespace before start pattern, >= 50% of width of start pattern - if (bestMatch >= 0 && - row.isRange(Math.max(0, patternStart - (i - patternStart) / 2), patternStart, false)) { - return Int32Array.from([patternStart, i, bestMatch]); - } - patternStart += counters[0] + counters[1]; - counters = counters.slice(2, counters.length - 1); - counters[counterPosition - 1] = 0; - counters[counterPosition] = 0; - counterPosition--; - } - else { - counterPosition++; - } - counters[counterPosition] = 1; - isWhite = !isWhite; - } - } - throw new NotFoundException(); - } - static decodeCode(row, counters, rowOffset) { - OneDReader.recordPattern(row, rowOffset, counters); - let bestVariance = Code128Reader.MAX_AVG_VARIANCE; // worst variance we'll accept - let bestMatch = -1; - for (let d = 0; d < Code128Reader.CODE_PATTERNS.length; d++) { - const pattern = Code128Reader.CODE_PATTERNS[d]; - const variance = this.patternMatchVariance(counters, pattern, Code128Reader.MAX_INDIVIDUAL_VARIANCE); - if (variance < bestVariance) { - bestVariance = variance; - bestMatch = d; - } - } - // TODO We're overlooking the fact that the STOP pattern has 7 values, not 6. - if (bestMatch >= 0) { - return bestMatch; - } - else { - throw new NotFoundException(); - } - } - decodeRow(rowNumber, row, hints) { - const convertFNC1 = hints && (hints.get(DecodeHintType$1.ASSUME_GS1) === true); - const startPatternInfo = Code128Reader.findStartPattern(row); - const startCode = startPatternInfo[2]; - let currentRawCodesIndex = 0; - const rawCodes = new Uint8Array(20); - rawCodes[currentRawCodesIndex++] = startCode; - let codeSet; - switch (startCode) { - case Code128Reader.CODE_START_A: - codeSet = Code128Reader.CODE_CODE_A; - break; - case Code128Reader.CODE_START_B: - codeSet = Code128Reader.CODE_CODE_B; - break; - case Code128Reader.CODE_START_C: - codeSet = Code128Reader.CODE_CODE_C; - break; - default: - throw new FormatException(); - } - let done = false; - let isNextShifted = false; - let result = ''; - let lastStart = startPatternInfo[0]; - let nextStart = startPatternInfo[1]; - const counters = Int32Array.from([0, 0, 0, 0, 0, 0]); - let lastCode = 0; - let code = 0; - let checksumTotal = startCode; - let multiplier = 0; - let lastCharacterWasPrintable = true; - let upperMode = false; - let shiftUpperMode = false; - while (!done) { - const unshift = isNextShifted; - isNextShifted = false; - // Save off last code - lastCode = code; - // Decode another code from image - code = Code128Reader.decodeCode(row, counters, nextStart); - rawCodes[currentRawCodesIndex++] = code; - // Remember whether the last code was printable or not (excluding CODE_STOP) - if (code !== Code128Reader.CODE_STOP) { - lastCharacterWasPrintable = true; - } - // Add to checksum computation (if not CODE_STOP of course) - if (code !== Code128Reader.CODE_STOP) { - multiplier++; - checksumTotal += multiplier * code; - } - // Advance to where the next code will to start - lastStart = nextStart; - nextStart += counters.reduce((previous, current) => previous + current, 0); - // Take care of illegal start codes - switch (code) { - case Code128Reader.CODE_START_A: - case Code128Reader.CODE_START_B: - case Code128Reader.CODE_START_C: - throw new FormatException(); - } - switch (codeSet) { - case Code128Reader.CODE_CODE_A: - if (code < 64) { - if (shiftUpperMode === upperMode) { - result += String.fromCharCode((' '.charCodeAt(0) + code)); - } - else { - result += String.fromCharCode((' '.charCodeAt(0) + code + 128)); - } - shiftUpperMode = false; - } - else if (code < 96) { - if (shiftUpperMode === upperMode) { - result += String.fromCharCode((code - 64)); - } - else { - result += String.fromCharCode((code + 64)); - } - shiftUpperMode = false; - } - else { - // Don't let CODE_STOP, which always appears, affect whether whether we think the last - // code was printable or not. - if (code !== Code128Reader.CODE_STOP) { - lastCharacterWasPrintable = false; - } - switch (code) { - case Code128Reader.CODE_FNC_1: - if (convertFNC1) { - if (result.length === 0) { - // GS1 specification 5.4.3.7. and 5.4.6.4. If the first char after the start code - // is FNC1 then this is GS1-128. We add the symbology identifier. - result += ']C1'; - } - else { - // GS1 specification 5.4.7.5. Every subsequent FNC1 is returned as ASCII 29 (GS) - result += String.fromCharCode(29); - } - } - break; - case Code128Reader.CODE_FNC_2: - case Code128Reader.CODE_FNC_3: - // do nothing? - break; - case Code128Reader.CODE_FNC_4_A: - if (!upperMode && shiftUpperMode) { - upperMode = true; - shiftUpperMode = false; - } - else if (upperMode && shiftUpperMode) { - upperMode = false; - shiftUpperMode = false; - } - else { - shiftUpperMode = true; - } - break; - case Code128Reader.CODE_SHIFT: - isNextShifted = true; - codeSet = Code128Reader.CODE_CODE_B; - break; - case Code128Reader.CODE_CODE_B: - codeSet = Code128Reader.CODE_CODE_B; - break; - case Code128Reader.CODE_CODE_C: - codeSet = Code128Reader.CODE_CODE_C; - break; - case Code128Reader.CODE_STOP: - done = true; - break; - } - } - break; - case Code128Reader.CODE_CODE_B: - if (code < 96) { - if (shiftUpperMode === upperMode) { - result += String.fromCharCode((' '.charCodeAt(0) + code)); - } - else { - result += String.fromCharCode((' '.charCodeAt(0) + code + 128)); - } - shiftUpperMode = false; - } - else { - if (code !== Code128Reader.CODE_STOP) { - lastCharacterWasPrintable = false; - } - switch (code) { - case Code128Reader.CODE_FNC_1: - if (convertFNC1) { - if (result.length === 0) { - // GS1 specification 5.4.3.7. and 5.4.6.4. If the first char after the start code - // is FNC1 then this is GS1-128. We add the symbology identifier. - result += ']C1'; - } - else { - // GS1 specification 5.4.7.5. Every subsequent FNC1 is returned as ASCII 29 (GS) - result += String.fromCharCode(29); - } - } - break; - case Code128Reader.CODE_FNC_2: - case Code128Reader.CODE_FNC_3: - // do nothing? - break; - case Code128Reader.CODE_FNC_4_B: - if (!upperMode && shiftUpperMode) { - upperMode = true; - shiftUpperMode = false; - } - else if (upperMode && shiftUpperMode) { - upperMode = false; - shiftUpperMode = false; - } - else { - shiftUpperMode = true; - } - break; - case Code128Reader.CODE_SHIFT: - isNextShifted = true; - codeSet = Code128Reader.CODE_CODE_A; - break; - case Code128Reader.CODE_CODE_A: - codeSet = Code128Reader.CODE_CODE_A; - break; - case Code128Reader.CODE_CODE_C: - codeSet = Code128Reader.CODE_CODE_C; - break; - case Code128Reader.CODE_STOP: - done = true; - break; - } - } - break; - case Code128Reader.CODE_CODE_C: - if (code < 100) { - if (code < 10) { - result += '0'; - } - result += code; - } - else { - if (code !== Code128Reader.CODE_STOP) { - lastCharacterWasPrintable = false; - } - switch (code) { - case Code128Reader.CODE_FNC_1: - if (convertFNC1) { - if (result.length === 0) { - // GS1 specification 5.4.3.7. and 5.4.6.4. If the first char after the start code - // is FNC1 then this is GS1-128. We add the symbology identifier. - result += ']C1'; - } - else { - // GS1 specification 5.4.7.5. Every subsequent FNC1 is returned as ASCII 29 (GS) - result += String.fromCharCode(29); - } - } - break; - case Code128Reader.CODE_CODE_A: - codeSet = Code128Reader.CODE_CODE_A; - break; - case Code128Reader.CODE_CODE_B: - codeSet = Code128Reader.CODE_CODE_B; - break; - case Code128Reader.CODE_STOP: - done = true; - break; - } - } - break; - } - // Unshift back to another code set if we were shifted - if (unshift) { - codeSet = codeSet === Code128Reader.CODE_CODE_A ? Code128Reader.CODE_CODE_B : Code128Reader.CODE_CODE_A; - } - } - const lastPatternSize = nextStart - lastStart; - // Check for ample whitespace following pattern, but, to do this we first need to remember that - // we fudged decoding CODE_STOP since it actually has 7 bars, not 6. There is a black bar left - // to read off. Would be slightly better to properly read. Here we just skip it: - nextStart = row.getNextUnset(nextStart); - if (!row.isRange(nextStart, Math.min(row.getSize(), nextStart + (nextStart - lastStart) / 2), false)) { - throw new NotFoundException(); - } - // Pull out from sum the value of the penultimate check code - checksumTotal -= multiplier * lastCode; - // lastCode is the checksum then: - if (checksumTotal % 103 !== lastCode) { - throw new ChecksumException(); - } - // Need to pull out the check digits from string - const resultLength = result.length; - if (resultLength === 0) { - // false positive - throw new NotFoundException(); - } - // Only bother if the result had at least one character, and if the checksum digit happened to - // be a printable character. If it was just interpreted as a control code, nothing to remove. - if (resultLength > 0 && lastCharacterWasPrintable) { - if (codeSet === Code128Reader.CODE_CODE_C) { - result = result.substring(0, resultLength - 2); - } - else { - result = result.substring(0, resultLength - 1); - } - } - const left = (startPatternInfo[1] + startPatternInfo[0]) / 2.0; - const right = lastStart + lastPatternSize / 2.0; - const rawCodesSize = rawCodes.length; - const rawBytes = new Uint8Array(rawCodesSize); - for (let i = 0; i < rawCodesSize; i++) { - rawBytes[i] = rawCodes[i]; - } - const points = [new ResultPoint(left, rowNumber), new ResultPoint(right, rowNumber)]; - return new Result(result, rawBytes, 0, points, BarcodeFormat$1.CODE_128, new Date().getTime()); - } - } - Code128Reader.CODE_PATTERNS = [ - Int32Array.from([2, 1, 2, 2, 2, 2]), - Int32Array.from([2, 2, 2, 1, 2, 2]), - Int32Array.from([2, 2, 2, 2, 2, 1]), - Int32Array.from([1, 2, 1, 2, 2, 3]), - Int32Array.from([1, 2, 1, 3, 2, 2]), - Int32Array.from([1, 3, 1, 2, 2, 2]), - Int32Array.from([1, 2, 2, 2, 1, 3]), - Int32Array.from([1, 2, 2, 3, 1, 2]), - Int32Array.from([1, 3, 2, 2, 1, 2]), - Int32Array.from([2, 2, 1, 2, 1, 3]), - Int32Array.from([2, 2, 1, 3, 1, 2]), - Int32Array.from([2, 3, 1, 2, 1, 2]), - Int32Array.from([1, 1, 2, 2, 3, 2]), - Int32Array.from([1, 2, 2, 1, 3, 2]), - Int32Array.from([1, 2, 2, 2, 3, 1]), - Int32Array.from([1, 1, 3, 2, 2, 2]), - Int32Array.from([1, 2, 3, 1, 2, 2]), - Int32Array.from([1, 2, 3, 2, 2, 1]), - Int32Array.from([2, 2, 3, 2, 1, 1]), - Int32Array.from([2, 2, 1, 1, 3, 2]), - Int32Array.from([2, 2, 1, 2, 3, 1]), - Int32Array.from([2, 1, 3, 2, 1, 2]), - Int32Array.from([2, 2, 3, 1, 1, 2]), - Int32Array.from([3, 1, 2, 1, 3, 1]), - Int32Array.from([3, 1, 1, 2, 2, 2]), - Int32Array.from([3, 2, 1, 1, 2, 2]), - Int32Array.from([3, 2, 1, 2, 2, 1]), - Int32Array.from([3, 1, 2, 2, 1, 2]), - Int32Array.from([3, 2, 2, 1, 1, 2]), - Int32Array.from([3, 2, 2, 2, 1, 1]), - Int32Array.from([2, 1, 2, 1, 2, 3]), - Int32Array.from([2, 1, 2, 3, 2, 1]), - Int32Array.from([2, 3, 2, 1, 2, 1]), - Int32Array.from([1, 1, 1, 3, 2, 3]), - Int32Array.from([1, 3, 1, 1, 2, 3]), - Int32Array.from([1, 3, 1, 3, 2, 1]), - Int32Array.from([1, 1, 2, 3, 1, 3]), - Int32Array.from([1, 3, 2, 1, 1, 3]), - Int32Array.from([1, 3, 2, 3, 1, 1]), - Int32Array.from([2, 1, 1, 3, 1, 3]), - Int32Array.from([2, 3, 1, 1, 1, 3]), - Int32Array.from([2, 3, 1, 3, 1, 1]), - Int32Array.from([1, 1, 2, 1, 3, 3]), - Int32Array.from([1, 1, 2, 3, 3, 1]), - Int32Array.from([1, 3, 2, 1, 3, 1]), - Int32Array.from([1, 1, 3, 1, 2, 3]), - Int32Array.from([1, 1, 3, 3, 2, 1]), - Int32Array.from([1, 3, 3, 1, 2, 1]), - Int32Array.from([3, 1, 3, 1, 2, 1]), - Int32Array.from([2, 1, 1, 3, 3, 1]), - Int32Array.from([2, 3, 1, 1, 3, 1]), - Int32Array.from([2, 1, 3, 1, 1, 3]), - Int32Array.from([2, 1, 3, 3, 1, 1]), - Int32Array.from([2, 1, 3, 1, 3, 1]), - Int32Array.from([3, 1, 1, 1, 2, 3]), - Int32Array.from([3, 1, 1, 3, 2, 1]), - Int32Array.from([3, 3, 1, 1, 2, 1]), - Int32Array.from([3, 1, 2, 1, 1, 3]), - Int32Array.from([3, 1, 2, 3, 1, 1]), - Int32Array.from([3, 3, 2, 1, 1, 1]), - Int32Array.from([3, 1, 4, 1, 1, 1]), - Int32Array.from([2, 2, 1, 4, 1, 1]), - Int32Array.from([4, 3, 1, 1, 1, 1]), - Int32Array.from([1, 1, 1, 2, 2, 4]), - Int32Array.from([1, 1, 1, 4, 2, 2]), - Int32Array.from([1, 2, 1, 1, 2, 4]), - Int32Array.from([1, 2, 1, 4, 2, 1]), - Int32Array.from([1, 4, 1, 1, 2, 2]), - Int32Array.from([1, 4, 1, 2, 2, 1]), - Int32Array.from([1, 1, 2, 2, 1, 4]), - Int32Array.from([1, 1, 2, 4, 1, 2]), - Int32Array.from([1, 2, 2, 1, 1, 4]), - Int32Array.from([1, 2, 2, 4, 1, 1]), - Int32Array.from([1, 4, 2, 1, 1, 2]), - Int32Array.from([1, 4, 2, 2, 1, 1]), - Int32Array.from([2, 4, 1, 2, 1, 1]), - Int32Array.from([2, 2, 1, 1, 1, 4]), - Int32Array.from([4, 1, 3, 1, 1, 1]), - Int32Array.from([2, 4, 1, 1, 1, 2]), - Int32Array.from([1, 3, 4, 1, 1, 1]), - Int32Array.from([1, 1, 1, 2, 4, 2]), - Int32Array.from([1, 2, 1, 1, 4, 2]), - Int32Array.from([1, 2, 1, 2, 4, 1]), - Int32Array.from([1, 1, 4, 2, 1, 2]), - Int32Array.from([1, 2, 4, 1, 1, 2]), - Int32Array.from([1, 2, 4, 2, 1, 1]), - Int32Array.from([4, 1, 1, 2, 1, 2]), - Int32Array.from([4, 2, 1, 1, 1, 2]), - Int32Array.from([4, 2, 1, 2, 1, 1]), - Int32Array.from([2, 1, 2, 1, 4, 1]), - Int32Array.from([2, 1, 4, 1, 2, 1]), - Int32Array.from([4, 1, 2, 1, 2, 1]), - Int32Array.from([1, 1, 1, 1, 4, 3]), - Int32Array.from([1, 1, 1, 3, 4, 1]), - Int32Array.from([1, 3, 1, 1, 4, 1]), - Int32Array.from([1, 1, 4, 1, 1, 3]), - Int32Array.from([1, 1, 4, 3, 1, 1]), - Int32Array.from([4, 1, 1, 1, 1, 3]), - Int32Array.from([4, 1, 1, 3, 1, 1]), - Int32Array.from([1, 1, 3, 1, 4, 1]), - Int32Array.from([1, 1, 4, 1, 3, 1]), - Int32Array.from([3, 1, 1, 1, 4, 1]), - Int32Array.from([4, 1, 1, 1, 3, 1]), - Int32Array.from([2, 1, 1, 4, 1, 2]), - Int32Array.from([2, 1, 1, 2, 1, 4]), - Int32Array.from([2, 1, 1, 2, 3, 2]), - Int32Array.from([2, 3, 3, 1, 1, 1, 2]), - ]; - Code128Reader.MAX_AVG_VARIANCE = 0.25; - Code128Reader.MAX_INDIVIDUAL_VARIANCE = 0.7; - Code128Reader.CODE_SHIFT = 98; - Code128Reader.CODE_CODE_C = 99; - Code128Reader.CODE_CODE_B = 100; - Code128Reader.CODE_CODE_A = 101; - Code128Reader.CODE_FNC_1 = 102; - Code128Reader.CODE_FNC_2 = 97; - Code128Reader.CODE_FNC_3 = 96; - Code128Reader.CODE_FNC_4_A = 101; - Code128Reader.CODE_FNC_4_B = 100; - Code128Reader.CODE_START_A = 103; - Code128Reader.CODE_START_B = 104; - Code128Reader.CODE_START_C = 105; - Code128Reader.CODE_STOP = 106; - - /* - * 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. - */ - /** - *Decodes Code 39 barcodes. Supports "Full ASCII Code 39" if USE_CODE_39_EXTENDED_MODE is set.
- * - * @author Sean Owen - * @see Code93Reader - */ - class Code39Reader extends OneDReader { - /** - * Creates a reader that assumes all encoded data is data, and does not treat the final - * character as a check digit. It will not decoded "extended Code 39" sequences. - */ - // public Code39Reader() { - // this(false); - // } - /** - * Creates a reader that can be configured to check the last character as a check digit. - * It will not decoded "extended Code 39" sequences. - * - * @param usingCheckDigit if true, treat the last data character as a check digit, not - * data, and verify that the checksum passes. - */ - // public Code39Reader(boolean usingCheckDigit) { - // this(usingCheckDigit, false); - // } - /** - * Creates a reader that can be configured to check the last character as a check digit, - * or optionally attempt to decode "extended Code 39" sequences that are used to encode - * the full ASCII character set. - * - * @param usingCheckDigit if true, treat the last data character as a check digit, not - * data, and verify that the checksum passes. - * @param extendedMode if true, will attempt to decode extended Code 39 sequences in the - * text. - */ - constructor(usingCheckDigit = false, extendedMode = false) { - super(); - this.usingCheckDigit = usingCheckDigit; - this.extendedMode = extendedMode; - this.decodeRowResult = ''; - this.counters = new Int32Array(9); - } - decodeRow(rowNumber, row, hints) { - let theCounters = this.counters; - theCounters.fill(0); - this.decodeRowResult = ''; - let start = Code39Reader.findAsteriskPattern(row, theCounters); - // Read off white space - let nextStart = row.getNextSet(start[1]); - let end = row.getSize(); - let decodedChar; - let lastStart; - do { - Code39Reader.recordPattern(row, nextStart, theCounters); - let pattern = Code39Reader.toNarrowWidePattern(theCounters); - if (pattern < 0) { - throw new NotFoundException(); - } - decodedChar = Code39Reader.patternToChar(pattern); - this.decodeRowResult += decodedChar; - lastStart = nextStart; - for (let counter of theCounters) { - nextStart += counter; - } - // Read off white space - nextStart = row.getNextSet(nextStart); - } while (decodedChar !== '*'); - this.decodeRowResult = this.decodeRowResult.substring(0, this.decodeRowResult.length - 1); // remove asterisk - // Look for whitespace after pattern: - let lastPatternSize = 0; - for (let counter of theCounters) { - lastPatternSize += counter; - } - let whiteSpaceAfterEnd = nextStart - lastStart - lastPatternSize; - // If 50% of last pattern size, following last pattern, is not whitespace, fail - // (but if it's whitespace to the very end of the image, that's OK) - if (nextStart !== end && (whiteSpaceAfterEnd * 2) < lastPatternSize) { - throw new NotFoundException(); - } - if (this.usingCheckDigit) { - let max = this.decodeRowResult.length - 1; - let total = 0; - for (let i = 0; i < max; i++) { - total += Code39Reader.ALPHABET_STRING.indexOf(this.decodeRowResult.charAt(i)); - } - if (this.decodeRowResult.charAt(max) !== Code39Reader.ALPHABET_STRING.charAt(total % 43)) { - throw new ChecksumException(); - } - this.decodeRowResult = this.decodeRowResult.substring(0, max); - } - if (this.decodeRowResult.length === 0) { - // false positive - throw new NotFoundException(); - } - let resultString; - if (this.extendedMode) { - resultString = Code39Reader.decodeExtended(this.decodeRowResult); - } - else { - resultString = this.decodeRowResult; - } - let left = (start[1] + start[0]) / 2.0; - let right = lastStart + lastPatternSize / 2.0; - return new Result(resultString, null, 0, [new ResultPoint(left, rowNumber), new ResultPoint(right, rowNumber)], BarcodeFormat$1.CODE_39, new Date().getTime()); - } - static findAsteriskPattern(row, counters) { - let width = row.getSize(); - let rowOffset = row.getNextSet(0); - let counterPosition = 0; - let patternStart = rowOffset; - let isWhite = false; - let patternLength = counters.length; - for (let i = rowOffset; i < width; i++) { - if (row.get(i) !== isWhite) { - counters[counterPosition]++; - } - else { - if (counterPosition === patternLength - 1) { - // Look for whitespace before start pattern, >= 50% of width of start pattern - if (this.toNarrowWidePattern(counters) === Code39Reader.ASTERISK_ENCODING && - row.isRange(Math.max(0, patternStart - Math.floor((i - patternStart) / 2)), patternStart, false)) { - return [patternStart, i]; - } - patternStart += counters[0] + counters[1]; - counters.copyWithin(0, 2, 2 + counterPosition - 1); - counters[counterPosition - 1] = 0; - counters[counterPosition] = 0; - counterPosition--; - } - else { - counterPosition++; - } - counters[counterPosition] = 1; - isWhite = !isWhite; - } - } - throw new NotFoundException(); - } - // For efficiency, returns -1 on failure. Not throwing here saved as many as 700 exceptions - // per image when using some of our blackbox images. - static toNarrowWidePattern(counters) { - let numCounters = counters.length; - let maxNarrowCounter = 0; - let wideCounters; - do { - let minCounter = 0x7fffffff; - for (let counter of counters) { - if (counter < minCounter && counter > maxNarrowCounter) { - minCounter = counter; - } - } - maxNarrowCounter = minCounter; - wideCounters = 0; - let totalWideCountersWidth = 0; - let pattern = 0; - for (let i = 0; i < numCounters; i++) { - let counter = counters[i]; - if (counter > maxNarrowCounter) { - pattern |= 1 << (numCounters - 1 - i); - wideCounters++; - totalWideCountersWidth += counter; - } - } - if (wideCounters === 3) { - // Found 3 wide counters, but are they close enough in width? - // We can perform a cheap, conservative check to see if any individual - // counter is more than 1.5 times the average: - for (let i = 0; i < numCounters && wideCounters > 0; i++) { - let counter = counters[i]; - if (counter > maxNarrowCounter) { - wideCounters--; - // totalWideCountersWidth = 3 * average, so this checks if counter >= 3/2 * average - if ((counter * 2) >= totalWideCountersWidth) { - return -1; - } - } - } - return pattern; - } - } while (wideCounters > 3); - return -1; - } - static patternToChar(pattern) { - for (let i = 0; i < Code39Reader.CHARACTER_ENCODINGS.length; i++) { - if (Code39Reader.CHARACTER_ENCODINGS[i] === pattern) { - return Code39Reader.ALPHABET_STRING.charAt(i); - } - } - if (pattern === Code39Reader.ASTERISK_ENCODING) { - return '*'; - } - throw new NotFoundException(); - } - static decodeExtended(encoded) { - let length = encoded.length; - let decoded = ''; - for (let i = 0; i < length; i++) { - let c = encoded.charAt(i); - if (c === '+' || c === '$' || c === '%' || c === '/') { - let next = encoded.charAt(i + 1); - let decodedChar = '\0'; - switch (c) { - case '+': - // +A to +Z map to a to z - if (next >= 'A' && next <= 'Z') { - decodedChar = String.fromCharCode(next.charCodeAt(0) + 32); - } - else { - throw new FormatException(); - } - break; - case '$': - // $A to $Z map to control codes SH to SB - if (next >= 'A' && next <= 'Z') { - decodedChar = String.fromCharCode(next.charCodeAt(0) - 64); - } - else { - throw new FormatException(); - } - break; - case '%': - // %A to %E map to control codes ESC to US - if (next >= 'A' && next <= 'E') { - decodedChar = String.fromCharCode(next.charCodeAt(0) - 38); - } - else if (next >= 'F' && next <= 'J') { - decodedChar = String.fromCharCode(next.charCodeAt(0) - 11); - } - else if (next >= 'K' && next <= 'O') { - decodedChar = String.fromCharCode(next.charCodeAt(0) + 16); - } - else if (next >= 'P' && next <= 'T') { - decodedChar = String.fromCharCode(next.charCodeAt(0) + 43); - } - else if (next === 'U') { - decodedChar = '\0'; - } - else if (next === 'V') { - decodedChar = '@'; - } - else if (next === 'W') { - decodedChar = '`'; - } - else if (next === 'X' || next === 'Y' || next === 'Z') { - decodedChar = '\x7f'; - } - else { - throw new FormatException(); - } - break; - case '/': - // /A to /O map to ! to , and /Z maps to : - if (next >= 'A' && next <= 'O') { - decodedChar = String.fromCharCode(next.charCodeAt(0) - 32); - } - else if (next === 'Z') { - decodedChar = ':'; - } - else { - throw new FormatException(); - } - break; - } - decoded += decodedChar; - // bump up i again since we read two characters - i++; - } - else { - decoded += c; - } - } - return decoded; - } - } - Code39Reader.ALPHABET_STRING = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%'; - /** - * These represent the encodings of characters, as patterns of wide and narrow bars. - * The 9 least-significant bits of each int correspond to the pattern of wide and narrow, - * with 1s representing "wide" and 0s representing narrow. - */ - Code39Reader.CHARACTER_ENCODINGS = [ - 0x034, 0x121, 0x061, 0x160, 0x031, 0x130, 0x070, 0x025, 0x124, 0x064, - 0x109, 0x049, 0x148, 0x019, 0x118, 0x058, 0x00D, 0x10C, 0x04C, 0x01C, - 0x103, 0x043, 0x142, 0x013, 0x112, 0x052, 0x007, 0x106, 0x046, 0x016, - 0x181, 0x0C1, 0x1C0, 0x091, 0x190, 0x0D0, 0x085, 0x184, 0x0C4, 0x0A8, - 0x0A2, 0x08A, 0x02A // /-% - ]; - Code39Reader.ASTERISK_ENCODING = 0x094; - - /* - * 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. - */ - /** - *Decodes ITF barcodes.
- * - * @author Tjieco - */ - class ITFReader extends OneDReader { - constructor() { - // private static W = 3; // Pixel width of a 3x wide line - // private static w = 2; // Pixel width of a 2x wide line - // private static N = 1; // Pixed width of a narrow line - super(...arguments); - // Stores the actual narrow line width of the image being decoded. - this.narrowLineWidth = -1; - } - // See ITFWriter.PATTERNS - /* - - /!** - * Patterns of Wide / Narrow lines to indicate each digit - *!/ - */ - decodeRow(rowNumber, row, hints) { - // Find out where the Middle section (payload) starts & ends - let startRange = this.decodeStart(row); - let endRange = this.decodeEnd(row); - let result = new StringBuilder(); - ITFReader.decodeMiddle(row, startRange[1], endRange[0], result); - let resultString = result.toString(); - let allowedLengths = null; - if (hints != null) { - allowedLengths = hints.get(DecodeHintType$1.ALLOWED_LENGTHS); - } - if (allowedLengths == null) { - allowedLengths = ITFReader.DEFAULT_ALLOWED_LENGTHS; - } - // To avoid false positives with 2D barcodes (and other patterns), make - // an assumption that the decoded string must be a 'standard' length if it's short - let length = resultString.length; - let lengthOK = false; - let maxAllowedLength = 0; - for (let value of allowedLengths) { - if (length === value) { - lengthOK = true; - break; - } - if (value > maxAllowedLength) { - maxAllowedLength = value; - } - } - if (!lengthOK && length > maxAllowedLength) { - lengthOK = true; - } - if (!lengthOK) { - throw new FormatException(); - } - const points = [new ResultPoint(startRange[1], rowNumber), new ResultPoint(endRange[0], rowNumber)]; - let resultReturn = new Result(resultString, null, // no natural byte representation for these barcodes - 0, points, BarcodeFormat$1.ITF, new Date().getTime()); - return resultReturn; - } - /* - /!** - * @param row row of black/white values to search - * @param payloadStart offset of start pattern - * @param resultString {@link StringBuilder} to append decoded chars to - * @throws NotFoundException if decoding could not complete successfully - *!/*/ - static decodeMiddle(row, payloadStart, payloadEnd, resultString) { - // Digits are interleaved in pairs - 5 black lines for one digit, and the - // 5 - // interleaved white lines for the second digit. - // Therefore, need to scan 10 lines and then - // split these into two arrays - let counterDigitPair = new Int32Array(10); // 10 - let counterBlack = new Int32Array(5); // 5 - let counterWhite = new Int32Array(5); // 5 - counterDigitPair.fill(0); - counterBlack.fill(0); - counterWhite.fill(0); - while (payloadStart < payloadEnd) { - // Get 10 runs of black/white. - OneDReader.recordPattern(row, payloadStart, counterDigitPair); - // Split them into each array - for (let k = 0; k < 5; k++) { - let twoK = 2 * k; - counterBlack[k] = counterDigitPair[twoK]; - counterWhite[k] = counterDigitPair[twoK + 1]; - } - let bestMatch = ITFReader.decodeDigit(counterBlack); - resultString.append(bestMatch.toString()); - bestMatch = this.decodeDigit(counterWhite); - resultString.append(bestMatch.toString()); - counterDigitPair.forEach(function (counterDigit) { - payloadStart += counterDigit; - }); - } - } - /*/!** - * Identify where the start of the middle / payload section starts. - * - * @param row row of black/white values to search - * @return Array, containing index of start of 'start block' and end of - * 'start block' - *!/*/ - decodeStart(row) { - let endStart = ITFReader.skipWhiteSpace(row); - let startPattern = ITFReader.findGuardPattern(row, endStart, ITFReader.START_PATTERN); - // Determine the width of a narrow line in pixels. We can do this by - // getting the width of the start pattern and dividing by 4 because its - // made up of 4 narrow lines. - this.narrowLineWidth = (startPattern[1] - startPattern[0]) / 4; - this.validateQuietZone(row, startPattern[0]); - return startPattern; - } - /*/!** - * The start & end patterns must be pre/post fixed by a quiet zone. This - * zone must be at least 10 times the width of a narrow line. Scan back until - * we either get to the start of the barcode or match the necessary number of - * quiet zone pixels. - * - * Note: Its assumed the row is reversed when using this method to find - * quiet zone after the end pattern. - * - * ref: http://www.barcode-1.net/i25code.html - * - * @param row bit array representing the scanned barcode. - * @param startPattern index into row of the start or end pattern. - * @throws NotFoundException if the quiet zone cannot be found - *!/*/ - validateQuietZone(row, startPattern) { - let quietCount = this.narrowLineWidth * 10; // expect to find this many pixels of quiet zone - // if there are not so many pixel at all let's try as many as possible - quietCount = quietCount < startPattern ? quietCount : startPattern; - for (let i = startPattern - 1; quietCount > 0 && i >= 0; i--) { - if (row.get(i)) { - break; - } - quietCount--; - } - if (quietCount !== 0) { - // Unable to find the necessary number of quiet zone pixels. - throw new NotFoundException(); - } - } - /* - /!** - * Skip all whitespace until we get to the first black line. - * - * @param row row of black/white values to search - * @return index of the first black line. - * @throws NotFoundException Throws exception if no black lines are found in the row - *!/*/ - static skipWhiteSpace(row) { - const width = row.getSize(); - const endStart = row.getNextSet(0); - if (endStart === width) { - throw new NotFoundException(); - } - return endStart; - } - /*/!** - * Identify where the end of the middle / payload section ends. - * - * @param row row of black/white values to search - * @return Array, containing index of start of 'end block' and end of 'end - * block' - *!/*/ - decodeEnd(row) { - // For convenience, reverse the row and then - // search from 'the start' for the end block - row.reverse(); - try { - let endStart = ITFReader.skipWhiteSpace(row); - let endPattern; - try { - endPattern = ITFReader.findGuardPattern(row, endStart, ITFReader.END_PATTERN_REVERSED[0]); - } - catch (error) { - if (error instanceof NotFoundException) { - endPattern = ITFReader.findGuardPattern(row, endStart, ITFReader.END_PATTERN_REVERSED[1]); - } - } - // The start & end patterns must be pre/post fixed by a quiet zone. This - // zone must be at least 10 times the width of a narrow line. - // ref: http://www.barcode-1.net/i25code.html - this.validateQuietZone(row, endPattern[0]); - // Now recalculate the indices of where the 'endblock' starts & stops to - // accommodate - // the reversed nature of the search - let temp = endPattern[0]; - endPattern[0] = row.getSize() - endPattern[1]; - endPattern[1] = row.getSize() - temp; - return endPattern; - } - finally { - // Put the row back the right way. - row.reverse(); - } - } - /* - /!** - * @param row row of black/white values to search - * @param rowOffset position to start search - * @param pattern pattern of counts of number of black and white pixels that are - * being searched for as a pattern - * @return start/end horizontal offset of guard pattern, as an array of two - * ints - * @throws NotFoundException if pattern is not found - *!/*/ - static findGuardPattern(row, rowOffset, pattern) { - let patternLength = pattern.length; - let counters = new Int32Array(patternLength); - let width = row.getSize(); - let isWhite = false; - let counterPosition = 0; - let patternStart = rowOffset; - counters.fill(0); - for (let x = rowOffset; x < width; x++) { - if (row.get(x) !== isWhite) { - counters[counterPosition]++; - } - else { - if (counterPosition === patternLength - 1) { - if (OneDReader.patternMatchVariance(counters, pattern, ITFReader.MAX_INDIVIDUAL_VARIANCE) < ITFReader.MAX_AVG_VARIANCE) { - return [patternStart, x]; - } - patternStart += counters[0] + counters[1]; - System.arraycopy(counters, 2, counters, 0, counterPosition - 1); - counters[counterPosition - 1] = 0; - counters[counterPosition] = 0; - counterPosition--; - } - else { - counterPosition++; - } - counters[counterPosition] = 1; - isWhite = !isWhite; - } - } - throw new NotFoundException(); - } - /*/!** - * Attempts to decode a sequence of ITF black/white lines into single - * digit. - * - * @param counters the counts of runs of observed black/white/black/... values - * @return The decoded digit - * @throws NotFoundException if digit cannot be decoded - *!/*/ - static decodeDigit(counters) { - let bestVariance = ITFReader.MAX_AVG_VARIANCE; // worst variance we'll accept - let bestMatch = -1; - let max = ITFReader.PATTERNS.length; - for (let i = 0; i < max; i++) { - let pattern = ITFReader.PATTERNS[i]; - let variance = OneDReader.patternMatchVariance(counters, pattern, ITFReader.MAX_INDIVIDUAL_VARIANCE); - if (variance < bestVariance) { - bestVariance = variance; - bestMatch = i; - } - else if (variance === bestVariance) { - // if we find a second 'best match' with the same variance, we can not reliably report to have a suitable match - bestMatch = -1; - } - } - if (bestMatch >= 0) { - return bestMatch % 10; - } - else { - throw new NotFoundException(); - } - } - } - ITFReader.PATTERNS = [ - Int32Array.from([1, 1, 2, 2, 1]), - Int32Array.from([2, 1, 1, 1, 2]), - Int32Array.from([1, 2, 1, 1, 2]), - Int32Array.from([2, 2, 1, 1, 1]), - Int32Array.from([1, 1, 2, 1, 2]), - Int32Array.from([2, 1, 2, 1, 1]), - Int32Array.from([1, 2, 2, 1, 1]), - Int32Array.from([1, 1, 1, 2, 2]), - Int32Array.from([2, 1, 1, 2, 1]), - Int32Array.from([1, 2, 1, 2, 1]), - Int32Array.from([1, 1, 3, 3, 1]), - Int32Array.from([3, 1, 1, 1, 3]), - Int32Array.from([1, 3, 1, 1, 3]), - Int32Array.from([3, 3, 1, 1, 1]), - Int32Array.from([1, 1, 3, 1, 3]), - Int32Array.from([3, 1, 3, 1, 1]), - Int32Array.from([1, 3, 3, 1, 1]), - Int32Array.from([1, 1, 1, 3, 3]), - Int32Array.from([3, 1, 1, 3, 1]), - Int32Array.from([1, 3, 1, 3, 1]) // 9 - ]; - ITFReader.MAX_AVG_VARIANCE = 0.38; - ITFReader.MAX_INDIVIDUAL_VARIANCE = 0.5; - /* /!** Valid ITF lengths. Anything longer than the largest value is also allowed. *!/*/ - ITFReader.DEFAULT_ALLOWED_LENGTHS = [6, 8, 10, 12, 14]; - /*/!** - * Start/end guard pattern. - * - * Note: The end pattern is reversed because the row is reversed before - * searching for the END_PATTERN - *!/*/ - ITFReader.START_PATTERN = Int32Array.from([1, 1, 1, 1]); - ITFReader.END_PATTERN_REVERSED = [ - Int32Array.from([1, 1, 2]), - Int32Array.from([1, 1, 3]) // 3x - ]; - - /* - * 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. - */ - /** - *Encapsulates functionality and implementation that is common to UPC and EAN families - * of one-dimensional barcodes.
- * - * @author dswitkin@google.com (Daniel Switkin) - * @author Sean Owen - * @author alasdair@google.com (Alasdair Mackintosh) - */ - class AbstractUPCEANReader extends OneDReader { - constructor() { - super(...arguments); - this.decodeRowStringBuffer = ''; - } - // private final UPCEANExtensionSupport extensionReader; - // private final EANManufacturerOrgSupport eanManSupport; - /* - protected UPCEANReader() { - decodeRowStringBuffer = new StringBuilder(20); - extensionReader = new UPCEANExtensionSupport(); - eanManSupport = new EANManufacturerOrgSupport(); - } - */ - static findStartGuardPattern(row) { - let foundStart = false; - let startRange; - let nextStart = 0; - let counters = Int32Array.from([0, 0, 0]); - while (!foundStart) { - counters = Int32Array.from([0, 0, 0]); - startRange = AbstractUPCEANReader.findGuardPattern(row, nextStart, false, this.START_END_PATTERN, counters); - let start = startRange[0]; - nextStart = startRange[1]; - let quietStart = start - (nextStart - start); - if (quietStart >= 0) { - foundStart = row.isRange(quietStart, start, false); - } - } - return startRange; - } - static checkChecksum(s) { - return AbstractUPCEANReader.checkStandardUPCEANChecksum(s); - } - static checkStandardUPCEANChecksum(s) { - let length = s.length; - if (length === 0) - return false; - let check = parseInt(s.charAt(length - 1), 10); - return AbstractUPCEANReader.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 AbstractUPCEANReader.findGuardPattern(row, endStart, false, AbstractUPCEANReader.START_END_PATTERN, new Int32Array(AbstractUPCEANReader.START_END_PATTERN.length).fill(0)); - } - /** - * @throws NotFoundException - */ - static findGuardPatternWithoutCounters(row, rowOffset, whiteFirst, pattern) { - return this.findGuardPattern(row, rowOffset, whiteFirst, pattern, new Int32Array(pattern.length)); - } - /** - * @param row row of black/white values to search - * @param rowOffset position to start search - * @param whiteFirst if true, indicates that the pattern specifies white/black/white/... - * pixel counts, otherwise, it is interpreted as black/white/black/... - * @param pattern pattern of counts of number of black and white pixels that are being - * searched for as a pattern - * @param counters array of counters, as long as pattern, to re-use - * @return start/end horizontal offset of guard pattern, as an array of two ints - * @throws NotFoundException if pattern is not found - */ - static findGuardPattern(row, rowOffset, whiteFirst, pattern, counters) { - let width = row.getSize(); - rowOffset = whiteFirst ? row.getNextUnset(rowOffset) : row.getNextSet(rowOffset); - let counterPosition = 0; - let patternStart = rowOffset; - let patternLength = pattern.length; - let isWhite = whiteFirst; - for (let x = rowOffset; x < width; x++) { - if (row.get(x) !== isWhite) { - counters[counterPosition]++; - } - else { - if (counterPosition === patternLength - 1) { - if (OneDReader.patternMatchVariance(counters, pattern, AbstractUPCEANReader.MAX_INDIVIDUAL_VARIANCE) < AbstractUPCEANReader.MAX_AVG_VARIANCE) { - return Int32Array.from([patternStart, x]); - } - patternStart += counters[0] + counters[1]; - let slice = counters.slice(2, counters.length - 1); - for (let i = 0; i < counterPosition - 1; i++) { - counters[i] = slice[i]; - } - counters[counterPosition - 1] = 0; - counters[counterPosition] = 0; - counterPosition--; - } - else { - counterPosition++; - } - counters[counterPosition] = 1; - isWhite = !isWhite; - } - } - throw new NotFoundException(); - } - static decodeDigit(row, counters, rowOffset, patterns) { - this.recordPattern(row, rowOffset, counters); - let bestVariance = this.MAX_AVG_VARIANCE; - let bestMatch = -1; - let max = patterns.length; - for (let i = 0; i < max; i++) { - let pattern = patterns[i]; - let variance = OneDReader.patternMatchVariance(counters, pattern, AbstractUPCEANReader.MAX_INDIVIDUAL_VARIANCE); - if (variance < bestVariance) { - bestVariance = variance; - bestMatch = i; - } - } - if (bestMatch >= 0) { - return bestMatch; - } - else { - throw new NotFoundException(); - } - } - } - // These two values are critical for determining how permissive the decoding will be. - // We've arrived at these values through a lot of trial and error. Setting them any higher - // lets false positives creep in quickly. - AbstractUPCEANReader.MAX_AVG_VARIANCE = 0.48; - AbstractUPCEANReader.MAX_INDIVIDUAL_VARIANCE = 0.7; - /** - * Start/end guard pattern. - */ - AbstractUPCEANReader.START_END_PATTERN = Int32Array.from([1, 1, 1]); - /** - * Pattern marking the middle of a UPC/EAN pattern, separating the two halves. - */ - AbstractUPCEANReader.MIDDLE_PATTERN = Int32Array.from([1, 1, 1, 1, 1]); - /** - * end guard pattern. - */ - AbstractUPCEANReader.END_PATTERN = Int32Array.from([1, 1, 1, 1, 1, 1]); - /** - * "Odd", or "L" patterns used to encode UPC/EAN digits. - */ - AbstractUPCEANReader.L_PATTERNS = [ - Int32Array.from([3, 2, 1, 1]), - Int32Array.from([2, 2, 2, 1]), - Int32Array.from([2, 1, 2, 2]), - Int32Array.from([1, 4, 1, 1]), - Int32Array.from([1, 1, 3, 2]), - Int32Array.from([1, 2, 3, 1]), - Int32Array.from([1, 1, 1, 4]), - Int32Array.from([1, 3, 1, 2]), - Int32Array.from([1, 2, 1, 3]), - Int32Array.from([3, 1, 1, 2]), - ]; - - /* - * Copyright (C) 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. - */ - /** - * @see UPCEANExtension2Support - */ - class UPCEANExtension5Support { - constructor() { - this.CHECK_DIGIT_ENCODINGS = [0x18, 0x14, 0x12, 0x11, 0x0C, 0x06, 0x03, 0x0A, 0x09, 0x05]; - this.decodeMiddleCounters = Int32Array.from([0, 0, 0, 0]); - this.decodeRowStringBuffer = ''; - } - decodeRow(rowNumber, row, extensionStartRange) { - let result = this.decodeRowStringBuffer; - let end = this.decodeMiddle(row, extensionStartRange, result); - let resultString = result.toString(); - let extensionData = UPCEANExtension5Support.parseExtensionString(resultString); - let resultPoints = [ - new ResultPoint((extensionStartRange[0] + extensionStartRange[1]) / 2.0, rowNumber), - new ResultPoint(end, rowNumber) - ]; - let extensionResult = new Result(resultString, null, 0, resultPoints, BarcodeFormat$1.UPC_EAN_EXTENSION, new Date().getTime()); - if (extensionData != null) { - extensionResult.putAllMetadata(extensionData); - } - return extensionResult; - } - decodeMiddle(row, startRange, resultString) { - let counters = this.decodeMiddleCounters; - counters[0] = 0; - counters[1] = 0; - counters[2] = 0; - counters[3] = 0; - let end = row.getSize(); - let rowOffset = startRange[1]; - let lgPatternFound = 0; - for (let x = 0; x < 5 && rowOffset < end; x++) { - let bestMatch = AbstractUPCEANReader.decodeDigit(row, counters, rowOffset, AbstractUPCEANReader.L_AND_G_PATTERNS); - resultString += String.fromCharCode(('0'.charCodeAt(0) + bestMatch % 10)); - for (let counter of counters) { - rowOffset += counter; - } - if (bestMatch >= 10) { - lgPatternFound |= 1 << (4 - x); - } - if (x !== 4) { - // Read off separator if not last - rowOffset = row.getNextSet(rowOffset); - rowOffset = row.getNextUnset(rowOffset); - } - } - if (resultString.length !== 5) { - throw new NotFoundException(); - } - let checkDigit = this.determineCheckDigit(lgPatternFound); - if (UPCEANExtension5Support.extensionChecksum(resultString.toString()) !== checkDigit) { - throw new NotFoundException(); - } - return rowOffset; - } - static extensionChecksum(s) { - let length = s.length; - let sum = 0; - for (let i = length - 2; i >= 0; i -= 2) { - sum += s.charAt(i).charCodeAt(0) - '0'.charCodeAt(0); - } - sum *= 3; - for (let i = length - 1; i >= 0; i -= 2) { - sum += s.charAt(i).charCodeAt(0) - '0'.charCodeAt(0); - } - sum *= 3; - return sum % 10; - } - determineCheckDigit(lgPatternFound) { - for (let d = 0; d < 10; d++) { - if (lgPatternFound === this.CHECK_DIGIT_ENCODINGS[d]) { - return d; - } - } - throw new NotFoundException(); - } - static parseExtensionString(raw) { - if (raw.length !== 5) { - return null; - } - let value = UPCEANExtension5Support.parseExtension5String(raw); - if (value == null) { - return null; - } - return new Map([[ResultMetadataType$1.SUGGESTED_PRICE, value]]); - } - static parseExtension5String(raw) { - let currency; - switch (raw.charAt(0)) { - case '0': - currency = '£'; - break; - case '5': - currency = '$'; - break; - case '9': - // Reference: http://www.jollytech.com - switch (raw) { - case '90000': - // No suggested retail price - return null; - case '99991': - // Complementary - return '0.00'; - case '99990': - return 'Used'; - } - // Otherwise... unknown currency? - currency = ''; - break; - default: - currency = ''; - break; - } - let rawAmount = parseInt(raw.substring(1)); - let unitsString = (rawAmount / 100).toString(); - let hundredths = rawAmount % 100; - let hundredthsString = hundredths < 10 ? '0' + hundredths : hundredths.toString(); // fixme - return currency + unitsString + '.' + hundredthsString; - } - } - - /* - * Copyright (C) 2012 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. - */ - /** - * @see UPCEANExtension5Support - */ - class UPCEANExtension2Support { - constructor() { - this.decodeMiddleCounters = Int32Array.from([0, 0, 0, 0]); - this.decodeRowStringBuffer = ''; - } - decodeRow(rowNumber, row, extensionStartRange) { - let result = this.decodeRowStringBuffer; - let end = this.decodeMiddle(row, extensionStartRange, result); - let resultString = result.toString(); - let extensionData = UPCEANExtension2Support.parseExtensionString(resultString); - let resultPoints = [ - new ResultPoint((extensionStartRange[0] + extensionStartRange[1]) / 2.0, rowNumber), - new ResultPoint(end, rowNumber) - ]; - let extensionResult = new Result(resultString, null, 0, resultPoints, BarcodeFormat$1.UPC_EAN_EXTENSION, new Date().getTime()); - if (extensionData != null) { - extensionResult.putAllMetadata(extensionData); - } - return extensionResult; - } - decodeMiddle(row, startRange, resultString) { - let counters = this.decodeMiddleCounters; - counters[0] = 0; - counters[1] = 0; - counters[2] = 0; - counters[3] = 0; - let end = row.getSize(); - let rowOffset = startRange[1]; - let checkParity = 0; - for (let x = 0; x < 2 && rowOffset < end; x++) { - let bestMatch = AbstractUPCEANReader.decodeDigit(row, counters, rowOffset, AbstractUPCEANReader.L_AND_G_PATTERNS); - resultString += String.fromCharCode(('0'.charCodeAt(0) + bestMatch % 10)); - for (let counter of counters) { - rowOffset += counter; - } - if (bestMatch >= 10) { - checkParity |= 1 << (1 - x); - } - if (x !== 1) { - // Read off separator if not last - rowOffset = row.getNextSet(rowOffset); - rowOffset = row.getNextUnset(rowOffset); - } - } - if (resultString.length !== 2) { - throw new NotFoundException(); - } - if (parseInt(resultString.toString()) % 4 !== checkParity) { - throw new NotFoundException(); - } - return rowOffset; - } - static parseExtensionString(raw) { - if (raw.length !== 2) { - return null; - } - return new Map([[ResultMetadataType$1.ISSUE_NUMBER, parseInt(raw)]]); - } - } - - /* - * Copyright (C) 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. - */ - class UPCEANExtensionSupport { - static decodeRow(rowNumber, row, rowOffset) { - let extensionStartRange = AbstractUPCEANReader.findGuardPattern(row, rowOffset, false, this.EXTENSION_START_PATTERN, new Int32Array(this.EXTENSION_START_PATTERN.length).fill(0)); - try { - // return null; - let fiveSupport = new UPCEANExtension5Support(); - return fiveSupport.decodeRow(rowNumber, row, extensionStartRange); - } - catch (err) { - // return null; - let twoSupport = new UPCEANExtension2Support(); - return twoSupport.decodeRow(rowNumber, row, extensionStartRange); - } - } - } - UPCEANExtensionSupport.EXTENSION_START_PATTERN = Int32Array.from([1, 1, 2]); - - /* - * 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. - */ - /** - *Encapsulates functionality and implementation that is common to UPC and EAN families - * of one-dimensional barcodes.
- * - * @author dswitkin@google.com (Daniel Switkin) - * @author Sean Owen - * @author alasdair@google.com (Alasdair Mackintosh) - */ - 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$1.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$1.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$1.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$1.EAN_13 || format === BarcodeFormat$1.UPC_A) ; - 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)); - } - } - - /* - * 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. - */ - /** - *Implements decoding of the EAN-13 format.
- * - * @author dswitkin@google.com (Daniel Switkin) - * @author Sean Owen - * @author alasdair@google.com (Alasdair Mackintosh) - */ - class EAN13Reader extends UPCEANReader { - constructor() { - super(); - this.decodeMiddleCounters = Int32Array.from([0, 0, 0, 0]); - } - decodeMiddle(row, startRange, resultString) { - let counters = this.decodeMiddleCounters; - counters[0] = 0; - counters[1] = 0; - counters[2] = 0; - counters[3] = 0; - let end = row.getSize(); - let rowOffset = startRange[1]; - let lgPatternFound = 0; - for (let x = 0; x < 6 && rowOffset < end; x++) { - let bestMatch = UPCEANReader.decodeDigit(row, counters, rowOffset, UPCEANReader.L_AND_G_PATTERNS); - resultString += String.fromCharCode(('0'.charCodeAt(0) + bestMatch % 10)); - for (let counter of counters) { - rowOffset += counter; - } - if (bestMatch >= 10) { - lgPatternFound |= 1 << (5 - x); - } - } - resultString = EAN13Reader.determineFirstDigit(resultString, lgPatternFound); - let middleRange = UPCEANReader.findGuardPattern(row, rowOffset, true, UPCEANReader.MIDDLE_PATTERN, new Int32Array(UPCEANReader.MIDDLE_PATTERN.length).fill(0)); - rowOffset = middleRange[1]; - for (let x = 0; x < 6 && rowOffset < end; x++) { - let bestMatch = UPCEANReader.decodeDigit(row, counters, rowOffset, UPCEANReader.L_PATTERNS); - resultString += String.fromCharCode(('0'.charCodeAt(0) + bestMatch)); - for (let counter of counters) { - rowOffset += counter; - } - } - return { rowOffset, resultString }; - } - getBarcodeFormat() { - return BarcodeFormat$1.EAN_13; - } - static determineFirstDigit(resultString, lgPatternFound) { - for (let d = 0; d < 10; d++) { - if (lgPatternFound === this.FIRST_DIGIT_ENCODINGS[d]) { - resultString = String.fromCharCode(('0'.charCodeAt(0) + d)) + resultString; - return resultString; - } - } - throw new NotFoundException(); - } - } - EAN13Reader.FIRST_DIGIT_ENCODINGS = [0x00, 0x0B, 0x0D, 0xE, 0x13, 0x19, 0x1C, 0x15, 0x16, 0x1A]; - - /* - * 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. - */ - /** - *Implements decoding of the EAN-8 format.
- * - * @author Sean Owen - */ - class EAN8Reader extends UPCEANReader { - constructor() { - super(); - this.decodeMiddleCounters = Int32Array.from([0, 0, 0, 0]); - } - decodeMiddle(row, startRange, resultString) { - const counters = this.decodeMiddleCounters; - counters[0] = 0; - counters[1] = 0; - counters[2] = 0; - counters[3] = 0; - let end = row.getSize(); - let rowOffset = startRange[1]; - for (let x = 0; x < 4 && rowOffset < end; x++) { - let bestMatch = UPCEANReader.decodeDigit(row, counters, rowOffset, UPCEANReader.L_PATTERNS); - resultString += String.fromCharCode(('0'.charCodeAt(0) + bestMatch)); - for (let counter of counters) { - rowOffset += counter; - } - } - let middleRange = UPCEANReader.findGuardPattern(row, rowOffset, true, UPCEANReader.MIDDLE_PATTERN, new Int32Array(UPCEANReader.MIDDLE_PATTERN.length).fill(0)); - rowOffset = middleRange[1]; - for (let x = 0; x < 4 && rowOffset < end; x++) { - let bestMatch = UPCEANReader.decodeDigit(row, counters, rowOffset, UPCEANReader.L_PATTERNS); - resultString += String.fromCharCode(('0'.charCodeAt(0) + bestMatch)); - for (let counter of counters) { - rowOffset += counter; - } - } - return { rowOffset, resultString }; - } - getBarcodeFormat() { - return BarcodeFormat$1.EAN_8; - } - } - - /* - * 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. - */ - /** - * Encapsulates functionality and implementation that is common to all families - * of one-dimensional barcodes. - * - * @author dswitkin@google.com (Daniel Switkin) - * @author Sean Owen - * @author sam2332 (Sam Rudloff) - * - * @source https://github.com/zxing/zxing/blob/3c96923276dd5785d58eb970b6ba3f80d36a9505/core/src/main/java/com/google/zxing/oned/UPCAReader.java - * - * @experimental - */ - class UPCAReader extends UPCEANReader { - constructor() { - super(...arguments); - this.ean13Reader = new EAN13Reader(); - } - // @Override - getBarcodeFormat() { - return BarcodeFormat$1.UPC_A; - } - // Note that we don't try rotation without the try harder flag, even if rotation was supported. - // @Override - decode(image, hints) { - return this.maybeReturnResult(this.ean13Reader.decode(image)); - } - // @Override - decodeRow(rowNumber, row, hints) { - return this.maybeReturnResult(this.ean13Reader.decodeRow(rowNumber, row, hints)); - } - // @Override - decodeMiddle(row, startRange, resultString) { - return this.ean13Reader.decodeMiddle(row, startRange, resultString); - } - maybeReturnResult(result) { - let text = result.getText(); - if (text.charAt(0) === '0') { - let upcaResult = new Result(text.substring(1), null, null, result.getResultPoints(), BarcodeFormat$1.UPC_A); - if (result.getResultMetadata() != null) { - upcaResult.putAllMetadata(result.getResultMetadata()); - } - return upcaResult; - } - else { - throw new NotFoundException(); - } - } - reset() { - this.ean13Reader.reset(); - } - } - - /* - * 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. - */ - // package com.google.zxing.oned; - // import com.google.zxing.BarcodeFormat; - // import com.google.zxing.FormatException; - // import com.google.zxing.NotFoundException; - // import com.google.zxing.common.BitArray; - /** - *Implements decoding of the UPC-E format.
- *This is a great reference for - * UPC-E information.
- * - * @author Sean Owen - * - * @source https://github.com/zxing/zxing/blob/3c96923276dd5785d58eb970b6ba3f80d36a9505/core/src/main/java/com/google/zxing/oned/UPCEReader.java - * - * @experimental - */ - /* final */ class UPCEReader extends UPCEANReader { - constructor() { - super(); - this.decodeMiddleCounters = new Int32Array(4); - } - /** - * @throws NotFoundException - */ - // @Override - decodeMiddle(row, startRange, result) { - const counters = this.decodeMiddleCounters.map(x => x); - counters[0] = 0; - counters[1] = 0; - counters[2] = 0; - counters[3] = 0; - const end = row.getSize(); - let rowOffset = startRange[1]; - let lgPatternFound = 0; - for (let x = 0; x < 6 && rowOffset < end; x++) { - const bestMatch = UPCEReader.decodeDigit(row, counters, rowOffset, UPCEReader.L_AND_G_PATTERNS); - result += String.fromCharCode(('0'.charCodeAt(0) + bestMatch % 10)); - for (let counter of counters) { - rowOffset += counter; - } - if (bestMatch >= 10) { - lgPatternFound |= 1 << (5 - x); - } - } - UPCEReader.determineNumSysAndCheckDigit(new StringBuilder(result), lgPatternFound); - return rowOffset; - } - /** - * @throws NotFoundException - */ - // @Override - decodeEnd(row, endStart) { - return UPCEReader.findGuardPatternWithoutCounters(row, endStart, true, UPCEReader.MIDDLE_END_PATTERN); - } - /** - * @throws FormatException - */ - // @Override - checkChecksum(s) { - return UPCEANReader.checkChecksum(UPCEReader.convertUPCEtoUPCA(s)); - } - /** - * @throws NotFoundException - */ - static determineNumSysAndCheckDigit(resultString, lgPatternFound) { - for (let numSys = 0; numSys <= 1; numSys++) { - for (let d = 0; d < 10; d++) { - if (lgPatternFound === this.NUMSYS_AND_CHECK_DIGIT_PATTERNS[numSys][d]) { - resultString.insert(0, /*(char)*/ ('0' + numSys)); - resultString.append(/*(char)*/ ('0' + d)); - return; - } - } - } - throw NotFoundException.getNotFoundInstance(); - } - // @Override - getBarcodeFormat() { - return BarcodeFormat$1.UPC_E; - } - /** - * Expands a UPC-E value back into its full, equivalent UPC-A code value. - * - * @param upce UPC-E code as string of digits - * @return equivalent UPC-A code as string of digits - */ - static convertUPCEtoUPCA(upce) { - // the following line is equivalent to upce.getChars(1, 7, upceChars, 0); - const upceChars = upce.slice(1, 7).split('').map(x => x.charCodeAt(0)); - const result = new StringBuilder( /*12*/); - result.append(upce.charAt(0)); - let lastChar = upceChars[5]; - switch (lastChar) { - case 0: - case 1: - case 2: - result.appendChars(upceChars, 0, 2); - result.append(lastChar); - result.append('0000'); - result.appendChars(upceChars, 2, 3); - break; - case 3: - result.appendChars(upceChars, 0, 3); - result.append('00000'); - result.appendChars(upceChars, 3, 2); - break; - case 4: - result.appendChars(upceChars, 0, 4); - result.append('00000'); - result.append(upceChars[4]); - break; - default: - result.appendChars(upceChars, 0, 5); - result.append('0000'); - result.append(lastChar); - break; - } - // Only append check digit in conversion if supplied - if (upce.length >= 8) { - result.append(upce.charAt(7)); - } - return result.toString(); - } - } - /** - * The pattern that marks the middle, and end, of a UPC-E pattern. - * There is no "second half" to a UPC-E barcode. - */ - UPCEReader.MIDDLE_END_PATTERN = Int32Array.from([1, 1, 1, 1, 1, 1]); - // For an UPC-E barcode, the final digit is represented by the parities used - // to encode the middle six digits, according to the table below. - // - // Parity of next 6 digits - // Digit 0 1 2 3 4 5 - // 0 Even Even Even Odd Odd Odd - // 1 Even Even Odd Even Odd Odd - // 2 Even Even Odd Odd Even Odd - // 3 Even Even Odd Odd Odd Even - // 4 Even Odd Even Even Odd Odd - // 5 Even Odd Odd Even Even Odd - // 6 Even Odd Odd Odd Even Even - // 7 Even Odd Even Odd Even Odd - // 8 Even Odd Even Odd Odd Even - // 9 Even Odd Odd Even Odd Even - // - // The encoding is represented by the following array, which is a bit pattern - // using Odd = 0 and Even = 1. For example, 5 is represented by: - // - // Odd Even Even Odd Odd Even - // in binary: - // 0 1 1 0 0 1 == 0x19 - // - /** - * See {@link #L_AND_G_PATTERNS}; these values similarly represent patterns of - * even-odd parity encodings of digits that imply both the number system (0 or 1) - * used, and the check digit. - */ - UPCEReader.NUMSYS_AND_CHECK_DIGIT_PATTERNS = [ - Int32Array.from([0x38, 0x34, 0x32, 0x31, 0x2C, 0x26, 0x23, 0x2A, 0x29, 0x25]), - Int32Array.from([0x07, 0x0B, 0x0D, 0x0E, 0x13, 0x19, 0x1C, 0x15, 0x16, 0x1]), - ]; - - /* - * 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. - */ - /** - *A reader that can read all available UPC/EAN formats. If a caller wants to try to - * read all such formats, it is most efficient to use this implementation rather than invoke - * individual readers.
- * - * @author Sean Owen - */ - class MultiFormatUPCEANReader extends OneDReader { - constructor(hints) { - super(); - let possibleFormats = hints == null ? null : hints.get(DecodeHintType$1.POSSIBLE_FORMATS); - let readers = []; - if (possibleFormats != null) { - if (possibleFormats.indexOf(BarcodeFormat$1.EAN_13) > -1) { - readers.push(new EAN13Reader()); - } - else if (possibleFormats.indexOf(BarcodeFormat$1.UPC_A) > -1) { - readers.push(new UPCAReader()); - } - if (possibleFormats.indexOf(BarcodeFormat$1.EAN_8) > -1) { - readers.push(new EAN8Reader()); - } - if (possibleFormats.indexOf(BarcodeFormat$1.UPC_E) > -1) { - readers.push(new UPCEReader()); - } - } - if (readers.length === 0) { - readers.push(new EAN13Reader()); - // UPC-A is covered by EAN-13 - readers.push(new EAN8Reader()); - readers.push(new UPCEReader()); - } - this.readers = readers; - } - decodeRow(rowNumber, row, hints) { - for (let reader of this.readers) { - try { - // const result: Result = reader.decodeRow(rowNumber, row, startGuardPattern, hints); - const result = reader.decodeRow(rowNumber, row, hints); - // Special case: a 12-digit code encoded in UPC-A is identical to a "0" - // followed by those 12 digits encoded as EAN-13. Each will recognize such a code, - // UPC-A as a 12-digit string and EAN-13 as a 13-digit string starting with "0". - // Individually these are correct and their readers will both read such a code - // and correctly call it EAN-13, or UPC-A, respectively. - // - // In this case, if we've been looking for both types, we'd like to call it - // a UPC-A code. But for efficiency we only run the EAN-13 decoder to also read - // UPC-A. So we special case it here, and convert an EAN-13 result to a UPC-A - // result if appropriate. - // - // But, don't return UPC-A if UPC-A was not a requested format! - const ean13MayBeUPCA = result.getBarcodeFormat() === BarcodeFormat$1.EAN_13 && - result.getText().charAt(0) === '0'; - // @SuppressWarnings("unchecked") - const possibleFormats = hints == null ? null : hints.get(DecodeHintType$1.POSSIBLE_FORMATS); - const canReturnUPCA = possibleFormats == null || possibleFormats.includes(BarcodeFormat$1.UPC_A); - if (ean13MayBeUPCA && canReturnUPCA) { - const rawBytes = result.getRawBytes(); - // Transfer the metadata across - const resultUPCA = new Result(result.getText().substring(1), rawBytes, rawBytes.length, result.getResultPoints(), BarcodeFormat$1.UPC_A); - resultUPCA.putAllMetadata(result.getResultMetadata()); - return resultUPCA; - } - return result; - } - catch (err) { - // continue; - } - } - throw new NotFoundException(); - } - reset() { - for (let reader of this.readers) { - reader.reset(); - } - } - } - - // import Integer from '../../util/Integer'; - // import Float from '../../util/Float'; - class AbstractRSSReader extends OneDReader { - constructor() { - super(); - this.decodeFinderCounters = new Int32Array(4); - this.dataCharacterCounters = new Int32Array(8); - this.oddRoundingErrors = new Array(4); - this.evenRoundingErrors = new Array(4); - this.oddCounts = new Array(this.dataCharacterCounters.length / 2); - this.evenCounts = new Array(this.dataCharacterCounters.length / 2); - } - getDecodeFinderCounters() { - return this.decodeFinderCounters; - } - getDataCharacterCounters() { - return this.dataCharacterCounters; - } - getOddRoundingErrors() { - return this.oddRoundingErrors; - } - getEvenRoundingErrors() { - return this.evenRoundingErrors; - } - getOddCounts() { - return this.oddCounts; - } - getEvenCounts() { - return this.evenCounts; - } - parseFinderValue(counters, finderPatterns) { - for (let value = 0; value < finderPatterns.length; value++) { - if (OneDReader.patternMatchVariance(counters, finderPatterns[value], AbstractRSSReader.MAX_INDIVIDUAL_VARIANCE) < AbstractRSSReader.MAX_AVG_VARIANCE) { - return value; - } - } - throw new NotFoundException(); - } - /** - * @param array values to sum - * @return sum of values - * @deprecated call {@link MathUtils#sum(int[])} - */ - static count(array) { - return MathUtils.sum(new Int32Array(array)); - } - static increment(array, errors) { - let index = 0; - let biggestError = errors[0]; - for (let i = 1; i < array.length; i++) { - if (errors[i] > biggestError) { - biggestError = errors[i]; - index = i; - } - } - array[index]++; - } - static decrement(array, errors) { - let index = 0; - let biggestError = errors[0]; - for (let i = 1; i < array.length; i++) { - if (errors[i] < biggestError) { - biggestError = errors[i]; - index = i; - } - } - array[index]--; - } - static isFinderPattern(counters) { - let firstTwoSum = counters[0] + counters[1]; - let sum = firstTwoSum + counters[2] + counters[3]; - let ratio = firstTwoSum / sum; - if (ratio >= AbstractRSSReader.MIN_FINDER_PATTERN_RATIO && ratio <= AbstractRSSReader.MAX_FINDER_PATTERN_RATIO) { - // passes ratio test in spec, but see if the counts are unreasonable - let minCounter = Number.MAX_SAFE_INTEGER; - let maxCounter = Number.MIN_SAFE_INTEGER; - for (let counter of counters) { - if (counter > maxCounter) { - maxCounter = counter; - } - if (counter < minCounter) { - minCounter = counter; - } - } - return maxCounter < 10 * minCounter; - } - return false; - } - } - AbstractRSSReader.MAX_AVG_VARIANCE = 0.2; - AbstractRSSReader.MAX_INDIVIDUAL_VARIANCE = 0.45; - AbstractRSSReader.MIN_FINDER_PATTERN_RATIO = 9.5 / 12.0; - AbstractRSSReader.MAX_FINDER_PATTERN_RATIO = 12.5 / 14.0; - - class DataCharacter { - constructor(value, checksumPortion) { - this.value = value; - this.checksumPortion = checksumPortion; - } - getValue() { - return this.value; - } - getChecksumPortion() { - return this.checksumPortion; - } - toString() { - return this.value + '(' + this.checksumPortion + ')'; - } - equals(o) { - if (!(o instanceof DataCharacter)) { - return false; - } - const that = o; - return this.value === that.value && this.checksumPortion === that.checksumPortion; - } - hashCode() { - return this.value ^ this.checksumPortion; - } - } - - class FinderPattern { - constructor(value, startEnd, start, end, rowNumber) { - this.value = value; - this.startEnd = startEnd; - this.value = value; - this.startEnd = startEnd; - this.resultPoints = new Array(); - this.resultPoints.push(new ResultPoint(start, rowNumber)); - this.resultPoints.push(new ResultPoint(end, rowNumber)); - } - getValue() { - return this.value; - } - getStartEnd() { - return this.startEnd; - } - getResultPoints() { - return this.resultPoints; - } - equals(o) { - if (!(o instanceof FinderPattern)) { - return false; - } - const that = o; - return this.value === that.value; - } - hashCode() { - return this.value; - } - } - - /** - * RSS util functions. - */ - class RSSUtils { - constructor() { } - static getRSSvalue(widths, maxWidth, noNarrow) { - let n = 0; - for (let width of widths) { - n += width; - } - let val = 0; - let narrowMask = 0; - let elements = widths.length; - for (let bar = 0; bar < elements - 1; bar++) { - let elmWidth; - for (elmWidth = 1, narrowMask |= 1 << bar; elmWidth < widths[bar]; elmWidth++, narrowMask &= ~(1 << bar)) { - let subVal = RSSUtils.combins(n - elmWidth - 1, elements - bar - 2); - if (noNarrow && (narrowMask === 0) && (n - elmWidth - (elements - bar - 1) >= elements - bar - 1)) { - subVal -= RSSUtils.combins(n - elmWidth - (elements - bar), elements - bar - 2); - } - if (elements - bar - 1 > 1) { - let lessVal = 0; - for (let mxwElement = n - elmWidth - (elements - bar - 2); mxwElement > maxWidth; mxwElement--) { - lessVal += RSSUtils.combins(n - elmWidth - mxwElement - 1, elements - bar - 3); - } - subVal -= lessVal * (elements - 1 - bar); - } - else if (n - elmWidth > maxWidth) { - subVal--; - } - val += subVal; - } - n -= elmWidth; - } - return val; - } - static combins(n, r) { - let maxDenom; - let minDenom; - if (n - r > r) { - minDenom = r; - maxDenom = n - r; - } - else { - minDenom = n - r; - maxDenom = r; - } - let val = 1; - let j = 1; - for (let i = n; i > maxDenom; i--) { - val *= i; - if (j <= minDenom) { - val /= j; - j++; - } - } - while ((j <= minDenom)) { - val /= j; - j++; - } - return val; - } - } - - class BitArrayBuilder { - static buildBitArray(pairs) { - let charNumber = (pairs.length * 2) - 1; - if (pairs[pairs.length - 1].getRightChar() == null) { - charNumber -= 1; - } - let size = 12 * charNumber; - let binary = new BitArray(size); - let accPos = 0; - let firstPair = pairs[0]; - let firstValue = firstPair.getRightChar().getValue(); - for (let i = 11; i >= 0; --i) { - if ((firstValue & (1 << i)) != 0) { - binary.set(accPos); - } - accPos++; - } - for (let i = 1; i < pairs.length; ++i) { - let currentPair = pairs[i]; - let leftValue = currentPair.getLeftChar().getValue(); - for (let j = 11; j >= 0; --j) { - if ((leftValue & (1 << j)) != 0) { - binary.set(accPos); - } - accPos++; - } - if (currentPair.getRightChar() != null) { - let rightValue = currentPair.getRightChar().getValue(); - for (let j = 11; j >= 0; --j) { - if ((rightValue & (1 << j)) != 0) { - binary.set(accPos); - } - accPos++; - } - } - } - return binary; - } - } - - class BlockParsedResult { - constructor(finished, decodedInformation) { - if (decodedInformation) { - this.decodedInformation = null; - } - else { - this.finished = finished; - this.decodedInformation = decodedInformation; - } - } - getDecodedInformation() { - return this.decodedInformation; - } - isFinished() { - return this.finished; - } - } - - class DecodedObject { - constructor(newPosition) { - this.newPosition = newPosition; - } - getNewPosition() { - return this.newPosition; - } - } - - class DecodedChar extends DecodedObject { - constructor(newPosition, value) { - super(newPosition); - this.value = value; - } - getValue() { - return this.value; - } - isFNC1() { - return this.value === DecodedChar.FNC1; - } - } - DecodedChar.FNC1 = '$'; - - class DecodedInformation extends DecodedObject { - constructor(newPosition, newString, remainingValue) { - super(newPosition); - if (remainingValue) { - this.remaining = true; - this.remainingValue = this.remainingValue; - } - else { - this.remaining = false; - this.remainingValue = 0; - } - this.newString = newString; - } - getNewString() { - return this.newString; - } - isRemaining() { - return this.remaining; - } - getRemainingValue() { - return this.remainingValue; - } - } - - class DecodedNumeric extends DecodedObject { - constructor(newPosition, firstDigit, secondDigit) { - super(newPosition); - if (firstDigit < 0 || firstDigit > 10 || secondDigit < 0 || secondDigit > 10) { - throw new FormatException(); - } - this.firstDigit = firstDigit; - this.secondDigit = secondDigit; - } - getFirstDigit() { - return this.firstDigit; - } - getSecondDigit() { - return this.secondDigit; - } - getValue() { - return this.firstDigit * 10 + this.secondDigit; - } - isFirstDigitFNC1() { - return this.firstDigit === DecodedNumeric.FNC1; - } - isSecondDigitFNC1() { - return this.secondDigit === DecodedNumeric.FNC1; - } - isAnyFNC1() { - return this.firstDigit === DecodedNumeric.FNC1 || this.secondDigit === DecodedNumeric.FNC1; - } - } - DecodedNumeric.FNC1 = 10; - - class FieldParser { - constructor() { - } - static parseFieldsInGeneralPurpose(rawInformation) { - if (!rawInformation) { - return null; - } - // Processing 2-digit AIs - if (rawInformation.length < 2) { - throw new NotFoundException(); - } - let firstTwoDigits = rawInformation.substring(0, 2); - for (let dataLength of FieldParser.TWO_DIGIT_DATA_LENGTH) { - if (dataLength[0] === firstTwoDigits) { - if (dataLength[1] === FieldParser.VARIABLE_LENGTH) { - return FieldParser.processVariableAI(2, dataLength[2], rawInformation); - } - return FieldParser.processFixedAI(2, dataLength[1], rawInformation); - } - } - if (rawInformation.length < 3) { - throw new NotFoundException(); - } - let firstThreeDigits = rawInformation.substring(0, 3); - for (let dataLength of FieldParser.THREE_DIGIT_DATA_LENGTH) { - if (dataLength[0] === firstThreeDigits) { - if (dataLength[1] === FieldParser.VARIABLE_LENGTH) { - return FieldParser.processVariableAI(3, dataLength[2], rawInformation); - } - return FieldParser.processFixedAI(3, dataLength[1], rawInformation); - } - } - for (let dataLength of FieldParser.THREE_DIGIT_PLUS_DIGIT_DATA_LENGTH) { - if (dataLength[0] === firstThreeDigits) { - if (dataLength[1] === FieldParser.VARIABLE_LENGTH) { - return FieldParser.processVariableAI(4, dataLength[2], rawInformation); - } - return FieldParser.processFixedAI(4, dataLength[1], rawInformation); - } - } - if (rawInformation.length < 4) { - throw new NotFoundException(); - } - let firstFourDigits = rawInformation.substring(0, 4); - for (let dataLength of FieldParser.FOUR_DIGIT_DATA_LENGTH) { - if (dataLength[0] === firstFourDigits) { - if (dataLength[1] === FieldParser.VARIABLE_LENGTH) { - return FieldParser.processVariableAI(4, dataLength[2], rawInformation); - } - return FieldParser.processFixedAI(4, dataLength[1], rawInformation); - } - } - throw new NotFoundException(); - } - static processFixedAI(aiSize, fieldSize, rawInformation) { - if (rawInformation.length < aiSize) { - throw new NotFoundException(); - } - let ai = rawInformation.substring(0, aiSize); - if (rawInformation.length < aiSize + fieldSize) { - throw new NotFoundException(); - } - let field = rawInformation.substring(aiSize, aiSize + fieldSize); - let remaining = rawInformation.substring(aiSize + fieldSize); - let result = '(' + ai + ')' + field; - let parsedAI = FieldParser.parseFieldsInGeneralPurpose(remaining); - return parsedAI == null ? result : result + parsedAI; - } - static processVariableAI(aiSize, variableFieldSize, rawInformation) { - let ai = rawInformation.substring(0, aiSize); - let maxSize; - if (rawInformation.length < aiSize + variableFieldSize) { - maxSize = rawInformation.length; - } - else { - maxSize = aiSize + variableFieldSize; - } - let field = rawInformation.substring(aiSize, maxSize); - let remaining = rawInformation.substring(maxSize); - let result = '(' + ai + ')' + field; - let parsedAI = FieldParser.parseFieldsInGeneralPurpose(remaining); - return parsedAI == null ? result : result + parsedAI; - } - } - FieldParser.VARIABLE_LENGTH = []; - FieldParser.TWO_DIGIT_DATA_LENGTH = [ - ['00', 18], - ['01', 14], - ['02', 14], - ['10', FieldParser.VARIABLE_LENGTH, 20], - ['11', 6], - ['12', 6], - ['13', 6], - ['15', 6], - ['17', 6], - ['20', 2], - ['21', FieldParser.VARIABLE_LENGTH, 20], - ['22', FieldParser.VARIABLE_LENGTH, 29], - ['30', FieldParser.VARIABLE_LENGTH, 8], - ['37', FieldParser.VARIABLE_LENGTH, 8], - // internal company codes - ['90', FieldParser.VARIABLE_LENGTH, 30], - ['91', FieldParser.VARIABLE_LENGTH, 30], - ['92', FieldParser.VARIABLE_LENGTH, 30], - ['93', FieldParser.VARIABLE_LENGTH, 30], - ['94', FieldParser.VARIABLE_LENGTH, 30], - ['95', FieldParser.VARIABLE_LENGTH, 30], - ['96', FieldParser.VARIABLE_LENGTH, 30], - ['97', FieldParser.VARIABLE_LENGTH, 3], - ['98', FieldParser.VARIABLE_LENGTH, 30], - ['99', FieldParser.VARIABLE_LENGTH, 30], - ]; - FieldParser.THREE_DIGIT_DATA_LENGTH = [ - // Same format as above - ['240', FieldParser.VARIABLE_LENGTH, 30], - ['241', FieldParser.VARIABLE_LENGTH, 30], - ['242', FieldParser.VARIABLE_LENGTH, 6], - ['250', FieldParser.VARIABLE_LENGTH, 30], - ['251', FieldParser.VARIABLE_LENGTH, 30], - ['253', FieldParser.VARIABLE_LENGTH, 17], - ['254', FieldParser.VARIABLE_LENGTH, 20], - ['400', FieldParser.VARIABLE_LENGTH, 30], - ['401', FieldParser.VARIABLE_LENGTH, 30], - ['402', 17], - ['403', FieldParser.VARIABLE_LENGTH, 30], - ['410', 13], - ['411', 13], - ['412', 13], - ['413', 13], - ['414', 13], - ['420', FieldParser.VARIABLE_LENGTH, 20], - ['421', FieldParser.VARIABLE_LENGTH, 15], - ['422', 3], - ['423', FieldParser.VARIABLE_LENGTH, 15], - ['424', 3], - ['425', 3], - ['426', 3], - ]; - FieldParser.THREE_DIGIT_PLUS_DIGIT_DATA_LENGTH = [ - // Same format as above - ['310', 6], - ['311', 6], - ['312', 6], - ['313', 6], - ['314', 6], - ['315', 6], - ['316', 6], - ['320', 6], - ['321', 6], - ['322', 6], - ['323', 6], - ['324', 6], - ['325', 6], - ['326', 6], - ['327', 6], - ['328', 6], - ['329', 6], - ['330', 6], - ['331', 6], - ['332', 6], - ['333', 6], - ['334', 6], - ['335', 6], - ['336', 6], - ['340', 6], - ['341', 6], - ['342', 6], - ['343', 6], - ['344', 6], - ['345', 6], - ['346', 6], - ['347', 6], - ['348', 6], - ['349', 6], - ['350', 6], - ['351', 6], - ['352', 6], - ['353', 6], - ['354', 6], - ['355', 6], - ['356', 6], - ['357', 6], - ['360', 6], - ['361', 6], - ['362', 6], - ['363', 6], - ['364', 6], - ['365', 6], - ['366', 6], - ['367', 6], - ['368', 6], - ['369', 6], - ['390', FieldParser.VARIABLE_LENGTH, 15], - ['391', FieldParser.VARIABLE_LENGTH, 18], - ['392', FieldParser.VARIABLE_LENGTH, 15], - ['393', FieldParser.VARIABLE_LENGTH, 18], - ['703', FieldParser.VARIABLE_LENGTH, 30], - ]; - FieldParser.FOUR_DIGIT_DATA_LENGTH = [ - // Same format as above - ['7001', 13], - ['7002', FieldParser.VARIABLE_LENGTH, 30], - ['7003', 10], - ['8001', 14], - ['8002', FieldParser.VARIABLE_LENGTH, 20], - ['8003', FieldParser.VARIABLE_LENGTH, 30], - ['8004', FieldParser.VARIABLE_LENGTH, 30], - ['8005', 6], - ['8006', 18], - ['8007', FieldParser.VARIABLE_LENGTH, 30], - ['8008', FieldParser.VARIABLE_LENGTH, 12], - ['8018', 18], - ['8020', FieldParser.VARIABLE_LENGTH, 25], - ['8100', 6], - ['8101', 10], - ['8102', 2], - ['8110', FieldParser.VARIABLE_LENGTH, 70], - ['8200', FieldParser.VARIABLE_LENGTH, 70], - ]; - - class GeneralAppIdDecoder { - constructor(information) { - this.buffer = new StringBuilder(); - this.information = information; - } - decodeAllCodes(buff, initialPosition) { - let currentPosition = initialPosition; - let remaining = null; - do { - let info = this.decodeGeneralPurposeField(currentPosition, remaining); - let parsedFields = FieldParser.parseFieldsInGeneralPurpose(info.getNewString()); - if (parsedFields != null) { - buff.append(parsedFields); - } - if (info.isRemaining()) { - remaining = '' + info.getRemainingValue(); - } - else { - remaining = null; - } - if (currentPosition === info.getNewPosition()) { // No step forward! - break; - } - currentPosition = info.getNewPosition(); - } while (true); - return buff.toString(); - } - isStillNumeric(pos) { - // It's numeric if it still has 7 positions - // and one of the first 4 bits is "1". - if (pos + 7 > this.information.getSize()) { - return pos + 4 <= this.information.getSize(); - } - for (let i = pos; i < pos + 3; ++i) { - if (this.information.get(i)) { - return true; - } - } - return this.information.get(pos + 3); - } - decodeNumeric(pos) { - if (pos + 7 > this.information.getSize()) { - let numeric = this.extractNumericValueFromBitArray(pos, 4); - if (numeric === 0) { - return new DecodedNumeric(this.information.getSize(), DecodedNumeric.FNC1, DecodedNumeric.FNC1); - } - return new DecodedNumeric(this.information.getSize(), numeric - 1, DecodedNumeric.FNC1); - } - let numeric = this.extractNumericValueFromBitArray(pos, 7); - let digit1 = (numeric - 8) / 11; - let digit2 = (numeric - 8) % 11; - return new DecodedNumeric(pos + 7, digit1, digit2); - } - extractNumericValueFromBitArray(pos, bits) { - return GeneralAppIdDecoder.extractNumericValueFromBitArray(this.information, pos, bits); - } - static extractNumericValueFromBitArray(information, pos, bits) { - let value = 0; - for (let i = 0; i < bits; ++i) { - if (information.get(pos + i)) { - value |= 1 << (bits - i - 1); - } - } - return value; - } - decodeGeneralPurposeField(pos, remaining) { - // this.buffer.setLength(0); - this.buffer.setLengthToZero(); - if (remaining != null) { - this.buffer.append(remaining); - } - this.current.setPosition(pos); - let lastDecoded = this.parseBlocks(); - if (lastDecoded != null && lastDecoded.isRemaining()) { - return new DecodedInformation(this.current.getPosition(), this.buffer.toString(), lastDecoded.getRemainingValue()); - } - return new DecodedInformation(this.current.getPosition(), this.buffer.toString()); - } - parseBlocks() { - let isFinished; - let result; - do { - let initialPosition = this.current.getPosition(); - if (this.current.isAlpha()) { - result = this.parseAlphaBlock(); - isFinished = result.isFinished(); - } - else if (this.current.isIsoIec646()) { - result = this.parseIsoIec646Block(); - isFinished = result.isFinished(); - } - else { // it must be numeric - result = this.parseNumericBlock(); - isFinished = result.isFinished(); - } - let positionChanged = initialPosition !== this.current.getPosition(); - if (!positionChanged && !isFinished) { - break; - } - } while (!isFinished); - return result.getDecodedInformation(); - } - parseNumericBlock() { - while (this.isStillNumeric(this.current.getPosition())) { - let numeric = this.decodeNumeric(this.current.getPosition()); - this.current.setPosition(numeric.getNewPosition()); - if (numeric.isFirstDigitFNC1()) { - let information; - if (numeric.isSecondDigitFNC1()) { - information = new DecodedInformation(this.current.getPosition(), this.buffer.toString()); - } - else { - information = new DecodedInformation(this.current.getPosition(), this.buffer.toString(), numeric.getSecondDigit()); - } - return new BlockParsedResult(true, information); - } - this.buffer.append(numeric.getFirstDigit()); - if (numeric.isSecondDigitFNC1()) { - let information = new DecodedInformation(this.current.getPosition(), this.buffer.toString()); - return new BlockParsedResult(true, information); - } - this.buffer.append(numeric.getSecondDigit()); - } - if (this.isNumericToAlphaNumericLatch(this.current.getPosition())) { - this.current.setAlpha(); - this.current.incrementPosition(4); - } - return new BlockParsedResult(false); - } - parseIsoIec646Block() { - while (this.isStillIsoIec646(this.current.getPosition())) { - let iso = this.decodeIsoIec646(this.current.getPosition()); - this.current.setPosition(iso.getNewPosition()); - if (iso.isFNC1()) { - let information = new DecodedInformation(this.current.getPosition(), this.buffer.toString()); - return new BlockParsedResult(true, information); - } - this.buffer.append(iso.getValue()); - } - if (this.isAlphaOr646ToNumericLatch(this.current.getPosition())) { - this.current.incrementPosition(3); - this.current.setNumeric(); - } - else if (this.isAlphaTo646ToAlphaLatch(this.current.getPosition())) { - if (this.current.getPosition() + 5 < this.information.getSize()) { - this.current.incrementPosition(5); - } - else { - this.current.setPosition(this.information.getSize()); - } - this.current.setAlpha(); - } - return new BlockParsedResult(false); - } - parseAlphaBlock() { - while (this.isStillAlpha(this.current.getPosition())) { - let alpha = this.decodeAlphanumeric(this.current.getPosition()); - this.current.setPosition(alpha.getNewPosition()); - if (alpha.isFNC1()) { - let information = new DecodedInformation(this.current.getPosition(), this.buffer.toString()); - return new BlockParsedResult(true, information); // end of the char block - } - this.buffer.append(alpha.getValue()); - } - if (this.isAlphaOr646ToNumericLatch(this.current.getPosition())) { - this.current.incrementPosition(3); - this.current.setNumeric(); - } - else if (this.isAlphaTo646ToAlphaLatch(this.current.getPosition())) { - if (this.current.getPosition() + 5 < this.information.getSize()) { - this.current.incrementPosition(5); - } - else { - this.current.setPosition(this.information.getSize()); - } - this.current.setIsoIec646(); - } - return new BlockParsedResult(false); - } - isStillIsoIec646(pos) { - if (pos + 5 > this.information.getSize()) { - return false; - } - let fiveBitValue = this.extractNumericValueFromBitArray(pos, 5); - if (fiveBitValue >= 5 && fiveBitValue < 16) { - return true; - } - if (pos + 7 > this.information.getSize()) { - return false; - } - let sevenBitValue = this.extractNumericValueFromBitArray(pos, 7); - if (sevenBitValue >= 64 && sevenBitValue < 116) { - return true; - } - if (pos + 8 > this.information.getSize()) { - return false; - } - let eightBitValue = this.extractNumericValueFromBitArray(pos, 8); - return eightBitValue >= 232 && eightBitValue < 253; - } - decodeIsoIec646(pos) { - let fiveBitValue = this.extractNumericValueFromBitArray(pos, 5); - if (fiveBitValue === 15) { - return new DecodedChar(pos + 5, DecodedChar.FNC1); - } - if (fiveBitValue >= 5 && fiveBitValue < 15) { - return new DecodedChar(pos + 5, ('0' + (fiveBitValue - 5))); - } - let sevenBitValue = this.extractNumericValueFromBitArray(pos, 7); - if (sevenBitValue >= 64 && sevenBitValue < 90) { - return new DecodedChar(pos + 7, ('' + (sevenBitValue + 1))); - } - if (sevenBitValue >= 90 && sevenBitValue < 116) { - return new DecodedChar(pos + 7, ('' + (sevenBitValue + 7))); - } - let eightBitValue = this.extractNumericValueFromBitArray(pos, 8); - let c; - switch (eightBitValue) { - case 232: - c = '!'; - break; - case 233: - c = '"'; - break; - case 234: - c = '%'; - break; - case 235: - c = '&'; - break; - case 236: - c = '\''; - break; - case 237: - c = '('; - break; - case 238: - c = ')'; - break; - case 239: - c = '*'; - break; - case 240: - c = '+'; - break; - case 241: - c = ','; - break; - case 242: - c = '-'; - break; - case 243: - c = '.'; - break; - case 244: - c = '/'; - break; - case 245: - c = ':'; - break; - case 246: - c = ';'; - break; - case 247: - c = '<'; - break; - case 248: - c = '='; - break; - case 249: - c = '>'; - break; - case 250: - c = '?'; - break; - case 251: - c = '_'; - break; - case 252: - c = ' '; - break; - default: - throw new FormatException(); - } - return new DecodedChar(pos + 8, c); - } - isStillAlpha(pos) { - if (pos + 5 > this.information.getSize()) { - return false; - } - // We now check if it's a valid 5-bit value (0..9 and FNC1) - let fiveBitValue = this.extractNumericValueFromBitArray(pos, 5); - if (fiveBitValue >= 5 && fiveBitValue < 16) { - return true; - } - if (pos + 6 > this.information.getSize()) { - return false; - } - let sixBitValue = this.extractNumericValueFromBitArray(pos, 6); - return sixBitValue >= 16 && sixBitValue < 63; // 63 not included - } - decodeAlphanumeric(pos) { - let fiveBitValue = this.extractNumericValueFromBitArray(pos, 5); - if (fiveBitValue === 15) { - return new DecodedChar(pos + 5, DecodedChar.FNC1); - } - if (fiveBitValue >= 5 && fiveBitValue < 15) { - return new DecodedChar(pos + 5, ('0' + (fiveBitValue - 5))); - } - let sixBitValue = this.extractNumericValueFromBitArray(pos, 6); - if (sixBitValue >= 32 && sixBitValue < 58) { - return new DecodedChar(pos + 6, ('' + (sixBitValue + 33))); - } - let c; - switch (sixBitValue) { - case 58: - c = '*'; - break; - case 59: - c = ','; - break; - case 60: - c = '-'; - break; - case 61: - c = '.'; - break; - case 62: - c = '/'; - break; - default: - throw new IllegalStateException('Decoding invalid alphanumeric value: ' + sixBitValue); - } - return new DecodedChar(pos + 6, c); - } - isAlphaTo646ToAlphaLatch(pos) { - if (pos + 1 > this.information.getSize()) { - return false; - } - for (let i = 0; i < 5 && i + pos < this.information.getSize(); ++i) { - if (i === 2) { - if (!this.information.get(pos + 2)) { - return false; - } - } - else if (this.information.get(pos + i)) { - return false; - } - } - return true; - } - isAlphaOr646ToNumericLatch(pos) { - // Next is alphanumeric if there are 3 positions and they are all zeros - if (pos + 3 > this.information.getSize()) { - return false; - } - for (let i = pos; i < pos + 3; ++i) { - if (this.information.get(i)) { - return false; - } - } - return true; - } - isNumericToAlphaNumericLatch(pos) { - // Next is alphanumeric if there are 4 positions and they are all zeros, or - // if there is a subset of this just before the end of the symbol - if (pos + 1 > this.information.getSize()) { - return false; - } - for (let i = 0; i < 4 && i + pos < this.information.getSize(); ++i) { - if (this.information.get(pos + i)) { - return false; - } - } - return true; - } - } - - class AbstractExpandedDecoder { - constructor(information) { - this.information = information; - this.generalDecoder = new GeneralAppIdDecoder(information); - } - getInformation() { - return this.information; - } - getGeneralDecoder() { - return this.generalDecoder; - } - } - - class AI01decoder extends AbstractExpandedDecoder { - constructor(information) { - super(information); - } - encodeCompressedGtin(buf, currentPos) { - buf.append('(01)'); - let initialPosition = buf.length(); - buf.append('9'); - this.encodeCompressedGtinWithoutAI(buf, currentPos, initialPosition); - } - encodeCompressedGtinWithoutAI(buf, currentPos, initialBufferPosition) { - for (let i = 0; i < 4; ++i) { - let currentBlock = this.getGeneralDecoder().extractNumericValueFromBitArray(currentPos + 10 * i, 10); - if (currentBlock / 100 === 0) { - buf.append('0'); - } - if (currentBlock / 10 === 0) { - buf.append('0'); - } - buf.append(currentBlock); - } - AI01decoder.appendCheckDigit(buf, initialBufferPosition); - } - static appendCheckDigit(buf, currentPos) { - let checkDigit = 0; - for (let i = 0; i < 13; i++) { - // let digit = buf.charAt(i + currentPos) - '0'; - // To be checked - let digit = buf.charAt(i + currentPos).charCodeAt(0) - '0'.charCodeAt(0); - checkDigit += (i & 0x01) === 0 ? 3 * digit : digit; - } - checkDigit = 10 - (checkDigit % 10); - if (checkDigit === 10) { - checkDigit = 0; - } - buf.append(checkDigit); - } - } - AI01decoder.GTIN_SIZE = 40; - - class AI01AndOtherAIs extends AI01decoder { - // the second one is the encodation method, and the other two are for the variable length - constructor(information) { - super(information); - } - parseInformation() { - let buff = new StringBuilder(); - buff.append('(01)'); - let initialGtinPosition = buff.length(); - let firstGtinDigit = this.getGeneralDecoder().extractNumericValueFromBitArray(AI01AndOtherAIs.HEADER_SIZE, 4); - buff.append(firstGtinDigit); - this.encodeCompressedGtinWithoutAI(buff, AI01AndOtherAIs.HEADER_SIZE + 4, initialGtinPosition); - return this.getGeneralDecoder().decodeAllCodes(buff, AI01AndOtherAIs.HEADER_SIZE + 44); - } - } - AI01AndOtherAIs.HEADER_SIZE = 1 + 1 + 2; // first bit encodes the linkage flag, - - class AnyAIDecoder extends AbstractExpandedDecoder { - constructor(information) { - super(information); - } - parseInformation() { - let buf = new StringBuilder(); - return this.getGeneralDecoder().decodeAllCodes(buf, AnyAIDecoder.HEADER_SIZE); - } - } - AnyAIDecoder.HEADER_SIZE = 2 + 1 + 2; - - class AI01weightDecoder extends AI01decoder { - constructor(information) { - super(information); - } - encodeCompressedWeight(buf, currentPos, weightSize) { - let originalWeightNumeric = this.getGeneralDecoder().extractNumericValueFromBitArray(currentPos, weightSize); - this.addWeightCode(buf, originalWeightNumeric); - let weightNumeric = this.checkWeight(originalWeightNumeric); - let currentDivisor = 100000; - for (let i = 0; i < 5; ++i) { - if (weightNumeric / currentDivisor === 0) { - buf.append('0'); - } - currentDivisor /= 10; - } - buf.append(weightNumeric); - } - } - - class AI013x0xDecoder extends AI01weightDecoder { - constructor(information) { - super(information); - } - parseInformation() { - if (this.getInformation().getSize() != AI013x0xDecoder.HEADER_SIZE + AI01weightDecoder.GTIN_SIZE + AI013x0xDecoder.WEIGHT_SIZE) { - throw new NotFoundException(); - } - let buf = new StringBuilder(); - this.encodeCompressedGtin(buf, AI013x0xDecoder.HEADER_SIZE); - this.encodeCompressedWeight(buf, AI013x0xDecoder.HEADER_SIZE + AI01weightDecoder.GTIN_SIZE, AI013x0xDecoder.WEIGHT_SIZE); - return buf.toString(); - } - } - AI013x0xDecoder.HEADER_SIZE = 4 + 1; - AI013x0xDecoder.WEIGHT_SIZE = 15; - - class AI013103decoder extends AI013x0xDecoder { - constructor(information) { - super(information); - } - addWeightCode(buf, weight) { - buf.append('(3103)'); - } - checkWeight(weight) { - return weight; - } - } - - class AI01320xDecoder extends AI013x0xDecoder { - constructor(information) { - super(information); - } - addWeightCode(buf, weight) { - if (weight < 10000) { - buf.append('(3202)'); - } - else { - buf.append('(3203)'); - } - } - checkWeight(weight) { - if (weight < 10000) { - return weight; - } - return weight - 10000; - } - } - - class AI01392xDecoder extends AI01decoder { - constructor(information) { - super(information); - } - parseInformation() { - if (this.getInformation().getSize() < AI01392xDecoder.HEADER_SIZE + AI01decoder.GTIN_SIZE) { - throw new NotFoundException(); - } - let buf = new StringBuilder(); - this.encodeCompressedGtin(buf, AI01392xDecoder.HEADER_SIZE); - let lastAIdigit = this.getGeneralDecoder().extractNumericValueFromBitArray(AI01392xDecoder.HEADER_SIZE + AI01decoder.GTIN_SIZE, AI01392xDecoder.LAST_DIGIT_SIZE); - buf.append('(392'); - buf.append(lastAIdigit); - buf.append(')'); - let decodedInformation = this.getGeneralDecoder().decodeGeneralPurposeField(AI01392xDecoder.HEADER_SIZE + AI01decoder.GTIN_SIZE + AI01392xDecoder.LAST_DIGIT_SIZE, null); - buf.append(decodedInformation.getNewString()); - return buf.toString(); - } - } - AI01392xDecoder.HEADER_SIZE = 5 + 1 + 2; - AI01392xDecoder.LAST_DIGIT_SIZE = 2; - - class AI01393xDecoder extends AI01decoder { - constructor(information) { - super(information); - } - parseInformation() { - if (this.getInformation().getSize() < AI01393xDecoder.HEADER_SIZE + AI01decoder.GTIN_SIZE) { - throw new NotFoundException(); - } - let buf = new StringBuilder(); - this.encodeCompressedGtin(buf, AI01393xDecoder.HEADER_SIZE); - let lastAIdigit = this.getGeneralDecoder().extractNumericValueFromBitArray(AI01393xDecoder.HEADER_SIZE + AI01decoder.GTIN_SIZE, AI01393xDecoder.LAST_DIGIT_SIZE); - buf.append('(393'); - buf.append(lastAIdigit); - buf.append(')'); - let firstThreeDigits = this.getGeneralDecoder().extractNumericValueFromBitArray(AI01393xDecoder.HEADER_SIZE + AI01decoder.GTIN_SIZE + AI01393xDecoder.LAST_DIGIT_SIZE, AI01393xDecoder.FIRST_THREE_DIGITS_SIZE); - if (firstThreeDigits / 100 == 0) { - buf.append('0'); - } - if (firstThreeDigits / 10 == 0) { - buf.append('0'); - } - buf.append(firstThreeDigits); - let generalInformation = this.getGeneralDecoder().decodeGeneralPurposeField(AI01393xDecoder.HEADER_SIZE + AI01decoder.GTIN_SIZE + AI01393xDecoder.LAST_DIGIT_SIZE + AI01393xDecoder.FIRST_THREE_DIGITS_SIZE, null); - buf.append(generalInformation.getNewString()); - return buf.toString(); - } - } - AI01393xDecoder.HEADER_SIZE = 5 + 1 + 2; - AI01393xDecoder.LAST_DIGIT_SIZE = 2; - AI01393xDecoder.FIRST_THREE_DIGITS_SIZE = 10; - - class AI013x0x1xDecoder extends AI01weightDecoder { - constructor(information, firstAIdigits, dateCode) { - super(information); - this.dateCode = dateCode; - this.firstAIdigits = firstAIdigits; - } - parseInformation() { - if (this.getInformation().getSize() != AI013x0x1xDecoder.HEADER_SIZE + AI013x0x1xDecoder.GTIN_SIZE + AI013x0x1xDecoder.WEIGHT_SIZE + AI013x0x1xDecoder.DATE_SIZE) { - throw new NotFoundException(); - } - let buf = new StringBuilder(); - this.encodeCompressedGtin(buf, AI013x0x1xDecoder.HEADER_SIZE); - this.encodeCompressedWeight(buf, AI013x0x1xDecoder.HEADER_SIZE + AI013x0x1xDecoder.GTIN_SIZE, AI013x0x1xDecoder.WEIGHT_SIZE); - this.encodeCompressedDate(buf, AI013x0x1xDecoder.HEADER_SIZE + AI013x0x1xDecoder.GTIN_SIZE + AI013x0x1xDecoder.WEIGHT_SIZE); - return buf.toString(); - } - encodeCompressedDate(buf, currentPos) { - let numericDate = this.getGeneralDecoder().extractNumericValueFromBitArray(currentPos, AI013x0x1xDecoder.DATE_SIZE); - if (numericDate == 38400) { - return; - } - buf.append('('); - buf.append(this.dateCode); - buf.append(')'); - let day = numericDate % 32; - numericDate /= 32; - let month = numericDate % 12 + 1; - numericDate /= 12; - let year = numericDate; - if (year / 10 == 0) { - buf.append('0'); - } - buf.append(year); - if (month / 10 == 0) { - buf.append('0'); - } - buf.append(month); - if (day / 10 == 0) { - buf.append('0'); - } - buf.append(day); - } - addWeightCode(buf, weight) { - buf.append('('); - buf.append(this.firstAIdigits); - buf.append(weight / 100000); - buf.append(')'); - } - checkWeight(weight) { - return weight % 100000; - } - } - AI013x0x1xDecoder.HEADER_SIZE = 7 + 1; - AI013x0x1xDecoder.WEIGHT_SIZE = 20; - AI013x0x1xDecoder.DATE_SIZE = 16; - - function createDecoder(information) { - try { - if (information.get(1)) { - return new AI01AndOtherAIs(information); - } - if (!information.get(2)) { - return new AnyAIDecoder(information); - } - let fourBitEncodationMethod = GeneralAppIdDecoder.extractNumericValueFromBitArray(information, 1, 4); - switch (fourBitEncodationMethod) { - case 4: return new AI013103decoder(information); - case 5: return new AI01320xDecoder(information); - } - let fiveBitEncodationMethod = GeneralAppIdDecoder.extractNumericValueFromBitArray(information, 1, 5); - switch (fiveBitEncodationMethod) { - case 12: return new AI01392xDecoder(information); - case 13: return new AI01393xDecoder(information); - } - let sevenBitEncodationMethod = GeneralAppIdDecoder.extractNumericValueFromBitArray(information, 1, 7); - switch (sevenBitEncodationMethod) { - case 56: return new AI013x0x1xDecoder(information, '310', '11'); - case 57: return new AI013x0x1xDecoder(information, '320', '11'); - case 58: return new AI013x0x1xDecoder(information, '310', '13'); - case 59: return new AI013x0x1xDecoder(information, '320', '13'); - case 60: return new AI013x0x1xDecoder(information, '310', '15'); - case 61: return new AI013x0x1xDecoder(information, '320', '15'); - case 62: return new AI013x0x1xDecoder(information, '310', '17'); - case 63: return new AI013x0x1xDecoder(information, '320', '17'); - } - } - catch (e) { - console.log(e); - throw new IllegalStateException('unknown decoder: ' + information); - } - } - - class ExpandedPair { - constructor(leftChar, rightChar, finderPatter, mayBeLast) { - this.leftchar = leftChar; - this.rightchar = rightChar; - this.finderpattern = finderPatter; - this.maybeLast = mayBeLast; - } - mayBeLast() { - return this.maybeLast; - } - getLeftChar() { - return this.leftchar; - } - getRightChar() { - return this.rightchar; - } - getFinderPattern() { - return this.finderpattern; - } - mustBeLast() { - return this.rightchar == null; - } - toString() { - return '[ ' + this.leftchar + ', ' + this.rightchar + ' : ' + (this.finderpattern == null ? 'null' : this.finderpattern.getValue()) + ' ]'; - } - static equals(o1, o2) { - if (!(o1 instanceof ExpandedPair)) { - return false; - } - return ExpandedPair.equalsOrNull(o1.leftchar, o2.leftchar) && - ExpandedPair.equalsOrNull(o1.rightchar, o2.rightchar) && - ExpandedPair.equalsOrNull(o1.finderpattern, o2.finderpattern); - } - static equalsOrNull(o1, o2) { - return o1 === null ? o2 === null : ExpandedPair.equals(o1, o2); - } - hashCode() { - // return ExpandedPair.hashNotNull(leftChar) ^ hashNotNull(rightChar) ^ hashNotNull(finderPattern); - let value = this.leftchar.getValue() ^ this.rightchar.getValue() ^ this.finderpattern.getValue(); - return value; - } - } - - class ExpandedRow { - constructor(pairs, rowNumber, wasReversed) { - this.pairs = pairs; - this.rowNumber = rowNumber; - this.wasReversed = wasReversed; - } - getPairs() { - return this.pairs; - } - getRowNumber() { - return this.rowNumber; - } - isReversed() { - return this.wasReversed; - } - // check implementation - isEquivalent(otherPairs) { - return this.checkEqualitity(this, otherPairs); - } - // @Override - toString() { - return '{ ' + this.pairs + ' }'; - } - /** - * Two rows are equal if they contain the same pairs in the same order. - */ - // @Override - // check implementation - equals(o1, o2) { - if (!(o1 instanceof ExpandedRow)) { - return false; - } - return this.checkEqualitity(o1, o2) && o1.wasReversed === o2.wasReversed; - } - checkEqualitity(pair1, pair2) { - if (!pair1 || !pair2) - return; - let result; - pair1.forEach((e1, i) => { - pair2.forEach(e2 => { - if (e1.getLeftChar().getValue() === e2.getLeftChar().getValue() && e1.getRightChar().getValue() === e2.getRightChar().getValue() && e1.getFinderPatter().getValue() === e2.getFinderPatter().getValue()) { - result = true; - } - }); - }); - return result; - } - } - - // import java.util.ArrayList; - // import java.util.Iterator; - // import java.util.List; - // import java.util.Map; - // import java.util.Collections; - class RSSExpandedReader extends AbstractRSSReader { - constructor(verbose) { - super(...arguments); - this.pairs = new Array(RSSExpandedReader.MAX_PAIRS); - this.rows = new Array(); - this.startEnd = [2]; - this.verbose = (verbose === true); - } - decodeRow(rowNumber, row, hints) { - // Rows can start with even pattern in case in prev rows there where odd number of patters. - // So lets try twice - // this.pairs.clear(); - this.pairs.length = 0; - this.startFromEven = false; - try { - return RSSExpandedReader.constructResult(this.decodeRow2pairs(rowNumber, row)); - } - catch (e) { - // OK - if (this.verbose) { - console.log(e); - } - } - this.pairs.length = 0; - this.startFromEven = true; - return RSSExpandedReader.constructResult(this.decodeRow2pairs(rowNumber, row)); - } - reset() { - this.pairs.length = 0; - this.rows.length = 0; - } - // Not private for testing - decodeRow2pairs(rowNumber, row) { - let done = false; - while (!done) { - try { - this.pairs.push(this.retrieveNextPair(row, this.pairs, rowNumber)); - } - catch (error) { - if (error instanceof NotFoundException) { - if (!this.pairs.length) { - throw new NotFoundException(); - } - // exit this loop when retrieveNextPair() fails and throws - done = true; - } - } - } - // TODO: verify sequence of finder patterns as in checkPairSequence() - if (this.checkChecksum()) { - return this.pairs; - } - let tryStackedDecode; - if (this.rows.length) { - tryStackedDecode = true; - } - else { - tryStackedDecode = false; - } - // let tryStackedDecode = !this.rows.isEmpty(); - this.storeRow(rowNumber, false); // TODO: deal with reversed rows - if (tryStackedDecode) { - // When the image is 180-rotated, then rows are sorted in wrong direction. - // Try twice with both the directions. - let ps = this.checkRowsBoolean(false); - if (ps != null) { - return ps; - } - ps = this.checkRowsBoolean(true); - if (ps != null) { - return ps; - } - } - throw new NotFoundException(); - } - // Need to Verify - checkRowsBoolean(reverse) { - // Limit number of rows we are checking - // We use recursive algorithm with pure complexity and don't want it to take forever - // Stacked barcode can have up to 11 rows, so 25 seems reasonable enough - if (this.rows.length > 25) { - this.rows.length = 0; // We will never have a chance to get result, so clear it - return null; - } - this.pairs.length = 0; - if (reverse) { - this.rows = this.rows.reverse(); - // Collections.reverse(this.rows); - } - let ps = null; - try { - ps = this.checkRows(new Array(), 0); - } - catch (e) { - // OK - if (this.verbose) { - console.log(e); - } - } - if (reverse) { - this.rows = this.rows.reverse(); - // Collections.reverse(this.rows); - } - return ps; - } - // Try to construct a valid rows sequence - // Recursion is used to implement backtracking - checkRows(collectedRows, currentRow) { - for (let i = currentRow; i < this.rows.length; i++) { - let row = this.rows[i]; - this.pairs.length = 0; - for (let collectedRow of collectedRows) { - this.pairs.push(collectedRow.getPairs()); - } - this.pairs.push(row.getPairs()); - if (!RSSExpandedReader.isValidSequence(this.pairs)) { - continue; - } - if (this.checkChecksum()) { - return this.pairs; - } - let rs = new Array(collectedRows); - rs.push(row); - try { - // Recursion: try to add more rows - return this.checkRows(rs, i + 1); - } - catch (e) { - // We failed, try the next candidate - if (this.verbose) { - console.log(e); - } - } - } - throw new NotFoundException(); - } - // Whether the pairs form a valid find pattern sequence, - // either complete or a prefix - static isValidSequence(pairs) { - for (let sequence of RSSExpandedReader.FINDER_PATTERN_SEQUENCES) { - if (pairs.length > sequence.length) { - continue; - } - let stop = true; - for (let j = 0; j < pairs.length; j++) { - if (pairs[j].getFinderPattern().getValue() != sequence[j]) { - stop = false; - break; - } - } - if (stop) { - return true; - } - } - return false; - } - storeRow(rowNumber, wasReversed) { - // Discard if duplicate above or below; otherwise insert in order by row number. - let insertPos = 0; - let prevIsSame = false; - let nextIsSame = false; - while (insertPos < this.rows.length) { - let erow = this.rows[insertPos]; - if (erow.getRowNumber() > rowNumber) { - nextIsSame = erow.isEquivalent(this.pairs); - break; - } - prevIsSame = erow.isEquivalent(this.pairs); - insertPos++; - } - if (nextIsSame || prevIsSame) { - return; - } - // When the row was partially decoded (e.g. 2 pairs found instead of 3), - // it will prevent us from detecting the barcode. - // Try to merge partial rows - // Check whether the row is part of an allready detected row - if (RSSExpandedReader.isPartialRow(this.pairs, this.rows)) { - return; - } - this.rows.push(insertPos, new ExpandedRow(this.pairs, rowNumber, wasReversed)); - this.removePartialRows(this.pairs, this.rows); - } - // Remove all the rows that contains only specified pairs - removePartialRows(pairs, rows) { - // for (IteratorEncapsulates a set of error-correction blocks in one symbol version. Most versions will - * use blocks of differing sizes within one version, so, this encapsulates the parameters for - * each set of blocks. It also holds the number of error-correction codewords per block since it - * will be the same across all blocks within one version.
- */ - class ECBlocks { - constructor(ecCodewords, ecBlocks1, ecBlocks2) { - this.ecCodewords = ecCodewords; - this.ecBlocks = [ecBlocks1]; - ecBlocks2 && this.ecBlocks.push(ecBlocks2); - } - getECCodewords() { - return this.ecCodewords; - } - getECBlocks() { - return this.ecBlocks; - } - } - /** - *Encapsulates the parameters for one error-correction block in one symbol version. - * This includes the number of data codewords, and the number of times a block with these - * parameters is used consecutively in the Data Matrix code version's format.
- */ - class ECB { - constructor(count, dataCodewords) { - this.count = count; - this.dataCodewords = dataCodewords; - } - getCount() { - return this.count; - } - getDataCodewords() { - return this.dataCodewords; - } - } - /** - * The Version object encapsulates attributes about a particular - * size Data Matrix Code. - * - * @author bbrown@google.com (Brian Brown) - */ - class Version { - constructor(versionNumber, symbolSizeRows, symbolSizeColumns, dataRegionSizeRows, dataRegionSizeColumns, ecBlocks) { - this.versionNumber = versionNumber; - this.symbolSizeRows = symbolSizeRows; - this.symbolSizeColumns = symbolSizeColumns; - this.dataRegionSizeRows = dataRegionSizeRows; - this.dataRegionSizeColumns = dataRegionSizeColumns; - this.ecBlocks = ecBlocks; - // Calculate the total number of codewords - let total = 0; - const ecCodewords = ecBlocks.getECCodewords(); - const ecbArray = ecBlocks.getECBlocks(); - for (let ecBlock of ecbArray) { - total += ecBlock.getCount() * (ecBlock.getDataCodewords() + ecCodewords); - } - this.totalCodewords = total; - } - getVersionNumber() { - return this.versionNumber; - } - getSymbolSizeRows() { - return this.symbolSizeRows; - } - getSymbolSizeColumns() { - return this.symbolSizeColumns; - } - getDataRegionSizeRows() { - return this.dataRegionSizeRows; - } - getDataRegionSizeColumns() { - return this.dataRegionSizeColumns; - } - getTotalCodewords() { - return this.totalCodewords; - } - getECBlocks() { - return this.ecBlocks; - } - /** - *Deduces version information from Data Matrix dimensions.
- * - * @param numRows Number of rows in modules - * @param numColumns Number of columns in modules - * @return Version for a Data Matrix Code of those dimensions - * @throws FormatException if dimensions do correspond to a valid Data Matrix size - */ - static getVersionForDimensions(numRows, numColumns) { - if ((numRows & 0x01) !== 0 || (numColumns & 0x01) !== 0) { - throw new FormatException(); - } - for (let version of Version.VERSIONS) { - if (version.symbolSizeRows === numRows && version.symbolSizeColumns === numColumns) { - return version; - } - } - throw new FormatException(); - } - // @Override - toString() { - return '' + this.versionNumber; - } - /** - * See ISO 16022:2006 5.5.1 Table 7 - */ - static buildVersions() { - return [ - new Version(1, 10, 10, 8, 8, new ECBlocks(5, new ECB(1, 3))), - new Version(2, 12, 12, 10, 10, new ECBlocks(7, new ECB(1, 5))), - new Version(3, 14, 14, 12, 12, new ECBlocks(10, new ECB(1, 8))), - new Version(4, 16, 16, 14, 14, new ECBlocks(12, new ECB(1, 12))), - new Version(5, 18, 18, 16, 16, new ECBlocks(14, new ECB(1, 18))), - new Version(6, 20, 20, 18, 18, new ECBlocks(18, new ECB(1, 22))), - new Version(7, 22, 22, 20, 20, new ECBlocks(20, new ECB(1, 30))), - new Version(8, 24, 24, 22, 22, new ECBlocks(24, new ECB(1, 36))), - new Version(9, 26, 26, 24, 24, new ECBlocks(28, new ECB(1, 44))), - new Version(10, 32, 32, 14, 14, new ECBlocks(36, new ECB(1, 62))), - new Version(11, 36, 36, 16, 16, new ECBlocks(42, new ECB(1, 86))), - new Version(12, 40, 40, 18, 18, new ECBlocks(48, new ECB(1, 114))), - new Version(13, 44, 44, 20, 20, new ECBlocks(56, new ECB(1, 144))), - new Version(14, 48, 48, 22, 22, new ECBlocks(68, new ECB(1, 174))), - new Version(15, 52, 52, 24, 24, new ECBlocks(42, new ECB(2, 102))), - new Version(16, 64, 64, 14, 14, new ECBlocks(56, new ECB(2, 140))), - new Version(17, 72, 72, 16, 16, new ECBlocks(36, new ECB(4, 92))), - new Version(18, 80, 80, 18, 18, new ECBlocks(48, new ECB(4, 114))), - new Version(19, 88, 88, 20, 20, new ECBlocks(56, new ECB(4, 144))), - new Version(20, 96, 96, 22, 22, new ECBlocks(68, new ECB(4, 174))), - new Version(21, 104, 104, 24, 24, new ECBlocks(56, new ECB(6, 136))), - new Version(22, 120, 120, 18, 18, new ECBlocks(68, new ECB(6, 175))), - new Version(23, 132, 132, 20, 20, new ECBlocks(62, new ECB(8, 163))), - new Version(24, 144, 144, 22, 22, new ECBlocks(62, new ECB(8, 156), new ECB(2, 155))), - new Version(25, 8, 18, 6, 16, new ECBlocks(7, new ECB(1, 5))), - new Version(26, 8, 32, 6, 14, new ECBlocks(11, new ECB(1, 10))), - new Version(27, 12, 26, 10, 24, new ECBlocks(14, new ECB(1, 16))), - new Version(28, 12, 36, 10, 16, new ECBlocks(18, new ECB(1, 22))), - new Version(29, 16, 36, 14, 16, new ECBlocks(24, new ECB(1, 32))), - new Version(30, 16, 48, 14, 22, new ECBlocks(28, new ECB(1, 49))) - ]; - } - } - Version.VERSIONS = Version.buildVersions(); - - /* - * Copyright 2007 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. - */ - /** - * @author bbrown@google.com (Brian Brown) - */ - class BitMatrixParser { - /** - * @param bitMatrix {@link BitMatrix} to parse - * @throws FormatException if dimension is < 8 or > 144 or not 0 mod 2 - */ - constructor(bitMatrix) { - const dimension = bitMatrix.getHeight(); - if (dimension < 8 || dimension > 144 || (dimension & 0x01) !== 0) { - throw new FormatException(); - } - this.version = BitMatrixParser.readVersion(bitMatrix); - this.mappingBitMatrix = this.extractDataRegion(bitMatrix); - this.readMappingMatrix = new BitMatrix(this.mappingBitMatrix.getWidth(), this.mappingBitMatrix.getHeight()); - } - getVersion() { - return this.version; - } - /** - *Creates the version object based on the dimension of the original bit matrix from - * the datamatrix code.
- * - *See ISO 16022:2006 Table 7 - ECC 200 symbol attributes
- * - * @param bitMatrix Original {@link BitMatrix} including alignment patterns - * @return {@link Version} encapsulating the Data Matrix Code's "version" - * @throws FormatException if the dimensions of the mapping matrix are not valid - * Data Matrix dimensions. - */ - static readVersion(bitMatrix) { - const numRows = bitMatrix.getHeight(); - const numColumns = bitMatrix.getWidth(); - return Version.getVersionForDimensions(numRows, numColumns); - } - /** - *Reads the bits in the {@link BitMatrix} representing the mapping matrix (No alignment patterns) - * in the correct order in order to reconstitute the codewords bytes contained within the - * Data Matrix Code.
- * - * @return bytes encoded within the Data Matrix Code - * @throws FormatException if the exact number of bytes expected is not read - */ - readCodewords() { - const result = new Int8Array(this.version.getTotalCodewords()); - let resultOffset = 0; - let row = 4; - let column = 0; - const numRows = this.mappingBitMatrix.getHeight(); - const numColumns = this.mappingBitMatrix.getWidth(); - let corner1Read = false; - let corner2Read = false; - let corner3Read = false; - let corner4Read = false; - // Read all of the codewords - do { - // Check the four corner cases - if ((row === numRows) && (column === 0) && !corner1Read) { - result[resultOffset++] = this.readCorner1(numRows, numColumns) & 0xff; - row -= 2; - column += 2; - corner1Read = true; - } - else if ((row === numRows - 2) && (column === 0) && ((numColumns & 0x03) !== 0) && !corner2Read) { - result[resultOffset++] = this.readCorner2(numRows, numColumns) & 0xff; - row -= 2; - column += 2; - corner2Read = true; - } - else if ((row === numRows + 4) && (column === 2) && ((numColumns & 0x07) === 0) && !corner3Read) { - result[resultOffset++] = this.readCorner3(numRows, numColumns) & 0xff; - row -= 2; - column += 2; - corner3Read = true; - } - else if ((row === numRows - 2) && (column === 0) && ((numColumns & 0x07) === 4) && !corner4Read) { - result[resultOffset++] = this.readCorner4(numRows, numColumns) & 0xff; - row -= 2; - column += 2; - corner4Read = true; - } - else { - // Sweep upward diagonally to the right - do { - if ((row < numRows) && (column >= 0) && !this.readMappingMatrix.get(column, row)) { - result[resultOffset++] = this.readUtah(row, column, numRows, numColumns) & 0xff; - } - row -= 2; - column += 2; - } while ((row >= 0) && (column < numColumns)); - row += 1; - column += 3; - // Sweep downward diagonally to the left - do { - if ((row >= 0) && (column < numColumns) && !this.readMappingMatrix.get(column, row)) { - result[resultOffset++] = this.readUtah(row, column, numRows, numColumns) & 0xff; - } - row += 2; - column -= 2; - } while ((row < numRows) && (column >= 0)); - row += 3; - column += 1; - } - } while ((row < numRows) || (column < numColumns)); - if (resultOffset !== this.version.getTotalCodewords()) { - throw new FormatException(); - } - return result; - } - /** - *Reads a bit of the mapping matrix accounting for boundary wrapping.
- * - * @param row Row to read in the mapping matrix - * @param column Column to read in the mapping matrix - * @param numRows Number of rows in the mapping matrix - * @param numColumns Number of columns in the mapping matrix - * @return value of the given bit in the mapping matrix - */ - readModule(row, column, numRows, numColumns) { - // Adjust the row and column indices based on boundary wrapping - if (row < 0) { - row += numRows; - column += 4 - ((numRows + 4) & 0x07); - } - if (column < 0) { - column += numColumns; - row += 4 - ((numColumns + 4) & 0x07); - } - this.readMappingMatrix.set(column, row); - return this.mappingBitMatrix.get(column, row); - } - /** - *Reads the 8 bits of the standard Utah-shaped pattern.
- * - *See ISO 16022:2006, 5.8.1 Figure 6
- * - * @param row Current row in the mapping matrix, anchored at the 8th bit (LSB) of the pattern - * @param column Current column in the mapping matrix, anchored at the 8th bit (LSB) of the pattern - * @param numRows Number of rows in the mapping matrix - * @param numColumns Number of columns in the mapping matrix - * @return byte from the utah shape - */ - readUtah(row, column, numRows, numColumns) { - let currentByte = 0; - if (this.readModule(row - 2, column - 2, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (this.readModule(row - 2, column - 1, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (this.readModule(row - 1, column - 2, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (this.readModule(row - 1, column - 1, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (this.readModule(row - 1, column, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (this.readModule(row, column - 2, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (this.readModule(row, column - 1, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (this.readModule(row, column, numRows, numColumns)) { - currentByte |= 1; - } - return currentByte; - } - /** - *Reads the 8 bits of the special corner condition 1.
- * - *See ISO 16022:2006, Figure F.3
- * - * @param numRows Number of rows in the mapping matrix - * @param numColumns Number of columns in the mapping matrix - * @return byte from the Corner condition 1 - */ - readCorner1(numRows, numColumns) { - let currentByte = 0; - if (this.readModule(numRows - 1, 0, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (this.readModule(numRows - 1, 1, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (this.readModule(numRows - 1, 2, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (this.readModule(0, numColumns - 2, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (this.readModule(0, numColumns - 1, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (this.readModule(1, numColumns - 1, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (this.readModule(2, numColumns - 1, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (this.readModule(3, numColumns - 1, numRows, numColumns)) { - currentByte |= 1; - } - return currentByte; - } - /** - *Reads the 8 bits of the special corner condition 2.
- * - *See ISO 16022:2006, Figure F.4
- * - * @param numRows Number of rows in the mapping matrix - * @param numColumns Number of columns in the mapping matrix - * @return byte from the Corner condition 2 - */ - readCorner2(numRows, numColumns) { - let currentByte = 0; - if (this.readModule(numRows - 3, 0, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (this.readModule(numRows - 2, 0, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (this.readModule(numRows - 1, 0, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (this.readModule(0, numColumns - 4, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (this.readModule(0, numColumns - 3, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (this.readModule(0, numColumns - 2, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (this.readModule(0, numColumns - 1, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (this.readModule(1, numColumns - 1, numRows, numColumns)) { - currentByte |= 1; - } - return currentByte; - } - /** - *Reads the 8 bits of the special corner condition 3.
- * - *See ISO 16022:2006, Figure F.5
- * - * @param numRows Number of rows in the mapping matrix - * @param numColumns Number of columns in the mapping matrix - * @return byte from the Corner condition 3 - */ - readCorner3(numRows, numColumns) { - let currentByte = 0; - if (this.readModule(numRows - 1, 0, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (this.readModule(numRows - 1, numColumns - 1, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (this.readModule(0, numColumns - 3, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (this.readModule(0, numColumns - 2, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (this.readModule(0, numColumns - 1, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (this.readModule(1, numColumns - 3, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (this.readModule(1, numColumns - 2, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (this.readModule(1, numColumns - 1, numRows, numColumns)) { - currentByte |= 1; - } - return currentByte; - } - /** - *Reads the 8 bits of the special corner condition 4.
- * - *See ISO 16022:2006, Figure F.6
- * - * @param numRows Number of rows in the mapping matrix - * @param numColumns Number of columns in the mapping matrix - * @return byte from the Corner condition 4 - */ - readCorner4(numRows, numColumns) { - let currentByte = 0; - if (this.readModule(numRows - 3, 0, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (this.readModule(numRows - 2, 0, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (this.readModule(numRows - 1, 0, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (this.readModule(0, numColumns - 2, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (this.readModule(0, numColumns - 1, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (this.readModule(1, numColumns - 1, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (this.readModule(2, numColumns - 1, numRows, numColumns)) { - currentByte |= 1; - } - currentByte <<= 1; - if (this.readModule(3, numColumns - 1, numRows, numColumns)) { - currentByte |= 1; - } - return currentByte; - } - /** - *Extracts the data region from a {@link BitMatrix} that contains - * alignment patterns.
- * - * @param bitMatrix Original {@link BitMatrix} with alignment patterns - * @return BitMatrix that has the alignment patterns removed - */ - extractDataRegion(bitMatrix) { - const symbolSizeRows = this.version.getSymbolSizeRows(); - const symbolSizeColumns = this.version.getSymbolSizeColumns(); - if (bitMatrix.getHeight() !== symbolSizeRows) { - throw new IllegalArgumentException('Dimension of bitMatrix must match the version size'); - } - const dataRegionSizeRows = this.version.getDataRegionSizeRows(); - const dataRegionSizeColumns = this.version.getDataRegionSizeColumns(); - const numDataRegionsRow = symbolSizeRows / dataRegionSizeRows | 0; - const numDataRegionsColumn = symbolSizeColumns / dataRegionSizeColumns | 0; - const sizeDataRegionRow = numDataRegionsRow * dataRegionSizeRows; - const sizeDataRegionColumn = numDataRegionsColumn * dataRegionSizeColumns; - const bitMatrixWithoutAlignment = new BitMatrix(sizeDataRegionColumn, sizeDataRegionRow); - for (let dataRegionRow = 0; dataRegionRow < numDataRegionsRow; ++dataRegionRow) { - const dataRegionRowOffset = dataRegionRow * dataRegionSizeRows; - for (let dataRegionColumn = 0; dataRegionColumn < numDataRegionsColumn; ++dataRegionColumn) { - const dataRegionColumnOffset = dataRegionColumn * dataRegionSizeColumns; - for (let i = 0; i < dataRegionSizeRows; ++i) { - const readRowOffset = dataRegionRow * (dataRegionSizeRows + 2) + 1 + i; - const writeRowOffset = dataRegionRowOffset + i; - for (let j = 0; j < dataRegionSizeColumns; ++j) { - const readColumnOffset = dataRegionColumn * (dataRegionSizeColumns + 2) + 1 + j; - if (bitMatrix.get(readColumnOffset, readRowOffset)) { - const writeColumnOffset = dataRegionColumnOffset + j; - bitMatrixWithoutAlignment.set(writeColumnOffset, writeRowOffset); - } - } - } - } - } - return bitMatrixWithoutAlignment; - } - } - - /* - * 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. - */ - /** - *Encapsulates a block of data within a Data Matrix Code. Data Matrix Codes may split their data into - * multiple blocks, each of which is a unit of data and error-correction codewords. Each - * is represented by an instance of this class.
- * - * @author bbrown@google.com (Brian Brown) - */ - class DataBlock { - constructor(numDataCodewords, codewords) { - this.numDataCodewords = numDataCodewords; - this.codewords = codewords; - } - /** - *When Data Matrix Codes use multiple data blocks, they actually interleave the bytes of each of them. - * That is, the first byte of data block 1 to n is written, then the second bytes, and so on. This - * method will separate the data into original blocks.
- * - * @param rawCodewords bytes as read directly from the Data Matrix Code - * @param version version of the Data Matrix Code - * @return DataBlocks containing original bytes, "de-interleaved" from representation in the - * Data Matrix Code - */ - static getDataBlocks(rawCodewords, version) { - // Figure out the number and size of data blocks used by this version - const ecBlocks = version.getECBlocks(); - // First count the total number of data blocks - let totalBlocks = 0; - const ecBlockArray = ecBlocks.getECBlocks(); - for (let ecBlock of ecBlockArray) { - totalBlocks += ecBlock.getCount(); - } - // Now establish DataBlocks of the appropriate size and number of data codewords - const result = new Array(totalBlocks); - let numResultBlocks = 0; - for (let ecBlock of ecBlockArray) { - for (let i = 0; i < ecBlock.getCount(); i++) { - const numDataCodewords = ecBlock.getDataCodewords(); - const numBlockCodewords = ecBlocks.getECCodewords() + numDataCodewords; - result[numResultBlocks++] = new DataBlock(numDataCodewords, new Uint8Array(numBlockCodewords)); - } - } - // All blocks have the same amount of data, except that the last n - // (where n may be 0) have 1 less byte. Figure out where these start. - // TODO(bbrown): There is only one case where there is a difference for Data Matrix for size 144 - const longerBlocksTotalCodewords = result[0].codewords.length; - // int shorterBlocksTotalCodewords = longerBlocksTotalCodewords - 1; - const longerBlocksNumDataCodewords = longerBlocksTotalCodewords - ecBlocks.getECCodewords(); - const shorterBlocksNumDataCodewords = longerBlocksNumDataCodewords - 1; - // The last elements of result may be 1 element shorter for 144 matrix - // first fill out as many elements as all of them have minus 1 - let rawCodewordsOffset = 0; - for (let i = 0; i < shorterBlocksNumDataCodewords; i++) { - for (let j = 0; j < numResultBlocks; j++) { - result[j].codewords[i] = rawCodewords[rawCodewordsOffset++]; - } - } - // Fill out the last data block in the longer ones - const specialVersion = version.getVersionNumber() === 24; - const numLongerBlocks = specialVersion ? 8 : numResultBlocks; - for (let j = 0; j < numLongerBlocks; j++) { - result[j].codewords[longerBlocksNumDataCodewords - 1] = rawCodewords[rawCodewordsOffset++]; - } - // Now add in error correction blocks - const max = result[0].codewords.length; - for (let i = longerBlocksNumDataCodewords; i < max; i++) { - for (let j = 0; j < numResultBlocks; j++) { - const jOffset = specialVersion ? (j + 8) % numResultBlocks : j; - const iOffset = specialVersion && jOffset > 7 ? i - 1 : i; - result[jOffset].codewords[iOffset] = rawCodewords[rawCodewordsOffset++]; - } - } - if (rawCodewordsOffset !== rawCodewords.length) { - throw new IllegalArgumentException(); - } - return result; - } - getNumDataCodewords() { - return this.numDataCodewords; - } - getCodewords() { - return this.codewords; - } - } - - /* - * Copyright 2007 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. - */ - /** - *This provides an easy abstraction to read bits at a time from a sequence of bytes, where the - * number of bits read is not often a multiple of 8.
- * - *This class is thread-safe but not reentrant -- unless the caller modifies the bytes array - * it passed in, in which case all bets are off.
- * - * @author Sean Owen - */ - class BitSource { - /** - * @param bytes bytes from which this will read bits. Bits will be read from the first byte first. - * Bits are read within a byte from most-significant to least-significant bit. - */ - constructor(bytes) { - this.bytes = bytes; - this.byteOffset = 0; - this.bitOffset = 0; - } - /** - * @return index of next bit in current byte which would be read by the next call to {@link #readBits(int)}. - */ - getBitOffset() { - return this.bitOffset; - } - /** - * @return index of next byte in input byte array which would be read by the next call to {@link #readBits(int)}. - */ - getByteOffset() { - return this.byteOffset; - } - /** - * @param numBits number of bits to read - * @return int representing the bits read. The bits will appear as the least-significant - * bits of the int - * @throws IllegalArgumentException if numBits isn't in [1,32] or more than is available - */ - readBits(numBits /*int*/) { - if (numBits < 1 || numBits > 32 || numBits > this.available()) { - throw new IllegalArgumentException('' + numBits); - } - let result = 0; - let bitOffset = this.bitOffset; - let byteOffset = this.byteOffset; - const bytes = this.bytes; - // First, read remainder from current byte - if (bitOffset > 0) { - const bitsLeft = 8 - bitOffset; - const toRead = numBits < bitsLeft ? numBits : bitsLeft; - const bitsToNotRead = bitsLeft - toRead; - const mask = (0xFF >> (8 - toRead)) << bitsToNotRead; - result = (bytes[byteOffset] & mask) >> bitsToNotRead; - numBits -= toRead; - bitOffset += toRead; - if (bitOffset === 8) { - bitOffset = 0; - byteOffset++; - } - } - // Next read whole bytes - if (numBits > 0) { - while (numBits >= 8) { - result = (result << 8) | (bytes[byteOffset] & 0xFF); - byteOffset++; - numBits -= 8; - } - // Finally read a partial byte - if (numBits > 0) { - const bitsToNotRead = 8 - numBits; - const mask = (0xFF >> bitsToNotRead) << bitsToNotRead; - result = (result << numBits) | ((bytes[byteOffset] & mask) >> bitsToNotRead); - bitOffset += numBits; - } - } - this.bitOffset = bitOffset; - this.byteOffset = byteOffset; - return result; - } - /** - * @return number of bits that can be read successfully - */ - available() { - return 8 * (this.bytes.length - this.byteOffset) - this.bitOffset; - } - } - - /* - * 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. - */ - var Mode; - (function (Mode) { - Mode[Mode["PAD_ENCODE"] = 0] = "PAD_ENCODE"; - Mode[Mode["ASCII_ENCODE"] = 1] = "ASCII_ENCODE"; - Mode[Mode["C40_ENCODE"] = 2] = "C40_ENCODE"; - Mode[Mode["TEXT_ENCODE"] = 3] = "TEXT_ENCODE"; - Mode[Mode["ANSIX12_ENCODE"] = 4] = "ANSIX12_ENCODE"; - Mode[Mode["EDIFACT_ENCODE"] = 5] = "EDIFACT_ENCODE"; - Mode[Mode["BASE256_ENCODE"] = 6] = "BASE256_ENCODE"; - })(Mode || (Mode = {})); - /** - *Data Matrix Codes can encode text as bits in one of several modes, and can use multiple modes - * in one Data Matrix Code. This class decodes the bits back into text.
- * - *See ISO 16022:2006, 5.2.1 - 5.2.9.2
- * - * @author bbrown@google.com (Brian Brown) - * @author Sean Owen - */ - class DecodedBitStreamParser { - static decode(bytes) { - const bits = new BitSource(bytes); - const result = new StringBuilder(); - const resultTrailer = new StringBuilder(); - const byteSegments = new Array(); - let mode = Mode.ASCII_ENCODE; - do { - if (mode === Mode.ASCII_ENCODE) { - mode = this.decodeAsciiSegment(bits, result, resultTrailer); - } - else { - switch (mode) { - case Mode.C40_ENCODE: - this.decodeC40Segment(bits, result); - break; - case Mode.TEXT_ENCODE: - this.decodeTextSegment(bits, result); - break; - case Mode.ANSIX12_ENCODE: - this.decodeAnsiX12Segment(bits, result); - break; - case Mode.EDIFACT_ENCODE: - this.decodeEdifactSegment(bits, result); - break; - case Mode.BASE256_ENCODE: - this.decodeBase256Segment(bits, result, byteSegments); - break; - default: - throw new FormatException(); - } - mode = Mode.ASCII_ENCODE; - } - } while (mode !== Mode.PAD_ENCODE && bits.available() > 0); - if (resultTrailer.length() > 0) { - result.append(resultTrailer.toString()); - } - return new DecoderResult(bytes, result.toString(), byteSegments.length === 0 ? null : byteSegments, null); - } - /** - * See ISO 16022:2006, 5.2.3 and Annex C, Table C.2 - */ - static decodeAsciiSegment(bits, result, resultTrailer) { - let upperShift = false; - do { - let oneByte = bits.readBits(8); - if (oneByte === 0) { - throw new FormatException(); - } - else if (oneByte <= 128) { // ASCII data (ASCII value + 1) - if (upperShift) { - oneByte += 128; - // upperShift = false; - } - result.append(String.fromCharCode(oneByte - 1)); - return Mode.ASCII_ENCODE; - } - else if (oneByte === 129) { // Pad - return Mode.PAD_ENCODE; - } - else if (oneByte <= 229) { // 2-digit data 00-99 (Numeric Value + 130) - const value = oneByte - 130; - if (value < 10) { // pad with '0' for single digit values - result.append('0'); - } - result.append('' + value); - } - else { - switch (oneByte) { - case 230: // Latch to C40 encodation - return Mode.C40_ENCODE; - case 231: // Latch to Base 256 encodation - return Mode.BASE256_ENCODE; - case 232: // FNC1 - result.append(String.fromCharCode(29)); // translate as ASCII 29 - break; - case 233: // Structured Append - case 234: // Reader Programming - // Ignore these symbols for now - // throw ReaderException.getInstance(); - break; - case 235: // Upper Shift (shift to Extended ASCII) - upperShift = true; - break; - case 236: // 05 Macro - result.append('[)>\u001E05\u001D'); - resultTrailer.insert(0, '\u001E\u0004'); - break; - case 237: // 06 Macro - result.append('[)>\u001E06\u001D'); - resultTrailer.insert(0, '\u001E\u0004'); - break; - case 238: // Latch to ANSI X12 encodation - return Mode.ANSIX12_ENCODE; - case 239: // Latch to Text encodation - return Mode.TEXT_ENCODE; - case 240: // Latch to EDIFACT encodation - return Mode.EDIFACT_ENCODE; - case 241: // ECI Character - // TODO(bbrown): I think we need to support ECI - // throw ReaderException.getInstance(); - // Ignore this symbol for now - break; - default: - // Not to be used in ASCII encodation - // but work around encoders that end with 254, latch back to ASCII - if (oneByte !== 254 || bits.available() !== 0) { - throw new FormatException(); - } - break; - } - } - } while (bits.available() > 0); - return Mode.ASCII_ENCODE; - } - /** - * See ISO 16022:2006, 5.2.5 and Annex C, Table C.1 - */ - static decodeC40Segment(bits, result) { - // Three C40 values are encoded in a 16-bit value as - // (1600 * C1) + (40 * C2) + C3 + 1 - // TODO(bbrown): The Upper Shift with C40 doesn't work in the 4 value scenario all the time - let upperShift = false; - const cValues = []; - let shift = 0; - do { - // If there is only one byte left then it will be encoded as ASCII - if (bits.available() === 8) { - return; - } - const firstByte = bits.readBits(8); - if (firstByte === 254) { // Unlatch codeword - return; - } - this.parseTwoBytes(firstByte, bits.readBits(8), cValues); - for (let i = 0; i < 3; i++) { - const cValue = cValues[i]; - switch (shift) { - case 0: - if (cValue < 3) { - shift = cValue + 1; - } - else if (cValue < this.C40_BASIC_SET_CHARS.length) { - const c40char = this.C40_BASIC_SET_CHARS[cValue]; - if (upperShift) { - result.append(String.fromCharCode(c40char.charCodeAt(0) + 128)); - upperShift = false; - } - else { - result.append(c40char); - } - } - else { - throw new FormatException(); - } - break; - case 1: - if (upperShift) { - result.append(String.fromCharCode(cValue + 128)); - upperShift = false; - } - else { - result.append(String.fromCharCode(cValue)); - } - shift = 0; - break; - case 2: - if (cValue < this.C40_SHIFT2_SET_CHARS.length) { - const c40char = this.C40_SHIFT2_SET_CHARS[cValue]; - if (upperShift) { - result.append(String.fromCharCode(c40char.charCodeAt(0) + 128)); - upperShift = false; - } - else { - result.append(c40char); - } - } - else { - switch (cValue) { - case 27: // FNC1 - result.append(String.fromCharCode(29)); // translate as ASCII 29 - break; - case 30: // Upper Shift - upperShift = true; - break; - default: - throw new FormatException(); - } - } - shift = 0; - break; - case 3: - if (upperShift) { - result.append(String.fromCharCode(cValue + 224)); - upperShift = false; - } - else { - result.append(String.fromCharCode(cValue + 96)); - } - shift = 0; - break; - default: - throw new FormatException(); - } - } - } while (bits.available() > 0); - } - /** - * See ISO 16022:2006, 5.2.6 and Annex C, Table C.2 - */ - static decodeTextSegment(bits, result) { - // Three Text values are encoded in a 16-bit value as - // (1600 * C1) + (40 * C2) + C3 + 1 - // TODO(bbrown): The Upper Shift with Text doesn't work in the 4 value scenario all the time - let upperShift = false; - let cValues = []; - let shift = 0; - do { - // If there is only one byte left then it will be encoded as ASCII - if (bits.available() === 8) { - return; - } - const firstByte = bits.readBits(8); - if (firstByte === 254) { // Unlatch codeword - return; - } - this.parseTwoBytes(firstByte, bits.readBits(8), cValues); - for (let i = 0; i < 3; i++) { - const cValue = cValues[i]; - switch (shift) { - case 0: - if (cValue < 3) { - shift = cValue + 1; - } - else if (cValue < this.TEXT_BASIC_SET_CHARS.length) { - const textChar = this.TEXT_BASIC_SET_CHARS[cValue]; - if (upperShift) { - result.append(String.fromCharCode(textChar.charCodeAt(0) + 128)); - upperShift = false; - } - else { - result.append(textChar); - } - } - else { - throw new FormatException(); - } - break; - case 1: - if (upperShift) { - result.append(String.fromCharCode(cValue + 128)); - upperShift = false; - } - else { - result.append(String.fromCharCode(cValue)); - } - shift = 0; - break; - case 2: - // Shift 2 for Text is the same encoding as C40 - if (cValue < this.TEXT_SHIFT2_SET_CHARS.length) { - const textChar = this.TEXT_SHIFT2_SET_CHARS[cValue]; - if (upperShift) { - result.append(String.fromCharCode(textChar.charCodeAt(0) + 128)); - upperShift = false; - } - else { - result.append(textChar); - } - } - else { - switch (cValue) { - case 27: // FNC1 - result.append(String.fromCharCode(29)); // translate as ASCII 29 - break; - case 30: // Upper Shift - upperShift = true; - break; - default: - throw new FormatException(); - } - } - shift = 0; - break; - case 3: - if (cValue < this.TEXT_SHIFT3_SET_CHARS.length) { - const textChar = this.TEXT_SHIFT3_SET_CHARS[cValue]; - if (upperShift) { - result.append(String.fromCharCode(textChar.charCodeAt(0) + 128)); - upperShift = false; - } - else { - result.append(textChar); - } - shift = 0; - } - else { - throw new FormatException(); - } - break; - default: - throw new FormatException(); - } - } - } while (bits.available() > 0); - } - /** - * See ISO 16022:2006, 5.2.7 - */ - static decodeAnsiX12Segment(bits, result) { - // Three ANSI X12 values are encoded in a 16-bit value as - // (1600 * C1) + (40 * C2) + C3 + 1 - const cValues = []; - do { - // If there is only one byte left then it will be encoded as ASCII - if (bits.available() === 8) { - return; - } - const firstByte = bits.readBits(8); - if (firstByte === 254) { // Unlatch codeword - return; - } - this.parseTwoBytes(firstByte, bits.readBits(8), cValues); - for (let i = 0; i < 3; i++) { - const cValue = cValues[i]; - switch (cValue) { - case 0: // X12 segment terminatorThe main class which implements Data Matrix Code decoding -- as opposed to locating and extracting - * the Data Matrix Code from an image.
- * - * @author bbrown@google.com (Brian Brown) - */ - class Decoder$1 { - constructor() { - this.rsDecoder = new ReedSolomonDecoder(GenericGF.DATA_MATRIX_FIELD_256); - } - /** - *Decodes a Data Matrix Code represented as a {@link BitMatrix}. A 1 or "true" is taken - * to mean a black module.
- * - * @param bits booleans representing white/black Data Matrix Code modules - * @return text and bytes encoded within the Data Matrix Code - * @throws FormatException if the Data Matrix Code cannot be decoded - * @throws ChecksumException if error correction fails - */ - decode(bits) { - // Construct a parser and read version, error-correction level - const parser = new BitMatrixParser(bits); - const version = parser.getVersion(); - // Read codewords - const codewords = parser.readCodewords(); - // Separate into data blocks - const dataBlocks = DataBlock.getDataBlocks(codewords, version); - // Count total number of data bytes - let totalBytes = 0; - for (let db of dataBlocks) { - totalBytes += db.getNumDataCodewords(); - } - const resultBytes = new Uint8Array(totalBytes); - const dataBlocksCount = dataBlocks.length; - // Error-correct and copy data blocks together into a stream of bytes - for (let j = 0; j < dataBlocksCount; j++) { - const dataBlock = dataBlocks[j]; - const codewordBytes = dataBlock.getCodewords(); - const numDataCodewords = dataBlock.getNumDataCodewords(); - this.correctErrors(codewordBytes, numDataCodewords); - for (let i = 0; i < numDataCodewords; i++) { - // De-interlace data blocks. - resultBytes[i * dataBlocksCount + j] = codewordBytes[i]; - } - } - // Decode the contents of that stream of bytes - return DecodedBitStreamParser.decode(resultBytes); - } - /** - *Given data and error-correction codewords received, possibly corrupted by errors, attempts to - * correct the errors in-place using Reed-Solomon error correction.
- * - * @param codewordBytes data and error correction codewords - * @param numDataCodewords number of codewords that are data bytes - * @throws ChecksumException if error correction fails - */ - correctErrors(codewordBytes, numDataCodewords) { - // const numCodewords = codewordBytes.length; - // First read into an array of ints - const codewordsInts = new Int32Array(codewordBytes); - // for (let i = 0; i < numCodewords; i++) { - // codewordsInts[i] = codewordBytes[i] & 0xFF; - // } - try { - this.rsDecoder.decode(codewordsInts, codewordBytes.length - numDataCodewords); - } - catch (ignored /* ReedSolomonException */) { - throw new ChecksumException(); - } - // Copy back into array of bytes -- only need to worry about the bytes that were data - // We don't care about errors in the error-correction codewords - for (let i = 0; i < numDataCodewords; i++) { - codewordBytes[i] = codewordsInts[i]; - } - } - } - - /* - * 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. - */ - /** - *Encapsulates logic that can detect a Data Matrix Code in an image, even if the Data Matrix Code - * is rotated or skewed, or partially obscured.
- * - * @author Sean Owen - */ - class Detector$1 { - constructor(image) { - this.image = image; - this.rectangleDetector = new WhiteRectangleDetector(this.image); - } - /** - *Detects a Data Matrix Code in an image.
- * - * @return {@link DetectorResult} encapsulating results of detecting a Data Matrix Code - * @throws NotFoundException if no Data Matrix Code can be found - */ - detect() { - const cornerPoints = this.rectangleDetector.detect(); - let points = this.detectSolid1(cornerPoints); - points = this.detectSolid2(points); - points[3] = this.correctTopRight(points); - if (!points[3]) { - throw new NotFoundException(); - } - points = this.shiftToModuleCenter(points); - const topLeft = points[0]; - const bottomLeft = points[1]; - const bottomRight = points[2]; - const topRight = points[3]; - let dimensionTop = this.transitionsBetween(topLeft, topRight) + 1; - let dimensionRight = this.transitionsBetween(bottomRight, topRight) + 1; - if ((dimensionTop & 0x01) === 1) { - dimensionTop += 1; - } - if ((dimensionRight & 0x01) === 1) { - dimensionRight += 1; - } - if (4 * dimensionTop < 7 * dimensionRight && 4 * dimensionRight < 7 * dimensionTop) { - // The matrix is square - dimensionTop = dimensionRight = Math.max(dimensionTop, dimensionRight); - } - let bits = Detector$1.sampleGrid(this.image, topLeft, bottomLeft, bottomRight, topRight, dimensionTop, dimensionRight); - return new DetectorResult(bits, [topLeft, bottomLeft, bottomRight, topRight]); - } - static shiftPoint(point, to, div) { - let x = (to.getX() - point.getX()) / (div + 1); - let y = (to.getY() - point.getY()) / (div + 1); - return new ResultPoint(point.getX() + x, point.getY() + y); - } - static moveAway(point, fromX, fromY) { - let x = point.getX(); - let y = point.getY(); - if (x < fromX) { - x -= 1; - } - else { - x += 1; - } - if (y < fromY) { - y -= 1; - } - else { - y += 1; - } - return new ResultPoint(x, y); - } - /** - * Detect a solid side which has minimum transition. - */ - detectSolid1(cornerPoints) { - // 0 2 - // 1 3 - let pointA = cornerPoints[0]; - let pointB = cornerPoints[1]; - let pointC = cornerPoints[3]; - let pointD = cornerPoints[2]; - let trAB = this.transitionsBetween(pointA, pointB); - let trBC = this.transitionsBetween(pointB, pointC); - let trCD = this.transitionsBetween(pointC, pointD); - let trDA = this.transitionsBetween(pointD, pointA); - // 0..3 - // : : - // 1--2 - let min = trAB; - let points = [pointD, pointA, pointB, pointC]; - if (min > trBC) { - min = trBC; - points[0] = pointA; - points[1] = pointB; - points[2] = pointC; - points[3] = pointD; - } - if (min > trCD) { - min = trCD; - points[0] = pointB; - points[1] = pointC; - points[2] = pointD; - points[3] = pointA; - } - if (min > trDA) { - points[0] = pointC; - points[1] = pointD; - points[2] = pointA; - points[3] = pointB; - } - return points; - } - /** - * Detect a second solid side next to first solid side. - */ - detectSolid2(points) { - // A..D - // : : - // B--C - let pointA = points[0]; - let pointB = points[1]; - let pointC = points[2]; - let pointD = points[3]; - // Transition detection on the edge is not stable. - // To safely detect, shift the points to the module center. - let tr = this.transitionsBetween(pointA, pointD); - let pointBs = Detector$1.shiftPoint(pointB, pointC, (tr + 1) * 4); - let pointCs = Detector$1.shiftPoint(pointC, pointB, (tr + 1) * 4); - let trBA = this.transitionsBetween(pointBs, pointA); - let trCD = this.transitionsBetween(pointCs, pointD); - // 0..3 - // | : - // 1--2 - if (trBA < trCD) { - // solid sides: A-B-C - points[0] = pointA; - points[1] = pointB; - points[2] = pointC; - points[3] = pointD; - } - else { - // solid sides: B-C-D - points[0] = pointB; - points[1] = pointC; - points[2] = pointD; - points[3] = pointA; - } - return points; - } - /** - * Calculates the corner position of the white top right module. - */ - correctTopRight(points) { - // A..D - // | : - // B--C - let pointA = points[0]; - let pointB = points[1]; - let pointC = points[2]; - let pointD = points[3]; - // shift points for safe transition detection. - let trTop = this.transitionsBetween(pointA, pointD); - let trRight = this.transitionsBetween(pointB, pointD); - let pointAs = Detector$1.shiftPoint(pointA, pointB, (trRight + 1) * 4); - let pointCs = Detector$1.shiftPoint(pointC, pointB, (trTop + 1) * 4); - trTop = this.transitionsBetween(pointAs, pointD); - trRight = this.transitionsBetween(pointCs, pointD); - let candidate1 = new ResultPoint(pointD.getX() + (pointC.getX() - pointB.getX()) / (trTop + 1), pointD.getY() + (pointC.getY() - pointB.getY()) / (trTop + 1)); - let candidate2 = new ResultPoint(pointD.getX() + (pointA.getX() - pointB.getX()) / (trRight + 1), pointD.getY() + (pointA.getY() - pointB.getY()) / (trRight + 1)); - if (!this.isValid(candidate1)) { - if (this.isValid(candidate2)) { - return candidate2; - } - return null; - } - if (!this.isValid(candidate2)) { - return candidate1; - } - let sumc1 = this.transitionsBetween(pointAs, candidate1) + this.transitionsBetween(pointCs, candidate1); - let sumc2 = this.transitionsBetween(pointAs, candidate2) + this.transitionsBetween(pointCs, candidate2); - if (sumc1 > sumc2) { - return candidate1; - } - else { - return candidate2; - } - } - /** - * Shift the edge points to the module center. - */ - shiftToModuleCenter(points) { - // A..D - // | : - // B--C - let pointA = points[0]; - let pointB = points[1]; - let pointC = points[2]; - let pointD = points[3]; - // calculate pseudo dimensions - let dimH = this.transitionsBetween(pointA, pointD) + 1; - let dimV = this.transitionsBetween(pointC, pointD) + 1; - // shift points for safe dimension detection - let pointAs = Detector$1.shiftPoint(pointA, pointB, dimV * 4); - let pointCs = Detector$1.shiftPoint(pointC, pointB, dimH * 4); - // calculate more precise dimensions - dimH = this.transitionsBetween(pointAs, pointD) + 1; - dimV = this.transitionsBetween(pointCs, pointD) + 1; - if ((dimH & 0x01) === 1) { - dimH += 1; - } - if ((dimV & 0x01) === 1) { - dimV += 1; - } - // WhiteRectangleDetector returns points inside of the rectangle. - // I want points on the edges. - let centerX = (pointA.getX() + pointB.getX() + pointC.getX() + pointD.getX()) / 4; - let centerY = (pointA.getY() + pointB.getY() + pointC.getY() + pointD.getY()) / 4; - pointA = Detector$1.moveAway(pointA, centerX, centerY); - pointB = Detector$1.moveAway(pointB, centerX, centerY); - pointC = Detector$1.moveAway(pointC, centerX, centerY); - pointD = Detector$1.moveAway(pointD, centerX, centerY); - let pointBs; - let pointDs; - // shift points to the center of each modules - pointAs = Detector$1.shiftPoint(pointA, pointB, dimV * 4); - pointAs = Detector$1.shiftPoint(pointAs, pointD, dimH * 4); - pointBs = Detector$1.shiftPoint(pointB, pointA, dimV * 4); - pointBs = Detector$1.shiftPoint(pointBs, pointC, dimH * 4); - pointCs = Detector$1.shiftPoint(pointC, pointD, dimV * 4); - pointCs = Detector$1.shiftPoint(pointCs, pointB, dimH * 4); - pointDs = Detector$1.shiftPoint(pointD, pointC, dimV * 4); - pointDs = Detector$1.shiftPoint(pointDs, pointA, dimH * 4); - return [pointAs, pointBs, pointCs, pointDs]; - } - isValid(p) { - return p.getX() >= 0 && p.getX() < this.image.getWidth() && p.getY() > 0 && p.getY() < this.image.getHeight(); - } - static sampleGrid(image, topLeft, bottomLeft, bottomRight, topRight, dimensionX, dimensionY) { - const sampler = GridSamplerInstance.getInstance(); - return sampler.sampleGrid(image, dimensionX, dimensionY, 0.5, 0.5, dimensionX - 0.5, 0.5, dimensionX - 0.5, dimensionY - 0.5, 0.5, dimensionY - 0.5, topLeft.getX(), topLeft.getY(), topRight.getX(), topRight.getY(), bottomRight.getX(), bottomRight.getY(), bottomLeft.getX(), bottomLeft.getY()); - } - /** - * Counts the number of black/white transitions between two points, using something like Bresenham's algorithm. - */ - transitionsBetween(from, to) { - // See QR Code Detector, sizeOfBlackWhiteBlackRun() - let fromX = Math.trunc(from.getX()); - let fromY = Math.trunc(from.getY()); - let toX = Math.trunc(to.getX()); - let toY = Math.trunc(to.getY()); - let steep = Math.abs(toY - fromY) > Math.abs(toX - fromX); - if (steep) { - let temp = fromX; - fromX = fromY; - fromY = temp; - temp = toX; - toX = toY; - toY = temp; - } - let dx = Math.abs(toX - fromX); - let dy = Math.abs(toY - fromY); - let error = -dx / 2; - let ystep = fromY < toY ? 1 : -1; - let xstep = fromX < toX ? 1 : -1; - let transitions = 0; - let inBlack = this.image.get(steep ? fromY : fromX, steep ? fromX : fromY); - for (let x = fromX, y = fromY; x !== toX; x += xstep) { - let isBlack = this.image.get(steep ? y : x, steep ? x : y); - if (isBlack !== inBlack) { - transitions++; - inBlack = isBlack; - } - error += dy; - if (error > 0) { - if (y === toY) { - break; - } - y += ystep; - error -= dx; - } - } - return transitions; - } - } - - /* - * Copyright 2007 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. - */ - /** - * This implementation can detect and decode Data Matrix codes in an image. - * - * @author bbrown@google.com (Brian Brown) - */ - class DataMatrixReader { - constructor() { - this.decoder = new Decoder$1(); - } - /** - * Locates and decodes a Data Matrix code in an image. - * - * @return a String representing the content encoded by the Data Matrix code - * @throws NotFoundException if a Data Matrix code cannot be found - * @throws FormatException if a Data Matrix code cannot be decoded - * @throws ChecksumException if error correction fails - */ - // @Override - // public Result decode(BinaryBitmap image) throws NotFoundException, ChecksumException, FormatException { - // return decode(image, null); - // } - // @Override - decode(image, hints = null) { - let decoderResult; - let points; - if (hints != null && hints.has(DecodeHintType$1.PURE_BARCODE)) { - const bits = DataMatrixReader.extractPureBits(image.getBlackMatrix()); - decoderResult = this.decoder.decode(bits); - points = DataMatrixReader.NO_POINTS; - } - else { - const detectorResult = new Detector$1(image.getBlackMatrix()).detect(); - decoderResult = this.decoder.decode(detectorResult.getBits()); - points = detectorResult.getPoints(); - } - const rawBytes = decoderResult.getRawBytes(); - const result = new Result(decoderResult.getText(), rawBytes, 8 * rawBytes.length, points, BarcodeFormat$1.DATA_MATRIX, System.currentTimeMillis()); - const byteSegments = decoderResult.getByteSegments(); - if (byteSegments != null) { - result.putMetadata(ResultMetadataType$1.BYTE_SEGMENTS, byteSegments); - } - const ecLevel = decoderResult.getECLevel(); - if (ecLevel != null) { - result.putMetadata(ResultMetadataType$1.ERROR_CORRECTION_LEVEL, ecLevel); - } - return result; - } - // @Override - reset() { - // do nothing - } - /** - * This method detects a code in a "pure" image -- that is, pure monochrome image - * which contains only an unrotated, unskewed, image of a code, with some white border - * around it. This is a specialized method that works exceptionally fast in this special - * case. - * - * @see com.google.zxing.qrcode.QRCodeReader#extractPureBits(BitMatrix) - */ - static extractPureBits(image) { - const leftTopBlack = image.getTopLeftOnBit(); - const rightBottomBlack = image.getBottomRightOnBit(); - if (leftTopBlack == null || rightBottomBlack == null) { - throw new NotFoundException(); - } - const moduleSize = this.moduleSize(leftTopBlack, image); - let top = leftTopBlack[1]; - const bottom = rightBottomBlack[1]; - let left = leftTopBlack[0]; - const right = rightBottomBlack[0]; - const matrixWidth = (right - left + 1) / moduleSize; - const matrixHeight = (bottom - top + 1) / moduleSize; - if (matrixWidth <= 0 || matrixHeight <= 0) { - throw new NotFoundException(); - } - // Push in the "border" by half the module width so that we start - // sampling in the middle of the module. Just in case the image is a - // little off, this will help recover. - const nudge = moduleSize / 2; - top += nudge; - left += nudge; - // Now just read off the bits - const bits = new BitMatrix(matrixWidth, matrixHeight); - for (let y = 0; y < matrixHeight; y++) { - const iOffset = top + y * moduleSize; - for (let x = 0; x < matrixWidth; x++) { - if (image.get(left + x * moduleSize, iOffset)) { - bits.set(x, y); - } - } - } - return bits; - } - static moduleSize(leftTopBlack, image) { - const width = image.getWidth(); - let x = leftTopBlack[0]; - const y = leftTopBlack[1]; - while (x < width && image.get(x, y)) { - x++; - } - if (x === width) { - throw new NotFoundException(); - } - const moduleSize = x - leftTopBlack[0]; - if (moduleSize === 0) { - throw new NotFoundException(); - } - return moduleSize; - } - } - DataMatrixReader.NO_POINTS = []; - - /** - * @deprecated Moving to @zxing/browser - * - * QR Code reader to use from browser. - */ - class BrowserDatamatrixCodeReader extends BrowserCodeReader { - /** - * Creates an instance of BrowserQRCodeReader. - * @param {number} [timeBetweenScansMillis=500] the time delay between subsequent decode tries - */ - constructor(timeBetweenScansMillis = 500) { - super(new DataMatrixReader(), timeBetweenScansMillis); - } - } - - /* - * Copyright 2007 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 ErrorCorrectionLevelValues; - (function (ErrorCorrectionLevelValues) { - ErrorCorrectionLevelValues[ErrorCorrectionLevelValues["L"] = 0] = "L"; - ErrorCorrectionLevelValues[ErrorCorrectionLevelValues["M"] = 1] = "M"; - ErrorCorrectionLevelValues[ErrorCorrectionLevelValues["Q"] = 2] = "Q"; - ErrorCorrectionLevelValues[ErrorCorrectionLevelValues["H"] = 3] = "H"; - })(ErrorCorrectionLevelValues || (ErrorCorrectionLevelValues = {})); - /** - *See ISO 18004:2006, 6.5.1. This enum encapsulates the four error correction levels - * defined by the QR code standard.
- * - * @author Sean Owen - */ - class ErrorCorrectionLevel { - constructor(value, stringValue, bits /*int*/) { - this.value = value; - this.stringValue = stringValue; - this.bits = bits; - ErrorCorrectionLevel.FOR_BITS.set(bits, this); - ErrorCorrectionLevel.FOR_VALUE.set(value, this); - } - getValue() { - return this.value; - } - getBits() { - return this.bits; - } - static fromString(s) { - switch (s) { - case 'L': return ErrorCorrectionLevel.L; - case 'M': return ErrorCorrectionLevel.M; - case 'Q': return ErrorCorrectionLevel.Q; - case 'H': return ErrorCorrectionLevel.H; - default: throw new ArgumentException(s + 'not available'); - } - } - toString() { - return this.stringValue; - } - equals(o) { - if (!(o instanceof ErrorCorrectionLevel)) { - return false; - } - const other = o; - return this.value === other.value; - } - /** - * @param bits int containing the two bits encoding a QR Code's error correction level - * @return ErrorCorrectionLevel representing the encoded error correction level - */ - static forBits(bits /*int*/) { - if (bits < 0 || bits >= ErrorCorrectionLevel.FOR_BITS.size) { - throw new IllegalArgumentException(); - } - return ErrorCorrectionLevel.FOR_BITS.get(bits); - } - } - ErrorCorrectionLevel.FOR_BITS = new Map(); - ErrorCorrectionLevel.FOR_VALUE = new Map(); - /** L = ~7% correction */ - ErrorCorrectionLevel.L = new ErrorCorrectionLevel(ErrorCorrectionLevelValues.L, 'L', 0x01); - /** M = ~15% correction */ - ErrorCorrectionLevel.M = new ErrorCorrectionLevel(ErrorCorrectionLevelValues.M, 'M', 0x00); - /** Q = ~25% correction */ - ErrorCorrectionLevel.Q = new ErrorCorrectionLevel(ErrorCorrectionLevelValues.Q, 'Q', 0x03); - /** H = ~30% correction */ - ErrorCorrectionLevel.H = new ErrorCorrectionLevel(ErrorCorrectionLevelValues.H, 'H', 0x02); - - /* - * Copyright 2007 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. - */ - /** - *Encapsulates a QR Code's format information, including the data mask used and - * error correction level.
- * - * @author Sean Owen - * @see DataMask - * @see ErrorCorrectionLevel - */ - class FormatInformation { - constructor(formatInfo /*int*/) { - // Bits 3,4 - this.errorCorrectionLevel = ErrorCorrectionLevel.forBits((formatInfo >> 3) & 0x03); - // Bottom 3 bits - this.dataMask = /*(byte) */ (formatInfo & 0x07); - } - static numBitsDiffering(a /*int*/, b /*int*/) { - return Integer.bitCount(a ^ b); - } - /** - * @param maskedFormatInfo1 format info indicator, with mask still applied - * @param maskedFormatInfo2 second copy of same info; both are checked at the same time - * to establish best match - * @return information about the format it specifies, or {@code null} - * if doesn't seem to match any known pattern - */ - static decodeFormatInformation(maskedFormatInfo1 /*int*/, maskedFormatInfo2 /*int*/) { - const formatInfo = FormatInformation.doDecodeFormatInformation(maskedFormatInfo1, maskedFormatInfo2); - if (formatInfo !== null) { - return formatInfo; - } - // Should return null, but, some QR codes apparently - // do not mask this info. Try again by actually masking the pattern - // first - return FormatInformation.doDecodeFormatInformation(maskedFormatInfo1 ^ FormatInformation.FORMAT_INFO_MASK_QR, maskedFormatInfo2 ^ FormatInformation.FORMAT_INFO_MASK_QR); - } - static doDecodeFormatInformation(maskedFormatInfo1 /*int*/, maskedFormatInfo2 /*int*/) { - // Find the int in FORMAT_INFO_DECODE_LOOKUP with fewest bits differing - let bestDifference = Number.MAX_SAFE_INTEGER; - let bestFormatInfo = 0; - for (const decodeInfo of FormatInformation.FORMAT_INFO_DECODE_LOOKUP) { - const targetInfo = decodeInfo[0]; - if (targetInfo === maskedFormatInfo1 || targetInfo === maskedFormatInfo2) { - // Found an exact match - return new FormatInformation(decodeInfo[1]); - } - let bitsDifference = FormatInformation.numBitsDiffering(maskedFormatInfo1, targetInfo); - if (bitsDifference < bestDifference) { - bestFormatInfo = decodeInfo[1]; - bestDifference = bitsDifference; - } - if (maskedFormatInfo1 !== maskedFormatInfo2) { - // also try the other option - bitsDifference = FormatInformation.numBitsDiffering(maskedFormatInfo2, targetInfo); - if (bitsDifference < bestDifference) { - bestFormatInfo = decodeInfo[1]; - bestDifference = bitsDifference; - } - } - } - // Hamming distance of the 32 masked codes is 7, by construction, so <= 3 bits - // differing means we found a match - if (bestDifference <= 3) { - return new FormatInformation(bestFormatInfo); - } - return null; - } - getErrorCorrectionLevel() { - return this.errorCorrectionLevel; - } - getDataMask() { - return this.dataMask; - } - /*@Override*/ - hashCode() { - return (this.errorCorrectionLevel.getBits() << 3) | this.dataMask; - } - /*@Override*/ - equals(o) { - if (!(o instanceof FormatInformation)) { - return false; - } - const other = o; - return this.errorCorrectionLevel === other.errorCorrectionLevel && - this.dataMask === other.dataMask; - } - } - FormatInformation.FORMAT_INFO_MASK_QR = 0x5412; - /** - * See ISO 18004:2006, Annex C, Table C.1 - */ - FormatInformation.FORMAT_INFO_DECODE_LOOKUP = [ - Int32Array.from([0x5412, 0x00]), - Int32Array.from([0x5125, 0x01]), - Int32Array.from([0x5E7C, 0x02]), - Int32Array.from([0x5B4B, 0x03]), - Int32Array.from([0x45F9, 0x04]), - Int32Array.from([0x40CE, 0x05]), - Int32Array.from([0x4F97, 0x06]), - Int32Array.from([0x4AA0, 0x07]), - Int32Array.from([0x77C4, 0x08]), - Int32Array.from([0x72F3, 0x09]), - Int32Array.from([0x7DAA, 0x0A]), - Int32Array.from([0x789D, 0x0B]), - Int32Array.from([0x662F, 0x0C]), - Int32Array.from([0x6318, 0x0D]), - Int32Array.from([0x6C41, 0x0E]), - Int32Array.from([0x6976, 0x0F]), - Int32Array.from([0x1689, 0x10]), - Int32Array.from([0x13BE, 0x11]), - Int32Array.from([0x1CE7, 0x12]), - Int32Array.from([0x19D0, 0x13]), - Int32Array.from([0x0762, 0x14]), - Int32Array.from([0x0255, 0x15]), - Int32Array.from([0x0D0C, 0x16]), - Int32Array.from([0x083B, 0x17]), - Int32Array.from([0x355F, 0x18]), - Int32Array.from([0x3068, 0x19]), - Int32Array.from([0x3F31, 0x1A]), - Int32Array.from([0x3A06, 0x1B]), - Int32Array.from([0x24B4, 0x1C]), - Int32Array.from([0x2183, 0x1D]), - Int32Array.from([0x2EDA, 0x1E]), - Int32Array.from([0x2BED, 0x1F]), - ]; - - /** - *Encapsulates a set of error-correction blocks in one symbol version. Most versions will - * use blocks of differing sizes within one version, so, this encapsulates the parameters for - * each set of blocks. It also holds the number of error-correction codewords per block since it - * will be the same across all blocks within one version.
- */ - class ECBlocks$1 { - constructor(ecCodewordsPerBlock /*int*/, ...ecBlocks) { - this.ecCodewordsPerBlock = ecCodewordsPerBlock; - this.ecBlocks = ecBlocks; - } - getECCodewordsPerBlock() { - return this.ecCodewordsPerBlock; - } - getNumBlocks() { - let total = 0; - const ecBlocks = this.ecBlocks; - for (const ecBlock of ecBlocks) { - total += ecBlock.getCount(); - } - return total; - } - getTotalECCodewords() { - return this.ecCodewordsPerBlock * this.getNumBlocks(); - } - getECBlocks() { - return this.ecBlocks; - } - } - - /** - *Encapsulates the parameters for one error-correction block in one symbol version. - * This includes the number of data codewords, and the number of times a block with these - * parameters is used consecutively in the QR code version's format.
- */ - class ECB$1 { - constructor(count /*int*/, dataCodewords /*int*/) { - this.count = count; - this.dataCodewords = dataCodewords; - } - getCount() { - return this.count; - } - getDataCodewords() { - return this.dataCodewords; - } - } - - /* - * Copyright 2007 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. - */ - /** - * See ISO 18004:2006 Annex D - * - * @author Sean Owen - */ - class Version$1 { - constructor(versionNumber /*int*/, alignmentPatternCenters, ...ecBlocks) { - this.versionNumber = versionNumber; - this.alignmentPatternCenters = alignmentPatternCenters; - this.ecBlocks = ecBlocks; - let total = 0; - const ecCodewords = ecBlocks[0].getECCodewordsPerBlock(); - const ecbArray = ecBlocks[0].getECBlocks(); - for (const ecBlock of ecbArray) { - total += ecBlock.getCount() * (ecBlock.getDataCodewords() + ecCodewords); - } - this.totalCodewords = total; - } - getVersionNumber() { - return this.versionNumber; - } - getAlignmentPatternCenters() { - return this.alignmentPatternCenters; - } - getTotalCodewords() { - return this.totalCodewords; - } - getDimensionForVersion() { - return 17 + 4 * this.versionNumber; - } - getECBlocksForLevel(ecLevel) { - return this.ecBlocks[ecLevel.getValue()]; - // TYPESCRIPTPORT: original was using ordinal, and using the order of levels as defined in ErrorCorrectionLevel enum (LMQH) - // I will use the direct value from ErrorCorrectionLevelValues enum which in typescript goes to a number - } - /** - *Deduces version information purely from QR Code dimensions.
- * - * @param dimension dimension in modules - * @return Version for a QR Code of that dimension - * @throws FormatException if dimension is not 1 mod 4 - */ - static getProvisionalVersionForDimension(dimension /*int*/) { - if (dimension % 4 !== 1) { - throw new FormatException(); - } - try { - return this.getVersionForNumber((dimension - 17) / 4); - } - catch (ignored /*: IllegalArgumentException*/) { - throw new FormatException(); - } - } - static getVersionForNumber(versionNumber /*int*/) { - if (versionNumber < 1 || versionNumber > 40) { - throw new IllegalArgumentException(); - } - return Version$1.VERSIONS[versionNumber - 1]; - } - static decodeVersionInformation(versionBits /*int*/) { - let bestDifference = Number.MAX_SAFE_INTEGER; - let bestVersion = 0; - for (let i = 0; i < Version$1.VERSION_DECODE_INFO.length; i++) { - const targetVersion = Version$1.VERSION_DECODE_INFO[i]; - // Do the version info bits match exactly? done. - if (targetVersion === versionBits) { - return Version$1.getVersionForNumber(i + 7); - } - // Otherwise see if this is the closest to a real version info bit string - // we have seen so far - const bitsDifference = FormatInformation.numBitsDiffering(versionBits, targetVersion); - if (bitsDifference < bestDifference) { - bestVersion = i + 7; - bestDifference = bitsDifference; - } - } - // We can tolerate up to 3 bits of error since no two version info codewords will - // differ in less than 8 bits. - if (bestDifference <= 3) { - return Version$1.getVersionForNumber(bestVersion); - } - // If we didn't find a close enough match, fail - return null; - } - /** - * See ISO 18004:2006 Annex E - */ - buildFunctionPattern() { - const dimension = this.getDimensionForVersion(); - const bitMatrix = new BitMatrix(dimension); - // Top left finder pattern + separator + format - bitMatrix.setRegion(0, 0, 9, 9); - // Top right finder pattern + separator + format - bitMatrix.setRegion(dimension - 8, 0, 8, 9); - // Bottom left finder pattern + separator + format - bitMatrix.setRegion(0, dimension - 8, 9, 8); - // Alignment patterns - const max = this.alignmentPatternCenters.length; - for (let x = 0; x < max; x++) { - const i = this.alignmentPatternCenters[x] - 2; - for (let y = 0; y < max; y++) { - if ((x === 0 && (y === 0 || y === max - 1)) || (x === max - 1 && y === 0)) { - // No alignment patterns near the three finder patterns - continue; - } - bitMatrix.setRegion(this.alignmentPatternCenters[y] - 2, i, 5, 5); - } - } - // Vertical timing pattern - bitMatrix.setRegion(6, 9, 1, dimension - 17); - // Horizontal timing pattern - bitMatrix.setRegion(9, 6, dimension - 17, 1); - if (this.versionNumber > 6) { - // Version info, top right - bitMatrix.setRegion(dimension - 11, 0, 3, 6); - // Version info, bottom left - bitMatrix.setRegion(0, dimension - 11, 6, 3); - } - return bitMatrix; - } - /*@Override*/ - toString() { - return '' + this.versionNumber; - } - } - /** - * See ISO 18004:2006 Annex D. - * Element i represents the raw version bits that specify version i + 7 - */ - Version$1.VERSION_DECODE_INFO = Int32Array.from([ - 0x07C94, 0x085BC, 0x09A99, 0x0A4D3, 0x0BBF6, - 0x0C762, 0x0D847, 0x0E60D, 0x0F928, 0x10B78, - 0x1145D, 0x12A17, 0x13532, 0x149A6, 0x15683, - 0x168C9, 0x177EC, 0x18EC4, 0x191E1, 0x1AFAB, - 0x1B08E, 0x1CC1A, 0x1D33F, 0x1ED75, 0x1F250, - 0x209D5, 0x216F0, 0x228BA, 0x2379F, 0x24B0B, - 0x2542E, 0x26A64, 0x27541, 0x28C69 - ]); - /** - * See ISO 18004:2006 6.5.1 Table 9 - */ - Version$1.VERSIONS = [ - new Version$1(1, new Int32Array(0), new ECBlocks$1(7, new ECB$1(1, 19)), new ECBlocks$1(10, new ECB$1(1, 16)), new ECBlocks$1(13, new ECB$1(1, 13)), new ECBlocks$1(17, new ECB$1(1, 9))), - new Version$1(2, Int32Array.from([6, 18]), new ECBlocks$1(10, new ECB$1(1, 34)), new ECBlocks$1(16, new ECB$1(1, 28)), new ECBlocks$1(22, new ECB$1(1, 22)), new ECBlocks$1(28, new ECB$1(1, 16))), - new Version$1(3, Int32Array.from([6, 22]), new ECBlocks$1(15, new ECB$1(1, 55)), new ECBlocks$1(26, new ECB$1(1, 44)), new ECBlocks$1(18, new ECB$1(2, 17)), new ECBlocks$1(22, new ECB$1(2, 13))), - new Version$1(4, Int32Array.from([6, 26]), new ECBlocks$1(20, new ECB$1(1, 80)), new ECBlocks$1(18, new ECB$1(2, 32)), new ECBlocks$1(26, new ECB$1(2, 24)), new ECBlocks$1(16, new ECB$1(4, 9))), - new Version$1(5, Int32Array.from([6, 30]), new ECBlocks$1(26, new ECB$1(1, 108)), new ECBlocks$1(24, new ECB$1(2, 43)), new ECBlocks$1(18, new ECB$1(2, 15), new ECB$1(2, 16)), new ECBlocks$1(22, new ECB$1(2, 11), new ECB$1(2, 12))), - new Version$1(6, Int32Array.from([6, 34]), new ECBlocks$1(18, new ECB$1(2, 68)), new ECBlocks$1(16, new ECB$1(4, 27)), new ECBlocks$1(24, new ECB$1(4, 19)), new ECBlocks$1(28, new ECB$1(4, 15))), - new Version$1(7, Int32Array.from([6, 22, 38]), new ECBlocks$1(20, new ECB$1(2, 78)), new ECBlocks$1(18, new ECB$1(4, 31)), new ECBlocks$1(18, new ECB$1(2, 14), new ECB$1(4, 15)), new ECBlocks$1(26, new ECB$1(4, 13), new ECB$1(1, 14))), - new Version$1(8, Int32Array.from([6, 24, 42]), new ECBlocks$1(24, new ECB$1(2, 97)), new ECBlocks$1(22, new ECB$1(2, 38), new ECB$1(2, 39)), new ECBlocks$1(22, new ECB$1(4, 18), new ECB$1(2, 19)), new ECBlocks$1(26, new ECB$1(4, 14), new ECB$1(2, 15))), - new Version$1(9, Int32Array.from([6, 26, 46]), new ECBlocks$1(30, new ECB$1(2, 116)), new ECBlocks$1(22, new ECB$1(3, 36), new ECB$1(2, 37)), new ECBlocks$1(20, new ECB$1(4, 16), new ECB$1(4, 17)), new ECBlocks$1(24, new ECB$1(4, 12), new ECB$1(4, 13))), - new Version$1(10, Int32Array.from([6, 28, 50]), new ECBlocks$1(18, new ECB$1(2, 68), new ECB$1(2, 69)), new ECBlocks$1(26, new ECB$1(4, 43), new ECB$1(1, 44)), new ECBlocks$1(24, new ECB$1(6, 19), new ECB$1(2, 20)), new ECBlocks$1(28, new ECB$1(6, 15), new ECB$1(2, 16))), - new Version$1(11, Int32Array.from([6, 30, 54]), new ECBlocks$1(20, new ECB$1(4, 81)), new ECBlocks$1(30, new ECB$1(1, 50), new ECB$1(4, 51)), new ECBlocks$1(28, new ECB$1(4, 22), new ECB$1(4, 23)), new ECBlocks$1(24, new ECB$1(3, 12), new ECB$1(8, 13))), - new Version$1(12, Int32Array.from([6, 32, 58]), new ECBlocks$1(24, new ECB$1(2, 92), new ECB$1(2, 93)), new ECBlocks$1(22, new ECB$1(6, 36), new ECB$1(2, 37)), new ECBlocks$1(26, new ECB$1(4, 20), new ECB$1(6, 21)), new ECBlocks$1(28, new ECB$1(7, 14), new ECB$1(4, 15))), - new Version$1(13, Int32Array.from([6, 34, 62]), new ECBlocks$1(26, new ECB$1(4, 107)), new ECBlocks$1(22, new ECB$1(8, 37), new ECB$1(1, 38)), new ECBlocks$1(24, new ECB$1(8, 20), new ECB$1(4, 21)), new ECBlocks$1(22, new ECB$1(12, 11), new ECB$1(4, 12))), - new Version$1(14, Int32Array.from([6, 26, 46, 66]), new ECBlocks$1(30, new ECB$1(3, 115), new ECB$1(1, 116)), new ECBlocks$1(24, new ECB$1(4, 40), new ECB$1(5, 41)), new ECBlocks$1(20, new ECB$1(11, 16), new ECB$1(5, 17)), new ECBlocks$1(24, new ECB$1(11, 12), new ECB$1(5, 13))), - new Version$1(15, Int32Array.from([6, 26, 48, 70]), new ECBlocks$1(22, new ECB$1(5, 87), new ECB$1(1, 88)), new ECBlocks$1(24, new ECB$1(5, 41), new ECB$1(5, 42)), new ECBlocks$1(30, new ECB$1(5, 24), new ECB$1(7, 25)), new ECBlocks$1(24, new ECB$1(11, 12), new ECB$1(7, 13))), - new Version$1(16, Int32Array.from([6, 26, 50, 74]), new ECBlocks$1(24, new ECB$1(5, 98), new ECB$1(1, 99)), new ECBlocks$1(28, new ECB$1(7, 45), new ECB$1(3, 46)), new ECBlocks$1(24, new ECB$1(15, 19), new ECB$1(2, 20)), new ECBlocks$1(30, new ECB$1(3, 15), new ECB$1(13, 16))), - new Version$1(17, Int32Array.from([6, 30, 54, 78]), new ECBlocks$1(28, new ECB$1(1, 107), new ECB$1(5, 108)), new ECBlocks$1(28, new ECB$1(10, 46), new ECB$1(1, 47)), new ECBlocks$1(28, new ECB$1(1, 22), new ECB$1(15, 23)), new ECBlocks$1(28, new ECB$1(2, 14), new ECB$1(17, 15))), - new Version$1(18, Int32Array.from([6, 30, 56, 82]), new ECBlocks$1(30, new ECB$1(5, 120), new ECB$1(1, 121)), new ECBlocks$1(26, new ECB$1(9, 43), new ECB$1(4, 44)), new ECBlocks$1(28, new ECB$1(17, 22), new ECB$1(1, 23)), new ECBlocks$1(28, new ECB$1(2, 14), new ECB$1(19, 15))), - new Version$1(19, Int32Array.from([6, 30, 58, 86]), new ECBlocks$1(28, new ECB$1(3, 113), new ECB$1(4, 114)), new ECBlocks$1(26, new ECB$1(3, 44), new ECB$1(11, 45)), new ECBlocks$1(26, new ECB$1(17, 21), new ECB$1(4, 22)), new ECBlocks$1(26, new ECB$1(9, 13), new ECB$1(16, 14))), - new Version$1(20, Int32Array.from([6, 34, 62, 90]), new ECBlocks$1(28, new ECB$1(3, 107), new ECB$1(5, 108)), new ECBlocks$1(26, new ECB$1(3, 41), new ECB$1(13, 42)), new ECBlocks$1(30, new ECB$1(15, 24), new ECB$1(5, 25)), new ECBlocks$1(28, new ECB$1(15, 15), new ECB$1(10, 16))), - new Version$1(21, Int32Array.from([6, 28, 50, 72, 94]), new ECBlocks$1(28, new ECB$1(4, 116), new ECB$1(4, 117)), new ECBlocks$1(26, new ECB$1(17, 42)), new ECBlocks$1(28, new ECB$1(17, 22), new ECB$1(6, 23)), new ECBlocks$1(30, new ECB$1(19, 16), new ECB$1(6, 17))), - new Version$1(22, Int32Array.from([6, 26, 50, 74, 98]), new ECBlocks$1(28, new ECB$1(2, 111), new ECB$1(7, 112)), new ECBlocks$1(28, new ECB$1(17, 46)), new ECBlocks$1(30, new ECB$1(7, 24), new ECB$1(16, 25)), new ECBlocks$1(24, new ECB$1(34, 13))), - new Version$1(23, Int32Array.from([6, 30, 54, 78, 102]), new ECBlocks$1(30, new ECB$1(4, 121), new ECB$1(5, 122)), new ECBlocks$1(28, new ECB$1(4, 47), new ECB$1(14, 48)), new ECBlocks$1(30, new ECB$1(11, 24), new ECB$1(14, 25)), new ECBlocks$1(30, new ECB$1(16, 15), new ECB$1(14, 16))), - new Version$1(24, Int32Array.from([6, 28, 54, 80, 106]), new ECBlocks$1(30, new ECB$1(6, 117), new ECB$1(4, 118)), new ECBlocks$1(28, new ECB$1(6, 45), new ECB$1(14, 46)), new ECBlocks$1(30, new ECB$1(11, 24), new ECB$1(16, 25)), new ECBlocks$1(30, new ECB$1(30, 16), new ECB$1(2, 17))), - new Version$1(25, Int32Array.from([6, 32, 58, 84, 110]), new ECBlocks$1(26, new ECB$1(8, 106), new ECB$1(4, 107)), new ECBlocks$1(28, new ECB$1(8, 47), new ECB$1(13, 48)), new ECBlocks$1(30, new ECB$1(7, 24), new ECB$1(22, 25)), new ECBlocks$1(30, new ECB$1(22, 15), new ECB$1(13, 16))), - new Version$1(26, Int32Array.from([6, 30, 58, 86, 114]), new ECBlocks$1(28, new ECB$1(10, 114), new ECB$1(2, 115)), new ECBlocks$1(28, new ECB$1(19, 46), new ECB$1(4, 47)), new ECBlocks$1(28, new ECB$1(28, 22), new ECB$1(6, 23)), new ECBlocks$1(30, new ECB$1(33, 16), new ECB$1(4, 17))), - new Version$1(27, Int32Array.from([6, 34, 62, 90, 118]), new ECBlocks$1(30, new ECB$1(8, 122), new ECB$1(4, 123)), new ECBlocks$1(28, new ECB$1(22, 45), new ECB$1(3, 46)), new ECBlocks$1(30, new ECB$1(8, 23), new ECB$1(26, 24)), new ECBlocks$1(30, new ECB$1(12, 15), new ECB$1(28, 16))), - new Version$1(28, Int32Array.from([6, 26, 50, 74, 98, 122]), new ECBlocks$1(30, new ECB$1(3, 117), new ECB$1(10, 118)), new ECBlocks$1(28, new ECB$1(3, 45), new ECB$1(23, 46)), new ECBlocks$1(30, new ECB$1(4, 24), new ECB$1(31, 25)), new ECBlocks$1(30, new ECB$1(11, 15), new ECB$1(31, 16))), - new Version$1(29, Int32Array.from([6, 30, 54, 78, 102, 126]), new ECBlocks$1(30, new ECB$1(7, 116), new ECB$1(7, 117)), new ECBlocks$1(28, new ECB$1(21, 45), new ECB$1(7, 46)), new ECBlocks$1(30, new ECB$1(1, 23), new ECB$1(37, 24)), new ECBlocks$1(30, new ECB$1(19, 15), new ECB$1(26, 16))), - new Version$1(30, Int32Array.from([6, 26, 52, 78, 104, 130]), new ECBlocks$1(30, new ECB$1(5, 115), new ECB$1(10, 116)), new ECBlocks$1(28, new ECB$1(19, 47), new ECB$1(10, 48)), new ECBlocks$1(30, new ECB$1(15, 24), new ECB$1(25, 25)), new ECBlocks$1(30, new ECB$1(23, 15), new ECB$1(25, 16))), - new Version$1(31, Int32Array.from([6, 30, 56, 82, 108, 134]), new ECBlocks$1(30, new ECB$1(13, 115), new ECB$1(3, 116)), new ECBlocks$1(28, new ECB$1(2, 46), new ECB$1(29, 47)), new ECBlocks$1(30, new ECB$1(42, 24), new ECB$1(1, 25)), new ECBlocks$1(30, new ECB$1(23, 15), new ECB$1(28, 16))), - new Version$1(32, Int32Array.from([6, 34, 60, 86, 112, 138]), new ECBlocks$1(30, new ECB$1(17, 115)), new ECBlocks$1(28, new ECB$1(10, 46), new ECB$1(23, 47)), new ECBlocks$1(30, new ECB$1(10, 24), new ECB$1(35, 25)), new ECBlocks$1(30, new ECB$1(19, 15), new ECB$1(35, 16))), - new Version$1(33, Int32Array.from([6, 30, 58, 86, 114, 142]), new ECBlocks$1(30, new ECB$1(17, 115), new ECB$1(1, 116)), new ECBlocks$1(28, new ECB$1(14, 46), new ECB$1(21, 47)), new ECBlocks$1(30, new ECB$1(29, 24), new ECB$1(19, 25)), new ECBlocks$1(30, new ECB$1(11, 15), new ECB$1(46, 16))), - new Version$1(34, Int32Array.from([6, 34, 62, 90, 118, 146]), new ECBlocks$1(30, new ECB$1(13, 115), new ECB$1(6, 116)), new ECBlocks$1(28, new ECB$1(14, 46), new ECB$1(23, 47)), new ECBlocks$1(30, new ECB$1(44, 24), new ECB$1(7, 25)), new ECBlocks$1(30, new ECB$1(59, 16), new ECB$1(1, 17))), - new Version$1(35, Int32Array.from([6, 30, 54, 78, 102, 126, 150]), new ECBlocks$1(30, new ECB$1(12, 121), new ECB$1(7, 122)), new ECBlocks$1(28, new ECB$1(12, 47), new ECB$1(26, 48)), new ECBlocks$1(30, new ECB$1(39, 24), new ECB$1(14, 25)), new ECBlocks$1(30, new ECB$1(22, 15), new ECB$1(41, 16))), - new Version$1(36, Int32Array.from([6, 24, 50, 76, 102, 128, 154]), new ECBlocks$1(30, new ECB$1(6, 121), new ECB$1(14, 122)), new ECBlocks$1(28, new ECB$1(6, 47), new ECB$1(34, 48)), new ECBlocks$1(30, new ECB$1(46, 24), new ECB$1(10, 25)), new ECBlocks$1(30, new ECB$1(2, 15), new ECB$1(64, 16))), - new Version$1(37, Int32Array.from([6, 28, 54, 80, 106, 132, 158]), new ECBlocks$1(30, new ECB$1(17, 122), new ECB$1(4, 123)), new ECBlocks$1(28, new ECB$1(29, 46), new ECB$1(14, 47)), new ECBlocks$1(30, new ECB$1(49, 24), new ECB$1(10, 25)), new ECBlocks$1(30, new ECB$1(24, 15), new ECB$1(46, 16))), - new Version$1(38, Int32Array.from([6, 32, 58, 84, 110, 136, 162]), new ECBlocks$1(30, new ECB$1(4, 122), new ECB$1(18, 123)), new ECBlocks$1(28, new ECB$1(13, 46), new ECB$1(32, 47)), new ECBlocks$1(30, new ECB$1(48, 24), new ECB$1(14, 25)), new ECBlocks$1(30, new ECB$1(42, 15), new ECB$1(32, 16))), - new Version$1(39, Int32Array.from([6, 26, 54, 82, 110, 138, 166]), new ECBlocks$1(30, new ECB$1(20, 117), new ECB$1(4, 118)), new ECBlocks$1(28, new ECB$1(40, 47), new ECB$1(7, 48)), new ECBlocks$1(30, new ECB$1(43, 24), new ECB$1(22, 25)), new ECBlocks$1(30, new ECB$1(10, 15), new ECB$1(67, 16))), - new Version$1(40, Int32Array.from([6, 30, 58, 86, 114, 142, 170]), new ECBlocks$1(30, new ECB$1(19, 118), new ECB$1(6, 119)), new ECBlocks$1(28, new ECB$1(18, 47), new ECB$1(31, 48)), new ECBlocks$1(30, new ECB$1(34, 24), new ECB$1(34, 25)), new ECBlocks$1(30, new ECB$1(20, 15), new ECB$1(61, 16))) - ]; - - /* - * Copyright 2007 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 DataMaskValues; - (function (DataMaskValues) { - DataMaskValues[DataMaskValues["DATA_MASK_000"] = 0] = "DATA_MASK_000"; - DataMaskValues[DataMaskValues["DATA_MASK_001"] = 1] = "DATA_MASK_001"; - DataMaskValues[DataMaskValues["DATA_MASK_010"] = 2] = "DATA_MASK_010"; - DataMaskValues[DataMaskValues["DATA_MASK_011"] = 3] = "DATA_MASK_011"; - DataMaskValues[DataMaskValues["DATA_MASK_100"] = 4] = "DATA_MASK_100"; - DataMaskValues[DataMaskValues["DATA_MASK_101"] = 5] = "DATA_MASK_101"; - DataMaskValues[DataMaskValues["DATA_MASK_110"] = 6] = "DATA_MASK_110"; - DataMaskValues[DataMaskValues["DATA_MASK_111"] = 7] = "DATA_MASK_111"; - })(DataMaskValues || (DataMaskValues = {})); - /** - *Encapsulates data masks for the data bits in a QR code, per ISO 18004:2006 6.8. Implementations - * of this class can un-mask a raw BitMatrix. For simplicity, they will unmask the entire BitMatrix, - * including areas used for finder patterns, timing patterns, etc. These areas should be unused - * after the point they are unmasked anyway.
- * - *Note that the diagram in section 6.8.1 is misleading since it indicates that i is column position - * and j is row position. In fact, as the text says, i is row position and j is column position.
- * - * @author Sean Owen - */ - class DataMask { - // See ISO 18004:2006 6.8.1 - constructor(value, isMasked) { - this.value = value; - this.isMasked = isMasked; - } - // End of enum constants. - /** - *Implementations of this method reverse the data masking process applied to a QR Code and - * make its bits ready to read.
- * - * @param bits representation of QR Code bits - * @param dimension dimension of QR Code, represented by bits, being unmasked - */ - unmaskBitMatrix(bits, dimension /*int*/) { - for (let i = 0; i < dimension; i++) { - for (let j = 0; j < dimension; j++) { - if (this.isMasked(i, j)) { - bits.flip(j, i); - } - } - } - } - } - DataMask.values = new Map([ - /** - * 000: mask bits for which (x + y) mod 2 == 0 - */ - [DataMaskValues.DATA_MASK_000, new DataMask(DataMaskValues.DATA_MASK_000, (i /*int*/, j /*int*/) => { return ((i + j) & 0x01) === 0; })], - /** - * 001: mask bits for which x mod 2 == 0 - */ - [DataMaskValues.DATA_MASK_001, new DataMask(DataMaskValues.DATA_MASK_001, (i /*int*/, j /*int*/) => { return (i & 0x01) === 0; })], - /** - * 010: mask bits for which y mod 3 == 0 - */ - [DataMaskValues.DATA_MASK_010, new DataMask(DataMaskValues.DATA_MASK_010, (i /*int*/, j /*int*/) => { return j % 3 === 0; })], - /** - * 011: mask bits for which (x + y) mod 3 == 0 - */ - [DataMaskValues.DATA_MASK_011, new DataMask(DataMaskValues.DATA_MASK_011, (i /*int*/, j /*int*/) => { return (i + j) % 3 === 0; })], - /** - * 100: mask bits for which (x/2 + y/3) mod 2 == 0 - */ - [DataMaskValues.DATA_MASK_100, new DataMask(DataMaskValues.DATA_MASK_100, (i /*int*/, j /*int*/) => { return ((Math.floor(i / 2) + Math.floor(j / 3)) & 0x01) === 0; })], - /** - * 101: mask bits for which xy mod 2 + xy mod 3 == 0 - * equivalently, such that xy mod 6 == 0 - */ - [DataMaskValues.DATA_MASK_101, new DataMask(DataMaskValues.DATA_MASK_101, (i /*int*/, j /*int*/) => { return (i * j) % 6 === 0; })], - /** - * 110: mask bits for which (xy mod 2 + xy mod 3) mod 2 == 0 - * equivalently, such that xy mod 6 < 3 - */ - [DataMaskValues.DATA_MASK_110, new DataMask(DataMaskValues.DATA_MASK_110, (i /*int*/, j /*int*/) => { return ((i * j) % 6) < 3; })], - /** - * 111: mask bits for which ((x+y)mod 2 + xy mod 3) mod 2 == 0 - * equivalently, such that (x + y + xy mod 3) mod 2 == 0 - */ - [DataMaskValues.DATA_MASK_111, new DataMask(DataMaskValues.DATA_MASK_111, (i /*int*/, j /*int*/) => { return ((i + j + ((i * j) % 3)) & 0x01) === 0; })], - ]); - - /* - * Copyright 2007 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. - */ - /** - * @author Sean Owen - */ - class BitMatrixParser$1 { - /** - * @param bitMatrix {@link BitMatrix} to parse - * @throws FormatException if dimension is not >= 21 and 1 mod 4 - */ - constructor(bitMatrix) { - const dimension = bitMatrix.getHeight(); - if (dimension < 21 || (dimension & 0x03) !== 1) { - throw new FormatException(); - } - this.bitMatrix = bitMatrix; - } - /** - *Reads format information from one of its two locations within the QR Code.
- * - * @return {@link FormatInformation} encapsulating the QR Code's format info - * @throws FormatException if both format information locations cannot be parsed as - * the valid encoding of format information - */ - readFormatInformation() { - if (this.parsedFormatInfo !== null && this.parsedFormatInfo !== undefined) { - return this.parsedFormatInfo; - } - // Read top-left format info bits - let formatInfoBits1 = 0; - for (let i = 0; i < 6; i++) { - formatInfoBits1 = this.copyBit(i, 8, formatInfoBits1); - } - // .. and skip a bit in the timing pattern ... - formatInfoBits1 = this.copyBit(7, 8, formatInfoBits1); - formatInfoBits1 = this.copyBit(8, 8, formatInfoBits1); - formatInfoBits1 = this.copyBit(8, 7, formatInfoBits1); - // .. and skip a bit in the timing pattern ... - for (let j = 5; j >= 0; j--) { - formatInfoBits1 = this.copyBit(8, j, formatInfoBits1); - } - // Read the top-right/bottom-left pattern too - const dimension = this.bitMatrix.getHeight(); - let formatInfoBits2 = 0; - const jMin = dimension - 7; - for (let j = dimension - 1; j >= jMin; j--) { - formatInfoBits2 = this.copyBit(8, j, formatInfoBits2); - } - for (let i = dimension - 8; i < dimension; i++) { - formatInfoBits2 = this.copyBit(i, 8, formatInfoBits2); - } - this.parsedFormatInfo = FormatInformation.decodeFormatInformation(formatInfoBits1, formatInfoBits2); - if (this.parsedFormatInfo !== null) { - return this.parsedFormatInfo; - } - throw new FormatException(); - } - /** - *Reads version information from one of its two locations within the QR Code.
- * - * @return {@link Version} encapsulating the QR Code's version - * @throws FormatException if both version information locations cannot be parsed as - * the valid encoding of version information - */ - readVersion() { - if (this.parsedVersion !== null && this.parsedVersion !== undefined) { - return this.parsedVersion; - } - const dimension = this.bitMatrix.getHeight(); - const provisionalVersion = Math.floor((dimension - 17) / 4); - if (provisionalVersion <= 6) { - return Version$1.getVersionForNumber(provisionalVersion); - } - // Read top-right version info: 3 wide by 6 tall - let versionBits = 0; - const ijMin = dimension - 11; - for (let j = 5; j >= 0; j--) { - for (let i = dimension - 9; i >= ijMin; i--) { - versionBits = this.copyBit(i, j, versionBits); - } - } - let theParsedVersion = Version$1.decodeVersionInformation(versionBits); - if (theParsedVersion !== null && theParsedVersion.getDimensionForVersion() === dimension) { - this.parsedVersion = theParsedVersion; - return theParsedVersion; - } - // Hmm, failed. Try bottom left: 6 wide by 3 tall - versionBits = 0; - for (let i = 5; i >= 0; i--) { - for (let j = dimension - 9; j >= ijMin; j--) { - versionBits = this.copyBit(i, j, versionBits); - } - } - theParsedVersion = Version$1.decodeVersionInformation(versionBits); - if (theParsedVersion !== null && theParsedVersion.getDimensionForVersion() === dimension) { - this.parsedVersion = theParsedVersion; - return theParsedVersion; - } - throw new FormatException(); - } - copyBit(i /*int*/, j /*int*/, versionBits /*int*/) { - const bit = this.isMirror ? this.bitMatrix.get(j, i) : this.bitMatrix.get(i, j); - return bit ? (versionBits << 1) | 0x1 : versionBits << 1; - } - /** - *Reads the bits in the {@link BitMatrix} representing the finder pattern in the - * correct order in order to reconstruct the codewords bytes contained within the - * QR Code.
- * - * @return bytes encoded within the QR Code - * @throws FormatException if the exact number of bytes expected is not read - */ - readCodewords() { - const formatInfo = this.readFormatInformation(); - const version = this.readVersion(); - // Get the data mask for the format used in this QR Code. This will exclude - // some bits from reading as we wind through the bit matrix. - const dataMask = DataMask.values.get(formatInfo.getDataMask()); - const dimension = this.bitMatrix.getHeight(); - dataMask.unmaskBitMatrix(this.bitMatrix, dimension); - const functionPattern = version.buildFunctionPattern(); - let readingUp = true; - const result = new Uint8Array(version.getTotalCodewords()); - let resultOffset = 0; - let currentByte = 0; - let bitsRead = 0; - // Read columns in pairs, from right to left - for (let j = dimension - 1; j > 0; j -= 2) { - if (j === 6) { - // Skip whole column with vertical alignment pattern - // saves time and makes the other code proceed more cleanly - j--; - } - // Read alternatingly from bottom to top then top to bottom - for (let count = 0; count < dimension; count++) { - const i = readingUp ? dimension - 1 - count : count; - for (let col = 0; col < 2; col++) { - // Ignore bits covered by the function pattern - if (!functionPattern.get(j - col, i)) { - // Read a bit - bitsRead++; - currentByte <<= 1; - if (this.bitMatrix.get(j - col, i)) { - currentByte |= 1; - } - // If we've made a whole byte, save it off - if (bitsRead === 8) { - result[resultOffset++] = /*(byte) */ currentByte; - bitsRead = 0; - currentByte = 0; - } - } - } - } - readingUp = !readingUp; // readingUp ^= true; // readingUp = !readingUp; // switch directions - } - if (resultOffset !== version.getTotalCodewords()) { - throw new FormatException(); - } - return result; - } - /** - * Revert the mask removal done while reading the code words. The bit matrix should revert to its original state. - */ - remask() { - if (this.parsedFormatInfo === null) { - return; // We have no format information, and have no data mask - } - const dataMask = DataMask.values[this.parsedFormatInfo.getDataMask()]; - const dimension = this.bitMatrix.getHeight(); - dataMask.unmaskBitMatrix(this.bitMatrix, dimension); - } - /** - * Prepare the parser for a mirrored operation. - * This flag has effect only on the {@link #readFormatInformation()} and the - * {@link #readVersion()}. Before proceeding with {@link #readCodewords()} the - * {@link #mirror()} method should be called. - * - * @param mirror Whether to read version and format information mirrored. - */ - setMirror(isMirror) { - this.parsedVersion = null; - this.parsedFormatInfo = null; - this.isMirror = isMirror; - } - /** Mirror the bit matrix in order to attempt a second reading. */ - mirror() { - const bitMatrix = this.bitMatrix; - for (let x = 0, width = bitMatrix.getWidth(); x < width; x++) { - for (let y = x + 1, height = bitMatrix.getHeight(); y < height; y++) { - if (bitMatrix.get(x, y) !== bitMatrix.get(y, x)) { - bitMatrix.flip(y, x); - bitMatrix.flip(x, y); - } - } - } - } - } - - /* - * Copyright 2007 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. - */ - /** - *Encapsulates a block of data within a QR Code. QR Codes may split their data into - * multiple blocks, each of which is a unit of data and error-correction codewords. Each - * is represented by an instance of this class.
- * - * @author Sean Owen - */ - class DataBlock$1 { - constructor(numDataCodewords /*int*/, codewords) { - this.numDataCodewords = numDataCodewords; - this.codewords = codewords; - } - /** - *When QR Codes use multiple data blocks, they are actually interleaved. - * That is, the first byte of data block 1 to n is written, then the second bytes, and so on. This - * method will separate the data into original blocks.
- * - * @param rawCodewords bytes as read directly from the QR Code - * @param version version of the QR Code - * @param ecLevel error-correction level of the QR Code - * @return DataBlocks containing original bytes, "de-interleaved" from representation in the - * QR Code - */ - static getDataBlocks(rawCodewords, version, ecLevel) { - if (rawCodewords.length !== version.getTotalCodewords()) { - throw new IllegalArgumentException(); - } - // Figure out the number and size of data blocks used by this version and - // error correction level - const ecBlocks = version.getECBlocksForLevel(ecLevel); - // First count the total number of data blocks - let totalBlocks = 0; - const ecBlockArray = ecBlocks.getECBlocks(); - for (const ecBlock of ecBlockArray) { - totalBlocks += ecBlock.getCount(); - } - // Now establish DataBlocks of the appropriate size and number of data codewords - const result = new Array(totalBlocks); - let numResultBlocks = 0; - for (const ecBlock of ecBlockArray) { - for (let i = 0; i < ecBlock.getCount(); i++) { - const numDataCodewords = ecBlock.getDataCodewords(); - const numBlockCodewords = ecBlocks.getECCodewordsPerBlock() + numDataCodewords; - result[numResultBlocks++] = new DataBlock$1(numDataCodewords, new Uint8Array(numBlockCodewords)); - } - } - // All blocks have the same amount of data, except that the last n - // (where n may be 0) have 1 more byte. Figure out where these start. - const shorterBlocksTotalCodewords = result[0].codewords.length; - let longerBlocksStartAt = result.length - 1; - // TYPESCRIPTPORT: check length is correct here - while (longerBlocksStartAt >= 0) { - const numCodewords = result[longerBlocksStartAt].codewords.length; - if (numCodewords === shorterBlocksTotalCodewords) { - break; - } - longerBlocksStartAt--; - } - longerBlocksStartAt++; - const shorterBlocksNumDataCodewords = shorterBlocksTotalCodewords - ecBlocks.getECCodewordsPerBlock(); - // The last elements of result may be 1 element longer - // first fill out as many elements as all of them have - let rawCodewordsOffset = 0; - for (let i = 0; i < shorterBlocksNumDataCodewords; i++) { - for (let j = 0; j < numResultBlocks; j++) { - result[j].codewords[i] = rawCodewords[rawCodewordsOffset++]; - } - } - // Fill out the last data block in the longer ones - for (let j = longerBlocksStartAt; j < numResultBlocks; j++) { - result[j].codewords[shorterBlocksNumDataCodewords] = rawCodewords[rawCodewordsOffset++]; - } - // Now add in error correction blocks - const max = result[0].codewords.length; - for (let i = shorterBlocksNumDataCodewords; i < max; i++) { - for (let j = 0; j < numResultBlocks; j++) { - const iOffset = j < longerBlocksStartAt ? i : i + 1; - result[j].codewords[iOffset] = rawCodewords[rawCodewordsOffset++]; - } - } - return result; - } - getNumDataCodewords() { - return this.numDataCodewords; - } - getCodewords() { - return this.codewords; - } - } - - /* - * Copyright 2007 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 ModeValues; - (function (ModeValues) { - ModeValues[ModeValues["TERMINATOR"] = 0] = "TERMINATOR"; - ModeValues[ModeValues["NUMERIC"] = 1] = "NUMERIC"; - ModeValues[ModeValues["ALPHANUMERIC"] = 2] = "ALPHANUMERIC"; - ModeValues[ModeValues["STRUCTURED_APPEND"] = 3] = "STRUCTURED_APPEND"; - ModeValues[ModeValues["BYTE"] = 4] = "BYTE"; - ModeValues[ModeValues["ECI"] = 5] = "ECI"; - ModeValues[ModeValues["KANJI"] = 6] = "KANJI"; - ModeValues[ModeValues["FNC1_FIRST_POSITION"] = 7] = "FNC1_FIRST_POSITION"; - ModeValues[ModeValues["FNC1_SECOND_POSITION"] = 8] = "FNC1_SECOND_POSITION"; - /** See GBT 18284-2000; "Hanzi" is a transliteration of this mode name. */ - ModeValues[ModeValues["HANZI"] = 9] = "HANZI"; - })(ModeValues || (ModeValues = {})); - /** - *See ISO 18004:2006, 6.4.1, Tables 2 and 3. This enum encapsulates the various modes in which - * data can be encoded to bits in the QR code standard.
- * - * @author Sean Owen - */ - class Mode$1 { - constructor(value, stringValue, characterCountBitsForVersions, bits /*int*/) { - this.value = value; - this.stringValue = stringValue; - this.characterCountBitsForVersions = characterCountBitsForVersions; - this.bits = bits; - Mode$1.FOR_BITS.set(bits, this); - Mode$1.FOR_VALUE.set(value, this); - } - /** - * @param bits four bits encoding a QR Code data mode - * @return Mode encoded by these bits - * @throws IllegalArgumentException if bits do not correspond to a known mode - */ - static forBits(bits /*int*/) { - const mode = Mode$1.FOR_BITS.get(bits); - if (undefined === mode) { - throw new IllegalArgumentException(); - } - return mode; - } - /** - * @param version version in question - * @return number of bits used, in this QR Code symbol {@link Version}, to encode the - * count of characters that will follow encoded in this Mode - */ - getCharacterCountBits(version) { - const versionNumber = version.getVersionNumber(); - let offset; - if (versionNumber <= 9) { - offset = 0; - } - else if (versionNumber <= 26) { - offset = 1; - } - else { - offset = 2; - } - return this.characterCountBitsForVersions[offset]; - } - getValue() { - return this.value; - } - getBits() { - return this.bits; - } - equals(o) { - if (!(o instanceof Mode$1)) { - return false; - } - const other = o; - return this.value === other.value; - } - toString() { - return this.stringValue; - } - } - Mode$1.FOR_BITS = new Map(); - Mode$1.FOR_VALUE = new Map(); - Mode$1.TERMINATOR = new Mode$1(ModeValues.TERMINATOR, 'TERMINATOR', Int32Array.from([0, 0, 0]), 0x00); // Not really a mode... - Mode$1.NUMERIC = new Mode$1(ModeValues.NUMERIC, 'NUMERIC', Int32Array.from([10, 12, 14]), 0x01); - Mode$1.ALPHANUMERIC = new Mode$1(ModeValues.ALPHANUMERIC, 'ALPHANUMERIC', Int32Array.from([9, 11, 13]), 0x02); - Mode$1.STRUCTURED_APPEND = new Mode$1(ModeValues.STRUCTURED_APPEND, 'STRUCTURED_APPEND', Int32Array.from([0, 0, 0]), 0x03); // Not supported - Mode$1.BYTE = new Mode$1(ModeValues.BYTE, 'BYTE', Int32Array.from([8, 16, 16]), 0x04); - Mode$1.ECI = new Mode$1(ModeValues.ECI, 'ECI', Int32Array.from([0, 0, 0]), 0x07); // character counts don't apply - Mode$1.KANJI = new Mode$1(ModeValues.KANJI, 'KANJI', Int32Array.from([8, 10, 12]), 0x08); - Mode$1.FNC1_FIRST_POSITION = new Mode$1(ModeValues.FNC1_FIRST_POSITION, 'FNC1_FIRST_POSITION', Int32Array.from([0, 0, 0]), 0x05); - Mode$1.FNC1_SECOND_POSITION = new Mode$1(ModeValues.FNC1_SECOND_POSITION, 'FNC1_SECOND_POSITION', Int32Array.from([0, 0, 0]), 0x09); - /** See GBT 18284-2000; "Hanzi" is a transliteration of this mode name. */ - Mode$1.HANZI = new Mode$1(ModeValues.HANZI, 'HANZI', Int32Array.from([8, 10, 12]), 0x0D); - - /* - * Copyright 2007 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 java.io.UnsupportedEncodingException;*/ - /*import java.util.ArrayList;*/ - /*import java.util.Collection;*/ - /*import java.util.List;*/ - /*import java.util.Map;*/ - /** - *QR Codes can encode text as bits in one of several modes, and can use multiple modes - * in one QR Code. This class decodes the bits back into text.
- * - *See ISO 18004:2006, 6.4.3 - 6.4.7
- * - * @author Sean Owen - */ - class DecodedBitStreamParser$1 { - static decode(bytes, version, ecLevel, hints) { - const bits = new BitSource(bytes); - let result = new StringBuilder(); - const byteSegments = new Array(); // 1 - // TYPESCRIPTPORT: I do not use constructor with size 1 as in original Java means capacity and the array length is checked below - let symbolSequence = -1; - let parityData = -1; - try { - let currentCharacterSetECI = null; - let fc1InEffect = false; - let mode; - do { - // While still another segment to read... - if (bits.available() < 4) { - // OK, assume we're done. Really, a TERMINATOR mode should have been recorded here - mode = Mode$1.TERMINATOR; - } - else { - const modeBits = bits.readBits(4); - mode = Mode$1.forBits(modeBits); // mode is encoded by 4 bits - } - switch (mode) { - case Mode$1.TERMINATOR: - break; - case Mode$1.FNC1_FIRST_POSITION: - case Mode$1.FNC1_SECOND_POSITION: - // We do little with FNC1 except alter the parsed result a bit according to the spec - fc1InEffect = true; - break; - case Mode$1.STRUCTURED_APPEND: - if (bits.available() < 16) { - throw new FormatException(); - } - // sequence number and parity is added later to the result metadata - // Read next 8 bits (symbol sequence #) and 8 bits (data: parity), then continue - symbolSequence = bits.readBits(8); - parityData = bits.readBits(8); - break; - case Mode$1.ECI: - // Count doesn't apply to ECI - const value = DecodedBitStreamParser$1.parseECIValue(bits); - currentCharacterSetECI = CharacterSetECI.getCharacterSetECIByValue(value); - if (currentCharacterSetECI === null) { - throw new FormatException(); - } - break; - case Mode$1.HANZI: - // First handle Hanzi mode which does not start with character count - // Chinese mode contains a sub set indicator right after mode indicator - const subset = bits.readBits(4); - const countHanzi = bits.readBits(mode.getCharacterCountBits(version)); - if (subset === DecodedBitStreamParser$1.GB2312_SUBSET) { - DecodedBitStreamParser$1.decodeHanziSegment(bits, result, countHanzi); - } - break; - default: - // "Normal" QR code modes: - // How many characters will follow, encoded in this mode? - const count = bits.readBits(mode.getCharacterCountBits(version)); - switch (mode) { - case Mode$1.NUMERIC: - DecodedBitStreamParser$1.decodeNumericSegment(bits, result, count); - break; - case Mode$1.ALPHANUMERIC: - DecodedBitStreamParser$1.decodeAlphanumericSegment(bits, result, count, fc1InEffect); - break; - case Mode$1.BYTE: - DecodedBitStreamParser$1.decodeByteSegment(bits, result, count, currentCharacterSetECI, byteSegments, hints); - break; - case Mode$1.KANJI: - DecodedBitStreamParser$1.decodeKanjiSegment(bits, result, count); - break; - default: - throw new FormatException(); - } - break; - } - } while (mode !== Mode$1.TERMINATOR); - } - catch (iae /*: IllegalArgumentException*/) { - // from readBits() calls - throw new FormatException(); - } - return new DecoderResult(bytes, result.toString(), byteSegments.length === 0 ? null : byteSegments, ecLevel === null ? null : ecLevel.toString(), symbolSequence, parityData); - } - /** - * See specification GBT 18284-2000 - */ - static decodeHanziSegment(bits, result, count /*int*/) { - // Don't crash trying to read more bits than we have available. - if (count * 13 > bits.available()) { - throw new FormatException(); - } - // Each character will require 2 bytes. Read the characters as 2-byte pairs - // and decode as GB2312 afterwards - const buffer = new Uint8Array(2 * count); - let offset = 0; - while (count > 0) { - // Each 13 bits encodes a 2-byte character - const twoBytes = bits.readBits(13); - let assembledTwoBytes = (((twoBytes / 0x060) << 8) & 0xFFFFFFFF) | (twoBytes % 0x060); - if (assembledTwoBytes < 0x003BF) { - // In the 0xA1A1 to 0xAAFE range - assembledTwoBytes += 0x0A1A1; - } - else { - // In the 0xB0A1 to 0xFAFE range - assembledTwoBytes += 0x0A6A1; - } - buffer[offset] = /*(byte) */ ((assembledTwoBytes >> 8) & 0xFF); - buffer[offset + 1] = /*(byte) */ (assembledTwoBytes & 0xFF); - offset += 2; - count--; - } - try { - result.append(StringEncoding.decode(buffer, StringUtils.GB2312)); - // TYPESCRIPTPORT: TODO: implement GB2312 decode. StringView from MDN could be a starting point - } - catch (ignored /*: UnsupportedEncodingException*/) { - throw new FormatException(ignored); - } - } - static decodeKanjiSegment(bits, result, count /*int*/) { - // Don't crash trying to read more bits than we have available. - if (count * 13 > bits.available()) { - throw new FormatException(); - } - // Each character will require 2 bytes. Read the characters as 2-byte pairs - // and decode as Shift_JIS afterwards - const buffer = new Uint8Array(2 * count); - let offset = 0; - while (count > 0) { - // Each 13 bits encodes a 2-byte character - const twoBytes = bits.readBits(13); - let assembledTwoBytes = (((twoBytes / 0x0C0) << 8) & 0xFFFFFFFF) | (twoBytes % 0x0C0); - if (assembledTwoBytes < 0x01F00) { - // In the 0x8140 to 0x9FFC range - assembledTwoBytes += 0x08140; - } - else { - // In the 0xE040 to 0xEBBF range - assembledTwoBytes += 0x0C140; - } - buffer[offset] = /*(byte) */ (assembledTwoBytes >> 8); - buffer[offset + 1] = /*(byte) */ assembledTwoBytes; - offset += 2; - count--; - } - // Shift_JIS may not be supported in some environments: - try { - result.append(StringEncoding.decode(buffer, StringUtils.SHIFT_JIS)); - // TYPESCRIPTPORT: TODO: implement SHIFT_JIS decode. StringView from MDN could be a starting point - } - catch (ignored /*: UnsupportedEncodingException*/) { - throw new FormatException(ignored); - } - } - static decodeByteSegment(bits, result, count /*int*/, currentCharacterSetECI, byteSegments, hints) { - // Don't crash trying to read more bits than we have available. - if (8 * count > bits.available()) { - throw new FormatException(); - } - const readBytes = new Uint8Array(count); - for (let i = 0; i < count; i++) { - readBytes[i] = /*(byte) */ bits.readBits(8); - } - let encoding; - if (currentCharacterSetECI === null) { - // The spec isn't clear on this mode; see - // section 6.4.5: t does not say which encoding to assuming - // upon decoding. I have seen ISO-8859-1 used as well as - // Shift_JIS -- without anything like an ECI designator to - // give a hint. - encoding = StringUtils.guessEncoding(readBytes, hints); - } - else { - encoding = currentCharacterSetECI.getName(); - } - try { - result.append(StringEncoding.decode(readBytes, encoding)); - } - catch (ignored /*: UnsupportedEncodingException*/) { - throw new FormatException(ignored); - } - byteSegments.push(readBytes); - } - static toAlphaNumericChar(value /*int*/) { - if (value >= DecodedBitStreamParser$1.ALPHANUMERIC_CHARS.length) { - throw new FormatException(); - } - return DecodedBitStreamParser$1.ALPHANUMERIC_CHARS[value]; - } - static decodeAlphanumericSegment(bits, result, count /*int*/, fc1InEffect) { - // Read two characters at a time - const start = result.length(); - while (count > 1) { - if (bits.available() < 11) { - throw new FormatException(); - } - const nextTwoCharsBits = bits.readBits(11); - result.append(DecodedBitStreamParser$1.toAlphaNumericChar(Math.floor(nextTwoCharsBits / 45))); - result.append(DecodedBitStreamParser$1.toAlphaNumericChar(nextTwoCharsBits % 45)); - count -= 2; - } - if (count === 1) { - // special case: one character left - if (bits.available() < 6) { - throw new FormatException(); - } - result.append(DecodedBitStreamParser$1.toAlphaNumericChar(bits.readBits(6))); - } - // See section 6.4.8.1, 6.4.8.2 - if (fc1InEffect) { - // We need to massage the result a bit if in an FNC1 mode: - for (let i = start; i < result.length(); i++) { - if (result.charAt(i) === '%') { - if (i < result.length() - 1 && result.charAt(i + 1) === '%') { - // %% is rendered as % - result.deleteCharAt(i + 1); - } - else { - // In alpha mode, % should be converted to FNC1 separator 0x1D - result.setCharAt(i, String.fromCharCode(0x1D)); - } - } - } - } - } - static decodeNumericSegment(bits, result, count /*int*/) { - // Read three digits at a time - while (count >= 3) { - // Each 10 bits encodes three digits - if (bits.available() < 10) { - throw new FormatException(); - } - const threeDigitsBits = bits.readBits(10); - if (threeDigitsBits >= 1000) { - throw new FormatException(); - } - result.append(DecodedBitStreamParser$1.toAlphaNumericChar(Math.floor(threeDigitsBits / 100))); - result.append(DecodedBitStreamParser$1.toAlphaNumericChar(Math.floor(threeDigitsBits / 10) % 10)); - result.append(DecodedBitStreamParser$1.toAlphaNumericChar(threeDigitsBits % 10)); - count -= 3; - } - if (count === 2) { - // Two digits left over to read, encoded in 7 bits - if (bits.available() < 7) { - throw new FormatException(); - } - const twoDigitsBits = bits.readBits(7); - if (twoDigitsBits >= 100) { - throw new FormatException(); - } - result.append(DecodedBitStreamParser$1.toAlphaNumericChar(Math.floor(twoDigitsBits / 10))); - result.append(DecodedBitStreamParser$1.toAlphaNumericChar(twoDigitsBits % 10)); - } - else if (count === 1) { - // One digit left over to read - if (bits.available() < 4) { - throw new FormatException(); - } - const digitBits = bits.readBits(4); - if (digitBits >= 10) { - throw new FormatException(); - } - result.append(DecodedBitStreamParser$1.toAlphaNumericChar(digitBits)); - } - } - static parseECIValue(bits) { - const firstByte = bits.readBits(8); - if ((firstByte & 0x80) === 0) { - // just one byte - return firstByte & 0x7F; - } - if ((firstByte & 0xC0) === 0x80) { - // two bytes - const secondByte = bits.readBits(8); - return (((firstByte & 0x3F) << 8) & 0xFFFFFFFF) | secondByte; - } - if ((firstByte & 0xE0) === 0xC0) { - // three bytes - const secondThirdBytes = bits.readBits(16); - return (((firstByte & 0x1F) << 16) & 0xFFFFFFFF) | secondThirdBytes; - } - throw new FormatException(); - } - } - /** - * See ISO 18004:2006, 6.4.4 Table 5 - */ - DecodedBitStreamParser$1.ALPHANUMERIC_CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:'; - DecodedBitStreamParser$1.GB2312_SUBSET = 1; - // function Uint8ArrayToString(a: Uint8Array): string { - // const CHUNK_SZ = 0x8000; - // const c = new StringBuilder(); - // for (let i = 0, length = a.length; i < length; i += CHUNK_SZ) { - // c.append(String.fromCharCode.apply(null, a.subarray(i, i + CHUNK_SZ))); - // } - // return c.toString(); - // } - - /* - * 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. - */ - /** - * Meta-data container for QR Code decoding. Instances of this class may be used to convey information back to the - * decoding caller. Callers are expected to process this. - * - * @see com.google.zxing.common.DecoderResult#getOther() - */ - class QRCodeDecoderMetaData { - constructor(mirrored) { - this.mirrored = mirrored; - } - /** - * @return true if the QR Code was mirrored. - */ - isMirrored() { - return this.mirrored; - } - /** - * Apply the result points' order correction due to mirroring. - * - * @param points Array of points to apply mirror correction to. - */ - applyMirroredCorrection(points) { - if (!this.mirrored || points === null || points.length < 3) { - return; - } - const bottomLeft = points[0]; - points[0] = points[2]; - points[2] = bottomLeft; - // No need to 'fix' top-left and alignment pattern. - } - } - - /* - * Copyright 2007 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 java.util.Map;*/ - /** - *The main class which implements QR Code decoding -- as opposed to locating and extracting - * the QR Code from an image.
- * - * @author Sean Owen - */ - class Decoder$2 { - constructor() { - this.rsDecoder = new ReedSolomonDecoder(GenericGF.QR_CODE_FIELD_256); - } - // public decode(image: boolean[][]): DecoderResult /*throws ChecksumException, FormatException*/ { - // return decode(image, null) - // } - /** - *Convenience method that can decode a QR Code represented as a 2D array of booleans. - * "true" is taken to mean a black module.
- * - * @param image booleans representing white/black QR Code modules - * @param hints decoding hints that should be used to influence decoding - * @return text and bytes encoded within the QR Code - * @throws FormatException if the QR Code cannot be decoded - * @throws ChecksumException if error correction fails - */ - decodeBooleanArray(image, hints) { - return this.decodeBitMatrix(BitMatrix.parseFromBooleanArray(image), hints); - } - // public decodeBitMatrix(bits: BitMatrix): DecoderResult /*throws ChecksumException, FormatException*/ { - // return decode(bits, null) - // } - /** - *Decodes a QR Code represented as a {@link BitMatrix}. A 1 or "true" is taken to mean a black module.
- * - * @param bits booleans representing white/black QR Code modules - * @param hints decoding hints that should be used to influence decoding - * @return text and bytes encoded within the QR Code - * @throws FormatException if the QR Code cannot be decoded - * @throws ChecksumException if error correction fails - */ - decodeBitMatrix(bits, hints) { - // Construct a parser and read version, error-correction level - const parser = new BitMatrixParser$1(bits); - let ex = null; - try { - return this.decodeBitMatrixParser(parser, hints); - } - catch (e /*: FormatException, ChecksumException*/) { - ex = e; - } - try { - // Revert the bit matrix - parser.remask(); - // Will be attempting a mirrored reading of the version and format info. - parser.setMirror(true); - // Preemptively read the version. - parser.readVersion(); - // Preemptively read the format information. - parser.readFormatInformation(); - /* - * Since we're here, this means we have successfully detected some kind - * of version and format information when mirrored. This is a good sign, - * that the QR code may be mirrored, and we should try once more with a - * mirrored content. - */ - // Prepare for a mirrored reading. - parser.mirror(); - const result = this.decodeBitMatrixParser(parser, hints); - // Success! Notify the caller that the code was mirrored. - result.setOther(new QRCodeDecoderMetaData(true)); - return result; - } - catch (e /*FormatException | ChecksumException*/) { - // Throw the exception from the original reading - if (ex !== null) { - throw ex; - } - throw e; - } - } - decodeBitMatrixParser(parser, hints) { - const version = parser.readVersion(); - const ecLevel = parser.readFormatInformation().getErrorCorrectionLevel(); - // Read codewords - const codewords = parser.readCodewords(); - // Separate into data blocks - const dataBlocks = DataBlock$1.getDataBlocks(codewords, version, ecLevel); - // Count total number of data bytes - let totalBytes = 0; - for (const dataBlock of dataBlocks) { - totalBytes += dataBlock.getNumDataCodewords(); - } - const resultBytes = new Uint8Array(totalBytes); - let resultOffset = 0; - // Error-correct and copy data blocks together into a stream of bytes - for (const dataBlock of dataBlocks) { - const codewordBytes = dataBlock.getCodewords(); - const numDataCodewords = dataBlock.getNumDataCodewords(); - this.correctErrors(codewordBytes, numDataCodewords); - for (let i = 0; i < numDataCodewords; i++) { - resultBytes[resultOffset++] = codewordBytes[i]; - } - } - // Decode the contents of that stream of bytes - return DecodedBitStreamParser$1.decode(resultBytes, version, ecLevel, hints); - } - /** - *Given data and error-correction codewords received, possibly corrupted by errors, attempts to - * correct the errors in-place using Reed-Solomon error correction.
- * - * @param codewordBytes data and error correction codewords - * @param numDataCodewords number of codewords that are data bytes - * @throws ChecksumException if error correction fails - */ - correctErrors(codewordBytes, numDataCodewords /*int*/) { - // const numCodewords = codewordBytes.length; - // First read into an array of ints - const codewordsInts = new Int32Array(codewordBytes); - // TYPESCRIPTPORT: not realy necessary to transform to ints? could redesign everything to work with unsigned bytes? - // const codewordsInts = new Int32Array(numCodewords) - // for (let i = 0; i < numCodewords; i++) { - // codewordsInts[i] = codewordBytes[i] & 0xFF - // } - try { - this.rsDecoder.decode(codewordsInts, codewordBytes.length - numDataCodewords); - } - catch (ignored /*: ReedSolomonException*/) { - throw new ChecksumException(); - } - // Copy back into array of bytes -- only need to worry about the bytes that were data - // We don't care about errors in the error-correction codewords - for (let i = 0; i < numDataCodewords; i++) { - codewordBytes[i] = /*(byte) */ codewordsInts[i]; - } - } - } - - /* - * Copyright 2007 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. - */ - /** - *Encapsulates an alignment pattern, which are the smaller square patterns found in - * all but the simplest QR Codes.
- * - * @author Sean Owen - */ - class AlignmentPattern extends ResultPoint { - constructor(posX /*float*/, posY /*float*/, estimatedModuleSize /*float*/) { - super(posX, posY); - this.estimatedModuleSize = estimatedModuleSize; - } - /** - *Determines if this alignment pattern "about equals" an alignment pattern at the stated - * position and size -- meaning, it is at nearly the same center with nearly the same size.
- */ - aboutEquals(moduleSize /*float*/, i /*float*/, j /*float*/) { - if (Math.abs(i - this.getY()) <= moduleSize && Math.abs(j - this.getX()) <= moduleSize) { - const moduleSizeDiff = Math.abs(moduleSize - this.estimatedModuleSize); - return moduleSizeDiff <= 1.0 || moduleSizeDiff <= this.estimatedModuleSize; - } - return false; - } - /** - * Combines this object's current estimate of a finder pattern position and module size - * with a new estimate. It returns a new {@code FinderPattern} containing an average of the two. - */ - combineEstimate(i /*float*/, j /*float*/, newModuleSize /*float*/) { - const combinedX = (this.getX() + j) / 2.0; - const combinedY = (this.getY() + i) / 2.0; - const combinedModuleSize = (this.estimatedModuleSize + newModuleSize) / 2.0; - return new AlignmentPattern(combinedX, combinedY, combinedModuleSize); - } - } - - /* - * Copyright 2007 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 java.util.ArrayList;*/ - /*import java.util.List;*/ - /** - *This class attempts to find alignment patterns in a QR Code. Alignment patterns look like finder - * patterns but are smaller and appear at regular intervals throughout the image.
- * - *At the moment this only looks for the bottom-right alignment pattern.
- * - *This is mostly a simplified copy of {@link FinderPatternFinder}. It is copied, - * pasted and stripped down here for maximum performance but does unfortunately duplicate - * some code.
- * - *This class is thread-safe but not reentrant. Each thread must allocate its own object.
- * - * @author Sean Owen - */ - class AlignmentPatternFinder { - /** - *Creates a finder that will look in a portion of the whole image.
- * - * @param image image to search - * @param startX left column from which to start searching - * @param startY top row from which to start searching - * @param width width of region to search - * @param height height of region to search - * @param moduleSize estimated module size so far - */ - constructor(image, startX /*int*/, startY /*int*/, width /*int*/, height /*int*/, moduleSize /*float*/, resultPointCallback) { - this.image = image; - this.startX = startX; - this.startY = startY; - this.width = width; - this.height = height; - this.moduleSize = moduleSize; - this.resultPointCallback = resultPointCallback; - this.possibleCenters = []; // new ArrayThis method attempts to find the bottom-right alignment pattern in the image. It is a bit messy since - * it's pretty performance-critical and so is written to be fast foremost.
- * - * @return {@link AlignmentPattern} if found - * @throws NotFoundException if not found - */ - find() { - const startX = this.startX; - const height = this.height; - const width = this.width; - const maxJ = startX + width; - const middleI = this.startY + (height / 2); - // We are looking for black/white/black modules in 1:1:1 ratio - // this tracks the number of black/white/black modules seen so far - const stateCount = new Int32Array(3); - const image = this.image; - for (let iGen = 0; iGen < height; iGen++) { - // Search from middle outwards - const i = middleI + ((iGen & 0x01) === 0 ? Math.floor((iGen + 1) / 2) : -Math.floor((iGen + 1) / 2)); - stateCount[0] = 0; - stateCount[1] = 0; - stateCount[2] = 0; - let j = startX; - // Burn off leading white pixels before anything else; if we start in the middle of - // a white run, it doesn't make sense to count its length, since we don't know if the - // white run continued to the left of the start point - while (j < maxJ && !image.get(j, i)) { - j++; - } - let currentState = 0; - while (j < maxJ) { - if (image.get(j, i)) { - // Black pixel - if (currentState === 1) { // Counting black pixels - stateCount[1]++; - } - else { // Counting white pixels - if (currentState === 2) { // A winner? - if (this.foundPatternCross(stateCount)) { // Yes - const confirmed = this.handlePossibleCenter(stateCount, i, j); - if (confirmed !== null) { - return confirmed; - } - } - stateCount[0] = stateCount[2]; - stateCount[1] = 1; - stateCount[2] = 0; - currentState = 1; - } - else { - stateCount[++currentState]++; - } - } - } - else { // White pixel - if (currentState === 1) { // Counting black pixels - currentState++; - } - stateCount[currentState]++; - } - j++; - } - if (this.foundPatternCross(stateCount)) { - const confirmed = this.handlePossibleCenter(stateCount, i, maxJ); - if (confirmed !== null) { - return confirmed; - } - } - } - // Hmm, nothing we saw was observed and confirmed twice. If we had - // any guess at all, return it. - if (this.possibleCenters.length !== 0) { - return this.possibleCenters[0]; - } - throw new NotFoundException(); - } - /** - * Given a count of black/white/black pixels just seen and an end position, - * figures the location of the center of this black/white/black run. - */ - static centerFromEnd(stateCount, end /*int*/) { - return (end - stateCount[2]) - stateCount[1] / 2.0; - } - /** - * @param stateCount count of black/white/black pixels just read - * @return true iff the proportions of the counts is close enough to the 1/1/1 ratios - * used by alignment patterns to be considered a match - */ - foundPatternCross(stateCount) { - const moduleSize = this.moduleSize; - const maxVariance = moduleSize / 2.0; - for (let i = 0; i < 3; i++) { - if (Math.abs(moduleSize - stateCount[i]) >= maxVariance) { - return false; - } - } - return true; - } - /** - *After a horizontal scan finds a potential alignment pattern, this method - * "cross-checks" by scanning down vertically through the center of the possible - * alignment pattern to see if the same proportion is detected.
- * - * @param startI row where an alignment pattern was detected - * @param centerJ center of the section that appears to cross an alignment pattern - * @param maxCount maximum reasonable number of modules that should be - * observed in any reading state, based on the results of the horizontal scan - * @return vertical center of alignment pattern, or {@link Float#NaN} if not found - */ - crossCheckVertical(startI /*int*/, centerJ /*int*/, maxCount /*int*/, originalStateCountTotal /*int*/) { - const image = this.image; - const maxI = image.getHeight(); - const stateCount = this.crossCheckStateCount; - stateCount[0] = 0; - stateCount[1] = 0; - stateCount[2] = 0; - // Start counting up from center - let i = startI; - while (i >= 0 && image.get(centerJ, i) && stateCount[1] <= maxCount) { - stateCount[1]++; - i--; - } - // If already too many modules in this state or ran off the edge: - if (i < 0 || stateCount[1] > maxCount) { - return NaN; - } - while (i >= 0 && !image.get(centerJ, i) && stateCount[0] <= maxCount) { - stateCount[0]++; - i--; - } - if (stateCount[0] > maxCount) { - return NaN; - } - // Now also count down from center - i = startI + 1; - while (i < maxI && image.get(centerJ, i) && stateCount[1] <= maxCount) { - stateCount[1]++; - i++; - } - if (i === maxI || stateCount[1] > maxCount) { - return NaN; - } - while (i < maxI && !image.get(centerJ, i) && stateCount[2] <= maxCount) { - stateCount[2]++; - i++; - } - if (stateCount[2] > maxCount) { - return NaN; - } - const stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2]; - if (5 * Math.abs(stateCountTotal - originalStateCountTotal) >= 2 * originalStateCountTotal) { - return NaN; - } - return this.foundPatternCross(stateCount) ? AlignmentPatternFinder.centerFromEnd(stateCount, i) : NaN; - } - /** - *This is called when a horizontal scan finds a possible alignment pattern. It will - * cross check with a vertical scan, and if successful, will see if this pattern had been - * found on a previous horizontal scan. If so, we consider it confirmed and conclude we have - * found the alignment pattern.
- * - * @param stateCount reading state module counts from horizontal scan - * @param i row where alignment pattern may be found - * @param j end of possible alignment pattern in row - * @return {@link AlignmentPattern} if we have found the same pattern twice, or null if not - */ - handlePossibleCenter(stateCount, i /*int*/, j /*int*/) { - const stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2]; - const centerJ = AlignmentPatternFinder.centerFromEnd(stateCount, j); - const centerI = this.crossCheckVertical(i, /*(int) */ centerJ, 2 * stateCount[1], stateCountTotal); - if (!isNaN(centerI)) { - const estimatedModuleSize = (stateCount[0] + stateCount[1] + stateCount[2]) / 3.0; - for (const center of this.possibleCenters) { - // Look for about the same center and module size: - if (center.aboutEquals(estimatedModuleSize, centerI, centerJ)) { - return center.combineEstimate(centerI, centerJ, estimatedModuleSize); - } - } - // Hadn't found this before; save it - const point = new AlignmentPattern(centerJ, centerI, estimatedModuleSize); - this.possibleCenters.push(point); - if (this.resultPointCallback !== null && this.resultPointCallback !== undefined) { - this.resultPointCallback.foundPossibleResultPoint(point); - } - } - return null; - } - } - - /* - * Copyright 2007 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. - */ - /** - *Encapsulates a finder pattern, which are the three square patterns found in - * the corners of QR Codes. It also encapsulates a count of similar finder patterns, - * as a convenience to the finder's bookkeeping.
- * - * @author Sean Owen - */ - class FinderPattern$1 extends ResultPoint { - // FinderPattern(posX: number/*float*/, posY: number/*float*/, estimatedModuleSize: number/*float*/) { - // this(posX, posY, estimatedModuleSize, 1) - // } - constructor(posX /*float*/, posY /*float*/, estimatedModuleSize /*float*/, count /*int*/) { - super(posX, posY); - this.estimatedModuleSize = estimatedModuleSize; - this.count = count; - if (undefined === count) { - this.count = 1; - } - } - getEstimatedModuleSize() { - return this.estimatedModuleSize; - } - getCount() { - return this.count; - } - /* - void incrementCount() { - this.count++ - } - */ - /** - *Determines if this finder pattern "about equals" a finder pattern at the stated - * position and size -- meaning, it is at nearly the same center with nearly the same size.
- */ - aboutEquals(moduleSize /*float*/, i /*float*/, j /*float*/) { - if (Math.abs(i - this.getY()) <= moduleSize && Math.abs(j - this.getX()) <= moduleSize) { - const moduleSizeDiff = Math.abs(moduleSize - this.estimatedModuleSize); - return moduleSizeDiff <= 1.0 || moduleSizeDiff <= this.estimatedModuleSize; - } - return false; - } - /** - * Combines this object's current estimate of a finder pattern position and module size - * with a new estimate. It returns a new {@code FinderPattern} containing a weighted average - * based on count. - */ - combineEstimate(i /*float*/, j /*float*/, newModuleSize /*float*/) { - const combinedCount = this.count + 1; - const combinedX = (this.count * this.getX() + j) / combinedCount; - const combinedY = (this.count * this.getY() + i) / combinedCount; - const combinedModuleSize = (this.count * this.estimatedModuleSize + newModuleSize) / combinedCount; - return new FinderPattern$1(combinedX, combinedY, combinedModuleSize, combinedCount); - } - } - - /* - * Copyright 2007 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. - */ - /** - *Encapsulates information about finder patterns in an image, including the location of - * the three finder patterns, and their estimated module size.
- * - * @author Sean Owen - */ - class FinderPatternInfo { - constructor(patternCenters) { - this.bottomLeft = patternCenters[0]; - this.topLeft = patternCenters[1]; - this.topRight = patternCenters[2]; - } - getBottomLeft() { - return this.bottomLeft; - } - getTopLeft() { - return this.topLeft; - } - getTopRight() { - return this.topRight; - } - } - - /* - * Copyright 2007 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 java.io.Serializable;*/ - /*import java.util.ArrayList;*/ - /*import java.util.Collections;*/ - /*import java.util.Comparator;*/ - /*import java.util.List;*/ - /*import java.util.Map;*/ - /** - *This class attempts to find finder patterns in a QR Code. Finder patterns are the square - * markers at three corners of a QR Code.
- * - *This class is thread-safe but not reentrant. Each thread must allocate its own object. - * - * @author Sean Owen - */ - class FinderPatternFinder { - /** - *
Creates a finder that will search the image for three finder patterns.
- * - * @param image image to search - */ - // public constructor(image: BitMatrix) { - // this(image, null) - // } - constructor(image, resultPointCallback) { - this.image = image; - this.resultPointCallback = resultPointCallback; - this.possibleCenters = []; - this.crossCheckStateCount = new Int32Array(5); - this.resultPointCallback = resultPointCallback; - } - getImage() { - return this.image; - } - getPossibleCenters() { - return this.possibleCenters; - } - find(hints) { - const tryHarder = (hints !== null && hints !== undefined) && undefined !== hints.get(DecodeHintType$1.TRY_HARDER); - const pureBarcode = (hints !== null && hints !== undefined) && undefined !== hints.get(DecodeHintType$1.PURE_BARCODE); - const image = this.image; - const maxI = image.getHeight(); - const maxJ = image.getWidth(); - // We are looking for black/white/black/white/black modules in - // 1:1:3:1:1 ratio; this tracks the number of such modules seen so far - // Let's assume that the maximum version QR Code we support takes up 1/4 the height of the - // image, and then account for the center being 3 modules in size. This gives the smallest - // number of pixels the center could be, so skip this often. When trying harder, look for all - // QR versions regardless of how dense they are. - let iSkip = Math.floor((3 * maxI) / (4 * FinderPatternFinder.MAX_MODULES)); - if (iSkip < FinderPatternFinder.MIN_SKIP || tryHarder) { - iSkip = FinderPatternFinder.MIN_SKIP; - } - let done = false; - const stateCount = new Int32Array(5); - for (let i = iSkip - 1; i < maxI && !done; i += iSkip) { - // Get a row of black/white values - stateCount[0] = 0; - stateCount[1] = 0; - stateCount[2] = 0; - stateCount[3] = 0; - stateCount[4] = 0; - let currentState = 0; - for (let j = 0; j < maxJ; j++) { - if (image.get(j, i)) { - // Black pixel - if ((currentState & 1) === 1) { // Counting white pixels - currentState++; - } - stateCount[currentState]++; - } - else { // White pixel - if ((currentState & 1) === 0) { // Counting black pixels - if (currentState === 4) { // A winner? - if (FinderPatternFinder.foundPatternCross(stateCount)) { // Yes - const confirmed = this.handlePossibleCenter(stateCount, i, j, pureBarcode); - if (confirmed === true) { - // Start examining every other line. Checking each line turned out to be too - // expensive and didn't improve performance. - iSkip = 2; - if (this.hasSkipped === true) { - done = this.haveMultiplyConfirmedCenters(); - } - else { - const rowSkip = this.findRowSkip(); - if (rowSkip > stateCount[2]) { - // Skip rows between row of lower confirmed center - // and top of presumed third confirmed center - // but back up a bit to get a full chance of detecting - // it, entire width of center of finder pattern - // Skip by rowSkip, but back off by stateCount[2] (size of last center - // of pattern we saw) to be conservative, and also back off by iSkip which - // is about to be re-added - i += rowSkip - stateCount[2] - iSkip; - j = maxJ - 1; - } - } - } - else { - stateCount[0] = stateCount[2]; - stateCount[1] = stateCount[3]; - stateCount[2] = stateCount[4]; - stateCount[3] = 1; - stateCount[4] = 0; - currentState = 3; - continue; - } - // Clear state to start looking again - currentState = 0; - stateCount[0] = 0; - stateCount[1] = 0; - stateCount[2] = 0; - stateCount[3] = 0; - stateCount[4] = 0; - } - else { // No, shift counts back by two - stateCount[0] = stateCount[2]; - stateCount[1] = stateCount[3]; - stateCount[2] = stateCount[4]; - stateCount[3] = 1; - stateCount[4] = 0; - currentState = 3; - } - } - else { - stateCount[++currentState]++; - } - } - else { // Counting white pixels - stateCount[currentState]++; - } - } - } - if (FinderPatternFinder.foundPatternCross(stateCount)) { - const confirmed = this.handlePossibleCenter(stateCount, i, maxJ, pureBarcode); - if (confirmed === true) { - iSkip = stateCount[0]; - if (this.hasSkipped) { - // Found a third one - done = this.haveMultiplyConfirmedCenters(); - } - } - } - } - const patternInfo = this.selectBestPatterns(); - ResultPoint.orderBestPatterns(patternInfo); - return new FinderPatternInfo(patternInfo); - } - /** - * Given a count of black/white/black/white/black pixels just seen and an end position, - * figures the location of the center of this run. - */ - static centerFromEnd(stateCount, end /*int*/) { - return (end - stateCount[4] - stateCount[3]) - stateCount[2] / 2.0; - } - /** - * @param stateCount count of black/white/black/white/black pixels just read - * @return true iff the proportions of the counts is close enough to the 1/1/3/1/1 ratios - * used by finder patterns to be considered a match - */ - static foundPatternCross(stateCount) { - let totalModuleSize = 0; - for (let i = 0; i < 5; i++) { - const count = stateCount[i]; - if (count === 0) { - return false; - } - totalModuleSize += count; - } - if (totalModuleSize < 7) { - return false; - } - const moduleSize = totalModuleSize / 7.0; - const maxVariance = moduleSize / 2.0; - // Allow less than 50% variance from 1-1-3-1-1 proportions - return Math.abs(moduleSize - stateCount[0]) < maxVariance && - Math.abs(moduleSize - stateCount[1]) < maxVariance && - Math.abs(3.0 * moduleSize - stateCount[2]) < 3 * maxVariance && - Math.abs(moduleSize - stateCount[3]) < maxVariance && - Math.abs(moduleSize - stateCount[4]) < maxVariance; - } - getCrossCheckStateCount() { - const crossCheckStateCount = this.crossCheckStateCount; - crossCheckStateCount[0] = 0; - crossCheckStateCount[1] = 0; - crossCheckStateCount[2] = 0; - crossCheckStateCount[3] = 0; - crossCheckStateCount[4] = 0; - return crossCheckStateCount; - } - /** - * After a vertical and horizontal scan finds a potential finder pattern, this method - * "cross-cross-cross-checks" by scanning down diagonally through the center of the possible - * finder pattern to see if the same proportion is detected. - * - * @param startI row where a finder pattern was detected - * @param centerJ center of the section that appears to cross a finder pattern - * @param maxCount maximum reasonable number of modules that should be - * observed in any reading state, based on the results of the horizontal scan - * @param originalStateCountTotal The original state count total. - * @return true if proportions are withing expected limits - */ - crossCheckDiagonal(startI /*int*/, centerJ /*int*/, maxCount /*int*/, originalStateCountTotal /*int*/) { - const stateCount = this.getCrossCheckStateCount(); - // Start counting up, left from center finding black center mass - let i = 0; - const image = this.image; - while (startI >= i && centerJ >= i && image.get(centerJ - i, startI - i)) { - stateCount[2]++; - i++; - } - if (startI < i || centerJ < i) { - return false; - } - // Continue up, left finding white space - while (startI >= i && centerJ >= i && !image.get(centerJ - i, startI - i) && - stateCount[1] <= maxCount) { - stateCount[1]++; - i++; - } - // If already too many modules in this state or ran off the edge: - if (startI < i || centerJ < i || stateCount[1] > maxCount) { - return false; - } - // Continue up, left finding black border - while (startI >= i && centerJ >= i && image.get(centerJ - i, startI - i) && - stateCount[0] <= maxCount) { - stateCount[0]++; - i++; - } - if (stateCount[0] > maxCount) { - return false; - } - const maxI = image.getHeight(); - const maxJ = image.getWidth(); - // Now also count down, right from center - i = 1; - while (startI + i < maxI && centerJ + i < maxJ && image.get(centerJ + i, startI + i)) { - stateCount[2]++; - i++; - } - // Ran off the edge? - if (startI + i >= maxI || centerJ + i >= maxJ) { - return false; - } - while (startI + i < maxI && centerJ + i < maxJ && !image.get(centerJ + i, startI + i) && - stateCount[3] < maxCount) { - stateCount[3]++; - i++; - } - if (startI + i >= maxI || centerJ + i >= maxJ || stateCount[3] >= maxCount) { - return false; - } - while (startI + i < maxI && centerJ + i < maxJ && image.get(centerJ + i, startI + i) && - stateCount[4] < maxCount) { - stateCount[4]++; - i++; - } - if (stateCount[4] >= maxCount) { - return false; - } - // If we found a finder-pattern-like section, but its size is more than 100% different than - // the original, assume it's a false positive - const stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + stateCount[4]; - return Math.abs(stateCountTotal - originalStateCountTotal) < 2 * originalStateCountTotal && - FinderPatternFinder.foundPatternCross(stateCount); - } - /** - *After a horizontal scan finds a potential finder pattern, this method - * "cross-checks" by scanning down vertically through the center of the possible - * finder pattern to see if the same proportion is detected.
- * - * @param startI row where a finder pattern was detected - * @param centerJ center of the section that appears to cross a finder pattern - * @param maxCount maximum reasonable number of modules that should be - * observed in any reading state, based on the results of the horizontal scan - * @return vertical center of finder pattern, or {@link Float#NaN} if not found - */ - crossCheckVertical(startI /*int*/, centerJ /*int*/, maxCount /*int*/, originalStateCountTotal /*int*/) { - const image = this.image; - const maxI = image.getHeight(); - const stateCount = this.getCrossCheckStateCount(); - // Start counting up from center - let i = startI; - while (i >= 0 && image.get(centerJ, i)) { - stateCount[2]++; - i--; - } - if (i < 0) { - return NaN; - } - while (i >= 0 && !image.get(centerJ, i) && stateCount[1] <= maxCount) { - stateCount[1]++; - i--; - } - // If already too many modules in this state or ran off the edge: - if (i < 0 || stateCount[1] > maxCount) { - return NaN; - } - while (i >= 0 && image.get(centerJ, i) && stateCount[0] <= maxCount) { - stateCount[0]++; - i--; - } - if (stateCount[0] > maxCount) { - return NaN; - } - // Now also count down from center - i = startI + 1; - while (i < maxI && image.get(centerJ, i)) { - stateCount[2]++; - i++; - } - if (i === maxI) { - return NaN; - } - while (i < maxI && !image.get(centerJ, i) && stateCount[3] < maxCount) { - stateCount[3]++; - i++; - } - if (i === maxI || stateCount[3] >= maxCount) { - return NaN; - } - while (i < maxI && image.get(centerJ, i) && stateCount[4] < maxCount) { - stateCount[4]++; - i++; - } - if (stateCount[4] >= maxCount) { - return NaN; - } - // If we found a finder-pattern-like section, but its size is more than 40% different than - // the original, assume it's a false positive - const stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + - stateCount[4]; - if (5 * Math.abs(stateCountTotal - originalStateCountTotal) >= 2 * originalStateCountTotal) { - return NaN; - } - return FinderPatternFinder.foundPatternCross(stateCount) ? FinderPatternFinder.centerFromEnd(stateCount, i) : NaN; - } - /** - *Like {@link #crossCheckVertical(int, int, int, int)}, and in fact is basically identical, - * except it reads horizontally instead of vertically. This is used to cross-cross - * check a vertical cross check and locate the real center of the alignment pattern.
- */ - crossCheckHorizontal(startJ /*int*/, centerI /*int*/, maxCount /*int*/, originalStateCountTotal /*int*/) { - const image = this.image; - const maxJ = image.getWidth(); - const stateCount = this.getCrossCheckStateCount(); - let j = startJ; - while (j >= 0 && image.get(j, centerI)) { - stateCount[2]++; - j--; - } - if (j < 0) { - return NaN; - } - while (j >= 0 && !image.get(j, centerI) && stateCount[1] <= maxCount) { - stateCount[1]++; - j--; - } - if (j < 0 || stateCount[1] > maxCount) { - return NaN; - } - while (j >= 0 && image.get(j, centerI) && stateCount[0] <= maxCount) { - stateCount[0]++; - j--; - } - if (stateCount[0] > maxCount) { - return NaN; - } - j = startJ + 1; - while (j < maxJ && image.get(j, centerI)) { - stateCount[2]++; - j++; - } - if (j === maxJ) { - return NaN; - } - while (j < maxJ && !image.get(j, centerI) && stateCount[3] < maxCount) { - stateCount[3]++; - j++; - } - if (j === maxJ || stateCount[3] >= maxCount) { - return NaN; - } - while (j < maxJ && image.get(j, centerI) && stateCount[4] < maxCount) { - stateCount[4]++; - j++; - } - if (stateCount[4] >= maxCount) { - return NaN; - } - // If we found a finder-pattern-like section, but its size is significantly different than - // the original, assume it's a false positive - const stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + - stateCount[4]; - if (5 * Math.abs(stateCountTotal - originalStateCountTotal) >= originalStateCountTotal) { - return NaN; - } - return FinderPatternFinder.foundPatternCross(stateCount) ? FinderPatternFinder.centerFromEnd(stateCount, j) : NaN; - } - /** - *This is called when a horizontal scan finds a possible alignment pattern. It will - * cross check with a vertical scan, and if successful, will, ah, cross-cross-check - * with another horizontal scan. This is needed primarily to locate the real horizontal - * center of the pattern in cases of extreme skew. - * And then we cross-cross-cross check with another diagonal scan.
- * - *If that succeeds the finder pattern location is added to a list that tracks - * the number of times each location has been nearly-matched as a finder pattern. - * Each additional find is more evidence that the location is in fact a finder - * pattern center - * - * @param stateCount reading state module counts from horizontal scan - * @param i row where finder pattern may be found - * @param j end of possible finder pattern in row - * @param pureBarcode true if in "pure barcode" mode - * @return true if a finder pattern candidate was found this time - */ - handlePossibleCenter(stateCount, i /*int*/, j /*int*/, pureBarcode) { - const stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + - stateCount[4]; - let centerJ = FinderPatternFinder.centerFromEnd(stateCount, j); - let centerI = this.crossCheckVertical(i, /*(int) */ Math.floor(centerJ), stateCount[2], stateCountTotal); - if (!isNaN(centerI)) { - // Re-cross check - centerJ = this.crossCheckHorizontal(/*(int) */ Math.floor(centerJ), /*(int) */ Math.floor(centerI), stateCount[2], stateCountTotal); - if (!isNaN(centerJ) && - (!pureBarcode || this.crossCheckDiagonal(/*(int) */ Math.floor(centerI), /*(int) */ Math.floor(centerJ), stateCount[2], stateCountTotal))) { - const estimatedModuleSize = stateCountTotal / 7.0; - let found = false; - const possibleCenters = this.possibleCenters; - for (let index = 0, length = possibleCenters.length; index < length; index++) { - const center = possibleCenters[index]; - // Look for about the same center and module size: - if (center.aboutEquals(estimatedModuleSize, centerI, centerJ)) { - possibleCenters[index] = center.combineEstimate(centerI, centerJ, estimatedModuleSize); - found = true; - break; - } - } - if (!found) { - const point = new FinderPattern$1(centerJ, centerI, estimatedModuleSize); - possibleCenters.push(point); - if (this.resultPointCallback !== null && this.resultPointCallback !== undefined) { - this.resultPointCallback.foundPossibleResultPoint(point); - } - } - return true; - } - } - return false; - } - /** - * @return number of rows we could safely skip during scanning, based on the first - * two finder patterns that have been located. In some cases their position will - * allow us to infer that the third pattern must lie below a certain point farther - * down in the image. - */ - findRowSkip() { - const max = this.possibleCenters.length; - if (max <= 1) { - return 0; - } - let firstConfirmedCenter = null; - for (const center of this.possibleCenters) { - if (center.getCount() >= FinderPatternFinder.CENTER_QUORUM) { - if (firstConfirmedCenter == null) { - firstConfirmedCenter = center; - } - else { - // We have two confirmed centers - // How far down can we skip before resuming looking for the next - // pattern? In the worst case, only the difference between the - // difference in the x / y coordinates of the two centers. - // This is the case where you find top left last. - this.hasSkipped = true; - return /*(int) */ Math.floor((Math.abs(firstConfirmedCenter.getX() - center.getX()) - - Math.abs(firstConfirmedCenter.getY() - center.getY())) / 2); - } - } - } - return 0; - } - /** - * @return true iff we have found at least 3 finder patterns that have been detected - * at least {@link #CENTER_QUORUM} times each, and, the estimated module size of the - * candidates is "pretty similar" - */ - haveMultiplyConfirmedCenters() { - let confirmedCount = 0; - let totalModuleSize = 0.0; - const max = this.possibleCenters.length; - for (const pattern of this.possibleCenters) { - if (pattern.getCount() >= FinderPatternFinder.CENTER_QUORUM) { - confirmedCount++; - totalModuleSize += pattern.getEstimatedModuleSize(); - } - } - if (confirmedCount < 3) { - return false; - } - // OK, we have at least 3 confirmed centers, but, it's possible that one is a "false positive" - // and that we need to keep looking. We detect this by asking if the estimated module sizes - // vary too much. We arbitrarily say that when the total deviation from average exceeds - // 5% of the total module size estimates, it's too much. - const average = totalModuleSize / max; - let totalDeviation = 0.0; - for (const pattern of this.possibleCenters) { - totalDeviation += Math.abs(pattern.getEstimatedModuleSize() - average); - } - return totalDeviation <= 0.05 * totalModuleSize; - } - /** - * @return the 3 best {@link FinderPattern}s from our list of candidates. The "best" are - * those that have been detected at least {@link #CENTER_QUORUM} times, and whose module - * size differs from the average among those patterns the least - * @throws NotFoundException if 3 such finder patterns do not exist - */ - selectBestPatterns() { - const startSize = this.possibleCenters.length; - if (startSize < 3) { - // Couldn't find enough finder patterns - throw new NotFoundException(); - } - const possibleCenters = this.possibleCenters; - let average; - // Filter outlier possibilities whose module size is too different - if (startSize > 3) { - // But we can only afford to do so if we have at least 4 possibilities to choose from - let totalModuleSize = 0.0; - let square = 0.0; - for (const center of this.possibleCenters) { - const size = center.getEstimatedModuleSize(); - totalModuleSize += size; - square += size * size; - } - average = totalModuleSize / startSize; - let stdDev = Math.sqrt(square / startSize - average * average); - possibleCenters.sort( - /** - *
Orders by furthest from average
- */ - // FurthestFromAverageComparator implements ComparatorOrders by {@link FinderPattern#getCount()}, descending.
- */ - // CenterComparator implements ComparatorEncapsulates logic that can detect a QR Code in an image, even if the QR Code - * is rotated or skewed, or partially obscured.
- * - * @author Sean Owen - */ - class Detector$2 { - constructor(image) { - this.image = image; - } - getImage() { - return this.image; - } - getResultPointCallback() { - return this.resultPointCallback; - } - /** - *Detects a QR Code in an image.
- * - * @return {@link DetectorResult} encapsulating results of detecting a QR Code - * @throws NotFoundException if QR Code cannot be found - * @throws FormatException if a QR Code cannot be decoded - */ - // public detect(): DetectorResult /*throws NotFoundException, FormatException*/ { - // return detect(null) - // } - /** - *Detects a QR Code in an image.
- * - * @param hints optional hints to detector - * @return {@link DetectorResult} encapsulating results of detecting a QR Code - * @throws NotFoundException if QR Code cannot be found - * @throws FormatException if a QR Code cannot be decoded - */ - detect(hints) { - this.resultPointCallback = (hints === null || hints === undefined) ? null : - /*(ResultPointCallback) */ hints.get(DecodeHintType$1.NEED_RESULT_POINT_CALLBACK); - const finder = new FinderPatternFinder(this.image, this.resultPointCallback); - const info = finder.find(hints); - return this.processFinderPatternInfo(info); - } - processFinderPatternInfo(info) { - const topLeft = info.getTopLeft(); - const topRight = info.getTopRight(); - const bottomLeft = info.getBottomLeft(); - const moduleSize = this.calculateModuleSize(topLeft, topRight, bottomLeft); - if (moduleSize < 1.0) { - throw new NotFoundException('No pattern found in proccess finder.'); - } - const dimension = Detector$2.computeDimension(topLeft, topRight, bottomLeft, moduleSize); - const provisionalVersion = Version$1.getProvisionalVersionForDimension(dimension); - const modulesBetweenFPCenters = provisionalVersion.getDimensionForVersion() - 7; - let alignmentPattern = null; - // Anything above version 1 has an alignment pattern - if (provisionalVersion.getAlignmentPatternCenters().length > 0) { - // Guess where a "bottom right" finder pattern would have been - const bottomRightX = topRight.getX() - topLeft.getX() + bottomLeft.getX(); - const bottomRightY = topRight.getY() - topLeft.getY() + bottomLeft.getY(); - // Estimate that alignment pattern is closer by 3 modules - // from "bottom right" to known top left location - const correctionToTopLeft = 1.0 - 3.0 / modulesBetweenFPCenters; - const estAlignmentX = /*(int) */ Math.floor(topLeft.getX() + correctionToTopLeft * (bottomRightX - topLeft.getX())); - const estAlignmentY = /*(int) */ Math.floor(topLeft.getY() + correctionToTopLeft * (bottomRightY - topLeft.getY())); - // Kind of arbitrary -- expand search radius before giving up - for (let i = 4; i <= 16; i <<= 1) { - try { - alignmentPattern = this.findAlignmentInRegion(moduleSize, estAlignmentX, estAlignmentY, i); - break; - } - catch (re /*NotFoundException*/) { - if (!(re instanceof NotFoundException)) { - throw re; - } - // try next round - } - } - // If we didn't find alignment pattern... well try anyway without it - } - const transform = Detector$2.createTransform(topLeft, topRight, bottomLeft, alignmentPattern, dimension); - const bits = Detector$2.sampleGrid(this.image, transform, dimension); - let points; - if (alignmentPattern === null) { - points = [bottomLeft, topLeft, topRight]; - } - else { - points = [bottomLeft, topLeft, topRight, alignmentPattern]; - } - return new DetectorResult(bits, points); - } - static createTransform(topLeft, topRight, bottomLeft, alignmentPattern, dimension /*int*/) { - const dimMinusThree = dimension - 3.5; - let bottomRightX; /*float*/ - let bottomRightY; /*float*/ - let sourceBottomRightX; /*float*/ - let sourceBottomRightY; /*float*/ - if (alignmentPattern !== null) { - bottomRightX = alignmentPattern.getX(); - bottomRightY = alignmentPattern.getY(); - sourceBottomRightX = dimMinusThree - 3.0; - sourceBottomRightY = sourceBottomRightX; - } - else { - // Don't have an alignment pattern, just make up the bottom-right point - bottomRightX = (topRight.getX() - topLeft.getX()) + bottomLeft.getX(); - bottomRightY = (topRight.getY() - topLeft.getY()) + bottomLeft.getY(); - sourceBottomRightX = dimMinusThree; - sourceBottomRightY = dimMinusThree; - } - return PerspectiveTransform.quadrilateralToQuadrilateral(3.5, 3.5, dimMinusThree, 3.5, sourceBottomRightX, sourceBottomRightY, 3.5, dimMinusThree, topLeft.getX(), topLeft.getY(), topRight.getX(), topRight.getY(), bottomRightX, bottomRightY, bottomLeft.getX(), bottomLeft.getY()); - } - static sampleGrid(image, transform, dimension /*int*/) { - const sampler = GridSamplerInstance.getInstance(); - return sampler.sampleGridWithTransform(image, dimension, dimension, transform); - } - /** - *Computes the dimension (number of modules on a size) of the QR Code based on the position - * of the finder patterns and estimated module size.
- */ - static computeDimension(topLeft, topRight, bottomLeft, moduleSize /*float*/) { - const tltrCentersDimension = MathUtils.round(ResultPoint.distance(topLeft, topRight) / moduleSize); - const tlblCentersDimension = MathUtils.round(ResultPoint.distance(topLeft, bottomLeft) / moduleSize); - let dimension = Math.floor((tltrCentersDimension + tlblCentersDimension) / 2) + 7; - switch (dimension & 0x03) { // mod 4 - case 0: - dimension++; - break; - // 1? do nothing - case 2: - dimension--; - break; - case 3: - throw new NotFoundException('Dimensions could be not found.'); - } - return dimension; - } - /** - *Computes an average estimated module size based on estimated derived from the positions - * of the three finder patterns.
- * - * @param topLeft detected top-left finder pattern center - * @param topRight detected top-right finder pattern center - * @param bottomLeft detected bottom-left finder pattern center - * @return estimated module size - */ - calculateModuleSize(topLeft, topRight, bottomLeft) { - // Take the average - return (this.calculateModuleSizeOneWay(topLeft, topRight) + - this.calculateModuleSizeOneWay(topLeft, bottomLeft)) / 2.0; - } - /** - *Estimates module size based on two finder patterns -- it uses - * {@link #sizeOfBlackWhiteBlackRunBothWays(int, int, int, int)} to figure the - * width of each, measuring along the axis between their centers.
- */ - calculateModuleSizeOneWay(pattern, otherPattern) { - const moduleSizeEst1 = this.sizeOfBlackWhiteBlackRunBothWays(/*(int) */ Math.floor(pattern.getX()), - /*(int) */ Math.floor(pattern.getY()), - /*(int) */ Math.floor(otherPattern.getX()), - /*(int) */ Math.floor(otherPattern.getY())); - const moduleSizeEst2 = this.sizeOfBlackWhiteBlackRunBothWays(/*(int) */ Math.floor(otherPattern.getX()), - /*(int) */ Math.floor(otherPattern.getY()), - /*(int) */ Math.floor(pattern.getX()), - /*(int) */ Math.floor(pattern.getY())); - if (isNaN(moduleSizeEst1)) { - return moduleSizeEst2 / 7.0; - } - if (isNaN(moduleSizeEst2)) { - return moduleSizeEst1 / 7.0; - } - // Average them, and divide by 7 since we've counted the width of 3 black modules, - // and 1 white and 1 black module on either side. Ergo, divide sum by 14. - return (moduleSizeEst1 + moduleSizeEst2) / 14.0; - } - /** - * See {@link #sizeOfBlackWhiteBlackRun(int, int, int, int)}; computes the total width of - * a finder pattern by looking for a black-white-black run from the center in the direction - * of another point (another finder pattern center), and in the opposite direction too. - */ - sizeOfBlackWhiteBlackRunBothWays(fromX /*int*/, fromY /*int*/, toX /*int*/, toY /*int*/) { - let result = this.sizeOfBlackWhiteBlackRun(fromX, fromY, toX, toY); - // Now count other way -- don't run off image though of course - let scale = 1.0; - let otherToX = fromX - (toX - fromX); - if (otherToX < 0) { - scale = fromX / /*(float) */ (fromX - otherToX); - otherToX = 0; - } - else if (otherToX >= this.image.getWidth()) { - scale = (this.image.getWidth() - 1 - fromX) / /*(float) */ (otherToX - fromX); - otherToX = this.image.getWidth() - 1; - } - let otherToY = /*(int) */ Math.floor(fromY - (toY - fromY) * scale); - scale = 1.0; - if (otherToY < 0) { - scale = fromY / /*(float) */ (fromY - otherToY); - otherToY = 0; - } - else if (otherToY >= this.image.getHeight()) { - scale = (this.image.getHeight() - 1 - fromY) / /*(float) */ (otherToY - fromY); - otherToY = this.image.getHeight() - 1; - } - otherToX = /*(int) */ Math.floor(fromX + (otherToX - fromX) * scale); - result += this.sizeOfBlackWhiteBlackRun(fromX, fromY, otherToX, otherToY); - // Middle pixel is double-counted this way; subtract 1 - return result - 1.0; - } - /** - *This method traces a line from a point in the image, in the direction towards another point. - * It begins in a black region, and keeps going until it finds white, then black, then white again. - * It reports the distance from the start to this point.
- * - *This is used when figuring out how wide a finder pattern is, when the finder pattern - * may be skewed or rotated.
- */ - sizeOfBlackWhiteBlackRun(fromX /*int*/, fromY /*int*/, toX /*int*/, toY /*int*/) { - // Mild variant of Bresenham's algorithm - // see http://en.wikipedia.org/wiki/Bresenham's_line_algorithm - const steep = Math.abs(toY - fromY) > Math.abs(toX - fromX); - if (steep) { - let temp = fromX; - fromX = fromY; - fromY = temp; - temp = toX; - toX = toY; - toY = temp; - } - const dx = Math.abs(toX - fromX); - const dy = Math.abs(toY - fromY); - let error = -dx / 2; - const xstep = fromX < toX ? 1 : -1; - const ystep = fromY < toY ? 1 : -1; - // In black pixels, looking for white, first or second time. - let state = 0; - // Loop up until x == toX, but not beyond - const xLimit = toX + xstep; - for (let x = fromX, y = fromY; x !== xLimit; x += xstep) { - const realX = steep ? y : x; - const realY = steep ? x : y; - // Does current pixel mean we have moved white to black or vice versa? - // Scanning black in state 0,2 and white in state 1, so if we find the wrong - // color, advance to next state or end if we are in state 2 already - if ((state === 1) === this.image.get(realX, realY)) { - if (state === 2) { - return MathUtils.distance(x, y, fromX, fromY); - } - state++; - } - error += dy; - if (error > 0) { - if (y === toY) { - break; - } - y += ystep; - error -= dx; - } - } - // Found black-white-black; give the benefit of the doubt that the next pixel outside the image - // is "white" so this last point at (toX+xStep,toY) is the right ending. This is really a - // small approximation; (toX+xStep,toY+yStep) might be really correct. Ignore this. - if (state === 2) { - return MathUtils.distance(toX + xstep, toY, fromX, fromY); - } - // else we didn't find even black-white-black; no estimate is really possible - return NaN; - } - /** - *Attempts to locate an alignment pattern in a limited region of the image, which is - * guessed to contain it. This method uses {@link AlignmentPattern}.
- * - * @param overallEstModuleSize estimated module size so far - * @param estAlignmentX x coordinate of center of area probably containing alignment pattern - * @param estAlignmentY y coordinate of above - * @param allowanceFactor number of pixels in all directions to search from the center - * @return {@link AlignmentPattern} if found, or null otherwise - * @throws NotFoundException if an unexpected error occurs during detection - */ - findAlignmentInRegion(overallEstModuleSize /*float*/, estAlignmentX /*int*/, estAlignmentY /*int*/, allowanceFactor /*float*/) { - // Look for an alignment pattern (3 modules in size) around where it - // should be - const allowance = /*(int) */ Math.floor(allowanceFactor * overallEstModuleSize); - const alignmentAreaLeftX = Math.max(0, estAlignmentX - allowance); - const alignmentAreaRightX = Math.min(this.image.getWidth() - 1, estAlignmentX + allowance); - if (alignmentAreaRightX - alignmentAreaLeftX < overallEstModuleSize * 3) { - throw new NotFoundException('Alignment top exceeds estimated module size.'); - } - const alignmentAreaTopY = Math.max(0, estAlignmentY - allowance); - const alignmentAreaBottomY = Math.min(this.image.getHeight() - 1, estAlignmentY + allowance); - if (alignmentAreaBottomY - alignmentAreaTopY < overallEstModuleSize * 3) { - throw new NotFoundException('Alignment bottom exceeds estimated module size.'); - } - const alignmentFinder = new AlignmentPatternFinder(this.image, alignmentAreaLeftX, alignmentAreaTopY, alignmentAreaRightX - alignmentAreaLeftX, alignmentAreaBottomY - alignmentAreaTopY, overallEstModuleSize, this.resultPointCallback); - return alignmentFinder.find(); - } - } - - /* - * Copyright 2007 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 java.util.List;*/ - /*import java.util.Map;*/ - /** - * This implementation can detect and decode QR Codes in an image. - * - * @author Sean Owen - */ - class QRCodeReader { - constructor() { - this.decoder = new Decoder$2(); - } - getDecoder() { - return this.decoder; - } - /** - * Locates and decodes a QR code in an image. - * - * @return a representing: string the content encoded by the QR code - * @throws NotFoundException if a QR code cannot be found - * @throws FormatException if a QR code cannot be decoded - * @throws ChecksumException if error correction fails - */ - /*@Override*/ - // public decode(image: BinaryBitmap): Result /*throws NotFoundException, ChecksumException, FormatException */ { - // return this.decode(image, null) - // } - /*@Override*/ - decode(image, hints) { - let decoderResult; - let points; - if (hints !== undefined && hints !== null && undefined !== hints.get(DecodeHintType$1.PURE_BARCODE)) { - const bits = QRCodeReader.extractPureBits(image.getBlackMatrix()); - decoderResult = this.decoder.decodeBitMatrix(bits, hints); - points = QRCodeReader.NO_POINTS; - } - else { - const detectorResult = new Detector$2(image.getBlackMatrix()).detect(hints); - decoderResult = this.decoder.decodeBitMatrix(detectorResult.getBits(), hints); - points = detectorResult.getPoints(); - } - // If the code was mirrored: swap the bottom-left and the top-right points. - if (decoderResult.getOther() instanceof QRCodeDecoderMetaData) { - decoderResult.getOther().applyMirroredCorrection(points); - } - const result = new Result(decoderResult.getText(), decoderResult.getRawBytes(), undefined, points, BarcodeFormat$1.QR_CODE, undefined); - const byteSegments = decoderResult.getByteSegments(); - if (byteSegments !== null) { - result.putMetadata(ResultMetadataType$1.BYTE_SEGMENTS, byteSegments); - } - const ecLevel = decoderResult.getECLevel(); - if (ecLevel !== null) { - result.putMetadata(ResultMetadataType$1.ERROR_CORRECTION_LEVEL, ecLevel); - } - if (decoderResult.hasStructuredAppend()) { - result.putMetadata(ResultMetadataType$1.STRUCTURED_APPEND_SEQUENCE, decoderResult.getStructuredAppendSequenceNumber()); - result.putMetadata(ResultMetadataType$1.STRUCTURED_APPEND_PARITY, decoderResult.getStructuredAppendParity()); - } - return result; - } - /*@Override*/ - reset() { - // do nothing - } - /** - * This method detects a code in a "pure" image -- that is, pure monochrome image - * which contains only an unrotated, unskewed, image of a code, with some white border - * around it. This is a specialized method that works exceptionally fast in this special - * case. - * - * @see com.google.zxing.datamatrix.DataMatrixReader#extractPureBits(BitMatrix) - */ - static extractPureBits(image) { - const leftTopBlack = image.getTopLeftOnBit(); - const rightBottomBlack = image.getBottomRightOnBit(); - if (leftTopBlack === null || rightBottomBlack === null) { - throw new NotFoundException(); - } - const moduleSize = this.moduleSize(leftTopBlack, image); - let top = leftTopBlack[1]; - let bottom = rightBottomBlack[1]; - let left = leftTopBlack[0]; - let right = rightBottomBlack[0]; - // Sanity check! - if (left >= right || top >= bottom) { - throw new NotFoundException(); - } - if (bottom - top !== right - left) { - // Special case, where bottom-right module wasn't black so we found something else in the last row - // Assume it's a square, so use height as the width - right = left + (bottom - top); - if (right >= image.getWidth()) { - // Abort if that would not make sense -- off image - throw new NotFoundException(); - } - } - const matrixWidth = Math.round((right - left + 1) / moduleSize); - const matrixHeight = Math.round((bottom - top + 1) / moduleSize); - if (matrixWidth <= 0 || matrixHeight <= 0) { - throw new NotFoundException(); - } - if (matrixHeight !== matrixWidth) { - // Only possibly decode square regions - throw new NotFoundException(); - } - // Push in the "border" by half the module width so that we start - // sampling in the middle of the module. Just in case the image is a - // little off, this will help recover. - const nudge = /*(int) */ Math.floor(moduleSize / 2.0); - top += nudge; - left += nudge; - // But careful that this does not sample off the edge - // "right" is the farthest-right valid pixel location -- right+1 is not necessarily - // This is positive by how much the inner x loop below would be too large - const nudgedTooFarRight = left + /*(int) */ Math.floor((matrixWidth - 1) * moduleSize) - right; - if (nudgedTooFarRight > 0) { - if (nudgedTooFarRight > nudge) { - // Neither way fits; abort - throw new NotFoundException(); - } - left -= nudgedTooFarRight; - } - // See logic above - const nudgedTooFarDown = top + /*(int) */ Math.floor((matrixHeight - 1) * moduleSize) - bottom; - if (nudgedTooFarDown > 0) { - if (nudgedTooFarDown > nudge) { - // Neither way fits; abort - throw new NotFoundException(); - } - top -= nudgedTooFarDown; - } - // Now just read off the bits - const bits = new BitMatrix(matrixWidth, matrixHeight); - for (let y = 0; y < matrixHeight; y++) { - const iOffset = top + /*(int) */ Math.floor(y * moduleSize); - for (let x = 0; x < matrixWidth; x++) { - if (image.get(left + /*(int) */ Math.floor(x * moduleSize), iOffset)) { - bits.set(x, y); - } - } - } - return bits; - } - static moduleSize(leftTopBlack, image) { - const height = image.getHeight(); - const width = image.getWidth(); - let x = leftTopBlack[0]; - let y = leftTopBlack[1]; - let inBlack = true; - let transitions = 0; - while (x < width && y < height) { - if (inBlack !== image.get(x, y)) { - if (++transitions === 5) { - break; - } - inBlack = !inBlack; - } - x++; - y++; - } - if (x === width || y === height) { - throw new NotFoundException(); - } - return (x - leftTopBlack[0]) / 7.0; - } - } - QRCodeReader.NO_POINTS = new Array(); - - /* - * 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. - */ - /** - * @author SITA Lab (kevin.osullivan@sita.aero) - * @author Guenther Grau - */ - /*public final*/ class PDF417Common { - PDF417Common() { - } - /** - * @param moduleBitCount values to sum - * @return sum of values - * @deprecated call {@link MathUtils#sum(int[])} - */ - // @Deprecated - static getBitCountSum(moduleBitCount) { - return MathUtils.sum(moduleBitCount); - } - static toIntArray(list) { - if (list == null || !list.length) { - return PDF417Common.EMPTY_INT_ARRAY; - } - const result = new Int32Array(list.length); - let i = 0; - for (const integer of list) { - result[i++] = integer; - } - return result; - } - /** - * @param symbol encoded symbol to translate to a codeword - * @return the codeword corresponding to the symbol. - */ - static getCodeword(symbol /*int*/) { - const i = Arrays.binarySearch(PDF417Common.SYMBOL_TABLE, symbol & 0x3FFFF); - if (i < 0) { - return -1; - } - return (PDF417Common.CODEWORD_TABLE[i] - 1) % PDF417Common.NUMBER_OF_CODEWORDS; - } - } - PDF417Common.NUMBER_OF_CODEWORDS = 929; - // Maximum Codewords (Data + Error). - PDF417Common.MAX_CODEWORDS_IN_BARCODE = PDF417Common.NUMBER_OF_CODEWORDS - 1; - PDF417Common.MIN_ROWS_IN_BARCODE = 3; - PDF417Common.MAX_ROWS_IN_BARCODE = 90; - // One left row indication column + max 30 data columns + one right row indicator column - // public static /*final*/ MAX_CODEWORDS_IN_ROW: /*int*/ number = 32; - PDF417Common.MODULES_IN_CODEWORD = 17; - PDF417Common.MODULES_IN_STOP_PATTERN = 18; - PDF417Common.BARS_IN_MODULE = 8; - PDF417Common.EMPTY_INT_ARRAY = new Int32Array([]); - /** - * The sorted table of all possible symbols. Extracted from the PDF417 - * specification. The index of a symbol in this table corresponds to the - * index into the codeword table. - */ - PDF417Common.SYMBOL_TABLE = Int32Array.from([ - 0x1025e, 0x1027a, 0x1029e, 0x102bc, 0x102f2, 0x102f4, 0x1032e, 0x1034e, 0x1035c, 0x10396, 0x103a6, 0x103ac, - 0x10422, 0x10428, 0x10436, 0x10442, 0x10444, 0x10448, 0x10450, 0x1045e, 0x10466, 0x1046c, 0x1047a, 0x10482, - 0x1049e, 0x104a0, 0x104bc, 0x104c6, 0x104d8, 0x104ee, 0x104f2, 0x104f4, 0x10504, 0x10508, 0x10510, 0x1051e, - 0x10520, 0x1053c, 0x10540, 0x10578, 0x10586, 0x1058c, 0x10598, 0x105b0, 0x105be, 0x105ce, 0x105dc, 0x105e2, - 0x105e4, 0x105e8, 0x105f6, 0x1062e, 0x1064e, 0x1065c, 0x1068e, 0x1069c, 0x106b8, 0x106de, 0x106fa, 0x10716, - 0x10726, 0x1072c, 0x10746, 0x1074c, 0x10758, 0x1076e, 0x10792, 0x10794, 0x107a2, 0x107a4, 0x107a8, 0x107b6, - 0x10822, 0x10828, 0x10842, 0x10848, 0x10850, 0x1085e, 0x10866, 0x1086c, 0x1087a, 0x10882, 0x10884, 0x10890, - 0x1089e, 0x108a0, 0x108bc, 0x108c6, 0x108cc, 0x108d8, 0x108ee, 0x108f2, 0x108f4, 0x10902, 0x10908, 0x1091e, - 0x10920, 0x1093c, 0x10940, 0x10978, 0x10986, 0x10998, 0x109b0, 0x109be, 0x109ce, 0x109dc, 0x109e2, 0x109e4, - 0x109e8, 0x109f6, 0x10a08, 0x10a10, 0x10a1e, 0x10a20, 0x10a3c, 0x10a40, 0x10a78, 0x10af0, 0x10b06, 0x10b0c, - 0x10b18, 0x10b30, 0x10b3e, 0x10b60, 0x10b7c, 0x10b8e, 0x10b9c, 0x10bb8, 0x10bc2, 0x10bc4, 0x10bc8, 0x10bd0, - 0x10bde, 0x10be6, 0x10bec, 0x10c2e, 0x10c4e, 0x10c5c, 0x10c62, 0x10c64, 0x10c68, 0x10c76, 0x10c8e, 0x10c9c, - 0x10cb8, 0x10cc2, 0x10cc4, 0x10cc8, 0x10cd0, 0x10cde, 0x10ce6, 0x10cec, 0x10cfa, 0x10d0e, 0x10d1c, 0x10d38, - 0x10d70, 0x10d7e, 0x10d82, 0x10d84, 0x10d88, 0x10d90, 0x10d9e, 0x10da0, 0x10dbc, 0x10dc6, 0x10dcc, 0x10dd8, - 0x10dee, 0x10df2, 0x10df4, 0x10e16, 0x10e26, 0x10e2c, 0x10e46, 0x10e58, 0x10e6e, 0x10e86, 0x10e8c, 0x10e98, - 0x10eb0, 0x10ebe, 0x10ece, 0x10edc, 0x10f0a, 0x10f12, 0x10f14, 0x10f22, 0x10f28, 0x10f36, 0x10f42, 0x10f44, - 0x10f48, 0x10f50, 0x10f5e, 0x10f66, 0x10f6c, 0x10fb2, 0x10fb4, 0x11022, 0x11028, 0x11042, 0x11048, 0x11050, - 0x1105e, 0x1107a, 0x11082, 0x11084, 0x11090, 0x1109e, 0x110a0, 0x110bc, 0x110c6, 0x110cc, 0x110d8, 0x110ee, - 0x110f2, 0x110f4, 0x11102, 0x1111e, 0x11120, 0x1113c, 0x11140, 0x11178, 0x11186, 0x11198, 0x111b0, 0x111be, - 0x111ce, 0x111dc, 0x111e2, 0x111e4, 0x111e8, 0x111f6, 0x11208, 0x1121e, 0x11220, 0x11278, 0x112f0, 0x1130c, - 0x11330, 0x1133e, 0x11360, 0x1137c, 0x1138e, 0x1139c, 0x113b8, 0x113c2, 0x113c8, 0x113d0, 0x113de, 0x113e6, - 0x113ec, 0x11408, 0x11410, 0x1141e, 0x11420, 0x1143c, 0x11440, 0x11478, 0x114f0, 0x115e0, 0x1160c, 0x11618, - 0x11630, 0x1163e, 0x11660, 0x1167c, 0x116c0, 0x116f8, 0x1171c, 0x11738, 0x11770, 0x1177e, 0x11782, 0x11784, - 0x11788, 0x11790, 0x1179e, 0x117a0, 0x117bc, 0x117c6, 0x117cc, 0x117d8, 0x117ee, 0x1182e, 0x11834, 0x1184e, - 0x1185c, 0x11862, 0x11864, 0x11868, 0x11876, 0x1188e, 0x1189c, 0x118b8, 0x118c2, 0x118c8, 0x118d0, 0x118de, - 0x118e6, 0x118ec, 0x118fa, 0x1190e, 0x1191c, 0x11938, 0x11970, 0x1197e, 0x11982, 0x11984, 0x11990, 0x1199e, - 0x119a0, 0x119bc, 0x119c6, 0x119cc, 0x119d8, 0x119ee, 0x119f2, 0x119f4, 0x11a0e, 0x11a1c, 0x11a38, 0x11a70, - 0x11a7e, 0x11ae0, 0x11afc, 0x11b08, 0x11b10, 0x11b1e, 0x11b20, 0x11b3c, 0x11b40, 0x11b78, 0x11b8c, 0x11b98, - 0x11bb0, 0x11bbe, 0x11bce, 0x11bdc, 0x11be2, 0x11be4, 0x11be8, 0x11bf6, 0x11c16, 0x11c26, 0x11c2c, 0x11c46, - 0x11c4c, 0x11c58, 0x11c6e, 0x11c86, 0x11c98, 0x11cb0, 0x11cbe, 0x11cce, 0x11cdc, 0x11ce2, 0x11ce4, 0x11ce8, - 0x11cf6, 0x11d06, 0x11d0c, 0x11d18, 0x11d30, 0x11d3e, 0x11d60, 0x11d7c, 0x11d8e, 0x11d9c, 0x11db8, 0x11dc4, - 0x11dc8, 0x11dd0, 0x11dde, 0x11de6, 0x11dec, 0x11dfa, 0x11e0a, 0x11e12, 0x11e14, 0x11e22, 0x11e24, 0x11e28, - 0x11e36, 0x11e42, 0x11e44, 0x11e50, 0x11e5e, 0x11e66, 0x11e6c, 0x11e82, 0x11e84, 0x11e88, 0x11e90, 0x11e9e, - 0x11ea0, 0x11ebc, 0x11ec6, 0x11ecc, 0x11ed8, 0x11eee, 0x11f1a, 0x11f2e, 0x11f32, 0x11f34, 0x11f4e, 0x11f5c, - 0x11f62, 0x11f64, 0x11f68, 0x11f76, 0x12048, 0x1205e, 0x12082, 0x12084, 0x12090, 0x1209e, 0x120a0, 0x120bc, - 0x120d8, 0x120f2, 0x120f4, 0x12108, 0x1211e, 0x12120, 0x1213c, 0x12140, 0x12178, 0x12186, 0x12198, 0x121b0, - 0x121be, 0x121e2, 0x121e4, 0x121e8, 0x121f6, 0x12204, 0x12210, 0x1221e, 0x12220, 0x12278, 0x122f0, 0x12306, - 0x1230c, 0x12330, 0x1233e, 0x12360, 0x1237c, 0x1238e, 0x1239c, 0x123b8, 0x123c2, 0x123c8, 0x123d0, 0x123e6, - 0x123ec, 0x1241e, 0x12420, 0x1243c, 0x124f0, 0x125e0, 0x12618, 0x1263e, 0x12660, 0x1267c, 0x126c0, 0x126f8, - 0x12738, 0x12770, 0x1277e, 0x12782, 0x12784, 0x12790, 0x1279e, 0x127a0, 0x127bc, 0x127c6, 0x127cc, 0x127d8, - 0x127ee, 0x12820, 0x1283c, 0x12840, 0x12878, 0x128f0, 0x129e0, 0x12bc0, 0x12c18, 0x12c30, 0x12c3e, 0x12c60, - 0x12c7c, 0x12cc0, 0x12cf8, 0x12df0, 0x12e1c, 0x12e38, 0x12e70, 0x12e7e, 0x12ee0, 0x12efc, 0x12f04, 0x12f08, - 0x12f10, 0x12f20, 0x12f3c, 0x12f40, 0x12f78, 0x12f86, 0x12f8c, 0x12f98, 0x12fb0, 0x12fbe, 0x12fce, 0x12fdc, - 0x1302e, 0x1304e, 0x1305c, 0x13062, 0x13068, 0x1308e, 0x1309c, 0x130b8, 0x130c2, 0x130c8, 0x130d0, 0x130de, - 0x130ec, 0x130fa, 0x1310e, 0x13138, 0x13170, 0x1317e, 0x13182, 0x13184, 0x13190, 0x1319e, 0x131a0, 0x131bc, - 0x131c6, 0x131cc, 0x131d8, 0x131f2, 0x131f4, 0x1320e, 0x1321c, 0x13270, 0x1327e, 0x132e0, 0x132fc, 0x13308, - 0x1331e, 0x13320, 0x1333c, 0x13340, 0x13378, 0x13386, 0x13398, 0x133b0, 0x133be, 0x133ce, 0x133dc, 0x133e2, - 0x133e4, 0x133e8, 0x133f6, 0x1340e, 0x1341c, 0x13438, 0x13470, 0x1347e, 0x134e0, 0x134fc, 0x135c0, 0x135f8, - 0x13608, 0x13610, 0x1361e, 0x13620, 0x1363c, 0x13640, 0x13678, 0x136f0, 0x1370c, 0x13718, 0x13730, 0x1373e, - 0x13760, 0x1377c, 0x1379c, 0x137b8, 0x137c2, 0x137c4, 0x137c8, 0x137d0, 0x137de, 0x137e6, 0x137ec, 0x13816, - 0x13826, 0x1382c, 0x13846, 0x1384c, 0x13858, 0x1386e, 0x13874, 0x13886, 0x13898, 0x138b0, 0x138be, 0x138ce, - 0x138dc, 0x138e2, 0x138e4, 0x138e8, 0x13906, 0x1390c, 0x13930, 0x1393e, 0x13960, 0x1397c, 0x1398e, 0x1399c, - 0x139b8, 0x139c8, 0x139d0, 0x139de, 0x139e6, 0x139ec, 0x139fa, 0x13a06, 0x13a0c, 0x13a18, 0x13a30, 0x13a3e, - 0x13a60, 0x13a7c, 0x13ac0, 0x13af8, 0x13b0e, 0x13b1c, 0x13b38, 0x13b70, 0x13b7e, 0x13b88, 0x13b90, 0x13b9e, - 0x13ba0, 0x13bbc, 0x13bcc, 0x13bd8, 0x13bee, 0x13bf2, 0x13bf4, 0x13c12, 0x13c14, 0x13c22, 0x13c24, 0x13c28, - 0x13c36, 0x13c42, 0x13c48, 0x13c50, 0x13c5e, 0x13c66, 0x13c6c, 0x13c82, 0x13c84, 0x13c90, 0x13c9e, 0x13ca0, - 0x13cbc, 0x13cc6, 0x13ccc, 0x13cd8, 0x13cee, 0x13d02, 0x13d04, 0x13d08, 0x13d10, 0x13d1e, 0x13d20, 0x13d3c, - 0x13d40, 0x13d78, 0x13d86, 0x13d8c, 0x13d98, 0x13db0, 0x13dbe, 0x13dce, 0x13ddc, 0x13de4, 0x13de8, 0x13df6, - 0x13e1a, 0x13e2e, 0x13e32, 0x13e34, 0x13e4e, 0x13e5c, 0x13e62, 0x13e64, 0x13e68, 0x13e76, 0x13e8e, 0x13e9c, - 0x13eb8, 0x13ec2, 0x13ec4, 0x13ec8, 0x13ed0, 0x13ede, 0x13ee6, 0x13eec, 0x13f26, 0x13f2c, 0x13f3a, 0x13f46, - 0x13f4c, 0x13f58, 0x13f6e, 0x13f72, 0x13f74, 0x14082, 0x1409e, 0x140a0, 0x140bc, 0x14104, 0x14108, 0x14110, - 0x1411e, 0x14120, 0x1413c, 0x14140, 0x14178, 0x1418c, 0x14198, 0x141b0, 0x141be, 0x141e2, 0x141e4, 0x141e8, - 0x14208, 0x14210, 0x1421e, 0x14220, 0x1423c, 0x14240, 0x14278, 0x142f0, 0x14306, 0x1430c, 0x14318, 0x14330, - 0x1433e, 0x14360, 0x1437c, 0x1438e, 0x143c2, 0x143c4, 0x143c8, 0x143d0, 0x143e6, 0x143ec, 0x14408, 0x14410, - 0x1441e, 0x14420, 0x1443c, 0x14440, 0x14478, 0x144f0, 0x145e0, 0x1460c, 0x14618, 0x14630, 0x1463e, 0x14660, - 0x1467c, 0x146c0, 0x146f8, 0x1471c, 0x14738, 0x14770, 0x1477e, 0x14782, 0x14784, 0x14788, 0x14790, 0x147a0, - 0x147bc, 0x147c6, 0x147cc, 0x147d8, 0x147ee, 0x14810, 0x14820, 0x1483c, 0x14840, 0x14878, 0x148f0, 0x149e0, - 0x14bc0, 0x14c30, 0x14c3e, 0x14c60, 0x14c7c, 0x14cc0, 0x14cf8, 0x14df0, 0x14e38, 0x14e70, 0x14e7e, 0x14ee0, - 0x14efc, 0x14f04, 0x14f08, 0x14f10, 0x14f1e, 0x14f20, 0x14f3c, 0x14f40, 0x14f78, 0x14f86, 0x14f8c, 0x14f98, - 0x14fb0, 0x14fce, 0x14fdc, 0x15020, 0x15040, 0x15078, 0x150f0, 0x151e0, 0x153c0, 0x15860, 0x1587c, 0x158c0, - 0x158f8, 0x159f0, 0x15be0, 0x15c70, 0x15c7e, 0x15ce0, 0x15cfc, 0x15dc0, 0x15df8, 0x15e08, 0x15e10, 0x15e20, - 0x15e40, 0x15e78, 0x15ef0, 0x15f0c, 0x15f18, 0x15f30, 0x15f60, 0x15f7c, 0x15f8e, 0x15f9c, 0x15fb8, 0x1604e, - 0x1605c, 0x1608e, 0x1609c, 0x160b8, 0x160c2, 0x160c4, 0x160c8, 0x160de, 0x1610e, 0x1611c, 0x16138, 0x16170, - 0x1617e, 0x16184, 0x16188, 0x16190, 0x1619e, 0x161a0, 0x161bc, 0x161c6, 0x161cc, 0x161d8, 0x161f2, 0x161f4, - 0x1620e, 0x1621c, 0x16238, 0x16270, 0x1627e, 0x162e0, 0x162fc, 0x16304, 0x16308, 0x16310, 0x1631e, 0x16320, - 0x1633c, 0x16340, 0x16378, 0x16386, 0x1638c, 0x16398, 0x163b0, 0x163be, 0x163ce, 0x163dc, 0x163e2, 0x163e4, - 0x163e8, 0x163f6, 0x1640e, 0x1641c, 0x16438, 0x16470, 0x1647e, 0x164e0, 0x164fc, 0x165c0, 0x165f8, 0x16610, - 0x1661e, 0x16620, 0x1663c, 0x16640, 0x16678, 0x166f0, 0x16718, 0x16730, 0x1673e, 0x16760, 0x1677c, 0x1678e, - 0x1679c, 0x167b8, 0x167c2, 0x167c4, 0x167c8, 0x167d0, 0x167de, 0x167e6, 0x167ec, 0x1681c, 0x16838, 0x16870, - 0x168e0, 0x168fc, 0x169c0, 0x169f8, 0x16bf0, 0x16c10, 0x16c1e, 0x16c20, 0x16c3c, 0x16c40, 0x16c78, 0x16cf0, - 0x16de0, 0x16e18, 0x16e30, 0x16e3e, 0x16e60, 0x16e7c, 0x16ec0, 0x16ef8, 0x16f1c, 0x16f38, 0x16f70, 0x16f7e, - 0x16f84, 0x16f88, 0x16f90, 0x16f9e, 0x16fa0, 0x16fbc, 0x16fc6, 0x16fcc, 0x16fd8, 0x17026, 0x1702c, 0x17046, - 0x1704c, 0x17058, 0x1706e, 0x17086, 0x1708c, 0x17098, 0x170b0, 0x170be, 0x170ce, 0x170dc, 0x170e8, 0x17106, - 0x1710c, 0x17118, 0x17130, 0x1713e, 0x17160, 0x1717c, 0x1718e, 0x1719c, 0x171b8, 0x171c2, 0x171c4, 0x171c8, - 0x171d0, 0x171de, 0x171e6, 0x171ec, 0x171fa, 0x17206, 0x1720c, 0x17218, 0x17230, 0x1723e, 0x17260, 0x1727c, - 0x172c0, 0x172f8, 0x1730e, 0x1731c, 0x17338, 0x17370, 0x1737e, 0x17388, 0x17390, 0x1739e, 0x173a0, 0x173bc, - 0x173cc, 0x173d8, 0x173ee, 0x173f2, 0x173f4, 0x1740c, 0x17418, 0x17430, 0x1743e, 0x17460, 0x1747c, 0x174c0, - 0x174f8, 0x175f0, 0x1760e, 0x1761c, 0x17638, 0x17670, 0x1767e, 0x176e0, 0x176fc, 0x17708, 0x17710, 0x1771e, - 0x17720, 0x1773c, 0x17740, 0x17778, 0x17798, 0x177b0, 0x177be, 0x177dc, 0x177e2, 0x177e4, 0x177e8, 0x17822, - 0x17824, 0x17828, 0x17836, 0x17842, 0x17844, 0x17848, 0x17850, 0x1785e, 0x17866, 0x1786c, 0x17882, 0x17884, - 0x17888, 0x17890, 0x1789e, 0x178a0, 0x178bc, 0x178c6, 0x178cc, 0x178d8, 0x178ee, 0x178f2, 0x178f4, 0x17902, - 0x17904, 0x17908, 0x17910, 0x1791e, 0x17920, 0x1793c, 0x17940, 0x17978, 0x17986, 0x1798c, 0x17998, 0x179b0, - 0x179be, 0x179ce, 0x179dc, 0x179e2, 0x179e4, 0x179e8, 0x179f6, 0x17a04, 0x17a08, 0x17a10, 0x17a1e, 0x17a20, - 0x17a3c, 0x17a40, 0x17a78, 0x17af0, 0x17b06, 0x17b0c, 0x17b18, 0x17b30, 0x17b3e, 0x17b60, 0x17b7c, 0x17b8e, - 0x17b9c, 0x17bb8, 0x17bc4, 0x17bc8, 0x17bd0, 0x17bde, 0x17be6, 0x17bec, 0x17c2e, 0x17c32, 0x17c34, 0x17c4e, - 0x17c5c, 0x17c62, 0x17c64, 0x17c68, 0x17c76, 0x17c8e, 0x17c9c, 0x17cb8, 0x17cc2, 0x17cc4, 0x17cc8, 0x17cd0, - 0x17cde, 0x17ce6, 0x17cec, 0x17d0e, 0x17d1c, 0x17d38, 0x17d70, 0x17d82, 0x17d84, 0x17d88, 0x17d90, 0x17d9e, - 0x17da0, 0x17dbc, 0x17dc6, 0x17dcc, 0x17dd8, 0x17dee, 0x17e26, 0x17e2c, 0x17e3a, 0x17e46, 0x17e4c, 0x17e58, - 0x17e6e, 0x17e72, 0x17e74, 0x17e86, 0x17e8c, 0x17e98, 0x17eb0, 0x17ece, 0x17edc, 0x17ee2, 0x17ee4, 0x17ee8, - 0x17ef6, 0x1813a, 0x18172, 0x18174, 0x18216, 0x18226, 0x1823a, 0x1824c, 0x18258, 0x1826e, 0x18272, 0x18274, - 0x18298, 0x182be, 0x182e2, 0x182e4, 0x182e8, 0x182f6, 0x1835e, 0x1837a, 0x183ae, 0x183d6, 0x18416, 0x18426, - 0x1842c, 0x1843a, 0x18446, 0x18458, 0x1846e, 0x18472, 0x18474, 0x18486, 0x184b0, 0x184be, 0x184ce, 0x184dc, - 0x184e2, 0x184e4, 0x184e8, 0x184f6, 0x18506, 0x1850c, 0x18518, 0x18530, 0x1853e, 0x18560, 0x1857c, 0x1858e, - 0x1859c, 0x185b8, 0x185c2, 0x185c4, 0x185c8, 0x185d0, 0x185de, 0x185e6, 0x185ec, 0x185fa, 0x18612, 0x18614, - 0x18622, 0x18628, 0x18636, 0x18642, 0x18650, 0x1865e, 0x1867a, 0x18682, 0x18684, 0x18688, 0x18690, 0x1869e, - 0x186a0, 0x186bc, 0x186c6, 0x186cc, 0x186d8, 0x186ee, 0x186f2, 0x186f4, 0x1872e, 0x1874e, 0x1875c, 0x18796, - 0x187a6, 0x187ac, 0x187d2, 0x187d4, 0x18826, 0x1882c, 0x1883a, 0x18846, 0x1884c, 0x18858, 0x1886e, 0x18872, - 0x18874, 0x18886, 0x18898, 0x188b0, 0x188be, 0x188ce, 0x188dc, 0x188e2, 0x188e4, 0x188e8, 0x188f6, 0x1890c, - 0x18930, 0x1893e, 0x18960, 0x1897c, 0x1898e, 0x189b8, 0x189c2, 0x189c8, 0x189d0, 0x189de, 0x189e6, 0x189ec, - 0x189fa, 0x18a18, 0x18a30, 0x18a3e, 0x18a60, 0x18a7c, 0x18ac0, 0x18af8, 0x18b1c, 0x18b38, 0x18b70, 0x18b7e, - 0x18b82, 0x18b84, 0x18b88, 0x18b90, 0x18b9e, 0x18ba0, 0x18bbc, 0x18bc6, 0x18bcc, 0x18bd8, 0x18bee, 0x18bf2, - 0x18bf4, 0x18c22, 0x18c24, 0x18c28, 0x18c36, 0x18c42, 0x18c48, 0x18c50, 0x18c5e, 0x18c66, 0x18c7a, 0x18c82, - 0x18c84, 0x18c90, 0x18c9e, 0x18ca0, 0x18cbc, 0x18ccc, 0x18cf2, 0x18cf4, 0x18d04, 0x18d08, 0x18d10, 0x18d1e, - 0x18d20, 0x18d3c, 0x18d40, 0x18d78, 0x18d86, 0x18d98, 0x18dce, 0x18de2, 0x18de4, 0x18de8, 0x18e2e, 0x18e32, - 0x18e34, 0x18e4e, 0x18e5c, 0x18e62, 0x18e64, 0x18e68, 0x18e8e, 0x18e9c, 0x18eb8, 0x18ec2, 0x18ec4, 0x18ec8, - 0x18ed0, 0x18efa, 0x18f16, 0x18f26, 0x18f2c, 0x18f46, 0x18f4c, 0x18f58, 0x18f6e, 0x18f8a, 0x18f92, 0x18f94, - 0x18fa2, 0x18fa4, 0x18fa8, 0x18fb6, 0x1902c, 0x1903a, 0x19046, 0x1904c, 0x19058, 0x19072, 0x19074, 0x19086, - 0x19098, 0x190b0, 0x190be, 0x190ce, 0x190dc, 0x190e2, 0x190e8, 0x190f6, 0x19106, 0x1910c, 0x19130, 0x1913e, - 0x19160, 0x1917c, 0x1918e, 0x1919c, 0x191b8, 0x191c2, 0x191c8, 0x191d0, 0x191de, 0x191e6, 0x191ec, 0x191fa, - 0x19218, 0x1923e, 0x19260, 0x1927c, 0x192c0, 0x192f8, 0x19338, 0x19370, 0x1937e, 0x19382, 0x19384, 0x19390, - 0x1939e, 0x193a0, 0x193bc, 0x193c6, 0x193cc, 0x193d8, 0x193ee, 0x193f2, 0x193f4, 0x19430, 0x1943e, 0x19460, - 0x1947c, 0x194c0, 0x194f8, 0x195f0, 0x19638, 0x19670, 0x1967e, 0x196e0, 0x196fc, 0x19702, 0x19704, 0x19708, - 0x19710, 0x19720, 0x1973c, 0x19740, 0x19778, 0x19786, 0x1978c, 0x19798, 0x197b0, 0x197be, 0x197ce, 0x197dc, - 0x197e2, 0x197e4, 0x197e8, 0x19822, 0x19824, 0x19842, 0x19848, 0x19850, 0x1985e, 0x19866, 0x1987a, 0x19882, - 0x19884, 0x19890, 0x1989e, 0x198a0, 0x198bc, 0x198cc, 0x198f2, 0x198f4, 0x19902, 0x19908, 0x1991e, 0x19920, - 0x1993c, 0x19940, 0x19978, 0x19986, 0x19998, 0x199ce, 0x199e2, 0x199e4, 0x199e8, 0x19a08, 0x19a10, 0x19a1e, - 0x19a20, 0x19a3c, 0x19a40, 0x19a78, 0x19af0, 0x19b18, 0x19b3e, 0x19b60, 0x19b9c, 0x19bc2, 0x19bc4, 0x19bc8, - 0x19bd0, 0x19be6, 0x19c2e, 0x19c34, 0x19c4e, 0x19c5c, 0x19c62, 0x19c64, 0x19c68, 0x19c8e, 0x19c9c, 0x19cb8, - 0x19cc2, 0x19cc8, 0x19cd0, 0x19ce6, 0x19cfa, 0x19d0e, 0x19d1c, 0x19d38, 0x19d70, 0x19d7e, 0x19d82, 0x19d84, - 0x19d88, 0x19d90, 0x19da0, 0x19dcc, 0x19df2, 0x19df4, 0x19e16, 0x19e26, 0x19e2c, 0x19e46, 0x19e4c, 0x19e58, - 0x19e74, 0x19e86, 0x19e8c, 0x19e98, 0x19eb0, 0x19ebe, 0x19ece, 0x19ee2, 0x19ee4, 0x19ee8, 0x19f0a, 0x19f12, - 0x19f14, 0x19f22, 0x19f24, 0x19f28, 0x19f42, 0x19f44, 0x19f48, 0x19f50, 0x19f5e, 0x19f6c, 0x19f9a, 0x19fae, - 0x19fb2, 0x19fb4, 0x1a046, 0x1a04c, 0x1a072, 0x1a074, 0x1a086, 0x1a08c, 0x1a098, 0x1a0b0, 0x1a0be, 0x1a0e2, - 0x1a0e4, 0x1a0e8, 0x1a0f6, 0x1a106, 0x1a10c, 0x1a118, 0x1a130, 0x1a13e, 0x1a160, 0x1a17c, 0x1a18e, 0x1a19c, - 0x1a1b8, 0x1a1c2, 0x1a1c4, 0x1a1c8, 0x1a1d0, 0x1a1de, 0x1a1e6, 0x1a1ec, 0x1a218, 0x1a230, 0x1a23e, 0x1a260, - 0x1a27c, 0x1a2c0, 0x1a2f8, 0x1a31c, 0x1a338, 0x1a370, 0x1a37e, 0x1a382, 0x1a384, 0x1a388, 0x1a390, 0x1a39e, - 0x1a3a0, 0x1a3bc, 0x1a3c6, 0x1a3cc, 0x1a3d8, 0x1a3ee, 0x1a3f2, 0x1a3f4, 0x1a418, 0x1a430, 0x1a43e, 0x1a460, - 0x1a47c, 0x1a4c0, 0x1a4f8, 0x1a5f0, 0x1a61c, 0x1a638, 0x1a670, 0x1a67e, 0x1a6e0, 0x1a6fc, 0x1a702, 0x1a704, - 0x1a708, 0x1a710, 0x1a71e, 0x1a720, 0x1a73c, 0x1a740, 0x1a778, 0x1a786, 0x1a78c, 0x1a798, 0x1a7b0, 0x1a7be, - 0x1a7ce, 0x1a7dc, 0x1a7e2, 0x1a7e4, 0x1a7e8, 0x1a830, 0x1a860, 0x1a87c, 0x1a8c0, 0x1a8f8, 0x1a9f0, 0x1abe0, - 0x1ac70, 0x1ac7e, 0x1ace0, 0x1acfc, 0x1adc0, 0x1adf8, 0x1ae04, 0x1ae08, 0x1ae10, 0x1ae20, 0x1ae3c, 0x1ae40, - 0x1ae78, 0x1aef0, 0x1af06, 0x1af0c, 0x1af18, 0x1af30, 0x1af3e, 0x1af60, 0x1af7c, 0x1af8e, 0x1af9c, 0x1afb8, - 0x1afc4, 0x1afc8, 0x1afd0, 0x1afde, 0x1b042, 0x1b05e, 0x1b07a, 0x1b082, 0x1b084, 0x1b088, 0x1b090, 0x1b09e, - 0x1b0a0, 0x1b0bc, 0x1b0cc, 0x1b0f2, 0x1b0f4, 0x1b102, 0x1b104, 0x1b108, 0x1b110, 0x1b11e, 0x1b120, 0x1b13c, - 0x1b140, 0x1b178, 0x1b186, 0x1b198, 0x1b1ce, 0x1b1e2, 0x1b1e4, 0x1b1e8, 0x1b204, 0x1b208, 0x1b210, 0x1b21e, - 0x1b220, 0x1b23c, 0x1b240, 0x1b278, 0x1b2f0, 0x1b30c, 0x1b33e, 0x1b360, 0x1b39c, 0x1b3c2, 0x1b3c4, 0x1b3c8, - 0x1b3d0, 0x1b3e6, 0x1b410, 0x1b41e, 0x1b420, 0x1b43c, 0x1b440, 0x1b478, 0x1b4f0, 0x1b5e0, 0x1b618, 0x1b660, - 0x1b67c, 0x1b6c0, 0x1b738, 0x1b782, 0x1b784, 0x1b788, 0x1b790, 0x1b79e, 0x1b7a0, 0x1b7cc, 0x1b82e, 0x1b84e, - 0x1b85c, 0x1b88e, 0x1b89c, 0x1b8b8, 0x1b8c2, 0x1b8c4, 0x1b8c8, 0x1b8d0, 0x1b8e6, 0x1b8fa, 0x1b90e, 0x1b91c, - 0x1b938, 0x1b970, 0x1b97e, 0x1b982, 0x1b984, 0x1b988, 0x1b990, 0x1b99e, 0x1b9a0, 0x1b9cc, 0x1b9f2, 0x1b9f4, - 0x1ba0e, 0x1ba1c, 0x1ba38, 0x1ba70, 0x1ba7e, 0x1bae0, 0x1bafc, 0x1bb08, 0x1bb10, 0x1bb20, 0x1bb3c, 0x1bb40, - 0x1bb98, 0x1bbce, 0x1bbe2, 0x1bbe4, 0x1bbe8, 0x1bc16, 0x1bc26, 0x1bc2c, 0x1bc46, 0x1bc4c, 0x1bc58, 0x1bc72, - 0x1bc74, 0x1bc86, 0x1bc8c, 0x1bc98, 0x1bcb0, 0x1bcbe, 0x1bcce, 0x1bce2, 0x1bce4, 0x1bce8, 0x1bd06, 0x1bd0c, - 0x1bd18, 0x1bd30, 0x1bd3e, 0x1bd60, 0x1bd7c, 0x1bd9c, 0x1bdc2, 0x1bdc4, 0x1bdc8, 0x1bdd0, 0x1bde6, 0x1bdfa, - 0x1be12, 0x1be14, 0x1be22, 0x1be24, 0x1be28, 0x1be42, 0x1be44, 0x1be48, 0x1be50, 0x1be5e, 0x1be66, 0x1be82, - 0x1be84, 0x1be88, 0x1be90, 0x1be9e, 0x1bea0, 0x1bebc, 0x1becc, 0x1bef4, 0x1bf1a, 0x1bf2e, 0x1bf32, 0x1bf34, - 0x1bf4e, 0x1bf5c, 0x1bf62, 0x1bf64, 0x1bf68, 0x1c09a, 0x1c0b2, 0x1c0b4, 0x1c11a, 0x1c132, 0x1c134, 0x1c162, - 0x1c164, 0x1c168, 0x1c176, 0x1c1ba, 0x1c21a, 0x1c232, 0x1c234, 0x1c24e, 0x1c25c, 0x1c262, 0x1c264, 0x1c268, - 0x1c276, 0x1c28e, 0x1c2c2, 0x1c2c4, 0x1c2c8, 0x1c2d0, 0x1c2de, 0x1c2e6, 0x1c2ec, 0x1c2fa, 0x1c316, 0x1c326, - 0x1c33a, 0x1c346, 0x1c34c, 0x1c372, 0x1c374, 0x1c41a, 0x1c42e, 0x1c432, 0x1c434, 0x1c44e, 0x1c45c, 0x1c462, - 0x1c464, 0x1c468, 0x1c476, 0x1c48e, 0x1c49c, 0x1c4b8, 0x1c4c2, 0x1c4c8, 0x1c4d0, 0x1c4de, 0x1c4e6, 0x1c4ec, - 0x1c4fa, 0x1c51c, 0x1c538, 0x1c570, 0x1c57e, 0x1c582, 0x1c584, 0x1c588, 0x1c590, 0x1c59e, 0x1c5a0, 0x1c5bc, - 0x1c5c6, 0x1c5cc, 0x1c5d8, 0x1c5ee, 0x1c5f2, 0x1c5f4, 0x1c616, 0x1c626, 0x1c62c, 0x1c63a, 0x1c646, 0x1c64c, - 0x1c658, 0x1c66e, 0x1c672, 0x1c674, 0x1c686, 0x1c68c, 0x1c698, 0x1c6b0, 0x1c6be, 0x1c6ce, 0x1c6dc, 0x1c6e2, - 0x1c6e4, 0x1c6e8, 0x1c712, 0x1c714, 0x1c722, 0x1c728, 0x1c736, 0x1c742, 0x1c744, 0x1c748, 0x1c750, 0x1c75e, - 0x1c766, 0x1c76c, 0x1c77a, 0x1c7ae, 0x1c7d6, 0x1c7ea, 0x1c81a, 0x1c82e, 0x1c832, 0x1c834, 0x1c84e, 0x1c85c, - 0x1c862, 0x1c864, 0x1c868, 0x1c876, 0x1c88e, 0x1c89c, 0x1c8b8, 0x1c8c2, 0x1c8c8, 0x1c8d0, 0x1c8de, 0x1c8e6, - 0x1c8ec, 0x1c8fa, 0x1c90e, 0x1c938, 0x1c970, 0x1c97e, 0x1c982, 0x1c984, 0x1c990, 0x1c99e, 0x1c9a0, 0x1c9bc, - 0x1c9c6, 0x1c9cc, 0x1c9d8, 0x1c9ee, 0x1c9f2, 0x1c9f4, 0x1ca38, 0x1ca70, 0x1ca7e, 0x1cae0, 0x1cafc, 0x1cb02, - 0x1cb04, 0x1cb08, 0x1cb10, 0x1cb20, 0x1cb3c, 0x1cb40, 0x1cb78, 0x1cb86, 0x1cb8c, 0x1cb98, 0x1cbb0, 0x1cbbe, - 0x1cbce, 0x1cbdc, 0x1cbe2, 0x1cbe4, 0x1cbe8, 0x1cbf6, 0x1cc16, 0x1cc26, 0x1cc2c, 0x1cc3a, 0x1cc46, 0x1cc58, - 0x1cc72, 0x1cc74, 0x1cc86, 0x1ccb0, 0x1ccbe, 0x1ccce, 0x1cce2, 0x1cce4, 0x1cce8, 0x1cd06, 0x1cd0c, 0x1cd18, - 0x1cd30, 0x1cd3e, 0x1cd60, 0x1cd7c, 0x1cd9c, 0x1cdc2, 0x1cdc4, 0x1cdc8, 0x1cdd0, 0x1cdde, 0x1cde6, 0x1cdfa, - 0x1ce22, 0x1ce28, 0x1ce42, 0x1ce50, 0x1ce5e, 0x1ce66, 0x1ce7a, 0x1ce82, 0x1ce84, 0x1ce88, 0x1ce90, 0x1ce9e, - 0x1cea0, 0x1cebc, 0x1cecc, 0x1cef2, 0x1cef4, 0x1cf2e, 0x1cf32, 0x1cf34, 0x1cf4e, 0x1cf5c, 0x1cf62, 0x1cf64, - 0x1cf68, 0x1cf96, 0x1cfa6, 0x1cfac, 0x1cfca, 0x1cfd2, 0x1cfd4, 0x1d02e, 0x1d032, 0x1d034, 0x1d04e, 0x1d05c, - 0x1d062, 0x1d064, 0x1d068, 0x1d076, 0x1d08e, 0x1d09c, 0x1d0b8, 0x1d0c2, 0x1d0c4, 0x1d0c8, 0x1d0d0, 0x1d0de, - 0x1d0e6, 0x1d0ec, 0x1d0fa, 0x1d11c, 0x1d138, 0x1d170, 0x1d17e, 0x1d182, 0x1d184, 0x1d188, 0x1d190, 0x1d19e, - 0x1d1a0, 0x1d1bc, 0x1d1c6, 0x1d1cc, 0x1d1d8, 0x1d1ee, 0x1d1f2, 0x1d1f4, 0x1d21c, 0x1d238, 0x1d270, 0x1d27e, - 0x1d2e0, 0x1d2fc, 0x1d302, 0x1d304, 0x1d308, 0x1d310, 0x1d31e, 0x1d320, 0x1d33c, 0x1d340, 0x1d378, 0x1d386, - 0x1d38c, 0x1d398, 0x1d3b0, 0x1d3be, 0x1d3ce, 0x1d3dc, 0x1d3e2, 0x1d3e4, 0x1d3e8, 0x1d3f6, 0x1d470, 0x1d47e, - 0x1d4e0, 0x1d4fc, 0x1d5c0, 0x1d5f8, 0x1d604, 0x1d608, 0x1d610, 0x1d620, 0x1d640, 0x1d678, 0x1d6f0, 0x1d706, - 0x1d70c, 0x1d718, 0x1d730, 0x1d73e, 0x1d760, 0x1d77c, 0x1d78e, 0x1d79c, 0x1d7b8, 0x1d7c2, 0x1d7c4, 0x1d7c8, - 0x1d7d0, 0x1d7de, 0x1d7e6, 0x1d7ec, 0x1d826, 0x1d82c, 0x1d83a, 0x1d846, 0x1d84c, 0x1d858, 0x1d872, 0x1d874, - 0x1d886, 0x1d88c, 0x1d898, 0x1d8b0, 0x1d8be, 0x1d8ce, 0x1d8e2, 0x1d8e4, 0x1d8e8, 0x1d8f6, 0x1d90c, 0x1d918, - 0x1d930, 0x1d93e, 0x1d960, 0x1d97c, 0x1d99c, 0x1d9c2, 0x1d9c4, 0x1d9c8, 0x1d9d0, 0x1d9e6, 0x1d9fa, 0x1da0c, - 0x1da18, 0x1da30, 0x1da3e, 0x1da60, 0x1da7c, 0x1dac0, 0x1daf8, 0x1db38, 0x1db82, 0x1db84, 0x1db88, 0x1db90, - 0x1db9e, 0x1dba0, 0x1dbcc, 0x1dbf2, 0x1dbf4, 0x1dc22, 0x1dc42, 0x1dc44, 0x1dc48, 0x1dc50, 0x1dc5e, 0x1dc66, - 0x1dc7a, 0x1dc82, 0x1dc84, 0x1dc88, 0x1dc90, 0x1dc9e, 0x1dca0, 0x1dcbc, 0x1dccc, 0x1dcf2, 0x1dcf4, 0x1dd04, - 0x1dd08, 0x1dd10, 0x1dd1e, 0x1dd20, 0x1dd3c, 0x1dd40, 0x1dd78, 0x1dd86, 0x1dd98, 0x1ddce, 0x1dde2, 0x1dde4, - 0x1dde8, 0x1de2e, 0x1de32, 0x1de34, 0x1de4e, 0x1de5c, 0x1de62, 0x1de64, 0x1de68, 0x1de8e, 0x1de9c, 0x1deb8, - 0x1dec2, 0x1dec4, 0x1dec8, 0x1ded0, 0x1dee6, 0x1defa, 0x1df16, 0x1df26, 0x1df2c, 0x1df46, 0x1df4c, 0x1df58, - 0x1df72, 0x1df74, 0x1df8a, 0x1df92, 0x1df94, 0x1dfa2, 0x1dfa4, 0x1dfa8, 0x1e08a, 0x1e092, 0x1e094, 0x1e0a2, - 0x1e0a4, 0x1e0a8, 0x1e0b6, 0x1e0da, 0x1e10a, 0x1e112, 0x1e114, 0x1e122, 0x1e124, 0x1e128, 0x1e136, 0x1e142, - 0x1e144, 0x1e148, 0x1e150, 0x1e166, 0x1e16c, 0x1e17a, 0x1e19a, 0x1e1b2, 0x1e1b4, 0x1e20a, 0x1e212, 0x1e214, - 0x1e222, 0x1e224, 0x1e228, 0x1e236, 0x1e242, 0x1e248, 0x1e250, 0x1e25e, 0x1e266, 0x1e26c, 0x1e27a, 0x1e282, - 0x1e284, 0x1e288, 0x1e290, 0x1e2a0, 0x1e2bc, 0x1e2c6, 0x1e2cc, 0x1e2d8, 0x1e2ee, 0x1e2f2, 0x1e2f4, 0x1e31a, - 0x1e332, 0x1e334, 0x1e35c, 0x1e362, 0x1e364, 0x1e368, 0x1e3ba, 0x1e40a, 0x1e412, 0x1e414, 0x1e422, 0x1e428, - 0x1e436, 0x1e442, 0x1e448, 0x1e450, 0x1e45e, 0x1e466, 0x1e46c, 0x1e47a, 0x1e482, 0x1e484, 0x1e490, 0x1e49e, - 0x1e4a0, 0x1e4bc, 0x1e4c6, 0x1e4cc, 0x1e4d8, 0x1e4ee, 0x1e4f2, 0x1e4f4, 0x1e502, 0x1e504, 0x1e508, 0x1e510, - 0x1e51e, 0x1e520, 0x1e53c, 0x1e540, 0x1e578, 0x1e586, 0x1e58c, 0x1e598, 0x1e5b0, 0x1e5be, 0x1e5ce, 0x1e5dc, - 0x1e5e2, 0x1e5e4, 0x1e5e8, 0x1e5f6, 0x1e61a, 0x1e62e, 0x1e632, 0x1e634, 0x1e64e, 0x1e65c, 0x1e662, 0x1e668, - 0x1e68e, 0x1e69c, 0x1e6b8, 0x1e6c2, 0x1e6c4, 0x1e6c8, 0x1e6d0, 0x1e6e6, 0x1e6fa, 0x1e716, 0x1e726, 0x1e72c, - 0x1e73a, 0x1e746, 0x1e74c, 0x1e758, 0x1e772, 0x1e774, 0x1e792, 0x1e794, 0x1e7a2, 0x1e7a4, 0x1e7a8, 0x1e7b6, - 0x1e812, 0x1e814, 0x1e822, 0x1e824, 0x1e828, 0x1e836, 0x1e842, 0x1e844, 0x1e848, 0x1e850, 0x1e85e, 0x1e866, - 0x1e86c, 0x1e87a, 0x1e882, 0x1e884, 0x1e888, 0x1e890, 0x1e89e, 0x1e8a0, 0x1e8bc, 0x1e8c6, 0x1e8cc, 0x1e8d8, - 0x1e8ee, 0x1e8f2, 0x1e8f4, 0x1e902, 0x1e904, 0x1e908, 0x1e910, 0x1e920, 0x1e93c, 0x1e940, 0x1e978, 0x1e986, - 0x1e98c, 0x1e998, 0x1e9b0, 0x1e9be, 0x1e9ce, 0x1e9dc, 0x1e9e2, 0x1e9e4, 0x1e9e8, 0x1e9f6, 0x1ea04, 0x1ea08, - 0x1ea10, 0x1ea20, 0x1ea40, 0x1ea78, 0x1eaf0, 0x1eb06, 0x1eb0c, 0x1eb18, 0x1eb30, 0x1eb3e, 0x1eb60, 0x1eb7c, - 0x1eb8e, 0x1eb9c, 0x1ebb8, 0x1ebc2, 0x1ebc4, 0x1ebc8, 0x1ebd0, 0x1ebde, 0x1ebe6, 0x1ebec, 0x1ec1a, 0x1ec2e, - 0x1ec32, 0x1ec34, 0x1ec4e, 0x1ec5c, 0x1ec62, 0x1ec64, 0x1ec68, 0x1ec8e, 0x1ec9c, 0x1ecb8, 0x1ecc2, 0x1ecc4, - 0x1ecc8, 0x1ecd0, 0x1ece6, 0x1ecfa, 0x1ed0e, 0x1ed1c, 0x1ed38, 0x1ed70, 0x1ed7e, 0x1ed82, 0x1ed84, 0x1ed88, - 0x1ed90, 0x1ed9e, 0x1eda0, 0x1edcc, 0x1edf2, 0x1edf4, 0x1ee16, 0x1ee26, 0x1ee2c, 0x1ee3a, 0x1ee46, 0x1ee4c, - 0x1ee58, 0x1ee6e, 0x1ee72, 0x1ee74, 0x1ee86, 0x1ee8c, 0x1ee98, 0x1eeb0, 0x1eebe, 0x1eece, 0x1eedc, 0x1eee2, - 0x1eee4, 0x1eee8, 0x1ef12, 0x1ef22, 0x1ef24, 0x1ef28, 0x1ef36, 0x1ef42, 0x1ef44, 0x1ef48, 0x1ef50, 0x1ef5e, - 0x1ef66, 0x1ef6c, 0x1ef7a, 0x1efae, 0x1efb2, 0x1efb4, 0x1efd6, 0x1f096, 0x1f0a6, 0x1f0ac, 0x1f0ba, 0x1f0ca, - 0x1f0d2, 0x1f0d4, 0x1f116, 0x1f126, 0x1f12c, 0x1f13a, 0x1f146, 0x1f14c, 0x1f158, 0x1f16e, 0x1f172, 0x1f174, - 0x1f18a, 0x1f192, 0x1f194, 0x1f1a2, 0x1f1a4, 0x1f1a8, 0x1f1da, 0x1f216, 0x1f226, 0x1f22c, 0x1f23a, 0x1f246, - 0x1f258, 0x1f26e, 0x1f272, 0x1f274, 0x1f286, 0x1f28c, 0x1f298, 0x1f2b0, 0x1f2be, 0x1f2ce, 0x1f2dc, 0x1f2e2, - 0x1f2e4, 0x1f2e8, 0x1f2f6, 0x1f30a, 0x1f312, 0x1f314, 0x1f322, 0x1f328, 0x1f342, 0x1f344, 0x1f348, 0x1f350, - 0x1f35e, 0x1f366, 0x1f37a, 0x1f39a, 0x1f3ae, 0x1f3b2, 0x1f3b4, 0x1f416, 0x1f426, 0x1f42c, 0x1f43a, 0x1f446, - 0x1f44c, 0x1f458, 0x1f46e, 0x1f472, 0x1f474, 0x1f486, 0x1f48c, 0x1f498, 0x1f4b0, 0x1f4be, 0x1f4ce, 0x1f4dc, - 0x1f4e2, 0x1f4e4, 0x1f4e8, 0x1f4f6, 0x1f506, 0x1f50c, 0x1f518, 0x1f530, 0x1f53e, 0x1f560, 0x1f57c, 0x1f58e, - 0x1f59c, 0x1f5b8, 0x1f5c2, 0x1f5c4, 0x1f5c8, 0x1f5d0, 0x1f5de, 0x1f5e6, 0x1f5ec, 0x1f5fa, 0x1f60a, 0x1f612, - 0x1f614, 0x1f622, 0x1f624, 0x1f628, 0x1f636, 0x1f642, 0x1f644, 0x1f648, 0x1f650, 0x1f65e, 0x1f666, 0x1f67a, - 0x1f682, 0x1f684, 0x1f688, 0x1f690, 0x1f69e, 0x1f6a0, 0x1f6bc, 0x1f6cc, 0x1f6f2, 0x1f6f4, 0x1f71a, 0x1f72e, - 0x1f732, 0x1f734, 0x1f74e, 0x1f75c, 0x1f762, 0x1f764, 0x1f768, 0x1f776, 0x1f796, 0x1f7a6, 0x1f7ac, 0x1f7ba, - 0x1f7d2, 0x1f7d4, 0x1f89a, 0x1f8ae, 0x1f8b2, 0x1f8b4, 0x1f8d6, 0x1f8ea, 0x1f91a, 0x1f92e, 0x1f932, 0x1f934, - 0x1f94e, 0x1f95c, 0x1f962, 0x1f964, 0x1f968, 0x1f976, 0x1f996, 0x1f9a6, 0x1f9ac, 0x1f9ba, 0x1f9ca, 0x1f9d2, - 0x1f9d4, 0x1fa1a, 0x1fa2e, 0x1fa32, 0x1fa34, 0x1fa4e, 0x1fa5c, 0x1fa62, 0x1fa64, 0x1fa68, 0x1fa76, 0x1fa8e, - 0x1fa9c, 0x1fab8, 0x1fac2, 0x1fac4, 0x1fac8, 0x1fad0, 0x1fade, 0x1fae6, 0x1faec, 0x1fb16, 0x1fb26, 0x1fb2c, - 0x1fb3a, 0x1fb46, 0x1fb4c, 0x1fb58, 0x1fb6e, 0x1fb72, 0x1fb74, 0x1fb8a, 0x1fb92, 0x1fb94, 0x1fba2, 0x1fba4, - 0x1fba8, 0x1fbb6, 0x1fbda - ]); - /** - * This table contains to codewords for all symbols. - */ - PDF417Common.CODEWORD_TABLE = Int32Array.from([ - 2627, 1819, 2622, 2621, 1813, 1812, 2729, 2724, 2723, 2779, 2774, 2773, 902, 896, 908, 868, 865, 861, 859, 2511, - 873, 871, 1780, 835, 2493, 825, 2491, 842, 837, 844, 1764, 1762, 811, 810, 809, 2483, 807, 2482, 806, 2480, 815, - 814, 813, 812, 2484, 817, 816, 1745, 1744, 1742, 1746, 2655, 2637, 2635, 2626, 2625, 2623, 2628, 1820, 2752, - 2739, 2737, 2728, 2727, 2725, 2730, 2785, 2783, 2778, 2777, 2775, 2780, 787, 781, 747, 739, 736, 2413, 754, 752, - 1719, 692, 689, 681, 2371, 678, 2369, 700, 697, 694, 703, 1688, 1686, 642, 638, 2343, 631, 2341, 627, 2338, 651, - 646, 643, 2345, 654, 652, 1652, 1650, 1647, 1654, 601, 599, 2322, 596, 2321, 594, 2319, 2317, 611, 610, 608, 606, - 2324, 603, 2323, 615, 614, 612, 1617, 1616, 1614, 1612, 616, 1619, 1618, 2575, 2538, 2536, 905, 901, 898, 909, - 2509, 2507, 2504, 870, 867, 864, 860, 2512, 875, 872, 1781, 2490, 2489, 2487, 2485, 1748, 836, 834, 832, 830, - 2494, 827, 2492, 843, 841, 839, 845, 1765, 1763, 2701, 2676, 2674, 2653, 2648, 2656, 2634, 2633, 2631, 2629, - 1821, 2638, 2636, 2770, 2763, 2761, 2750, 2745, 2753, 2736, 2735, 2733, 2731, 1848, 2740, 2738, 2786, 2784, 591, - 588, 576, 569, 566, 2296, 1590, 537, 534, 526, 2276, 522, 2274, 545, 542, 539, 548, 1572, 1570, 481, 2245, 466, - 2242, 462, 2239, 492, 485, 482, 2249, 496, 494, 1534, 1531, 1528, 1538, 413, 2196, 406, 2191, 2188, 425, 419, - 2202, 415, 2199, 432, 430, 427, 1472, 1467, 1464, 433, 1476, 1474, 368, 367, 2160, 365, 2159, 362, 2157, 2155, - 2152, 378, 377, 375, 2166, 372, 2165, 369, 2162, 383, 381, 379, 2168, 1419, 1418, 1416, 1414, 385, 1411, 384, - 1423, 1422, 1420, 1424, 2461, 802, 2441, 2439, 790, 786, 783, 794, 2409, 2406, 2403, 750, 742, 738, 2414, 756, - 753, 1720, 2367, 2365, 2362, 2359, 1663, 693, 691, 684, 2373, 680, 2370, 702, 699, 696, 704, 1690, 1687, 2337, - 2336, 2334, 2332, 1624, 2329, 1622, 640, 637, 2344, 634, 2342, 630, 2340, 650, 648, 645, 2346, 655, 653, 1653, - 1651, 1649, 1655, 2612, 2597, 2595, 2571, 2568, 2565, 2576, 2534, 2529, 2526, 1787, 2540, 2537, 907, 904, 900, - 910, 2503, 2502, 2500, 2498, 1768, 2495, 1767, 2510, 2508, 2506, 869, 866, 863, 2513, 876, 874, 1782, 2720, 2713, - 2711, 2697, 2694, 2691, 2702, 2672, 2670, 2664, 1828, 2678, 2675, 2647, 2646, 2644, 2642, 1823, 2639, 1822, 2654, - 2652, 2650, 2657, 2771, 1855, 2765, 2762, 1850, 1849, 2751, 2749, 2747, 2754, 353, 2148, 344, 342, 336, 2142, - 332, 2140, 345, 1375, 1373, 306, 2130, 299, 2128, 295, 2125, 319, 314, 311, 2132, 1354, 1352, 1349, 1356, 262, - 257, 2101, 253, 2096, 2093, 274, 273, 267, 2107, 263, 2104, 280, 278, 275, 1316, 1311, 1308, 1320, 1318, 2052, - 202, 2050, 2044, 2040, 219, 2063, 212, 2060, 208, 2055, 224, 221, 2066, 1260, 1258, 1252, 231, 1248, 229, 1266, - 1264, 1261, 1268, 155, 1998, 153, 1996, 1994, 1991, 1988, 165, 164, 2007, 162, 2006, 159, 2003, 2000, 172, 171, - 169, 2012, 166, 2010, 1186, 1184, 1182, 1179, 175, 1176, 173, 1192, 1191, 1189, 1187, 176, 1194, 1193, 2313, - 2307, 2305, 592, 589, 2294, 2292, 2289, 578, 572, 568, 2297, 580, 1591, 2272, 2267, 2264, 1547, 538, 536, 529, - 2278, 525, 2275, 547, 544, 541, 1574, 1571, 2237, 2235, 2229, 1493, 2225, 1489, 478, 2247, 470, 2244, 465, 2241, - 493, 488, 484, 2250, 498, 495, 1536, 1533, 1530, 1539, 2187, 2186, 2184, 2182, 1432, 2179, 1430, 2176, 1427, 414, - 412, 2197, 409, 2195, 405, 2193, 2190, 426, 424, 421, 2203, 418, 2201, 431, 429, 1473, 1471, 1469, 1466, 434, - 1477, 1475, 2478, 2472, 2470, 2459, 2457, 2454, 2462, 803, 2437, 2432, 2429, 1726, 2443, 2440, 792, 789, 785, - 2401, 2399, 2393, 1702, 2389, 1699, 2411, 2408, 2405, 745, 741, 2415, 758, 755, 1721, 2358, 2357, 2355, 2353, - 1661, 2350, 1660, 2347, 1657, 2368, 2366, 2364, 2361, 1666, 690, 687, 2374, 683, 2372, 701, 698, 705, 1691, 1689, - 2619, 2617, 2610, 2608, 2605, 2613, 2593, 2588, 2585, 1803, 2599, 2596, 2563, 2561, 2555, 1797, 2551, 1795, 2573, - 2570, 2567, 2577, 2525, 2524, 2522, 2520, 1786, 2517, 1785, 2514, 1783, 2535, 2533, 2531, 2528, 1788, 2541, 2539, - 906, 903, 911, 2721, 1844, 2715, 2712, 1838, 1836, 2699, 2696, 2693, 2703, 1827, 1826, 1824, 2673, 2671, 2669, - 2666, 1829, 2679, 2677, 1858, 1857, 2772, 1854, 1853, 1851, 1856, 2766, 2764, 143, 1987, 139, 1986, 135, 133, - 131, 1984, 128, 1983, 125, 1981, 138, 137, 136, 1985, 1133, 1132, 1130, 112, 110, 1974, 107, 1973, 104, 1971, - 1969, 122, 121, 119, 117, 1977, 114, 1976, 124, 1115, 1114, 1112, 1110, 1117, 1116, 84, 83, 1953, 81, 1952, 78, - 1950, 1948, 1945, 94, 93, 91, 1959, 88, 1958, 85, 1955, 99, 97, 95, 1961, 1086, 1085, 1083, 1081, 1078, 100, - 1090, 1089, 1087, 1091, 49, 47, 1917, 44, 1915, 1913, 1910, 1907, 59, 1926, 56, 1925, 53, 1922, 1919, 66, 64, - 1931, 61, 1929, 1042, 1040, 1038, 71, 1035, 70, 1032, 68, 1048, 1047, 1045, 1043, 1050, 1049, 12, 10, 1869, 1867, - 1864, 1861, 21, 1880, 19, 1877, 1874, 1871, 28, 1888, 25, 1886, 22, 1883, 982, 980, 977, 974, 32, 30, 991, 989, - 987, 984, 34, 995, 994, 992, 2151, 2150, 2147, 2146, 2144, 356, 355, 354, 2149, 2139, 2138, 2136, 2134, 1359, - 343, 341, 338, 2143, 335, 2141, 348, 347, 346, 1376, 1374, 2124, 2123, 2121, 2119, 1326, 2116, 1324, 310, 308, - 305, 2131, 302, 2129, 298, 2127, 320, 318, 316, 313, 2133, 322, 321, 1355, 1353, 1351, 1357, 2092, 2091, 2089, - 2087, 1276, 2084, 1274, 2081, 1271, 259, 2102, 256, 2100, 252, 2098, 2095, 272, 269, 2108, 266, 2106, 281, 279, - 277, 1317, 1315, 1313, 1310, 282, 1321, 1319, 2039, 2037, 2035, 2032, 1203, 2029, 1200, 1197, 207, 2053, 205, - 2051, 201, 2049, 2046, 2043, 220, 218, 2064, 215, 2062, 211, 2059, 228, 226, 223, 2069, 1259, 1257, 1254, 232, - 1251, 230, 1267, 1265, 1263, 2316, 2315, 2312, 2311, 2309, 2314, 2304, 2303, 2301, 2299, 1593, 2308, 2306, 590, - 2288, 2287, 2285, 2283, 1578, 2280, 1577, 2295, 2293, 2291, 579, 577, 574, 571, 2298, 582, 581, 1592, 2263, 2262, - 2260, 2258, 1545, 2255, 1544, 2252, 1541, 2273, 2271, 2269, 2266, 1550, 535, 532, 2279, 528, 2277, 546, 543, 549, - 1575, 1573, 2224, 2222, 2220, 1486, 2217, 1485, 2214, 1482, 1479, 2238, 2236, 2234, 2231, 1496, 2228, 1492, 480, - 477, 2248, 473, 2246, 469, 2243, 490, 487, 2251, 497, 1537, 1535, 1532, 2477, 2476, 2474, 2479, 2469, 2468, 2466, - 2464, 1730, 2473, 2471, 2453, 2452, 2450, 2448, 1729, 2445, 1728, 2460, 2458, 2456, 2463, 805, 804, 2428, 2427, - 2425, 2423, 1725, 2420, 1724, 2417, 1722, 2438, 2436, 2434, 2431, 1727, 2444, 2442, 793, 791, 788, 795, 2388, - 2386, 2384, 1697, 2381, 1696, 2378, 1694, 1692, 2402, 2400, 2398, 2395, 1703, 2392, 1701, 2412, 2410, 2407, 751, - 748, 744, 2416, 759, 757, 1807, 2620, 2618, 1806, 1805, 2611, 2609, 2607, 2614, 1802, 1801, 1799, 2594, 2592, - 2590, 2587, 1804, 2600, 2598, 1794, 1793, 1791, 1789, 2564, 2562, 2560, 2557, 1798, 2554, 1796, 2574, 2572, 2569, - 2578, 1847, 1846, 2722, 1843, 1842, 1840, 1845, 2716, 2714, 1835, 1834, 1832, 1830, 1839, 1837, 2700, 2698, 2695, - 2704, 1817, 1811, 1810, 897, 862, 1777, 829, 826, 838, 1760, 1758, 808, 2481, 1741, 1740, 1738, 1743, 2624, 1818, - 2726, 2776, 782, 740, 737, 1715, 686, 679, 695, 1682, 1680, 639, 628, 2339, 647, 644, 1645, 1643, 1640, 1648, - 602, 600, 597, 595, 2320, 593, 2318, 609, 607, 604, 1611, 1610, 1608, 1606, 613, 1615, 1613, 2328, 926, 924, 892, - 886, 899, 857, 850, 2505, 1778, 824, 823, 821, 819, 2488, 818, 2486, 833, 831, 828, 840, 1761, 1759, 2649, 2632, - 2630, 2746, 2734, 2732, 2782, 2781, 570, 567, 1587, 531, 527, 523, 540, 1566, 1564, 476, 467, 463, 2240, 486, - 483, 1524, 1521, 1518, 1529, 411, 403, 2192, 399, 2189, 423, 416, 1462, 1457, 1454, 428, 1468, 1465, 2210, 366, - 363, 2158, 360, 2156, 357, 2153, 376, 373, 370, 2163, 1410, 1409, 1407, 1405, 382, 1402, 380, 1417, 1415, 1412, - 1421, 2175, 2174, 777, 774, 771, 784, 732, 725, 722, 2404, 743, 1716, 676, 674, 668, 2363, 665, 2360, 685, 1684, - 1681, 626, 624, 622, 2335, 620, 2333, 617, 2330, 641, 635, 649, 1646, 1644, 1642, 2566, 928, 925, 2530, 2527, - 894, 891, 888, 2501, 2499, 2496, 858, 856, 854, 851, 1779, 2692, 2668, 2665, 2645, 2643, 2640, 2651, 2768, 2759, - 2757, 2744, 2743, 2741, 2748, 352, 1382, 340, 337, 333, 1371, 1369, 307, 300, 296, 2126, 315, 312, 1347, 1342, - 1350, 261, 258, 250, 2097, 246, 2094, 271, 268, 264, 1306, 1301, 1298, 276, 1312, 1309, 2115, 203, 2048, 195, - 2045, 191, 2041, 213, 209, 2056, 1246, 1244, 1238, 225, 1234, 222, 1256, 1253, 1249, 1262, 2080, 2079, 154, 1997, - 150, 1995, 147, 1992, 1989, 163, 160, 2004, 156, 2001, 1175, 1174, 1172, 1170, 1167, 170, 1164, 167, 1185, 1183, - 1180, 1177, 174, 1190, 1188, 2025, 2024, 2022, 587, 586, 564, 559, 556, 2290, 573, 1588, 520, 518, 512, 2268, - 508, 2265, 530, 1568, 1565, 461, 457, 2233, 450, 2230, 446, 2226, 479, 471, 489, 1526, 1523, 1520, 397, 395, - 2185, 392, 2183, 389, 2180, 2177, 410, 2194, 402, 422, 1463, 1461, 1459, 1456, 1470, 2455, 799, 2433, 2430, 779, - 776, 773, 2397, 2394, 2390, 734, 728, 724, 746, 1717, 2356, 2354, 2351, 2348, 1658, 677, 675, 673, 670, 667, 688, - 1685, 1683, 2606, 2589, 2586, 2559, 2556, 2552, 927, 2523, 2521, 2518, 2515, 1784, 2532, 895, 893, 890, 2718, - 2709, 2707, 2689, 2687, 2684, 2663, 2662, 2660, 2658, 1825, 2667, 2769, 1852, 2760, 2758, 142, 141, 1139, 1138, - 134, 132, 129, 126, 1982, 1129, 1128, 1126, 1131, 113, 111, 108, 105, 1972, 101, 1970, 120, 118, 115, 1109, 1108, - 1106, 1104, 123, 1113, 1111, 82, 79, 1951, 75, 1949, 72, 1946, 92, 89, 86, 1956, 1077, 1076, 1074, 1072, 98, - 1069, 96, 1084, 1082, 1079, 1088, 1968, 1967, 48, 45, 1916, 42, 1914, 39, 1911, 1908, 60, 57, 54, 1923, 50, 1920, - 1031, 1030, 1028, 1026, 67, 1023, 65, 1020, 62, 1041, 1039, 1036, 1033, 69, 1046, 1044, 1944, 1943, 1941, 11, 9, - 1868, 7, 1865, 1862, 1859, 20, 1878, 16, 1875, 13, 1872, 970, 968, 966, 963, 29, 960, 26, 23, 983, 981, 978, 975, - 33, 971, 31, 990, 988, 985, 1906, 1904, 1902, 993, 351, 2145, 1383, 331, 330, 328, 326, 2137, 323, 2135, 339, - 1372, 1370, 294, 293, 291, 289, 2122, 286, 2120, 283, 2117, 309, 303, 317, 1348, 1346, 1344, 245, 244, 242, 2090, - 239, 2088, 236, 2085, 2082, 260, 2099, 249, 270, 1307, 1305, 1303, 1300, 1314, 189, 2038, 186, 2036, 183, 2033, - 2030, 2026, 206, 198, 2047, 194, 216, 1247, 1245, 1243, 1240, 227, 1237, 1255, 2310, 2302, 2300, 2286, 2284, - 2281, 565, 563, 561, 558, 575, 1589, 2261, 2259, 2256, 2253, 1542, 521, 519, 517, 514, 2270, 511, 533, 1569, - 1567, 2223, 2221, 2218, 2215, 1483, 2211, 1480, 459, 456, 453, 2232, 449, 474, 491, 1527, 1525, 1522, 2475, 2467, - 2465, 2451, 2449, 2446, 801, 800, 2426, 2424, 2421, 2418, 1723, 2435, 780, 778, 775, 2387, 2385, 2382, 2379, - 1695, 2375, 1693, 2396, 735, 733, 730, 727, 749, 1718, 2616, 2615, 2604, 2603, 2601, 2584, 2583, 2581, 2579, - 1800, 2591, 2550, 2549, 2547, 2545, 1792, 2542, 1790, 2558, 929, 2719, 1841, 2710, 2708, 1833, 1831, 2690, 2688, - 2686, 1815, 1809, 1808, 1774, 1756, 1754, 1737, 1736, 1734, 1739, 1816, 1711, 1676, 1674, 633, 629, 1638, 1636, - 1633, 1641, 598, 1605, 1604, 1602, 1600, 605, 1609, 1607, 2327, 887, 853, 1775, 822, 820, 1757, 1755, 1584, 524, - 1560, 1558, 468, 464, 1514, 1511, 1508, 1519, 408, 404, 400, 1452, 1447, 1444, 417, 1458, 1455, 2208, 364, 361, - 358, 2154, 1401, 1400, 1398, 1396, 374, 1393, 371, 1408, 1406, 1403, 1413, 2173, 2172, 772, 726, 723, 1712, 672, - 669, 666, 682, 1678, 1675, 625, 623, 621, 618, 2331, 636, 632, 1639, 1637, 1635, 920, 918, 884, 880, 889, 849, - 848, 847, 846, 2497, 855, 852, 1776, 2641, 2742, 2787, 1380, 334, 1367, 1365, 301, 297, 1340, 1338, 1335, 1343, - 255, 251, 247, 1296, 1291, 1288, 265, 1302, 1299, 2113, 204, 196, 192, 2042, 1232, 1230, 1224, 214, 1220, 210, - 1242, 1239, 1235, 1250, 2077, 2075, 151, 148, 1993, 144, 1990, 1163, 1162, 1160, 1158, 1155, 161, 1152, 157, - 1173, 1171, 1168, 1165, 168, 1181, 1178, 2021, 2020, 2018, 2023, 585, 560, 557, 1585, 516, 509, 1562, 1559, 458, - 447, 2227, 472, 1516, 1513, 1510, 398, 396, 393, 390, 2181, 386, 2178, 407, 1453, 1451, 1449, 1446, 420, 1460, - 2209, 769, 764, 720, 712, 2391, 729, 1713, 664, 663, 661, 659, 2352, 656, 2349, 671, 1679, 1677, 2553, 922, 919, - 2519, 2516, 885, 883, 881, 2685, 2661, 2659, 2767, 2756, 2755, 140, 1137, 1136, 130, 127, 1125, 1124, 1122, 1127, - 109, 106, 102, 1103, 1102, 1100, 1098, 116, 1107, 1105, 1980, 80, 76, 73, 1947, 1068, 1067, 1065, 1063, 90, 1060, - 87, 1075, 1073, 1070, 1080, 1966, 1965, 46, 43, 40, 1912, 36, 1909, 1019, 1018, 1016, 1014, 58, 1011, 55, 1008, - 51, 1029, 1027, 1024, 1021, 63, 1037, 1034, 1940, 1939, 1937, 1942, 8, 1866, 4, 1863, 1, 1860, 956, 954, 952, - 949, 946, 17, 14, 969, 967, 964, 961, 27, 957, 24, 979, 976, 972, 1901, 1900, 1898, 1896, 986, 1905, 1903, 350, - 349, 1381, 329, 327, 324, 1368, 1366, 292, 290, 287, 284, 2118, 304, 1341, 1339, 1337, 1345, 243, 240, 237, 2086, - 233, 2083, 254, 1297, 1295, 1293, 1290, 1304, 2114, 190, 187, 184, 2034, 180, 2031, 177, 2027, 199, 1233, 1231, - 1229, 1226, 217, 1223, 1241, 2078, 2076, 584, 555, 554, 552, 550, 2282, 562, 1586, 507, 506, 504, 502, 2257, 499, - 2254, 515, 1563, 1561, 445, 443, 441, 2219, 438, 2216, 435, 2212, 460, 454, 475, 1517, 1515, 1512, 2447, 798, - 797, 2422, 2419, 770, 768, 766, 2383, 2380, 2376, 721, 719, 717, 714, 731, 1714, 2602, 2582, 2580, 2548, 2546, - 2543, 923, 921, 2717, 2706, 2705, 2683, 2682, 2680, 1771, 1752, 1750, 1733, 1732, 1731, 1735, 1814, 1707, 1670, - 1668, 1631, 1629, 1626, 1634, 1599, 1598, 1596, 1594, 1603, 1601, 2326, 1772, 1753, 1751, 1581, 1554, 1552, 1504, - 1501, 1498, 1509, 1442, 1437, 1434, 401, 1448, 1445, 2206, 1392, 1391, 1389, 1387, 1384, 359, 1399, 1397, 1394, - 1404, 2171, 2170, 1708, 1672, 1669, 619, 1632, 1630, 1628, 1773, 1378, 1363, 1361, 1333, 1328, 1336, 1286, 1281, - 1278, 248, 1292, 1289, 2111, 1218, 1216, 1210, 197, 1206, 193, 1228, 1225, 1221, 1236, 2073, 2071, 1151, 1150, - 1148, 1146, 152, 1143, 149, 1140, 145, 1161, 1159, 1156, 1153, 158, 1169, 1166, 2017, 2016, 2014, 2019, 1582, - 510, 1556, 1553, 452, 448, 1506, 1500, 394, 391, 387, 1443, 1441, 1439, 1436, 1450, 2207, 765, 716, 713, 1709, - 662, 660, 657, 1673, 1671, 916, 914, 879, 878, 877, 882, 1135, 1134, 1121, 1120, 1118, 1123, 1097, 1096, 1094, - 1092, 103, 1101, 1099, 1979, 1059, 1058, 1056, 1054, 77, 1051, 74, 1066, 1064, 1061, 1071, 1964, 1963, 1007, - 1006, 1004, 1002, 999, 41, 996, 37, 1017, 1015, 1012, 1009, 52, 1025, 1022, 1936, 1935, 1933, 1938, 942, 940, - 938, 935, 932, 5, 2, 955, 953, 950, 947, 18, 943, 15, 965, 962, 958, 1895, 1894, 1892, 1890, 973, 1899, 1897, - 1379, 325, 1364, 1362, 288, 285, 1334, 1332, 1330, 241, 238, 234, 1287, 1285, 1283, 1280, 1294, 2112, 188, 185, - 181, 178, 2028, 1219, 1217, 1215, 1212, 200, 1209, 1227, 2074, 2072, 583, 553, 551, 1583, 505, 503, 500, 513, - 1557, 1555, 444, 442, 439, 436, 2213, 455, 451, 1507, 1505, 1502, 796, 763, 762, 760, 767, 711, 710, 708, 706, - 2377, 718, 715, 1710, 2544, 917, 915, 2681, 1627, 1597, 1595, 2325, 1769, 1749, 1747, 1499, 1438, 1435, 2204, - 1390, 1388, 1385, 1395, 2169, 2167, 1704, 1665, 1662, 1625, 1623, 1620, 1770, 1329, 1282, 1279, 2109, 1214, 1207, - 1222, 2068, 2065, 1149, 1147, 1144, 1141, 146, 1157, 1154, 2013, 2011, 2008, 2015, 1579, 1549, 1546, 1495, 1487, - 1433, 1431, 1428, 1425, 388, 1440, 2205, 1705, 658, 1667, 1664, 1119, 1095, 1093, 1978, 1057, 1055, 1052, 1062, - 1962, 1960, 1005, 1003, 1000, 997, 38, 1013, 1010, 1932, 1930, 1927, 1934, 941, 939, 936, 933, 6, 930, 3, 951, - 948, 944, 1889, 1887, 1884, 1881, 959, 1893, 1891, 35, 1377, 1360, 1358, 1327, 1325, 1322, 1331, 1277, 1275, - 1272, 1269, 235, 1284, 2110, 1205, 1204, 1201, 1198, 182, 1195, 179, 1213, 2070, 2067, 1580, 501, 1551, 1548, - 440, 437, 1497, 1494, 1490, 1503, 761, 709, 707, 1706, 913, 912, 2198, 1386, 2164, 2161, 1621, 1766, 2103, 1208, - 2058, 2054, 1145, 1142, 2005, 2002, 1999, 2009, 1488, 1429, 1426, 2200, 1698, 1659, 1656, 1975, 1053, 1957, 1954, - 1001, 998, 1924, 1921, 1918, 1928, 937, 934, 931, 1879, 1876, 1873, 1870, 945, 1885, 1882, 1323, 1273, 1270, - 2105, 1202, 1199, 1196, 1211, 2061, 2057, 1576, 1543, 1540, 1484, 1481, 1478, 1491, 1700 - ]); - - /* - * Copyright 2007 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 java.util.List; - /** - * @author Guenther Grau - */ - /*public final*/ class PDF417DetectorResult { - constructor(bits, points) { - this.bits = bits; - this.points = points; - } - getBits() { - return this.bits; - } - getPoints() { - return this.points; - } - } - - /* - * 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. - */ - // import java.util.ArrayList; - // import java.util.Arrays; - // import java.util.List; - // import java.util.Map; - /** - *Encapsulates logic that can detect a PDF417 Code in an image, even if the - * PDF417 Code is rotated or skewed, or partially obscured.
- * - * @author SITA Lab (kevin.osullivan@sita.aero) - * @author dswitkin@google.com (Daniel Switkin) - * @author Guenther Grau - */ - /*public*/ /*final*/ class Detector$3 { - /** - *Detects a PDF417 Code in an image. Only checks 0 and 180 degree rotations.
- * - * @param image barcode image to decode - * @param hints optional hints to detector - * @param multiple if true, then the image is searched for multiple codes. If false, then at most one code will - * be found and returned - * @return {@link PDF417DetectorResult} encapsulating results of detecting a PDF417 code - * @throws NotFoundException if no PDF417 Code can be found - */ - static detectMultiple(image, hints, multiple) { - // TODO detection improvement, tryHarder could try several different luminance thresholds/blackpoints or even - // different binarizers - // boolean tryHarder = hints != null && hints.containsKey(DecodeHintType.TRY_HARDER); - let bitMatrix = image.getBlackMatrix(); - let barcodeCoordinates = Detector$3.detect(multiple, bitMatrix); - if (!barcodeCoordinates.length) { - bitMatrix = bitMatrix.clone(); - bitMatrix.rotate180(); - barcodeCoordinates = Detector$3.detect(multiple, bitMatrix); - } - return new PDF417DetectorResult(bitMatrix, barcodeCoordinates); - } - /** - * Detects PDF417 codes in an image. Only checks 0 degree rotation - * @param multiple if true, then the image is searched for multiple codes. If false, then at most one code will - * be found and returned - * @param bitMatrix bit matrix to detect barcodes in - * @return List of ResultPoint arrays containing the coordinates of found barcodes - */ - static detect(multiple, bitMatrix) { - const barcodeCoordinates = new Array(); - let row = 0; - let column = 0; - let foundBarcodeInRow = false; - while (row < bitMatrix.getHeight()) { - const vertices = Detector$3.findVertices(bitMatrix, row, column); - if (vertices[0] == null && vertices[3] == null) { - if (!foundBarcodeInRow) { - // we didn't find any barcode so that's the end of searching - break; - } - // we didn't find a barcode starting at the given column and row. Try again from the first column and slightly - // below the lowest barcode we found so far. - foundBarcodeInRow = false; - column = 0; - for (const barcodeCoordinate of barcodeCoordinates) { - if (barcodeCoordinate[1] != null) { - row = Math.trunc(Math.max(row, barcodeCoordinate[1].getY())); - } - if (barcodeCoordinate[3] != null) { - row = Math.max(row, Math.trunc(barcodeCoordinate[3].getY())); - } - } - row += Detector$3.ROW_STEP; - continue; - } - foundBarcodeInRow = true; - barcodeCoordinates.push(vertices); - if (!multiple) { - break; - } - // if we didn't find a right row indicator column, then continue the search for the next barcode after the - // start pattern of the barcode just found. - if (vertices[2] != null) { - column = Math.trunc(vertices[2].getX()); - row = Math.trunc(vertices[2].getY()); - } - else { - column = Math.trunc(vertices[4].getX()); - row = Math.trunc(vertices[4].getY()); - } - } - return barcodeCoordinates; - } - /** - * Locate the vertices and the codewords area of a black blob using the Start - * and Stop patterns as locators. - * - * @param matrix the scanned barcode image. - * @return an array containing the vertices: - * vertices[0] x, y top left barcode - * vertices[1] x, y bottom left barcode - * vertices[2] x, y top right barcode - * vertices[3] x, y bottom right barcode - * vertices[4] x, y top left codeword area - * vertices[5] x, y bottom left codeword area - * vertices[6] x, y top right codeword area - * vertices[7] x, y bottom right codeword area - */ - static findVertices(matrix, startRow, startColumn) { - const height = matrix.getHeight(); - const width = matrix.getWidth(); - // const result = new ResultPoint[8]; - const result = new Array(8); - Detector$3.copyToResult(result, Detector$3.findRowsWithPattern(matrix, height, width, startRow, startColumn, Detector$3.START_PATTERN), Detector$3.INDEXES_START_PATTERN); - if (result[4] != null) { - startColumn = Math.trunc(result[4].getX()); - startRow = Math.trunc(result[4].getY()); - } - Detector$3.copyToResult(result, Detector$3.findRowsWithPattern(matrix, height, width, startRow, startColumn, Detector$3.STOP_PATTERN), Detector$3.INDEXES_STOP_PATTERN); - return result; - } - static copyToResult(result, tmpResult, destinationIndexes) { - for (let i = 0; i < destinationIndexes.length; i++) { - result[destinationIndexes[i]] = tmpResult[i]; - } - } - static findRowsWithPattern(matrix, height, width, startRow, startColumn, pattern) { - // const result = new ResultPoint[4]; - const result = new Array(4); - let found = false; - const counters = new Int32Array(pattern.length); - for (; startRow < height; startRow += Detector$3.ROW_STEP) { - let loc = Detector$3.findGuardPattern(matrix, startColumn, startRow, width, false, pattern, counters); - if (loc != null) { - while (startRow > 0) { - const previousRowLoc = Detector$3.findGuardPattern(matrix, startColumn, --startRow, width, false, pattern, counters); - if (previousRowLoc != null) { - loc = previousRowLoc; - } - else { - startRow++; - break; - } - } - result[0] = new ResultPoint(loc[0], startRow); - result[1] = new ResultPoint(loc[1], startRow); - found = true; - break; - } - } - let stopRow = startRow + 1; - // Last row of the current symbol that contains pattern - if (found) { - let skippedRowCount = 0; - let previousRowLoc = Int32Array.from([Math.trunc(result[0].getX()), Math.trunc(result[1].getX())]); - for (; stopRow < height; stopRow++) { - const loc = Detector$3.findGuardPattern(matrix, previousRowLoc[0], stopRow, width, false, pattern, counters); - // a found pattern is only considered to belong to the same barcode if the start and end positions - // don't differ too much. Pattern drift should be not bigger than two for consecutive rows. With - // a higher number of skipped rows drift could be larger. To keep it simple for now, we allow a slightly - // larger drift and don't check for skipped rows. - if (loc != null && - Math.abs(previousRowLoc[0] - loc[0]) < Detector$3.MAX_PATTERN_DRIFT && - Math.abs(previousRowLoc[1] - loc[1]) < Detector$3.MAX_PATTERN_DRIFT) { - previousRowLoc = loc; - skippedRowCount = 0; - } - else { - if (skippedRowCount > Detector$3.SKIPPED_ROW_COUNT_MAX) { - break; - } - else { - skippedRowCount++; - } - } - } - stopRow -= skippedRowCount + 1; - result[2] = new ResultPoint(previousRowLoc[0], stopRow); - result[3] = new ResultPoint(previousRowLoc[1], stopRow); - } - if (stopRow - startRow < Detector$3.BARCODE_MIN_HEIGHT) { - Arrays.fill(result, null); - } - return result; - } - /** - * @param matrix row of black/white values to search - * @param column x position to start search - * @param row y position to start search - * @param width the number of pixels to search on this row - * @param pattern pattern of counts of number of black and white pixels that are - * being searched for as a pattern - * @param counters array of counters, as long as pattern, to re-use - * @return start/end horizontal offset of guard pattern, as an array of two ints. - */ - static findGuardPattern(matrix, column, row, width, whiteFirst, pattern, counters) { - Arrays.fillWithin(counters, 0, counters.length, 0); - let patternStart = column; - let pixelDrift = 0; - // if there are black pixels left of the current pixel shift to the left, but only for MAX_PIXEL_DRIFT pixels - while (matrix.get(patternStart, row) && patternStart > 0 && pixelDrift++ < Detector$3.MAX_PIXEL_DRIFT) { - patternStart--; - } - let x = patternStart; - let counterPosition = 0; - let patternLength = pattern.length; - for (let isWhite = whiteFirst; x < width; x++) { - let pixel = matrix.get(x, row); - if (pixel !== isWhite) { - counters[counterPosition]++; - } - else { - if (counterPosition === patternLength - 1) { - if (Detector$3.patternMatchVariance(counters, pattern, Detector$3.MAX_INDIVIDUAL_VARIANCE) < Detector$3.MAX_AVG_VARIANCE) { - return new Int32Array([patternStart, x]); - } - patternStart += counters[0] + counters[1]; - System.arraycopy(counters, 2, counters, 0, counterPosition - 1); - counters[counterPosition - 1] = 0; - counters[counterPosition] = 0; - counterPosition--; - } - else { - counterPosition++; - } - counters[counterPosition] = 1; - isWhite = !isWhite; - } - } - if (counterPosition === patternLength - 1 && - Detector$3.patternMatchVariance(counters, pattern, Detector$3.MAX_INDIVIDUAL_VARIANCE) < Detector$3.MAX_AVG_VARIANCE) { - return new Int32Array([patternStart, x - 1]); - } - return null; - } - /** - * Determines how closely a set of observed counts of runs of black/white - * values matches a given target pattern. This is reported as the ratio of - * the total variance from the expected pattern proportions across all - * pattern elements, to the length of the pattern. - * - * @param counters observed counters - * @param pattern expected pattern - * @param maxIndividualVariance The most any counter can differ before we give up - * @return ratio of total variance between counters and pattern compared to total pattern size - */ - static patternMatchVariance(counters, pattern, maxIndividualVariance) { - let numCounters = counters.length; - let total = 0; - let patternLength = 0; - for (let i = 0; i < numCounters; i++) { - total += counters[i]; - patternLength += pattern[i]; - } - if (total < patternLength) { - // If we don't even have one pixel per unit of bar width, assume this - // is too small to reliably match, so fail: - return /*Float.POSITIVE_INFINITY*/ Infinity; - } - // We're going to fake floating-point math in integers. We just need to use more bits. - // Scale up patternLength so that intermediate values below like scaledCounter will have - // more "significant digits". - let unitBarWidth = total / patternLength; - maxIndividualVariance *= unitBarWidth; - let totalVariance = 0.0; - for (let x = 0; x < numCounters; x++) { - let counter = counters[x]; - let scaledPattern = pattern[x] * unitBarWidth; - let variance = counter > scaledPattern ? counter - scaledPattern : scaledPattern - counter; - if (variance > maxIndividualVariance) { - return /*Float.POSITIVE_INFINITY*/ Infinity; - } - totalVariance += variance; - } - return totalVariance / total; - } - } - Detector$3.INDEXES_START_PATTERN = Int32Array.from([0, 4, 1, 5]); - Detector$3.INDEXES_STOP_PATTERN = Int32Array.from([6, 2, 7, 3]); - Detector$3.MAX_AVG_VARIANCE = 0.42; - Detector$3.MAX_INDIVIDUAL_VARIANCE = 0.8; - // B S B S B S B S Bar/Space pattern - // 11111111 0 1 0 1 0 1 000 - Detector$3.START_PATTERN = Int32Array.from([8, 1, 1, 1, 1, 1, 1, 3]); - // 1111111 0 1 000 1 0 1 00 1 - Detector$3.STOP_PATTERN = Int32Array.from([7, 1, 1, 3, 1, 1, 1, 2, 1]); - Detector$3.MAX_PIXEL_DRIFT = 3; - Detector$3.MAX_PATTERN_DRIFT = 5; - // if we set the value too low, then we don't detect the correct height of the bar if the start patterns are damaged. - // if we set the value too high, then we might detect the start pattern from a neighbor barcode. - Detector$3.SKIPPED_ROW_COUNT_MAX = 25; - // A PDF471 barcode should have at least 3 rows, with each row being >= 3 times the module width. Therefore it should be at least - // 9 pixels tall. To be conservative, we use about half the size to ensure we don't miss it. - Detector$3.ROW_STEP = 5; - Detector$3.BARCODE_MIN_HEIGHT = 10; - - /* - * Copyright 2012 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. - */ - /** - * @author Sean Owen - * @see com.google.zxing.common.reedsolomon.GenericGFPoly - */ - /*final*/ class ModulusPoly { - constructor(field, coefficients) { - if (coefficients.length === 0) { - throw new IllegalArgumentException(); - } - this.field = field; - let coefficientsLength = /*int*/ coefficients.length; - if (coefficientsLength > 1 && coefficients[0] === 0) { - // Leading term must be non-zero for anything except the constant polynomial "0" - let firstNonZero = /*int*/ 1; - while (firstNonZero < coefficientsLength && coefficients[firstNonZero] === 0) { - firstNonZero++; - } - if (firstNonZero === coefficientsLength) { - this.coefficients = new Int32Array([0]); - } - else { - this.coefficients = new Int32Array(coefficientsLength - firstNonZero); - System.arraycopy(coefficients, firstNonZero, this.coefficients, 0, this.coefficients.length); - } - } - else { - this.coefficients = coefficients; - } - } - getCoefficients() { - return this.coefficients; - } - /** - * @return degree of this polynomial - */ - getDegree() { - return this.coefficients.length - 1; - } - /** - * @return true iff this polynomial is the monomial "0" - */ - isZero() { - return this.coefficients[0] === 0; - } - /** - * @return coefficient of x^degree term in this polynomial - */ - getCoefficient(degree) { - return this.coefficients[this.coefficients.length - 1 - degree]; - } - /** - * @return evaluation of this polynomial at a given point - */ - evaluateAt(a) { - if (a === 0) { - // Just return the x^0 coefficient - return this.getCoefficient(0); - } - if (a === 1) { - // Just the sum of the coefficients - let sum = /*int*/ 0; - for (let coefficient /*int*/ of this.coefficients) { - sum = this.field.add(sum, coefficient); - } - return sum; - } - let result = /*int*/ this.coefficients[0]; - let size = /*int*/ this.coefficients.length; - for (let i /*int*/ = 1; i < size; i++) { - result = this.field.add(this.field.multiply(a, result), this.coefficients[i]); - } - return result; - } - add(other) { - if (!this.field.equals(other.field)) { - throw new IllegalArgumentException('ModulusPolys do not have same ModulusGF field'); - } - if (this.isZero()) { - return other; - } - if (other.isZero()) { - return this; - } - let smallerCoefficients = this.coefficients; - let largerCoefficients = other.coefficients; - if (smallerCoefficients.length > largerCoefficients.length) { - let temp = smallerCoefficients; - smallerCoefficients = largerCoefficients; - largerCoefficients = temp; - } - let sumDiff = new Int32Array(largerCoefficients.length); - let lengthDiff = /*int*/ largerCoefficients.length - smallerCoefficients.length; - // Copy high-order terms only found in higher-degree polynomial's coefficients - System.arraycopy(largerCoefficients, 0, sumDiff, 0, lengthDiff); - for (let i /*int*/ = lengthDiff; i < largerCoefficients.length; i++) { - sumDiff[i] = this.field.add(smallerCoefficients[i - lengthDiff], largerCoefficients[i]); - } - return new ModulusPoly(this.field, sumDiff); - } - subtract(other) { - if (!this.field.equals(other.field)) { - throw new IllegalArgumentException('ModulusPolys do not have same ModulusGF field'); - } - if (other.isZero()) { - return this; - } - return this.add(other.negative()); - } - multiply(other) { - if (other instanceof ModulusPoly) { - return this.multiplyOther(other); - } - return this.multiplyScalar(other); - } - multiplyOther(other) { - if (!this.field.equals(other.field)) { - throw new IllegalArgumentException('ModulusPolys do not have same ModulusGF field'); - } - if (this.isZero() || other.isZero()) { - // return this.field.getZero(); - return new ModulusPoly(this.field, new Int32Array([0])); - } - let aCoefficients = this.coefficients; - let aLength = /*int*/ aCoefficients.length; - let bCoefficients = other.coefficients; - let bLength = /*int*/ bCoefficients.length; - let product = new Int32Array(aLength + bLength - 1); - for (let i /*int*/ = 0; i < aLength; i++) { - let aCoeff = /*int*/ aCoefficients[i]; - for (let j /*int*/ = 0; j < bLength; j++) { - product[i + j] = this.field.add(product[i + j], this.field.multiply(aCoeff, bCoefficients[j])); - } - } - return new ModulusPoly(this.field, product); - } - negative() { - let size = /*int*/ this.coefficients.length; - let negativeCoefficients = new Int32Array(size); - for (let i /*int*/ = 0; i < size; i++) { - negativeCoefficients[i] = this.field.subtract(0, this.coefficients[i]); - } - return new ModulusPoly(this.field, negativeCoefficients); - } - multiplyScalar(scalar) { - if (scalar === 0) { - return new ModulusPoly(this.field, new Int32Array([0])); - } - if (scalar === 1) { - return this; - } - let size = /*int*/ this.coefficients.length; - let product = new Int32Array(size); - for (let i /*int*/ = 0; i < size; i++) { - product[i] = this.field.multiply(this.coefficients[i], scalar); - } - return new ModulusPoly(this.field, product); - } - multiplyByMonomial(degree, coefficient) { - if (degree < 0) { - throw new IllegalArgumentException(); - } - if (coefficient === 0) { - return new ModulusPoly(this.field, new Int32Array([0])); - } - let size = /*int*/ this.coefficients.length; - let product = new Int32Array(size + degree); - for (let i /*int*/ = 0; i < size; i++) { - product[i] = this.field.multiply(this.coefficients[i], coefficient); - } - return new ModulusPoly(this.field, product); - } - /* - ModulusPoly[] divide(other: ModulusPoly) { - if (!field.equals(other.field)) { - throw new IllegalArgumentException("ModulusPolys do not have same ModulusGF field"); - } - if (other.isZero()) { - throw new IllegalArgumentException("Divide by 0"); - } - - let quotient: ModulusPoly = field.getZero(); - let remainder: ModulusPoly = this; - - let denominatorLeadingTerm: /*int/ number = other.getCoefficient(other.getDegree()); - let inverseDenominatorLeadingTerm: /*int/ number = field.inverse(denominatorLeadingTerm); - - while (remainder.getDegree() >= other.getDegree() && !remainder.isZero()) { - let degreeDifference: /*int/ number = remainder.getDegree() - other.getDegree(); - let scale: /*int/ number = field.multiply(remainder.getCoefficient(remainder.getDegree()), inverseDenominatorLeadingTerm); - let term: ModulusPoly = other.multiplyByMonomial(degreeDifference, scale); - let iterationQuotient: ModulusPoly = field.buildMonomial(degreeDifference, scale); - quotient = quotient.add(iterationQuotient); - remainder = remainder.subtract(term); - } - - return new ModulusPoly[] { quotient, remainder }; - } - */ - // @Override - toString() { - let result = new StringBuilder( /*8 * this.getDegree()*/); // dynamic string size in JS - for (let degree /*int*/ = this.getDegree(); degree >= 0; degree--) { - let coefficient = /*int*/ this.getCoefficient(degree); - if (coefficient !== 0) { - if (coefficient < 0) { - result.append(' - '); - coefficient = -coefficient; - } - else { - if (result.length() > 0) { - result.append(' + '); - } - } - if (degree === 0 || coefficient !== 1) { - result.append(coefficient); - } - if (degree !== 0) { - if (degree === 1) { - result.append('x'); - } - else { - result.append('x^'); - result.append(degree); - } - } - } - } - return result.toString(); - } - } - - class ModulusBase { - add(a, b) { - return (a + b) % this.modulus; - } - subtract(a, b) { - return (this.modulus + a - b) % this.modulus; - } - exp(a) { - return this.expTable[a]; - } - log(a) { - if (a === 0) { - throw new IllegalArgumentException(); - } - return this.logTable[a]; - } - inverse(a) { - if (a === 0) { - throw new ArithmeticException(); - } - return this.expTable[this.modulus - this.logTable[a] - 1]; - } - multiply(a, b) { - if (a === 0 || b === 0) { - return 0; - } - return this.expTable[(this.logTable[a] + this.logTable[b]) % (this.modulus - 1)]; - } - getSize() { - return this.modulus; - } - equals(o) { - return o === this; - } - } - - /* - * Copyright 2012 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. - */ - /** - *A field based on powers of a generator integer, modulo some modulus.
- * - * @author Sean Owen - * @see com.google.zxing.common.reedsolomon.GenericGF - */ - /*public final*/ class ModulusGF extends ModulusBase { - // private /*final*/ modulus: /*int*/ number; - constructor(modulus, generator) { - super(); - this.modulus = modulus; - this.expTable = new Int32Array(modulus); - this.logTable = new Int32Array(modulus); - let x = /*int*/ 1; - for (let i /*int*/ = 0; i < modulus; i++) { - this.expTable[i] = x; - x = (x * generator) % modulus; - } - for (let i /*int*/ = 0; i < modulus - 1; i++) { - this.logTable[this.expTable[i]] = i; - } - // logTable[0] == 0 but this should never be used - this.zero = new ModulusPoly(this, new Int32Array([0])); - this.one = new ModulusPoly(this, new Int32Array([1])); - } - getZero() { - return this.zero; - } - getOne() { - return this.one; - } - buildMonomial(degree, coefficient) { - if (degree < 0) { - throw new IllegalArgumentException(); - } - if (coefficient === 0) { - return this.zero; - } - let coefficients = new Int32Array(degree + 1); - coefficients[0] = coefficient; - return new ModulusPoly(this, coefficients); - } - } - ModulusGF.PDF417_GF = new ModulusGF(PDF417Common.NUMBER_OF_CODEWORDS, 3); - - /* - * Copyright 2012 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. - */ - /** - *PDF417 error correction implementation.
- * - *This example - * is quite useful in understanding the algorithm.
- * - * @author Sean Owen - * @see com.google.zxing.common.reedsolomon.ReedSolomonDecoder - */ - /*public final*/ class ErrorCorrection { - constructor() { - this.field = ModulusGF.PDF417_GF; - } - /** - * @param received received codewords - * @param numECCodewords number of those codewords used for EC - * @param erasures location of erasures - * @return number of errors - * @throws ChecksumException if errors cannot be corrected, maybe because of too many errors - */ - decode(received, numECCodewords, erasures) { - let poly = new ModulusPoly(this.field, received); - let S = new Int32Array(numECCodewords); - let error = false; - for (let i /*int*/ = numECCodewords; i > 0; i--) { - let evaluation = poly.evaluateAt(this.field.exp(i)); - S[numECCodewords - i] = evaluation; - if (evaluation !== 0) { - error = true; - } - } - if (!error) { - return 0; - } - let knownErrors = this.field.getOne(); - if (erasures != null) { - for (const erasure of erasures) { - let b = this.field.exp(received.length - 1 - erasure); - // Add (1 - bx) term: - let term = new ModulusPoly(this.field, new Int32Array([this.field.subtract(0, b), 1])); - knownErrors = knownErrors.multiply(term); - } - } - let syndrome = new ModulusPoly(this.field, S); - // syndrome = syndrome.multiply(knownErrors); - let sigmaOmega = this.runEuclideanAlgorithm(this.field.buildMonomial(numECCodewords, 1), syndrome, numECCodewords); - let sigma = sigmaOmega[0]; - let omega = sigmaOmega[1]; - // sigma = sigma.multiply(knownErrors); - let errorLocations = this.findErrorLocations(sigma); - let errorMagnitudes = this.findErrorMagnitudes(omega, sigma, errorLocations); - for (let i /*int*/ = 0; i < errorLocations.length; i++) { - let position = received.length - 1 - this.field.log(errorLocations[i]); - if (position < 0) { - throw ChecksumException.getChecksumInstance(); - } - received[position] = this.field.subtract(received[position], errorMagnitudes[i]); - } - return errorLocations.length; - } - /** - * - * @param ModulusPoly - * @param a - * @param ModulusPoly - * @param b - * @param int - * @param R - * @throws ChecksumException - */ - runEuclideanAlgorithm(a, b, R) { - // Assume a's degree is >= b's - if (a.getDegree() < b.getDegree()) { - let temp = a; - a = b; - b = temp; - } - let rLast = a; - let r = b; - let tLast = this.field.getZero(); - let t = this.field.getOne(); - // Run Euclidean algorithm until r's degree is less than R/2 - while (r.getDegree() >= Math.round(R / 2)) { - let rLastLast = rLast; - let tLastLast = tLast; - rLast = r; - tLast = t; - // Divide rLastLast by rLast, with quotient in q and remainder in r - if (rLast.isZero()) { - // Oops, Euclidean algorithm already terminated? - throw ChecksumException.getChecksumInstance(); - } - r = rLastLast; - let q = this.field.getZero(); - let denominatorLeadingTerm = rLast.getCoefficient(rLast.getDegree()); - let dltInverse = this.field.inverse(denominatorLeadingTerm); - while (r.getDegree() >= rLast.getDegree() && !r.isZero()) { - let degreeDiff = r.getDegree() - rLast.getDegree(); - let scale = this.field.multiply(r.getCoefficient(r.getDegree()), dltInverse); - q = q.add(this.field.buildMonomial(degreeDiff, scale)); - r = r.subtract(rLast.multiplyByMonomial(degreeDiff, scale)); - } - t = q.multiply(tLast).subtract(tLastLast).negative(); - } - let sigmaTildeAtZero = t.getCoefficient(0); - if (sigmaTildeAtZero === 0) { - throw ChecksumException.getChecksumInstance(); - } - let inverse = this.field.inverse(sigmaTildeAtZero); - let sigma = t.multiply(inverse); - let omega = r.multiply(inverse); - return [sigma, omega]; - } - /** - * - * @param errorLocator - * @throws ChecksumException - */ - findErrorLocations(errorLocator) { - // This is a direct application of Chien's search - let numErrors = errorLocator.getDegree(); - let result = new Int32Array(numErrors); - let e = 0; - for (let i /*int*/ = 1; i < this.field.getSize() && e < numErrors; i++) { - if (errorLocator.evaluateAt(i) === 0) { - result[e] = this.field.inverse(i); - e++; - } - } - if (e !== numErrors) { - throw ChecksumException.getChecksumInstance(); - } - return result; - } - findErrorMagnitudes(errorEvaluator, errorLocator, errorLocations) { - let errorLocatorDegree = errorLocator.getDegree(); - let formalDerivativeCoefficients = new Int32Array(errorLocatorDegree); - for (let i /*int*/ = 1; i <= errorLocatorDegree; i++) { - formalDerivativeCoefficients[errorLocatorDegree - i] = - this.field.multiply(i, errorLocator.getCoefficient(i)); - } - let formalDerivative = new ModulusPoly(this.field, formalDerivativeCoefficients); - // This is directly applying Forney's Formula - let s = errorLocations.length; - let result = new Int32Array(s); - for (let i /*int*/ = 0; i < s; i++) { - let xiInverse = this.field.inverse(errorLocations[i]); - let numerator = this.field.subtract(0, errorEvaluator.evaluateAt(xiInverse)); - let denominator = this.field.inverse(formalDerivative.evaluateAt(xiInverse)); - result[i] = this.field.multiply(numerator, denominator); - } - return result; - } - } - - /* - * 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. - */ - /** - * @author Guenther Grau - */ - /*final*/ class BoundingBox { - constructor(image, topLeft, bottomLeft, topRight, bottomRight) { - if (image instanceof BoundingBox) { - this.constructor_2(image); - } - else { - this.constructor_1(image, topLeft, bottomLeft, topRight, bottomRight); - } - } - /** - * - * @param image - * @param topLeft - * @param bottomLeft - * @param topRight - * @param bottomRight - * - * @throws NotFoundException - */ - constructor_1(image, topLeft, bottomLeft, topRight, bottomRight) { - const leftUnspecified = topLeft == null || bottomLeft == null; - const rightUnspecified = topRight == null || bottomRight == null; - if (leftUnspecified && rightUnspecified) { - throw new NotFoundException(); - } - if (leftUnspecified) { - topLeft = new ResultPoint(0, topRight.getY()); - bottomLeft = new ResultPoint(0, bottomRight.getY()); - } - else if (rightUnspecified) { - topRight = new ResultPoint(image.getWidth() - 1, topLeft.getY()); - bottomRight = new ResultPoint(image.getWidth() - 1, bottomLeft.getY()); - } - this.image = image; - this.topLeft = topLeft; - this.bottomLeft = bottomLeft; - this.topRight = topRight; - this.bottomRight = bottomRight; - this.minX = Math.trunc(Math.min(topLeft.getX(), bottomLeft.getX())); - this.maxX = Math.trunc(Math.max(topRight.getX(), bottomRight.getX())); - this.minY = Math.trunc(Math.min(topLeft.getY(), topRight.getY())); - this.maxY = Math.trunc(Math.max(bottomLeft.getY(), bottomRight.getY())); - } - constructor_2(boundingBox) { - this.image = boundingBox.image; - this.topLeft = boundingBox.getTopLeft(); - this.bottomLeft = boundingBox.getBottomLeft(); - this.topRight = boundingBox.getTopRight(); - this.bottomRight = boundingBox.getBottomRight(); - this.minX = boundingBox.getMinX(); - this.maxX = boundingBox.getMaxX(); - this.minY = boundingBox.getMinY(); - this.maxY = boundingBox.getMaxY(); - } - /** - * @throws NotFoundException - */ - static merge(leftBox, rightBox) { - if (leftBox == null) { - return rightBox; - } - if (rightBox == null) { - return leftBox; - } - return new BoundingBox(leftBox.image, leftBox.topLeft, leftBox.bottomLeft, rightBox.topRight, rightBox.bottomRight); - } - /** - * @throws NotFoundException - */ - addMissingRows(missingStartRows, missingEndRows, isLeft) { - let newTopLeft = this.topLeft; - let newBottomLeft = this.bottomLeft; - let newTopRight = this.topRight; - let newBottomRight = this.bottomRight; - if (missingStartRows > 0) { - let top = isLeft ? this.topLeft : this.topRight; - let newMinY = Math.trunc(top.getY() - missingStartRows); - if (newMinY < 0) { - newMinY = 0; - } - let newTop = new ResultPoint(top.getX(), newMinY); - if (isLeft) { - newTopLeft = newTop; - } - else { - newTopRight = newTop; - } - } - if (missingEndRows > 0) { - let bottom = isLeft ? this.bottomLeft : this.bottomRight; - let newMaxY = Math.trunc(bottom.getY() + missingEndRows); - if (newMaxY >= this.image.getHeight()) { - newMaxY = this.image.getHeight() - 1; - } - let newBottom = new ResultPoint(bottom.getX(), newMaxY); - if (isLeft) { - newBottomLeft = newBottom; - } - else { - newBottomRight = newBottom; - } - } - return new BoundingBox(this.image, newTopLeft, newBottomLeft, newTopRight, newBottomRight); - } - getMinX() { - return this.minX; - } - getMaxX() { - return this.maxX; - } - getMinY() { - return this.minY; - } - getMaxY() { - return this.maxY; - } - getTopLeft() { - return this.topLeft; - } - getTopRight() { - return this.topRight; - } - getBottomLeft() { - return this.bottomLeft; - } - getBottomRight() { - return this.bottomRight; - } - } - - /* - * 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. - */ - // package com.google.zxing.pdf417.decoder; - /** - * @author Guenther Grau - */ - /*final*/ class BarcodeMetadata { - constructor(columnCount, rowCountUpperPart, rowCountLowerPart, errorCorrectionLevel) { - this.columnCount = columnCount; - this.errorCorrectionLevel = errorCorrectionLevel; - this.rowCountUpperPart = rowCountUpperPart; - this.rowCountLowerPart = rowCountLowerPart; - this.rowCount = rowCountUpperPart + rowCountLowerPart; - } - getColumnCount() { - return this.columnCount; - } - getErrorCorrectionLevel() { - return this.errorCorrectionLevel; - } - getRowCount() { - return this.rowCount; - } - getRowCountUpperPart() { - return this.rowCountUpperPart; - } - getRowCountLowerPart() { - return this.rowCountLowerPart; - } - } - - /** - * Java Formatter class polyfill that works in the JS way. - */ - class Formatter { - constructor() { - this.buffer = ''; - } - /** - * - * @see https://stackoverflow.com/a/13439711/4367683 - * - * @param str - * @param arr - */ - static form(str, arr) { - let i = -1; - function callback(exp, p0, p1, p2, p3, p4) { - if (exp === '%%') - return '%'; - if (arr[++i] === undefined) - return undefined; - exp = p2 ? parseInt(p2.substr(1)) : undefined; - let base = p3 ? parseInt(p3.substr(1)) : undefined; - let val; - switch (p4) { - case 's': - val = arr[i]; - break; - case 'c': - val = arr[i][0]; - break; - case 'f': - val = parseFloat(arr[i]).toFixed(exp); - break; - case 'p': - val = parseFloat(arr[i]).toPrecision(exp); - break; - case 'e': - val = parseFloat(arr[i]).toExponential(exp); - break; - case 'x': - val = parseInt(arr[i]).toString(base ? base : 16); - break; - case 'd': - val = parseFloat(parseInt(arr[i], base ? base : 10).toPrecision(exp)).toFixed(0); - break; - } - val = typeof val === 'object' ? JSON.stringify(val) : (+val).toString(base); - let size = parseInt(p1); /* padding size */ - let ch = p1 && (p1[0] + '') === '0' ? '0' : ' '; /* isnull? */ - while (val.length < size) - val = p0 !== undefined ? val + ch : ch + val; /* isminus? */ - return val; - } - let regex = /%(-)?(0?[0-9]+)?([.][0-9]+)?([#][0-9]+)?([scfpexd%])/g; - return str.replace(regex, callback); - } - /** - * - * @param append The new string to append. - * @param args Argumets values to be formated. - */ - format(append, ...args) { - this.buffer += Formatter.form(append, args); - } - /** - * Returns the Formatter string value. - */ - toString() { - return this.buffer; - } - } - - /* - * 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. - */ - /** - * @author Guenther Grau - */ - class DetectionResultColumn { - constructor(boundingBox) { - this.boundingBox = new BoundingBox(boundingBox); - // this.codewords = new Codeword[boundingBox.getMaxY() - boundingBox.getMinY() + 1]; - this.codewords = new Array(boundingBox.getMaxY() - boundingBox.getMinY() + 1); - } - /*final*/ getCodewordNearby(imageRow) { - let codeword = this.getCodeword(imageRow); - if (codeword != null) { - return codeword; - } - for (let i = 1; i < DetectionResultColumn.MAX_NEARBY_DISTANCE; i++) { - let nearImageRow = this.imageRowToCodewordIndex(imageRow) - i; - if (nearImageRow >= 0) { - codeword = this.codewords[nearImageRow]; - if (codeword != null) { - return codeword; - } - } - nearImageRow = this.imageRowToCodewordIndex(imageRow) + i; - if (nearImageRow < this.codewords.length) { - codeword = this.codewords[nearImageRow]; - if (codeword != null) { - return codeword; - } - } - } - return null; - } - /*final int*/ imageRowToCodewordIndex(imageRow) { - return imageRow - this.boundingBox.getMinY(); - } - /*final void*/ setCodeword(imageRow, codeword) { - this.codewords[this.imageRowToCodewordIndex(imageRow)] = codeword; - } - /*final*/ getCodeword(imageRow) { - return this.codewords[this.imageRowToCodewordIndex(imageRow)]; - } - /*final*/ getBoundingBox() { - return this.boundingBox; - } - /*final*/ getCodewords() { - return this.codewords; - } - // @Override - toString() { - const formatter = new Formatter(); - let row = 0; - for (const codeword of this.codewords) { - if (codeword == null) { - formatter.format('%3d: | %n', row++); - continue; - } - formatter.format('%3d: %3d|%3d%n', row++, codeword.getRowNumber(), codeword.getValue()); - } - return formatter.toString(); - } - } - DetectionResultColumn.MAX_NEARBY_DISTANCE = 5; - - /* - * 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. - */ - // import java.util.ArrayList; - // import java.util.Collection; - // import java.util.HashMap; - // import java.util.Map; - // import java.util.Map.Entry; - /** - * @author Guenther Grau - */ - /*final*/ class BarcodeValue { - constructor() { - this.values = new Map(); - } - /** - * Add an occurrence of a value - */ - setValue(value) { - value = Math.trunc(value); - let confidence = this.values.get(value); - if (confidence == null) { - confidence = 0; - } - confidence++; - this.values.set(value, confidence); - } - /** - * Determines the maximum occurrence of a set value and returns all values which were set with this occurrence. - * @return an array of int, containing the values with the highest occurrence, or null, if no value was set - */ - getValue() { - let maxConfidence = -1; - let result = new Array(); - for (const [key, value] of this.values.entries()) { - const entry = { - getKey: () => key, - getValue: () => value, - }; - if (entry.getValue() > maxConfidence) { - maxConfidence = entry.getValue(); - result = []; - result.push(entry.getKey()); - } - else if (entry.getValue() === maxConfidence) { - result.push(entry.getKey()); - } - } - return PDF417Common.toIntArray(result); - } - getConfidence(value) { - return this.values.get(value); - } - } - - /* - * 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. - */ - /** - * @author Guenther Grau - */ - /*final*/ class DetectionResultRowIndicatorColumn extends DetectionResultColumn { - constructor(boundingBox, isLeft) { - super(boundingBox); - this._isLeft = isLeft; - } - setRowNumbers() { - for (let codeword /*Codeword*/ of this.getCodewords()) { - if (codeword != null) { - codeword.setRowNumberAsRowIndicatorColumn(); - } - } - } - // TODO implement properly - // TODO maybe we should add missing codewords to store the correct row number to make - // finding row numbers for other columns easier - // use row height count to make detection of invalid row numbers more reliable - adjustCompleteIndicatorColumnRowNumbers(barcodeMetadata) { - let codewords = this.getCodewords(); - this.setRowNumbers(); - this.removeIncorrectCodewords(codewords, barcodeMetadata); - let boundingBox = this.getBoundingBox(); - let top = this._isLeft ? boundingBox.getTopLeft() : boundingBox.getTopRight(); - let bottom = this._isLeft ? boundingBox.getBottomLeft() : boundingBox.getBottomRight(); - let firstRow = this.imageRowToCodewordIndex(Math.trunc(top.getY())); - let lastRow = this.imageRowToCodewordIndex(Math.trunc(bottom.getY())); - // We need to be careful using the average row height. Barcode could be skewed so that we have smaller and - // taller rows - // float averageRowHeight = (lastRow - firstRow) / /*(float)*/ barcodeMetadata.getRowCount(); - let barcodeRow = -1; - let maxRowHeight = 1; - let currentRowHeight = 0; - for (let codewordsRow /*int*/ = firstRow; codewordsRow < lastRow; codewordsRow++) { - if (codewords[codewordsRow] == null) { - continue; - } - let codeword = codewords[codewordsRow]; - // float expectedRowNumber = (codewordsRow - firstRow) / averageRowHeight; - // if (Math.abs(codeword.getRowNumber() - expectedRowNumber) > 2) { - // SimpleLog.log(LEVEL.WARNING, - // "Removing codeword, rowNumberSkew too high, codeword[" + codewordsRow + "]: Expected Row: " + - // expectedRowNumber + ", RealRow: " + codeword.getRowNumber() + ", value: " + codeword.getValue()); - // codewords[codewordsRow] = null; - // } - let rowDifference = codeword.getRowNumber() - barcodeRow; - // TODO improve handling with case where first row indicator doesn't start with 0 - if (rowDifference === 0) { - currentRowHeight++; - } - else if (rowDifference === 1) { - maxRowHeight = Math.max(maxRowHeight, currentRowHeight); - currentRowHeight = 1; - barcodeRow = codeword.getRowNumber(); - } - else if (rowDifference < 0 || - codeword.getRowNumber() >= barcodeMetadata.getRowCount() || - rowDifference > codewordsRow) { - codewords[codewordsRow] = null; - } - else { - let checkedRows; - if (maxRowHeight > 2) { - checkedRows = (maxRowHeight - 2) * rowDifference; - } - else { - checkedRows = rowDifference; - } - let closePreviousCodewordFound = checkedRows >= codewordsRow; - for (let i /*int*/ = 1; i <= checkedRows && !closePreviousCodewordFound; i++) { - // there must be (height * rowDifference) number of codewords missing. For now we assume height = 1. - // This should hopefully get rid of most problems already. - closePreviousCodewordFound = codewords[codewordsRow - i] != null; - } - if (closePreviousCodewordFound) { - codewords[codewordsRow] = null; - } - else { - barcodeRow = codeword.getRowNumber(); - currentRowHeight = 1; - } - } - } - // return (int) (averageRowHeight + 0.5); - } - getRowHeights() { - let barcodeMetadata = this.getBarcodeMetadata(); - if (barcodeMetadata == null) { - return null; - } - this.adjustIncompleteIndicatorColumnRowNumbers(barcodeMetadata); - let result = new Int32Array(barcodeMetadata.getRowCount()); - for (let codeword /*Codeword*/ of this.getCodewords()) { - if (codeword != null) { - let rowNumber = codeword.getRowNumber(); - if (rowNumber >= result.length) { - // We have more rows than the barcode metadata allows for, ignore them. - continue; - } - result[rowNumber]++; - } // else throw exception? - } - return result; - } - // TODO maybe we should add missing codewords to store the correct row number to make - // finding row numbers for other columns easier - // use row height count to make detection of invalid row numbers more reliable - adjustIncompleteIndicatorColumnRowNumbers(barcodeMetadata) { - let boundingBox = this.getBoundingBox(); - let top = this._isLeft ? boundingBox.getTopLeft() : boundingBox.getTopRight(); - let bottom = this._isLeft ? boundingBox.getBottomLeft() : boundingBox.getBottomRight(); - let firstRow = this.imageRowToCodewordIndex(Math.trunc(top.getY())); - let lastRow = this.imageRowToCodewordIndex(Math.trunc(bottom.getY())); - // float averageRowHeight = (lastRow - firstRow) / /*(float)*/ barcodeMetadata.getRowCount(); - let codewords = this.getCodewords(); - let barcodeRow = -1; - for (let codewordsRow /*int*/ = firstRow; codewordsRow < lastRow; codewordsRow++) { - if (codewords[codewordsRow] == null) { - continue; - } - let codeword = codewords[codewordsRow]; - codeword.setRowNumberAsRowIndicatorColumn(); - let rowDifference = codeword.getRowNumber() - barcodeRow; - // TODO improve handling with case where first row indicator doesn't start with 0 - if (rowDifference === 0) ; - else if (rowDifference === 1) { - barcodeRow = codeword.getRowNumber(); - } - else if (codeword.getRowNumber() >= barcodeMetadata.getRowCount()) { - codewords[codewordsRow] = null; - } - else { - barcodeRow = codeword.getRowNumber(); - } - } - // return (int) (averageRowHeight + 0.5); - } - getBarcodeMetadata() { - let codewords = this.getCodewords(); - let barcodeColumnCount = new BarcodeValue(); - let barcodeRowCountUpperPart = new BarcodeValue(); - let barcodeRowCountLowerPart = new BarcodeValue(); - let barcodeECLevel = new BarcodeValue(); - for (let codeword /*Codeword*/ of codewords) { - if (codeword == null) { - continue; - } - codeword.setRowNumberAsRowIndicatorColumn(); - let rowIndicatorValue = codeword.getValue() % 30; - let codewordRowNumber = codeword.getRowNumber(); - if (!this._isLeft) { - codewordRowNumber += 2; - } - switch (codewordRowNumber % 3) { - case 0: - barcodeRowCountUpperPart.setValue(rowIndicatorValue * 3 + 1); - break; - case 1: - barcodeECLevel.setValue(rowIndicatorValue / 3); - barcodeRowCountLowerPart.setValue(rowIndicatorValue % 3); - break; - case 2: - barcodeColumnCount.setValue(rowIndicatorValue + 1); - break; - } - } - // Maybe we should check if we have ambiguous values? - if ((barcodeColumnCount.getValue().length === 0) || - (barcodeRowCountUpperPart.getValue().length === 0) || - (barcodeRowCountLowerPart.getValue().length === 0) || - (barcodeECLevel.getValue().length === 0) || - barcodeColumnCount.getValue()[0] < 1 || - barcodeRowCountUpperPart.getValue()[0] + barcodeRowCountLowerPart.getValue()[0] < PDF417Common.MIN_ROWS_IN_BARCODE || - barcodeRowCountUpperPart.getValue()[0] + barcodeRowCountLowerPart.getValue()[0] > PDF417Common.MAX_ROWS_IN_BARCODE) { - return null; - } - let barcodeMetadata = new BarcodeMetadata(barcodeColumnCount.getValue()[0], barcodeRowCountUpperPart.getValue()[0], barcodeRowCountLowerPart.getValue()[0], barcodeECLevel.getValue()[0]); - this.removeIncorrectCodewords(codewords, barcodeMetadata); - return barcodeMetadata; - } - removeIncorrectCodewords(codewords, barcodeMetadata) { - // Remove codewords which do not match the metadata - // TODO Maybe we should keep the incorrect codewords for the start and end positions? - for (let codewordRow /*int*/ = 0; codewordRow < codewords.length; codewordRow++) { - let codeword = codewords[codewordRow]; - if (codewords[codewordRow] == null) { - continue; - } - let rowIndicatorValue = codeword.getValue() % 30; - let codewordRowNumber = codeword.getRowNumber(); - if (codewordRowNumber > barcodeMetadata.getRowCount()) { - codewords[codewordRow] = null; - continue; - } - if (!this._isLeft) { - codewordRowNumber += 2; - } - switch (codewordRowNumber % 3) { - case 0: - if (rowIndicatorValue * 3 + 1 !== barcodeMetadata.getRowCountUpperPart()) { - codewords[codewordRow] = null; - } - break; - case 1: - if (Math.trunc(rowIndicatorValue / 3) !== barcodeMetadata.getErrorCorrectionLevel() || - rowIndicatorValue % 3 !== barcodeMetadata.getRowCountLowerPart()) { - codewords[codewordRow] = null; - } - break; - case 2: - if (rowIndicatorValue + 1 !== barcodeMetadata.getColumnCount()) { - codewords[codewordRow] = null; - } - break; - } - } - } - isLeft() { - return this._isLeft; - } - // @Override - toString() { - return 'IsLeft: ' + this._isLeft + '\n' + super.toString(); - } - } - - /* - * 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. - */ - /** - * @author Guenther Grau - */ - /*final*/ class DetectionResult { - constructor(barcodeMetadata, boundingBox) { - /*final*/ this.ADJUST_ROW_NUMBER_SKIP = 2; - this.barcodeMetadata = barcodeMetadata; - this.barcodeColumnCount = barcodeMetadata.getColumnCount(); - this.boundingBox = boundingBox; - // this.detectionResultColumns = new DetectionResultColumn[this.barcodeColumnCount + 2]; - this.detectionResultColumns = new Array(this.barcodeColumnCount + 2); - } - getDetectionResultColumns() { - this.adjustIndicatorColumnRowNumbers(this.detectionResultColumns[0]); - this.adjustIndicatorColumnRowNumbers(this.detectionResultColumns[this.barcodeColumnCount + 1]); - let unadjustedCodewordCount = PDF417Common.MAX_CODEWORDS_IN_BARCODE; - let previousUnadjustedCount; - do { - previousUnadjustedCount = unadjustedCodewordCount; - unadjustedCodewordCount = this.adjustRowNumbersAndGetCount(); - } while (unadjustedCodewordCount > 0 && unadjustedCodewordCount < previousUnadjustedCount); - return this.detectionResultColumns; - } - adjustIndicatorColumnRowNumbers(detectionResultColumn) { - if (detectionResultColumn != null) { - detectionResultColumn - .adjustCompleteIndicatorColumnRowNumbers(this.barcodeMetadata); - } - } - // TODO ensure that no detected codewords with unknown row number are left - // we should be able to estimate the row height and use it as a hint for the row number - // we should also fill the rows top to bottom and bottom to top - /** - * @return number of codewords which don't have a valid row number. Note that the count is not accurate as codewords - * will be counted several times. It just serves as an indicator to see when we can stop adjusting row numbers - */ - adjustRowNumbersAndGetCount() { - let unadjustedCount = this.adjustRowNumbersByRow(); - if (unadjustedCount === 0) { - return 0; - } - for (let barcodeColumn /*int*/ = 1; barcodeColumn < this.barcodeColumnCount + 1; barcodeColumn++) { - let codewords = this.detectionResultColumns[barcodeColumn].getCodewords(); - for (let codewordsRow /*int*/ = 0; codewordsRow < codewords.length; codewordsRow++) { - if (codewords[codewordsRow] == null) { - continue; - } - if (!codewords[codewordsRow].hasValidRowNumber()) { - this.adjustRowNumbers(barcodeColumn, codewordsRow, codewords); - } - } - } - return unadjustedCount; - } - adjustRowNumbersByRow() { - this.adjustRowNumbersFromBothRI(); - // TODO we should only do full row adjustments if row numbers of left and right row indicator column match. - // Maybe it's even better to calculated the height (rows: d) and divide it by the number of barcode - // rows. This, together with the LRI and RRI row numbers should allow us to get a good estimate where a row - // number starts and ends. - let unadjustedCount = this.adjustRowNumbersFromLRI(); - return unadjustedCount + this.adjustRowNumbersFromRRI(); - } - adjustRowNumbersFromBothRI() { - if (this.detectionResultColumns[0] == null || this.detectionResultColumns[this.barcodeColumnCount + 1] == null) { - return; - } - let LRIcodewords = this.detectionResultColumns[0].getCodewords(); - let RRIcodewords = this.detectionResultColumns[this.barcodeColumnCount + 1].getCodewords(); - for (let codewordsRow /*int*/ = 0; codewordsRow < LRIcodewords.length; codewordsRow++) { - if (LRIcodewords[codewordsRow] != null && - RRIcodewords[codewordsRow] != null && - LRIcodewords[codewordsRow].getRowNumber() === RRIcodewords[codewordsRow].getRowNumber()) { - for (let barcodeColumn /*int*/ = 1; barcodeColumn <= this.barcodeColumnCount; barcodeColumn++) { - let codeword = this.detectionResultColumns[barcodeColumn].getCodewords()[codewordsRow]; - if (codeword == null) { - continue; - } - codeword.setRowNumber(LRIcodewords[codewordsRow].getRowNumber()); - if (!codeword.hasValidRowNumber()) { - this.detectionResultColumns[barcodeColumn].getCodewords()[codewordsRow] = null; - } - } - } - } - } - adjustRowNumbersFromRRI() { - if (this.detectionResultColumns[this.barcodeColumnCount + 1] == null) { - return 0; - } - let unadjustedCount = 0; - let codewords = this.detectionResultColumns[this.barcodeColumnCount + 1].getCodewords(); - for (let codewordsRow /*int*/ = 0; codewordsRow < codewords.length; codewordsRow++) { - if (codewords[codewordsRow] == null) { - continue; - } - let rowIndicatorRowNumber = codewords[codewordsRow].getRowNumber(); - let invalidRowCounts = 0; - for (let barcodeColumn /*int*/ = this.barcodeColumnCount + 1; barcodeColumn > 0 && invalidRowCounts < this.ADJUST_ROW_NUMBER_SKIP; barcodeColumn--) { - let codeword = this.detectionResultColumns[barcodeColumn].getCodewords()[codewordsRow]; - if (codeword != null) { - invalidRowCounts = DetectionResult.adjustRowNumberIfValid(rowIndicatorRowNumber, invalidRowCounts, codeword); - if (!codeword.hasValidRowNumber()) { - unadjustedCount++; - } - } - } - } - return unadjustedCount; - } - adjustRowNumbersFromLRI() { - if (this.detectionResultColumns[0] == null) { - return 0; - } - let unadjustedCount = 0; - let codewords = this.detectionResultColumns[0].getCodewords(); - for (let codewordsRow /*int*/ = 0; codewordsRow < codewords.length; codewordsRow++) { - if (codewords[codewordsRow] == null) { - continue; - } - let rowIndicatorRowNumber = codewords[codewordsRow].getRowNumber(); - let invalidRowCounts = 0; - for (let barcodeColumn /*int*/ = 1; barcodeColumn < this.barcodeColumnCount + 1 && invalidRowCounts < this.ADJUST_ROW_NUMBER_SKIP; barcodeColumn++) { - let codeword = this.detectionResultColumns[barcodeColumn].getCodewords()[codewordsRow]; - if (codeword != null) { - invalidRowCounts = DetectionResult.adjustRowNumberIfValid(rowIndicatorRowNumber, invalidRowCounts, codeword); - if (!codeword.hasValidRowNumber()) { - unadjustedCount++; - } - } - } - } - return unadjustedCount; - } - static adjustRowNumberIfValid(rowIndicatorRowNumber, invalidRowCounts, codeword) { - if (codeword == null) { - return invalidRowCounts; - } - if (!codeword.hasValidRowNumber()) { - if (codeword.isValidRowNumber(rowIndicatorRowNumber)) { - codeword.setRowNumber(rowIndicatorRowNumber); - invalidRowCounts = 0; - } - else { - ++invalidRowCounts; - } - } - return invalidRowCounts; - } - adjustRowNumbers(barcodeColumn, codewordsRow, codewords) { - let codeword = codewords[codewordsRow]; - let previousColumnCodewords = this.detectionResultColumns[barcodeColumn - 1].getCodewords(); - let nextColumnCodewords = previousColumnCodewords; - if (this.detectionResultColumns[barcodeColumn + 1] != null) { - nextColumnCodewords = this.detectionResultColumns[barcodeColumn + 1].getCodewords(); - } - // let otherCodewords: Codeword[] = new Codeword[14]; - let otherCodewords = new Array(14); - otherCodewords[2] = previousColumnCodewords[codewordsRow]; - otherCodewords[3] = nextColumnCodewords[codewordsRow]; - if (codewordsRow > 0) { - otherCodewords[0] = codewords[codewordsRow - 1]; - otherCodewords[4] = previousColumnCodewords[codewordsRow - 1]; - otherCodewords[5] = nextColumnCodewords[codewordsRow - 1]; - } - if (codewordsRow > 1) { - otherCodewords[8] = codewords[codewordsRow - 2]; - otherCodewords[10] = previousColumnCodewords[codewordsRow - 2]; - otherCodewords[11] = nextColumnCodewords[codewordsRow - 2]; - } - if (codewordsRow < codewords.length - 1) { - otherCodewords[1] = codewords[codewordsRow + 1]; - otherCodewords[6] = previousColumnCodewords[codewordsRow + 1]; - otherCodewords[7] = nextColumnCodewords[codewordsRow + 1]; - } - if (codewordsRow < codewords.length - 2) { - otherCodewords[9] = codewords[codewordsRow + 2]; - otherCodewords[12] = previousColumnCodewords[codewordsRow + 2]; - otherCodewords[13] = nextColumnCodewords[codewordsRow + 2]; - } - for (let otherCodeword of otherCodewords) { - if (DetectionResult.adjustRowNumber(codeword, otherCodeword)) { - return; - } - } - } - /** - * @return true, if row number was adjusted, false otherwise - */ - static adjustRowNumber(codeword, otherCodeword) { - if (otherCodeword == null) { - return false; - } - if (otherCodeword.hasValidRowNumber() && otherCodeword.getBucket() === codeword.getBucket()) { - codeword.setRowNumber(otherCodeword.getRowNumber()); - return true; - } - return false; - } - getBarcodeColumnCount() { - return this.barcodeColumnCount; - } - getBarcodeRowCount() { - return this.barcodeMetadata.getRowCount(); - } - getBarcodeECLevel() { - return this.barcodeMetadata.getErrorCorrectionLevel(); - } - setBoundingBox(boundingBox) { - this.boundingBox = boundingBox; - } - getBoundingBox() { - return this.boundingBox; - } - setDetectionResultColumn(barcodeColumn, detectionResultColumn) { - this.detectionResultColumns[barcodeColumn] = detectionResultColumn; - } - getDetectionResultColumn(barcodeColumn) { - return this.detectionResultColumns[barcodeColumn]; - } - // @Override - toString() { - let rowIndicatorColumn = this.detectionResultColumns[0]; - if (rowIndicatorColumn == null) { - rowIndicatorColumn = this.detectionResultColumns[this.barcodeColumnCount + 1]; - } - // try ( - let formatter = new Formatter(); - // ) { - for (let codewordsRow /*int*/ = 0; codewordsRow < rowIndicatorColumn.getCodewords().length; codewordsRow++) { - formatter.format('CW %3d:', codewordsRow); - for (let barcodeColumn /*int*/ = 0; barcodeColumn < this.barcodeColumnCount + 2; barcodeColumn++) { - if (this.detectionResultColumns[barcodeColumn] == null) { - formatter.format(' | '); - continue; - } - let codeword = this.detectionResultColumns[barcodeColumn].getCodewords()[codewordsRow]; - if (codeword == null) { - formatter.format(' | '); - continue; - } - formatter.format(' %3d|%3d', codeword.getRowNumber(), codeword.getValue()); - } - formatter.format('%n'); - } - return formatter.toString(); - // } - } - } - - /* - * 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. - */ - // package com.google.zxing.pdf417.decoder; - /** - * @author Guenther Grau - */ - /*final*/ class Codeword { - constructor(startX, endX, bucket, value) { - this.rowNumber = Codeword.BARCODE_ROW_UNKNOWN; - this.startX = Math.trunc(startX); - this.endX = Math.trunc(endX); - this.bucket = Math.trunc(bucket); - this.value = Math.trunc(value); - } - hasValidRowNumber() { - return this.isValidRowNumber(this.rowNumber); - } - isValidRowNumber(rowNumber) { - return rowNumber !== Codeword.BARCODE_ROW_UNKNOWN && this.bucket === (rowNumber % 3) * 3; - } - setRowNumberAsRowIndicatorColumn() { - this.rowNumber = Math.trunc((Math.trunc(this.value / 30)) * 3 + Math.trunc(this.bucket / 3)); - } - getWidth() { - return this.endX - this.startX; - } - getStartX() { - return this.startX; - } - getEndX() { - return this.endX; - } - getBucket() { - return this.bucket; - } - getValue() { - return this.value; - } - getRowNumber() { - return this.rowNumber; - } - setRowNumber(rowNumber) { - this.rowNumber = rowNumber; - } - // @Override - toString() { - return this.rowNumber + '|' + this.value; - } - } - Codeword.BARCODE_ROW_UNKNOWN = -1; - - /* - * 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. - */ - /** - * @author Guenther Grau - * @author creatale GmbH (christoph.schulz@creatale.de) - */ - /*final*/ class PDF417CodewordDecoder { - /* @note - * this action have to be performed before first use of class - * - static constructor - * working with 32bit float (based from Java logic) - */ - static initialize() { - // Pre-computes the symbol ratio table. - for ( /*int*/let i = 0; i < PDF417Common.SYMBOL_TABLE.length; i++) { - let currentSymbol = PDF417Common.SYMBOL_TABLE[i]; - let currentBit = currentSymbol & 0x1; - for ( /*int*/let j = 0; j < PDF417Common.BARS_IN_MODULE; j++) { - let size = 0.0; - while ((currentSymbol & 0x1) === currentBit) { - size += 1.0; - currentSymbol >>= 1; - } - currentBit = currentSymbol & 0x1; - if (!PDF417CodewordDecoder.RATIOS_TABLE[i]) { - PDF417CodewordDecoder.RATIOS_TABLE[i] = new Array(PDF417Common.BARS_IN_MODULE); - } - PDF417CodewordDecoder.RATIOS_TABLE[i][PDF417Common.BARS_IN_MODULE - j - 1] = Math.fround(size / PDF417Common.MODULES_IN_CODEWORD); - } - } - this.bSymbolTableReady = true; - } - static getDecodedValue(moduleBitCount) { - let decodedValue = PDF417CodewordDecoder.getDecodedCodewordValue(PDF417CodewordDecoder.sampleBitCounts(moduleBitCount)); - if (decodedValue !== -1) { - return decodedValue; - } - return PDF417CodewordDecoder.getClosestDecodedValue(moduleBitCount); - } - static sampleBitCounts(moduleBitCount) { - let bitCountSum = MathUtils.sum(moduleBitCount); - let result = new Int32Array(PDF417Common.BARS_IN_MODULE); - let bitCountIndex = 0; - let sumPreviousBits = 0; - for ( /*int*/let i = 0; i < PDF417Common.MODULES_IN_CODEWORD; i++) { - let sampleIndex = bitCountSum / (2 * PDF417Common.MODULES_IN_CODEWORD) + - (i * bitCountSum) / PDF417Common.MODULES_IN_CODEWORD; - if (sumPreviousBits + moduleBitCount[bitCountIndex] <= sampleIndex) { - sumPreviousBits += moduleBitCount[bitCountIndex]; - bitCountIndex++; - } - result[bitCountIndex]++; - } - return result; - } - static getDecodedCodewordValue(moduleBitCount) { - let decodedValue = PDF417CodewordDecoder.getBitValue(moduleBitCount); - return PDF417Common.getCodeword(decodedValue) === -1 ? -1 : decodedValue; - } - static getBitValue(moduleBitCount) { - let result = /*long*/ 0; - for (let /*int*/ i = 0; i < moduleBitCount.length; i++) { - for ( /*int*/let bit = 0; bit < moduleBitCount[i]; bit++) { - result = (result << 1) | (i % 2 === 0 ? 1 : 0); - } - } - return Math.trunc(result); - } - // working with 32bit float (as in Java) - static getClosestDecodedValue(moduleBitCount) { - let bitCountSum = MathUtils.sum(moduleBitCount); - let bitCountRatios = new Array(PDF417Common.BARS_IN_MODULE); - if (bitCountSum > 1) { - for (let /*int*/ i = 0; i < bitCountRatios.length; i++) { - bitCountRatios[i] = Math.fround(moduleBitCount[i] / bitCountSum); - } - } - let bestMatchError = Float.MAX_VALUE; - let bestMatch = -1; - if (!this.bSymbolTableReady) { - PDF417CodewordDecoder.initialize(); - } - for ( /*int*/let j = 0; j < PDF417CodewordDecoder.RATIOS_TABLE.length; j++) { - let error = 0.0; - let ratioTableRow = PDF417CodewordDecoder.RATIOS_TABLE[j]; - for ( /*int*/let k = 0; k < PDF417Common.BARS_IN_MODULE; k++) { - let diff = Math.fround(ratioTableRow[k] - bitCountRatios[k]); - error += Math.fround(diff * diff); - if (error >= bestMatchError) { - break; - } - } - if (error < bestMatchError) { - bestMatchError = error; - bestMatch = PDF417Common.SYMBOL_TABLE[j]; - } - } - return bestMatch; - } - } - // flag that the table is ready for use - PDF417CodewordDecoder.bSymbolTableReady = false; - PDF417CodewordDecoder.RATIOS_TABLE = new Array(PDF417Common.SYMBOL_TABLE.length).map(x => x = new Array(PDF417Common.BARS_IN_MODULE)); - - /* - * 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. - */ - // package com.google.zxing.pdf417; - /** - * @author Guenther Grau - */ - /*public final*/ class PDF417ResultMetadata { - constructor() { - this.segmentCount = -1; - this.fileSize = -1; - this.timestamp = -1; - this.checksum = -1; - } - /** - * The Segment ID represents the segment of the whole file distributed over different symbols. - * - * @return File segment index - */ - getSegmentIndex() { - return this.segmentIndex; - } - setSegmentIndex(segmentIndex) { - this.segmentIndex = segmentIndex; - } - /** - * Is the same for each related PDF417 symbol - * - * @return File ID - */ - getFileId() { - return this.fileId; - } - setFileId(fileId) { - this.fileId = fileId; - } - /** - * @return always null - * @deprecated use dedicated already parsed fields - */ - // @Deprecated - getOptionalData() { - return this.optionalData; - } - /** - * @param optionalData old optional data format as int array - * @deprecated parse and use new fields - */ - // @Deprecated - setOptionalData(optionalData) { - this.optionalData = optionalData; - } - /** - * @return true if it is the last segment - */ - isLastSegment() { - return this.lastSegment; - } - setLastSegment(lastSegment) { - this.lastSegment = lastSegment; - } - /** - * @return count of segments, -1 if not set - */ - getSegmentCount() { - return this.segmentCount; - } - setSegmentCount(segmentCount /*int*/) { - this.segmentCount = segmentCount; - } - getSender() { - return this.sender || null; - } - setSender(sender) { - this.sender = sender; - } - getAddressee() { - return this.addressee || null; - } - setAddressee(addressee) { - this.addressee = addressee; - } - /** - * Filename of the encoded file - * - * @return filename - */ - getFileName() { - return this.fileName; - } - setFileName(fileName) { - this.fileName = fileName; - } - /** - * filesize in bytes of the encoded file - * - * @return filesize in bytes, -1 if not set - */ - getFileSize() { - return this.fileSize; - } - setFileSize(fileSize /*long*/) { - this.fileSize = fileSize; - } - /** - * 16-bit CRC checksum using CCITT-16 - * - * @return crc checksum, -1 if not set - */ - getChecksum() { - return this.checksum; - } - setChecksum(checksum /*int*/) { - this.checksum = checksum; - } - /** - * unix epock timestamp, elapsed seconds since 1970-01-01 - * - * @return elapsed seconds, -1 if not set - */ - getTimestamp() { - return this.timestamp; - } - setTimestamp(timestamp /*long*/) { - this.timestamp = timestamp; - } - } - - /** - * Ponyfill for Java's Long class. - */ - class Long { - /** - * Parses a string to a number, since JS has no really Int64. - * - * @param num Numeric string. - * @param radix Destination radix. - */ - static parseLong(num, radix = undefined) { - return parseInt(num, radix); - } - } - - /** - * Custom Error class of type Exception. - */ - class NullPointerException extends Exception { - } - NullPointerException.kind = 'NullPointerException'; - - /* - * Copyright (c) 1994, 2004, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - // package java.io; - /** - * This abstract class is the superclass of all classes representing - * an output stream of bytes. An output stream accepts output bytes - * and sends them to some sink. - *
- * Applications that need to define a subclass of
- * OutputStream
must always provide at least a method
- * that writes one byte of output.
- *
- * @author Arthur van Hoff
- * @see java.io.BufferedOutputStream
- * @see java.io.ByteArrayOutputStream
- * @see java.io.DataOutputStream
- * @see java.io.FilterOutputStream
- * @see java.io.InputStream
- * @see java.io.OutputStream#write(int)
- * @since JDK1.0
- */
- /*public*/ class OutputStream /*implements Closeable, Flushable*/ {
- /**
- * Writes b.length
bytes from the specified byte array
- * to this output stream. The general contract for write(b)
- * is that it should have exactly the same effect as the call
- * write(b, 0, b.length)
.
- *
- * @param b the data.
- * @exception IOException if an I/O error occurs.
- * @see java.io.OutputStream#write(byte[], int, int)
- */
- writeBytes(b) {
- this.writeBytesOffset(b, 0, b.length);
- }
- /**
- * Writes len
bytes from the specified byte array
- * starting at offset off
to this output stream.
- * The general contract for write(b, off, len)
is that
- * some of the bytes in the array b
are written to the
- * output stream in order; element b[off]
is the first
- * byte written and b[off+len-1]
is the last byte written
- * by this operation.
- *
- * The write
method of OutputStream
calls
- * the write method of one argument on each of the bytes to be
- * written out. Subclasses are encouraged to override this method and
- * provide a more efficient implementation.
- *
- * If b
is null
, a
- * NullPointerException
is thrown.
- *
- * If off
is negative, or len
is negative, or
- * off+len
is greater than the length of the array
- * b
, then an IndexOutOfBoundsException is thrown.
- *
- * @param b the data.
- * @param off the start offset in the data.
- * @param len the number of bytes to write.
- * @exception IOException if an I/O error occurs. In particular,
- * an IOException
is thrown if the output
- * stream is closed.
- */
- writeBytesOffset(b, off, len) {
- if (b == null) {
- throw new NullPointerException();
- }
- else if ((off < 0) || (off > b.length) || (len < 0) ||
- ((off + len) > b.length) || ((off + len) < 0)) {
- throw new IndexOutOfBoundsException();
- }
- else if (len === 0) {
- return;
- }
- for (let i = 0; i < len; i++) {
- this.write(b[off + i]);
- }
- }
- /**
- * Flushes this output stream and forces any buffered output bytes
- * to be written out. The general contract of flush
is
- * that calling it is an indication that, if any bytes previously
- * written have been buffered by the implementation of the output
- * stream, such bytes should immediately be written to their
- * intended destination.
- *
- * If the intended destination of this stream is an abstraction provided by - * the underlying operating system, for example a file, then flushing the - * stream guarantees only that bytes previously written to the stream are - * passed to the operating system for writing; it does not guarantee that - * they are actually written to a physical device such as a disk drive. - *
- * The flush
method of OutputStream
does nothing.
- *
- * @exception IOException if an I/O error occurs.
- */
- flush() {
- }
- /**
- * Closes this output stream and releases any system resources
- * associated with this stream. The general contract of close
- * is that it closes the output stream. A closed stream cannot perform
- * output operations and cannot be reopened.
- *
- * The close
method of OutputStream
does nothing.
- *
- * @exception IOException if an I/O error occurs.
- */
- close() {
- }
- }
-
- /**
- * Custom Error class of type Exception.
- */
- class OutOfMemoryError extends Exception {
- }
-
- /*
- * Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
- /**
- * This class implements an output stream in which the data is
- * written into a byte array. The buffer automatically grows as data
- * is written to it.
- * The data can be retrieved using toByteArray()
and
- * toString()
.
- *
- * Closing a ByteArrayOutputStream has no effect. The methods in
- * this class can be called after the stream has been closed without
- * generating an IOException.
- *
- * @author Arthur van Hoff
- * @since JDK1.0
- */
- /*public*/ class ByteArrayOutputStream extends OutputStream {
- /**
- * Creates a new byte array output stream. The buffer capacity is
- * initially 32 bytes, though its size increases if necessary.
- */
- // public constructor() {
- // this(32);
- // }
- /**
- * Creates a new byte array output stream, with a buffer capacity of
- * the specified size, in bytes.
- *
- * @param size the initial size.
- * @exception IllegalArgumentException if size is negative.
- */
- constructor(size = 32) {
- super();
- /**
- * The number of valid bytes in the buffer.
- */
- this.count = 0;
- if (size < 0) {
- throw new IllegalArgumentException('Negative initial size: '
- + size);
- }
- this.buf = new Uint8Array(size);
- }
- /**
- * Increases the capacity if necessary to ensure that it can hold
- * at least the number of elements specified by the minimum
- * capacity argument.
- *
- * @param minCapacity the desired minimum capacity
- * @throws OutOfMemoryError if {@code minCapacity < 0}. This is
- * interpreted as a request for the unsatisfiably large capacity
- * {@code (long) Integer.MAX_VALUE + (minCapacity - Integer.MAX_VALUE)}.
- */
- ensureCapacity(minCapacity) {
- // overflow-conscious code
- if (minCapacity - this.buf.length > 0)
- this.grow(minCapacity);
- }
- /**
- * Increases the capacity to ensure that it can hold at least the
- * number of elements specified by the minimum capacity argument.
- *
- * @param minCapacity the desired minimum capacity
- */
- grow(minCapacity) {
- // overflow-conscious code
- let oldCapacity = this.buf.length;
- let newCapacity = oldCapacity << 1;
- if (newCapacity - minCapacity < 0)
- newCapacity = minCapacity;
- if (newCapacity < 0) {
- if (minCapacity < 0) // overflow
- throw new OutOfMemoryError();
- newCapacity = Integer.MAX_VALUE;
- }
- this.buf = Arrays.copyOfUint8Array(this.buf, newCapacity);
- }
- /**
- * Writes the specified byte to this byte array output stream.
- *
- * @param b the byte to be written.
- */
- write(b) {
- this.ensureCapacity(this.count + 1);
- this.buf[this.count] = /*(byte)*/ b;
- this.count += 1;
- }
- /**
- * Writes len
bytes from the specified byte array
- * starting at offset off
to this byte array output stream.
- *
- * @param b the data.
- * @param off the start offset in the data.
- * @param len the number of bytes to write.
- */
- writeBytesOffset(b, off, len) {
- if ((off < 0) || (off > b.length) || (len < 0) ||
- ((off + len) - b.length > 0)) {
- throw new IndexOutOfBoundsException();
- }
- this.ensureCapacity(this.count + len);
- System.arraycopy(b, off, this.buf, this.count, len);
- this.count += len;
- }
- /**
- * Writes the complete contents of this byte array output stream to
- * the specified output stream argument, as if by calling the output
- * stream's write method using out.write(buf, 0, count)
.
- *
- * @param out the output stream to which to write the data.
- * @exception IOException if an I/O error occurs.
- */
- writeTo(out) {
- out.writeBytesOffset(this.buf, 0, this.count);
- }
- /**
- * Resets the count
field of this byte array output
- * stream to zero, so that all currently accumulated output in the
- * output stream is discarded. The output stream can be used again,
- * reusing the already allocated buffer space.
- *
- * @see java.io.ByteArrayInputStream#count
- */
- reset() {
- this.count = 0;
- }
- /**
- * Creates a newly allocated byte array. Its size is the current
- * size of this output stream and the valid contents of the buffer
- * have been copied into it.
- *
- * @return the current contents of this output stream, as a byte array.
- * @see java.io.ByteArrayOutputStream#size()
- */
- toByteArray() {
- return Arrays.copyOfUint8Array(this.buf, this.count);
- }
- /**
- * Returns the current size of the buffer.
- *
- * @return the value of the count
field, which is the number
- * of valid bytes in this output stream.
- * @see java.io.ByteArrayOutputStream#count
- */
- size() {
- return this.count;
- }
- toString(param) {
- if (!param) {
- return this.toString_void();
- }
- if (typeof param === 'string') {
- return this.toString_string(param);
- }
- return this.toString_number(param);
- }
- /**
- * Converts the buffer's contents into a string decoding bytes using the
- * platform's default character set. The length of the new String
- * is a function of the character set, and hence may not be equal to the
- * size of the buffer.
- *
- *
This method always replaces malformed-input and unmappable-character - * sequences with the default replacement string for the platform's - * default character set. The {@linkplain java.nio.charset.CharsetDecoder} - * class should be used when more control over the decoding process is - * required. - * - * @return String decoded from the buffer's contents. - * @since JDK1.1 - */ - toString_void() { - return new String(this.buf /*, 0, this.count*/).toString(); - } - /** - * Converts the buffer's contents into a string by decoding the bytes using - * the specified {@link java.nio.charset.Charset charsetName}. The length of - * the new String is a function of the charset, and hence may not be - * equal to the length of the byte array. - * - *
This method always replaces malformed-input and unmappable-character
- * sequences with this charset's default replacement string. The {@link
- * java.nio.charset.CharsetDecoder} class should be used when more control
- * over the decoding process is required.
- *
- * @param charsetName the name of a supported
- * {@linkplain java.nio.charset.Charset charset
- *
- * @throws IOException
- */
- close() {
- }
- }
-
- /*
- * 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.
- */
- /*private*/ var Mode$2;
- (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$2 || (Mode$2 = {}));
- /**
- * 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 __webpack_require__.g !== 'undefined') {
- return __webpack_require__.g['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;
- }
- /**
- * This class contains the methods for decoding the PDF417 codewords. Given data and error-correction codewords received, possibly corrupted by errors, attempts to
- * correct the errors in-place. Implements Reed-Solomon encoding, as the name implies. Encode a sequence of code words (symbols) using Reed-Solomon to allow decoders
- * to detect and correct errors that may have been introduced when the resulting
- * data is stored or transmitted.}
- * @return String decoded from the buffer's contents.
- * @exception UnsupportedEncodingException
- * If the named charset is not supported
- * @since JDK1.1
- */
- toString_string(charsetName) {
- return new String(this.buf /*, 0, this.count, charsetName*/).toString();
- }
- /**
- * Creates a newly allocated string. Its size is the current size of
- * the output stream and the valid contents of the buffer have been
- * copied into it. Each character c in the resulting string is
- * constructed from the corresponding element b in the byte
- * array such that:
- *
- *
- * @deprecated This method does not properly convert bytes into characters.
- * As of JDK 1.1, the preferred way to do this is via the
- *
- * c == (char)(((hibyte & 0xff) << 8) | (b & 0xff))
- *
toString(String enc)
method, which takes an encoding-name
- * argument, or the toString()
method, which uses the
- * platform's default character encoding.
- *
- * @param hibyte the high byte of each resulting Unicode character.
- * @return the current contents of the output stream, as a string.
- * @see java.io.ByteArrayOutputStream#size()
- * @see java.io.ByteArrayOutputStream#toString(String)
- * @see java.io.ByteArrayOutputStream#toString()
- */
- // @Deprecated
- toString_number(hibyte) {
- return new String(this.buf /*, hibyte, 0, this.count*/).toString();
- }
- /**
- * Closing a ByteArrayOutputStream has no effect. The methods in
- * this class can be called after the stream has been closed without
- * generating an IOException.
- *
";
- qrCodeScanRegion.appendChild(this.cameraScanImage);
- return;
- }
- this.cameraScanImage = new Image;
- this.cameraScanImage.onload = function (_) {
- qrCodeScanRegion.innerHTML = "
";
- qrCodeScanRegion.appendChild($this.cameraScanImage);
- };
- this.cameraScanImage.width = 64;
- this.cameraScanImage.style.opacity = "0.8";
- this.cameraScanImage.src = ASSET_CAMERA_SCAN;
- };
- Html5QrcodeScanner.prototype.insertFileScanImageToScanRegion = function () {
- var $this = this;
- var qrCodeScanRegion = document.getElementById(this.getScanRegionId());
- if (this.fileScanImage) {
- qrCodeScanRegion.innerHTML = "
";
- qrCodeScanRegion.appendChild(this.fileScanImage);
- return;
- }
- this.fileScanImage = new Image;
- this.fileScanImage.onload = function (_) {
- qrCodeScanRegion.innerHTML = "
";
- qrCodeScanRegion.appendChild($this.fileScanImage);
- };
- this.fileScanImage.width = 64;
- this.fileScanImage.style.opacity = "0.8";
- this.fileScanImage.src = ASSET_FILE_SCAN;
- };
- Html5QrcodeScanner.prototype.clearScanRegion = function () {
- var qrCodeScanRegion = document.getElementById(this.getScanRegionId());
- qrCodeScanRegion.innerHTML = "";
- };
- Html5QrcodeScanner.prototype.getDashboardSectionId = function () {
- return this.elementId + "__dashboard_section";
- };
- Html5QrcodeScanner.prototype.getDashboardSectionCameraScanRegionId = function () {
- return this.elementId + "__dashboard_section_csr";
- };
- Html5QrcodeScanner.prototype.getDashboardSectionFileScanRegionId = function () {
- return this.elementId + "__dashboard_section_fsr";
- };
- Html5QrcodeScanner.prototype.getDashboardSectionSwapLinkId = function () {
- return this.elementId + "__dashboard_section_swaplink";
- };
- Html5QrcodeScanner.prototype.getScanRegionId = function () {
- return this.elementId + "__scan_region";
- };
- Html5QrcodeScanner.prototype.getDashboardId = function () {
- return this.elementId + "__dashboard";
- };
- Html5QrcodeScanner.prototype.getFileScanInputId = function () {
- return this.elementId + "__filescan_input";
- };
- Html5QrcodeScanner.prototype.getStatusSpanId = function () {
- return this.elementId + "__status_span";
- };
- Html5QrcodeScanner.prototype.getHeaderMessageContainerId = function () {
- return this.elementId + "__header_message";
- };
- Html5QrcodeScanner.prototype.getCameraSelectionId = function () {
- return this.elementId + "__camera_selection";
- };
- Html5QrcodeScanner.prototype.getCameraPermissionButtonId = function () {
- return this.elementId + "__camera_permission_button";
- };
- Html5QrcodeScanner.prototype.getCameraScanRegion = function () {
- return document.getElementById(this.getDashboardSectionCameraScanRegionId());
- };
- Html5QrcodeScanner.prototype.getFileScanRegion = function () {
- return document.getElementById(this.getDashboardSectionFileScanRegionId());
- };
- Html5QrcodeScanner.prototype.getFileScanInput = function () {
- return document.getElementById(this.getFileScanInputId());
- };
- Html5QrcodeScanner.prototype.getDashboardSectionSwapLink = function () {
- return document.getElementById(this.getDashboardSectionSwapLinkId());
- };
- Html5QrcodeScanner.prototype.getHeaderMessageDiv = function () {
- return document.getElementById(this.getHeaderMessageContainerId());
- };
- return Html5QrcodeScanner;
-}());
-
-
-;// CONCATENATED MODULE: ./src/index.ts
-
-
-
-
-
-
-})();
-
-__Html5QrcodeLibrary__ = __webpack_exports__;
-/******/ })()
-;/** Append the libary components to globals for backwards compatibility. */
+/*! For license information please see html5-qrcode.min.js.LICENSE.txt */
+var __Html5QrcodeLibrary__;(()=>{var t={449:function(t,e,r){!function(t){"use strict";var e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r])};var n,i=function(t){function r(e){var r,n,i,o=this.constructor,s=t.call(this,e)||this;return Object.defineProperty(s,"name",{value:o.name,enumerable:!1}),r=s,n=o.prototype,(i=Object.setPrototypeOf)?i(r,n):r.__proto__=n,function(t,e){void 0===e&&(e=t.constructor);var r=Error.captureStackTrace;r&&r(t,e)}(s),s}return function(t,r){function n(){this.constructor=t}e(t,r),t.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)}(r,t),r}(Error);class o extends i{constructor(t){super(t),this.message=t}getKind(){return this.constructor.kind}}o.kind="Exception";class s extends o{}s.kind="ArgumentException";class a extends o{}a.kind="IllegalArgumentException";class l{constructor(t){if(this.binarizer=t,null===t)throw new a("Binarizer must be non-null.")}getWidth(){return this.binarizer.getWidth()}getHeight(){return this.binarizer.getHeight()}getBlackRow(t,e){return this.binarizer.getBlackRow(t,e)}getBlackMatrix(){return null!==this.matrix&&void 0!==this.matrix||(this.matrix=this.binarizer.getBlackMatrix()),this.matrix}isCropSupported(){return this.binarizer.getLuminanceSource().isCropSupported()}crop(t,e,r,n){const i=this.binarizer.getLuminanceSource().crop(t,e,r,n);return new l(this.binarizer.createBinarizer(i))}isRotateSupported(){return this.binarizer.getLuminanceSource().isRotateSupported()}rotateCounterClockwise(){const t=this.binarizer.getLuminanceSource().rotateCounterClockwise();return new l(this.binarizer.createBinarizer(t))}rotateCounterClockwise45(){const t=this.binarizer.getLuminanceSource().rotateCounterClockwise45();return new l(this.binarizer.createBinarizer(t))}toString(){try{return this.getBlackMatrix().toString()}catch(t){return""}}}class c extends o{static getChecksumInstance(){return new c}}c.kind="ChecksumException";class h{constructor(t){this.source=t}getLuminanceSource(){return this.source}getWidth(){return this.source.getWidth()}getHeight(){return this.source.getHeight()}}class u{static arraycopy(t,e,r,n,i){for(;i--;)r[n++]=t[e++]}static currentTimeMillis(){return Date.now()}}class d extends o{}d.kind="IndexOutOfBoundsException";class g extends d{constructor(t,e){super(e),this.index=t,this.message=e}}g.kind="ArrayIndexOutOfBoundsException";class f{static fill(t,e){for(let r=0,n=t.length;r=t.getRowCount()||i>r)e[r]=null;else{let t;t=l>2?(l-2)*i:i;let o=t>=r;for(let n=1;n<=t&&!o;n++)o=null!=e[r-n];o?e[r]=null:(a=n.getRowNumber(),c=1)}}}getRowHeights(){let t=this.getBarcodeMetadata();if(null==t)return null;this.adjustIncompleteIndicatorColumnRowNumbers(t);let e=new Int32Array(t.getRowCount());for(let t of this.getCodewords())if(null!=t){let r=t.getRowNumber();if(r>=e.length)continue;e[r]++}return e}adjustIncompleteIndicatorColumnRowNumbers(t){let e=this.getBoundingBox(),r=this._isLeft?e.getTopLeft():e.getTopRight(),n=this._isLeft?e.getBottomLeft():e.getBottomRight(),i=this.imageRowToCodewordIndex(Math.trunc(r.getY())),o=this.imageRowToCodewordIndex(Math.trunc(n.getY())),s=this.getCodewords(),a=-1;for(let e=i;ee||i+s>r)throw new a("Crop rectangle does not fit within image data.");l&&this.reverseHorizontal(o,s)}getRow(t,e){if(t<0||t>=this.getHeight())throw new a("Requested row is outside the image: "+t);const r=this.getWidth();(null==e||e.length
",void e.appendChild(this.fileScanImage);this.fileScanImage=new Image,this.fileScanImage.onload=function(r){e.innerHTML="
",e.appendChild(t.fileScanImage)},this.fileScanImage.width=64,this.fileScanImage.style.opacity="0.8",this.fileScanImage.src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1OS4wMTggNTkuMDE4IiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1OS4wMTggNTkuMDE4IiB4bWw6c3BhY2U9InByZXNlcnZlIj48cGF0aCBkPSJtNTguNzQxIDU0LjgwOS01Ljk2OS02LjI0NGExMC43NCAxMC43NCAwIDAgMCAyLjgyLTcuMjVjMC01Ljk1My00Ljg0My0xMC43OTYtMTAuNzk2LTEwLjc5NlMzNCAzNS4zNjEgMzQgNDEuMzE0IDM4Ljg0MyA1Mi4xMSA0NC43OTYgNTIuMTFjMi40NDEgMCA0LjY4OC0uODI0IDYuNDk5LTIuMTk2bDYuMDAxIDYuMjc3YS45OTguOTk4IDAgMCAwIDEuNDE0LjAzMiAxIDEgMCAwIDAgLjAzMS0xLjQxNHpNMzYgNDEuMzE0YzAtNC44NSAzLjk0Ni04Ljc5NiA4Ljc5Ni04Ljc5NnM4Ljc5NiAzLjk0NiA4Ljc5NiA4Ljc5Ni0zLjk0NiA4Ljc5Ni04Ljc5NiA4Ljc5NlMzNiA0Ni4xNjQgMzYgNDEuMzE0ek0xMC40MzEgMTYuMDg4YzAgMy4wNyAyLjQ5OCA1LjU2OCA1LjU2OSA1LjU2OHM1LjU2OS0yLjQ5OCA1LjU2OS01LjU2OGMwLTMuMDcxLTIuNDk4LTUuNTY5LTUuNTY5LTUuNTY5cy01LjU2OSAyLjQ5OC01LjU2OSA1LjU2OXptOS4xMzggMGMwIDEuOTY4LTEuNjAyIDMuNTY4LTMuNTY5IDMuNTY4cy0zLjU2OS0xLjYwMS0zLjU2OS0zLjU2OCAxLjYwMi0zLjU2OSAzLjU2OS0zLjU2OSAzLjU2OSAxLjYwMSAzLjU2OSAzLjU2OXoiLz48cGF0aCBkPSJtMzAuODgyIDI4Ljk4NyA5LjE4LTEwLjA1NCAxMS4yNjIgMTAuMzIzYTEgMSAwIDAgMCAxLjM1MS0xLjQ3NWwtMTItMTFhMSAxIDAgMCAwLTEuNDE0LjA2M2wtOS43OTQgMTAuNzI3LTQuNzQzLTQuNzQzYTEuMDAzIDEuMDAzIDAgMCAwLTEuMzY4LS4wNDRMNi4zMzkgMzcuNzY4YTEgMSAwIDEgMCAxLjMyMiAxLjUwMWwxNi4zMTMtMTQuMzYyIDcuMzE5IDcuMzE4YS45OTkuOTk5IDAgMSAwIDEuNDE0LTEuNDE0bC0xLjgyNS0xLjgyNHoiLz48cGF0aCBkPSJNMzAgNDYuNTE4SDJ2LTQyaDU0djI4YTEgMSAwIDEgMCAyIDB2LTI5YTEgMSAwIDAgMC0xLTFIMWExIDEgMCAwIDAtMSAxdjQ0YTEgMSAwIDAgMCAxIDFoMjlhMSAxIDAgMSAwIDAtMnoiLz48L3N2Zz4="},t.prototype.clearScanRegion=function(){document.getElementById(this.getScanRegionId()).innerHTML=""},t.prototype.getDashboardSectionId=function(){return this.elementId+"__dashboard_section"},t.prototype.getDashboardSectionCameraScanRegionId=function(){return this.elementId+"__dashboard_section_csr"},t.prototype.getDashboardSectionFileScanRegionId=function(){return this.elementId+"__dashboard_section_fsr"},t.prototype.getDashboardSectionSwapLinkId=function(){return this.elementId+"__dashboard_section_swaplink"},t.prototype.getScanRegionId=function(){return this.elementId+"__scan_region"},t.prototype.getDashboardId=function(){return this.elementId+"__dashboard"},t.prototype.getFileScanInputId=function(){return this.elementId+"__filescan_input"},t.prototype.getStatusSpanId=function(){return this.elementId+"__status_span"},t.prototype.getHeaderMessageContainerId=function(){return this.elementId+"__header_message"},t.prototype.getCameraSelectionId=function(){return this.elementId+"__camera_selection"},t.prototype.getCameraPermissionButtonId=function(){return this.elementId+"__camera_permission_button"},t.prototype.getCameraScanRegion=function(){return document.getElementById(this.getDashboardSectionCameraScanRegionId())},t.prototype.getFileScanRegion=function(){return document.getElementById(this.getDashboardSectionFileScanRegionId())},t.prototype.getFileScanInput=function(){return document.getElementById(this.getFileScanInputId())},t.prototype.getDashboardSectionSwapLink=function(){return document.getElementById(this.getDashboardSectionSwapLinkId())},t.prototype.getHeaderMessageDiv=function(){return document.getElementById(this.getHeaderMessageContainerId())},t}()})(),__Html5QrcodeLibrary__=n})();/** Append the libary components to globals for backwards compatibility. */
if (window) {
if (!Html5QrcodeScanner) {
var Html5QrcodeScanner = window.__Html5QrcodeLibrary__.Html5QrcodeScanner;
diff --git a/package.json b/package.json
index 8ffa0ca..25df26f 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "html5-qrcode",
- "version": "2.2.5",
+ "version": "2.2.6",
"description": "A cross platform HTML5 QR Code & bar code scanner",
"main": "./cjs/index.js",
"module": "./esm/index.js",