diff --git a/src/core/oned/MultiFormatOneDReader.ts b/src/core/oned/MultiFormatOneDReader.ts index 7ce03af0..fa5a0fe9 100644 --- a/src/core/oned/MultiFormatOneDReader.ts +++ b/src/core/oned/MultiFormatOneDReader.ts @@ -71,6 +71,7 @@ export default class MultiFormatOneDReader extends OneDReader { this.readers.push(new RSS14Reader()); } if (possibleFormats.includes(BarcodeFormat.RSS_EXPANDED)) { + // XXX console.warn('RSS Expanded reader IS NOT ready for production yet! use at your own risk.'); this.readers.push(new RSSExpandedReader()); } @@ -84,7 +85,7 @@ export default class MultiFormatOneDReader extends OneDReader { this.readers.push(new Code128Reader()); this.readers.push(new ITFReader()); this.readers.push(new RSS14Reader()); - // this.readers.push(new RSSExpandedReader()); + this.readers.push(new RSSExpandedReader()); } } diff --git a/src/core/oned/rss/AbstractRSSReader.ts b/src/core/oned/rss/AbstractRSSReader.ts index 7da91451..31d32947 100644 --- a/src/core/oned/rss/AbstractRSSReader.ts +++ b/src/core/oned/rss/AbstractRSSReader.ts @@ -25,8 +25,8 @@ export default abstract class AbstractRSSReader extends OneDReader { 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); + this.oddCounts = new Array(Math.trunc(this.dataCharacterCounters.length / 2)); + this.evenCounts = new Array(Math.trunc(this.dataCharacterCounters.length / 2)); } protected getDecodeFinderCounters(): Int32Array { diff --git a/src/core/oned/rss/FinderPattern.ts b/src/core/oned/rss/FinderPattern.ts index d78b4e7a..9b620396 100644 --- a/src/core/oned/rss/FinderPattern.ts +++ b/src/core/oned/rss/FinderPattern.ts @@ -3,9 +3,11 @@ import ResultPoint from '../../ResultPoint'; export default class FinderPattern { + private value: number; + private startEnd: number[]; private resultPoints: Array; - public constructor(private value: number, private startEnd: number[], start: number, end: number, rowNumber: number) { + public constructor(value: number, startEnd: number[], start: number, end: number, rowNumber: number) { this.value = value; this.startEnd = startEnd; this.resultPoints = new Array(); diff --git a/src/core/oned/rss/RSSUtils.ts b/src/core/oned/rss/RSSUtils.ts index 78d3d3ea..d9b0f650 100644 --- a/src/core/oned/rss/RSSUtils.ts +++ b/src/core/oned/rss/RSSUtils.ts @@ -36,9 +36,9 @@ export default class RSSUtils { return val; } - private static combins(n: number, r: number): number { - let maxDenom; - let minDenom; + private static combins(n: number /* int */, r: number /* int */): number /* int */ { + let maxDenom /* int */ = 0; + let minDenom /* int */ = 0; if (n - r > r) { minDenom = r; maxDenom = n - r; @@ -46,17 +46,17 @@ export default class RSSUtils { minDenom = n - r; maxDenom = r; } - let val = 1; - let j = 1; - for (let i: number = n; i > maxDenom; i--) { + let val /* int */ = 1; + let j /* int */ = 1; + for (let i = n; i > maxDenom; i--) { val *= i; if (j <= minDenom) { - val /= j; + val = Math.trunc(val / j); j++; } } while ((j <= minDenom)) { - val /= j; + val = Math.trunc(val / j); j++; } return val; diff --git a/src/core/oned/rss/expanded/BitArrayBuilder.ts b/src/core/oned/rss/expanded/BitArrayBuilder.ts index ca5af18f..7e1707f1 100644 --- a/src/core/oned/rss/expanded/BitArrayBuilder.ts +++ b/src/core/oned/rss/expanded/BitArrayBuilder.ts @@ -4,7 +4,7 @@ import ExpandedPair from './ExpandedPair'; export default class BitArrayBuilder { static buildBitArray(pairs: Array): BitArray { let charNumber: number = pairs.length * 2 - 1; - if (pairs[pairs.length - 1].getRightChar() == null) { + if (pairs[pairs.length - 1].getRightChar() === null) { charNumber -= 1; } @@ -23,9 +23,9 @@ export default class BitArrayBuilder { } for (let i = 1; i < pairs.length; ++i) { - let currentPair: ExpandedPair = pairs[i]; + const currentPair: ExpandedPair = pairs[i]; - let leftValue = currentPair.getLeftChar().getValue(); + const leftValue = currentPair.getLeftChar().getValue(); for (let j = 11; j >= 0; --j) { if ((leftValue & (1 << j)) !== 0) { binary.set(accPos); @@ -34,7 +34,7 @@ export default class BitArrayBuilder { } if (currentPair.getRightChar() !== null) { - let rightValue = currentPair.getRightChar().getValue(); + const rightValue = currentPair.getRightChar().getValue(); for (let j = 11; j >= 0; --j) { if ((rightValue & (1 << j)) !== 0) { binary.set(accPos); diff --git a/src/core/oned/rss/expanded/ExpandedPair.ts b/src/core/oned/rss/expanded/ExpandedPair.ts index 56cda656..2aa907af 100644 --- a/src/core/oned/rss/expanded/ExpandedPair.ts +++ b/src/core/oned/rss/expanded/ExpandedPair.ts @@ -2,58 +2,56 @@ import DataCharacter from '../../rss/DataCharacter'; import FinderPattern from '../../rss/FinderPattern'; export default class ExpandedPair { - private readonly maybeLast: boolean; - private readonly leftchar: DataCharacter; - private readonly rightchar: DataCharacter; - private readonly finderpattern: FinderPattern; + private readonly leftChar: DataCharacter | null; + private readonly rightChar: DataCharacter | null; + private readonly finderPattern: FinderPattern | null; - constructor(leftChar: DataCharacter, rightChar: DataCharacter, finderPatter: FinderPattern, mayBeLast: boolean) { - this.leftchar = leftChar; - this.rightchar = rightChar; - this.finderpattern = finderPatter; - this.maybeLast = mayBeLast; + constructor( + leftChar: DataCharacter | null, + rightChar: DataCharacter | null, + finderPatter: FinderPattern | null, + ) { + this.leftChar = leftChar; + this.rightChar = rightChar; + this.finderPattern = finderPatter; } - mayBeLast(): boolean { - return this.maybeLast; + getLeftChar(): DataCharacter | null { + return this.leftChar; } - getLeftChar(): DataCharacter { - return this.leftchar; - } - getRightChar(): DataCharacter { - return this.rightchar; + + getRightChar(): DataCharacter | null { + return this.rightChar; } - getFinderPattern(): FinderPattern { - return this.finderpattern; + + getFinderPattern(): FinderPattern | null { + return this.finderPattern; } - mustBeLast() { - return this.rightchar == null; + + mustBeLast(): boolean { + return this.rightChar === null; } + toString(): String { - return '[ ' + this.leftchar + ', ' + this.rightchar + ' : ' + (this.finderpattern == null ? 'null' : this.finderpattern.getValue()) + ' ]'; + return '[ ' + this.leftChar + ', ' + this.rightChar + ' : ' + (this.finderPattern === null ? 'null' : this.finderPattern.getValue()) + ' ]'; } - static equals(o1: any, o2: any): boolean { - if (!(o1 instanceof ExpandedPair)) { + static equals(o1: ExpandedPair | null, o2: any): boolean { + if (o2 === null) return o1 === null; + if (!(o2 instanceof ExpandedPair)) { return false; } - return ExpandedPair.equalsOrNull(o1.leftchar, o2.leftchar) && - ExpandedPair.equalsOrNull(o1.rightchar, o2.rightchar) && - ExpandedPair.equalsOrNull(o1.finderpattern, o2.finderpattern); + return (o1.leftChar === null ? o2.leftChar === null : o1.leftChar.equals(o2.leftChar)) && + (o1.rightChar === null ? o2.rightChar === null : o1.rightChar.equals(o2.rightChar)) && + (o1.finderPattern === null ? o2.finderPattern === null : o1.finderPattern.equals(o2.finderPattern)); } - private static equalsOrNull(o1: any, o2: any): boolean { - return o1 === null ? o2 === null : ExpandedPair.equals(o1, o2); + hashCode(): number { + return ExpandedPair.hashNotNull(this.leftChar) ^ ExpandedPair.hashNotNull(this.rightChar) ^ ExpandedPair.hashNotNull(this.finderPattern); } - hashCode(): any { - // return ExpandedPair.hashNotNull(leftChar) ^ hashNotNull(rightChar) ^ hashNotNull(finderPattern); - let value = this.leftchar.getValue() ^ this.rightchar.getValue() ^ this.finderpattern.getValue(); - return value; + private static hashNotNull(o: { hashCode(): number }): number { + return o === null ? 0 : o.hashCode(); } - // To do - Re check the implementation - // private static hashNotNull(o: ExpandedPair): number { - // return o === null ? 0 : o.hashCode(); - // } } diff --git a/src/core/oned/rss/expanded/ExpandedRow.ts b/src/core/oned/rss/expanded/ExpandedRow.ts index d1c7d7ee..dc6121c5 100644 --- a/src/core/oned/rss/expanded/ExpandedRow.ts +++ b/src/core/oned/rss/expanded/ExpandedRow.ts @@ -1,15 +1,12 @@ import ExpandedPair from './ExpandedPair'; - export default class ExpandedRow { private readonly pairs: Array; private readonly rowNumber: number; - private readonly wasReversed: boolean; - constructor(pairs: Array, rowNumber: number, wasReversed: boolean) { - this.pairs = pairs; + constructor(pairs: Array, rowNumber: number) { + this.pairs = [...pairs]; this.rowNumber = rowNumber; - this.wasReversed = wasReversed; } getPairs(): Array { @@ -20,15 +17,9 @@ export default class ExpandedRow { return this.rowNumber; } - isReversed(): boolean { - return this.wasReversed; - } - // check implementation - isEquivalent(otherPairs: Array): boolean { - return this.checkEqualitity(this, otherPairs); + return ExpandedRow.listEquals(this.getPairs(), otherPairs); } - // @Override public toString(): String { return '{ ' + this.pairs + ' }'; @@ -38,31 +29,19 @@ export default class ExpandedRow { * Two rows are equal if they contain the same pairs in the same order. */ // @Override - // check implementation - public equals(o1: ExpandedRow, o2: ExpandedRow): boolean { - if (!(o1 instanceof ExpandedRow)) { + public static equals(o1: ExpandedRow | null, o2: any): boolean { + if (o1 === null) return o2 === null; + if (!(o2 instanceof ExpandedRow)) { return false; } - return this.checkEqualitity(o1, o2) && o1.wasReversed === o2.wasReversed; + return ExpandedRow.listEquals(o1.pairs, o2.getPairs()); } - checkEqualitity(pair1: any, pair2: any): boolean { - 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; - } - }); + + static listEquals(pairs1: Array, pairs2: Array): boolean { + if (pairs1.length !== pairs2.length) return false; + return pairs1.every((pair1, index) => { + const pair2 = pairs2[index]; + return ExpandedPair.equals(pair1, pair2); }); - return result; } - - // @Override - // check implementation - // public int hashCode(): number { - // let hash = this.pairs.values ^ this.wasReversed - // //return pairs.hashCode() ^ Boolean.valueOf(wasReversed).hashCode(); - // } - } diff --git a/src/core/oned/rss/expanded/RSSExpandedReader.ts b/src/core/oned/rss/expanded/RSSExpandedReader.ts index 772df902..cb208711 100644 --- a/src/core/oned/rss/expanded/RSSExpandedReader.ts +++ b/src/core/oned/rss/expanded/RSSExpandedReader.ts @@ -11,7 +11,6 @@ import AbstractRSSReader from '../../rss/AbstractRSSReader'; import DataCharacter from '../../rss/DataCharacter'; import FinderPattern from '../../rss/FinderPattern'; import RSSUtils from '../../rss/RSSUtils'; -import AbstractExpandedDecoder from '../expanded/decoders/AbstractExpandedDecoder'; import BitArrayBuilder from './BitArrayBuilder'; import { createDecoder } from './decoders/AbstractExpandedDecoderComplement'; import ExpandedPair from './ExpandedPair'; @@ -158,32 +157,36 @@ export default class RSSExpandedReader extends AbstractRSSReader { private static readonly MAX_PAIRS = 11; - private pairs: any = new Array(RSSExpandedReader.MAX_PAIRS); - private rows: any = new Array(); + private static readonly FINDER_PATTERN_MODULES = 15; + private static readonly DATA_CHARACTER_MODULES = 17; + private static readonly MAX_FINDER_PATTERN_DISTANCE_VARIANCE = 0.1; - private readonly startEnd = [2]; - private startFromEven: boolean; + private pairs = new Array(RSSExpandedReader.MAX_PAIRS); + private rows = new Array(); + + private readonly startEnd: [number, number] = [0, 0]; + private startFromEven: boolean = false; public decodeRow( rowNumber: number, row: BitArray, hints: Map ): Result { - // 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; + // Rows can start with even pattern if previous rows had an odd number of patterns, so we try twice. this.startFromEven = false; try { return RSSExpandedReader.constructResult( this.decodeRow2pairs(rowNumber, row) ); - } catch (e) { - // OK - // console.log(e); + } catch (ex) { + if (ex instanceof NotFoundException) { + // OK + // console.log(ex); + } else { + throw ex; + } } - this.pairs.length = 0; this.startFromEven = true; return RSSExpandedReader.constructResult( this.decodeRow2pairs(rowNumber, row) @@ -197,50 +200,48 @@ export default class RSSExpandedReader extends AbstractRSSReader { // Not private for testing decodeRow2pairs(rowNumber: number, row: BitArray): Array { + this.pairs.length = 0; 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(); + if (this.pairs.length === 0) { + throw error; } // exit this loop when retrieveNextPair() fails and throws done = true; + } else { + throw error; } } } // TODO: verify sequence of finder patterns as in checkPairSequence() - if (this.checkChecksum()) { + if (this.checkChecksum() && RSSExpandedReader.isValidSequence(this.pairs, true)) { 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 + + let tryStackedDecode = this.rows.length > 0; + this.storeRow(rowNumber); // 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) { + if (ps !== null) { return ps; } ps = this.checkRowsBoolean(true); - if (ps != null) { + if (ps !== null) { return ps; } } throw new NotFoundException(); } - // Need to Verify - private checkRowsBoolean(reverse: boolean): Array { + + private checkRowsBoolean(reverse: boolean): Array | null { // 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 @@ -251,20 +252,23 @@ export default class RSSExpandedReader extends AbstractRSSReader { this.pairs.length = 0; if (reverse) { - this.rows = this.rows.reverse(); - // Collections.reverse(this.rows); + this.rows.reverse(); } - let ps: Array = null; + + let ps: Array | null = null; try { ps = this.checkRows(new Array(), 0); - } catch (e) { - // OK - console.log(e); + } catch (ex) { + if (ex instanceof NotFoundException) { + // OK + // console.log(ex); + } else { + throw ex; + } } if (reverse) { - this.rows = this.rows.reverse(); - // Collections.reverse(this.rows); + this.rows.reverse(); } return ps; @@ -273,70 +277,103 @@ export default class RSSExpandedReader extends AbstractRSSReader { // Try to construct a valid rows sequence // Recursion is used to implement backtracking private checkRows( - collectedRows: any, + collectedRows: Array, currentRow: number ): Array { for (let i = currentRow; i < this.rows.length; i++) { - let row: any = this.rows[i]; - this.pairs.length = 0; - for (let collectedRow of collectedRows) { - this.pairs.push(collectedRow.getPairs()); - } - this.pairs.push(row.getPairs()); + const row = this.rows[i]; + this.pairs.push(...row.getPairs()); + const addSize = row.getPairs().length; - if (!RSSExpandedReader.isValidSequence(this.pairs)) { - continue; + if (RSSExpandedReader.isValidSequence(this.pairs, false)) { + if (this.checkChecksum()) { + return this.pairs; + } + collectedRows.push(row); + try { + // Recursion: try to add more rows + return this.checkRows(collectedRows, i + 1); + } catch (ex) { + if (ex instanceof NotFoundException) { + // We failed, try the next candidate + collectedRows.pop(); + this.pairs.splice(this.pairs.length - addSize, addSize); + } else { + throw ex; + } + } + } else { + this.pairs.splice(this.pairs.length - addSize, addSize); } + } - if (this.checkChecksum()) { - return this.pairs; - } + throw new NotFoundException(); + } - 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 - console.log(e); + // Whether the pairs form a valid finder pattern sequence, either complete or a prefix + private static isValidSequence(pairs: Array, complete: boolean): boolean { + for (const sequence of RSSExpandedReader.FINDER_PATTERN_SEQUENCES) { + const sizeOk = (complete ? pairs.length === sequence.length : pairs.length <= sequence.length); + if (sizeOk) { + 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; + } } } - throw new NotFoundException(); + return false; } - // Whether the pairs form a valid find pattern sequence, - // either complete or a prefix - private static isValidSequence(pairs: Array): boolean { - for (let sequence of RSSExpandedReader.FINDER_PATTERN_SEQUENCES) { - if (pairs.length > sequence.length) { - continue; - } + // Whether the pairs, plus another pair of the specified type, would together + // form a valid finder pattern sequence, either complete or partial + private static mayFollow(pairs: Array, value: number /* int */): boolean { - let stop = true; - for (let j = 0; j < pairs.length; j++) { - if (pairs[j].getFinderPattern().getValue() !== sequence[j]) { - stop = false; - break; - } - } + if (pairs.length === 0) { + return true; + } - if (stop) { - return true; + for (const sequence of this.FINDER_PATTERN_SEQUENCES) { + if (pairs.length + 1 <= sequence.length) { + // the proposed sequence (i.e. pairs + value) would fit in this allowed sequence + for (let i = pairs.length; i < sequence.length; i++) { + if (sequence[i] === value) { + // we found our value in this allowed sequence, check to see if the elements preceding it match our existing + // pairs; note our existing pairs may not be a full sequence (e.g. if processing a row in a stacked symbol) + let matched = true; + for (let j = 0; j < pairs.length; j++) { + const allowed = sequence[i - j - 1]; + const actual = pairs[pairs.length - j - 1].getFinderPattern().getValue(); + if (allowed !== actual) { + matched = false; + break; + } + } + if (matched) { + return true; + } + } + } } } + // the proposed finder pattern sequence is illegal return false; } - private storeRow(rowNumber: number, wasReversed: boolean): void { + private storeRow(rowNumber: number): void { // 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]; + const erow = this.rows[insertPos]; if (erow.getRowNumber() > rowNumber) { nextIsSame = erow.isEquivalent(this.pairs); break; @@ -352,15 +389,12 @@ export default class RSSExpandedReader extends AbstractRSSReader { // it will prevent us from detecting the barcode. // Try to merge partial rows - // Check whether the row is part of an allready detected row + // Check whether the row is part of an already detected row if (RSSExpandedReader.isPartialRow(this.pairs, this.rows)) { return; } - this.rows.push( - insertPos, - new ExpandedRow(this.pairs, rowNumber, wasReversed) - ); + this.rows.splice(insertPos, 0, new ExpandedRow(this.pairs, rowNumber)); this.removePartialRows(this.pairs, this.rows); } @@ -370,58 +404,36 @@ export default class RSSExpandedReader extends AbstractRSSReader { pairs: Array, rows: Array ): void { - // for (Iterator iterator = rows.iterator(); iterator.hasNext();) { - // ExpandedRow r = iterator.next(); - // if (r.getPairs().size() == pairs.size()) { - // continue; - // } - // boolean allFound = true; - // for (ExpandedPair p : r.getPairs()) { - // boolean found = false; - // for (ExpandedPair pp : pairs) { - // if (p.equals(pp)) { - // found = true; - // break; - // } - // } - // if (!found) { - // allFound = false; - // break; - // } - // } - // if (allFound) { - // // 'pairs' contains all the pairs from the row 'r' - // iterator.remove(); - // } - // } - for (let row of rows) { - if (row.getPairs().length === pairs.length) { - continue; - } - let allFound = true; - for (let p of row.getPairs()) { - let found = false; - for (let pp of pairs) { - if (ExpandedPair.equals(p, pp)) { - found = true; + // Iterate backwards to prevent shifting indices. + for (let rowsIndex = rows.length - 1; rowsIndex >= 0; rowsIndex--) { + const r = rows[rowsIndex]; + if (r.getPairs().length !== pairs.length) { + let allFound = true; + for (const p of r.getPairs()) { + if (!pairs.some((otherPair) => ExpandedPair.equals(p, otherPair))) { + allFound = false; break; } } - if (!found) { - allFound = false; + if (allFound) { + // 'pairs' contains all the pairs from the row 'r' + rows.splice(rowsIndex, 1); } } } } // Returns true when one of the rows already contains all the pairs - private static isPartialRow(pairs: any, rows: any): boolean { - for (let r of rows) { + private static isPartialRow( + pairs: Array, + rows: Array, + ): boolean { + for (const r of rows) { let allFound = true; - for (let p of pairs) { + for (const p of pairs) { let found = false; - for (let pp of r.getPairs()) { - if (p.equals(pp)) { + for (const pp of r.getPairs()) { + if (ExpandedPair.equals(p, pp)) { found = true; break; } @@ -446,16 +458,16 @@ export default class RSSExpandedReader extends AbstractRSSReader { // Not private for unit testing static constructResult(pairs: Array) { - let binary = BitArrayBuilder.buildBitArray(pairs); + const binary = BitArrayBuilder.buildBitArray(pairs); - let decoder = createDecoder(binary); - let resultingString = decoder.parseInformation(); + const decoder = createDecoder(binary); + const resultingString = decoder.parseInformation(); - let firstPoints = pairs[0].getFinderPattern().getResultPoints(); - let lastPoints = pairs[pairs.length - 1] + const firstPoints = pairs[0].getFinderPattern().getResultPoints(); + const lastPoints = pairs[pairs.length - 1] .getFinderPattern() .getResultPoints(); - let points = [firstPoints[0], firstPoints[1], lastPoints[0], lastPoints[1]]; + const points = [firstPoints[0], firstPoints[1], lastPoints[0], lastPoints[1]]; return new Result( resultingString, null, @@ -467,9 +479,9 @@ export default class RSSExpandedReader extends AbstractRSSReader { } private checkChecksum(): boolean { - let firstPair = this.pairs.get(0); - let checkCharacter = firstPair.getLeftChar(); - let firstCharacter = firstPair.getRightChar(); + const firstPair = this.pairs[0]; + const checkCharacter = firstPair.getLeftChar(); + const firstCharacter = firstPair.getRightChar(); if (firstCharacter === null) { return false; @@ -478,12 +490,12 @@ export default class RSSExpandedReader extends AbstractRSSReader { let checksum = firstCharacter.getChecksumPortion(); let s = 2; - for (let i = 1; i < this.pairs.size(); ++i) { - let currentPair = this.pairs.get(i); + for (let i = 1; i < this.pairs.length; ++i) { + const currentPair = this.pairs[i]; checksum += currentPair.getLeftChar().getChecksumPortion(); s++; - let currentRightChar = currentPair.getRightChar(); - if (currentRightChar != null) { + const currentRightChar = currentPair.getRightChar(); + if (currentRightChar !== null) { checksum += currentRightChar.getChecksumPortion(); s++; } @@ -491,13 +503,13 @@ export default class RSSExpandedReader extends AbstractRSSReader { checksum %= 211; - let checkCharacterValue = 211 * (s - 4) + checksum; + const checkCharacterValue = 211 * (s - 4) + checksum; return checkCharacterValue === checkCharacter.getValue(); } private static getNextSecondBar(row: BitArray, initialPos: number): number { - let currentPos; + let currentPos = 0; if (row.get(initialPos)) { currentPos = row.getNextUnset(initialPos); currentPos = row.getNextSet(currentPos); @@ -519,56 +531,57 @@ export default class RSSExpandedReader extends AbstractRSSReader { isOddPattern = !isOddPattern; } - let pattern; + let pattern: FinderPattern | null = null; + let leftChar: DataCharacter | null = null; let keepFinding = true; let forcedOffset = -1; do { this.findNextPair(row, previousPairs, forcedOffset); - pattern = this.parseFoundFinderPattern(row, rowNumber, isOddPattern); + pattern = this.parseFoundFinderPattern(row, rowNumber, isOddPattern, previousPairs); if (pattern === null) { - forcedOffset = RSSExpandedReader.getNextSecondBar( - row, - this.startEnd[0] - ); + forcedOffset = RSSExpandedReader.getNextSecondBar(row, this.startEnd[0]); // probable false positive, keep looking } else { - keepFinding = false; + try { + leftChar = this.decodeDataCharacter(row, pattern, isOddPattern, true); + keepFinding = false; + } catch (ex) { + if (ex instanceof NotFoundException) { + forcedOffset = RSSExpandedReader.getNextSecondBar(row, this.startEnd[0]); // probable false positive, keep looking + } else { + throw ex; + } + } } } while (keepFinding); // When stacked symbol is split over multiple rows, there's no way to guess if this pair can be last or not. // boolean mayBeLast = checkPairSequence(previousPairs, pattern); - let leftChar = this.decodeDataCharacter(row, pattern, isOddPattern, true); - - if ( - !this.isEmptyPair(previousPairs) && - previousPairs[previousPairs.length - 1].mustBeLast() - ) { + if (previousPairs.length > 0 && previousPairs[previousPairs.length - 1].mustBeLast()) { throw new NotFoundException(); } - let rightChar; + let rightChar: DataCharacter | null = null; try { rightChar = this.decodeDataCharacter(row, pattern, isOddPattern, false); - } catch (e) { - rightChar = null; - console.log(e); - } - return new ExpandedPair(leftChar, rightChar, pattern, true); - } - isEmptyPair(pairs) { - if (pairs.length === 0) { - return true; + } catch (ex) { + if (ex instanceof NotFoundException) { + rightChar = null; + // console.log(ex); + } else { + throw ex; + } } - return false; + return new ExpandedPair(leftChar, rightChar, pattern); } + private findNextPair( row: BitArray, previousPairs: Array, - forcedOffset: number + forcedOffset: number, ): void { - let counters = this.getDecodeFinderCounters(); + const counters = this.getDecodeFinderCounters(); counters[0] = 0; counters[1] = 0; counters[2] = 0; @@ -576,13 +589,13 @@ export default class RSSExpandedReader extends AbstractRSSReader { let width = row.getSize(); - let rowOffset; + let rowOffset = 0; if (forcedOffset >= 0) { rowOffset = forcedOffset; - } else if (this.isEmptyPair(previousPairs)) { + } else if (previousPairs.length === 0) { rowOffset = 0; } else { - let lastPair = previousPairs[previousPairs.length - 1]; + const lastPair = previousPairs[previousPairs.length - 1]; rowOffset = lastPair.getFinderPattern().getStartEnd()[1]; } let searchingEvenPair = previousPairs.length % 2 !== 0; @@ -636,10 +649,10 @@ export default class RSSExpandedReader extends AbstractRSSReader { throw new NotFoundException(); } - private static reverseCounters(counters): void { - let length = counters.length; - for (let i = 0; i < length / 2; ++i) { - let tmp = counters[i]; + private static reverseCounters(counters: Int32Array): void { + const length = counters.length; + for (let i = 0; i < Math.trunc(length / 2); ++i) { + const tmp = counters[i]; counters[i] = counters[length - i - 1]; counters[length - i - 1] = tmp; } @@ -648,12 +661,13 @@ export default class RSSExpandedReader extends AbstractRSSReader { private parseFoundFinderPattern( row: BitArray, rowNumber: number, - oddPattern: boolean + oddPattern: boolean, + previousPairs: Array, ): FinderPattern { // Actually we found elements 2-5. - let firstCounter; - let start; - let end; + let firstCounter = 0; + let start = 0; + let end = 0; if (oddPattern) { // If pattern number is odd, we need to locate element 1 *before* the current block. @@ -668,6 +682,7 @@ export default class RSSExpandedReader extends AbstractRSSReader { firstCounter = this.startEnd[0] - firstElementStart; start = firstElementStart; end = this.startEnd[1]; + } else { // If pattern number is even, the pattern is reversed, so we need to locate element 1 *after* the current block. @@ -678,20 +693,41 @@ export default class RSSExpandedReader extends AbstractRSSReader { } // Make 'counters' hold 1-4 - let counters = this.getDecodeFinderCounters(); + const counters = this.getDecodeFinderCounters(); System.arraycopy(counters, 0, counters, 1, counters.length - 1); counters[0] = firstCounter; - let value; + let value = 0; try { - value = this.parseFinderValue( - counters, - RSSExpandedReader.FINDER_PATTERNS - ); - } catch (e) { + value = this.parseFinderValue(counters, RSSExpandedReader.FINDER_PATTERNS); + } catch (ex) { + if (ex instanceof NotFoundException) { + return null; + } else { + throw ex; + } + } + + // Check that the pattern type that we *think* we found can exist as part of a valid sequence of finder patterns. + if (!RSSExpandedReader.mayFollow(previousPairs, value)) { return null; } - // return new FinderPattern(value, new int[] { start, end }, start, end, rowNumber}); + + // Check that the finder pattern that we *think* we found is not too far from where we would expect to find it, + // given that finder patterns are 15 modules wide and the data characters between them are 17 modules wide. + if (previousPairs.length > 0) { + const prev: ExpandedPair = previousPairs[previousPairs.length - 1]; + const prevStart = prev.getFinderPattern().getStartEnd()[0]; + const prevEnd = prev.getFinderPattern().getStartEnd()[1]; + const prevWidth = prevEnd - prevStart; + const charWidth /* float */ = (prevWidth / /* float */ RSSExpandedReader.FINDER_PATTERN_MODULES) * RSSExpandedReader.DATA_CHARACTER_MODULES; + const minX = prevEnd + (2 * charWidth * (1 - RSSExpandedReader.MAX_FINDER_PATTERN_DISTANCE_VARIANCE)); + const maxX = prevEnd + (2 * charWidth * (1 + RSSExpandedReader.MAX_FINDER_PATTERN_DISTANCE_VARIANCE)); + if (start < minX || start > maxX) { + return null; + } + } + return new FinderPattern(value, [start, end], start, end, rowNumber); } @@ -699,9 +735,9 @@ export default class RSSExpandedReader extends AbstractRSSReader { row: BitArray, pattern: FinderPattern, isOddPattern: boolean, - leftChar: boolean - ) { - let counters = this.getDataCharacterCounters(); + leftChar: boolean, + ): DataCharacter { + const counters = this.getDataCharacterCounters(); for (let x = 0; x < counters.length; x++) { counters[x] = 0; } @@ -716,33 +752,31 @@ export default class RSSExpandedReader extends AbstractRSSReader { RSSExpandedReader.recordPattern(row, pattern.getStartEnd()[1], counters); // reverse it for (let i = 0, j = counters.length - 1; i < j; i++, j--) { - let temp = counters[i]; + const temp = counters[i]; counters[i] = counters[j]; counters[j] = temp; } } // counters[] has the pixels of the module let numModules = 17; // left and right data characters have all the same length - let elementWidth = MathUtils.sum(new Int32Array(counters)) / numModules; + let elementWidth /* float */ = MathUtils.sum(new Int32Array(counters)) / numModules; // Sanity check: element width for pattern and the character should match - let expectedElementWidth = - (pattern.getStartEnd()[1] - pattern.getStartEnd()[0]) / 15.0; + let expectedElementWidth /* float */ = (pattern.getStartEnd()[1] - pattern.getStartEnd()[0]) / 15.0; if ( - Math.abs(elementWidth - expectedElementWidth) / expectedElementWidth > - 0.3 + Math.abs(elementWidth - expectedElementWidth) / expectedElementWidth > 0.3 ) { throw new NotFoundException(); } - let oddCounts = this.getOddCounts(); - let evenCounts = this.getEvenCounts(); - let oddRoundingErrors = this.getOddRoundingErrors(); - let evenRoundingErrors = this.getEvenRoundingErrors(); + const oddCounts = this.getOddCounts(); + const evenCounts = this.getEvenCounts(); + const oddRoundingErrors = this.getOddRoundingErrors(); + const evenRoundingErrors = this.getEvenRoundingErrors(); for (let i = 0; i < counters.length; i++) { - let value = (1.0 * counters[i]) / elementWidth; - let count = value + 0.5; // Round + const value /* float */ = (1.0 * counters[i]) / elementWidth; + let count = Math.trunc(value + 0.5); // Round if (count < 1) { if (value < 0.3) { throw new NotFoundException(); @@ -754,7 +788,7 @@ export default class RSSExpandedReader extends AbstractRSSReader { } count = 8; } - let offset = i / 2; + const offset /* int */ = Math.trunc(i / 2); if ((i & 0x01) === 0) { oddCounts[offset] = count; oddRoundingErrors[offset] = value - count; @@ -766,26 +800,23 @@ export default class RSSExpandedReader extends AbstractRSSReader { this.adjustOddEvenCounts(numModules); - let weightRowNumber = - 4 * pattern.getValue() + (isOddPattern ? 0 : 2) + (leftChar ? 0 : 1) - 1; + let weightRowNumber = 4 * pattern.getValue() + (isOddPattern ? 0 : 2) + (leftChar ? 0 : 1) - 1; let oddSum = 0; let oddChecksumPortion = 0; for (let i = oddCounts.length - 1; i >= 0; i--) { if (RSSExpandedReader.isNotA1left(pattern, isOddPattern, leftChar)) { - let weight = RSSExpandedReader.WEIGHTS[weightRowNumber][2 * i]; + const weight = RSSExpandedReader.WEIGHTS[weightRowNumber][2 * i]; oddChecksumPortion += oddCounts[i] * weight; } oddSum += oddCounts[i]; } let evenChecksumPortion = 0; - // int evenSum = 0; for (let i = evenCounts.length - 1; i >= 0; i--) { if (RSSExpandedReader.isNotA1left(pattern, isOddPattern, leftChar)) { - let weight = RSSExpandedReader.WEIGHTS[weightRowNumber][2 * i + 1]; + const weight = RSSExpandedReader.WEIGHTS[weightRowNumber][2 * i + 1]; evenChecksumPortion += evenCounts[i] * weight; } - // evenSum += evenCounts[i]; } let checksumPortion = oddChecksumPortion + evenChecksumPortion; @@ -793,14 +824,14 @@ export default class RSSExpandedReader extends AbstractRSSReader { throw new NotFoundException(); } - let group = (13 - oddSum) / 2; - let oddWidest = RSSExpandedReader.SYMBOL_WIDEST[group]; - let evenWidest = 9 - oddWidest; - let vOdd = RSSUtils.getRSSvalue(oddCounts, oddWidest, true); - let vEven = RSSUtils.getRSSvalue(evenCounts, evenWidest, false); - let tEven = RSSExpandedReader.EVEN_TOTAL_SUBSET[group]; - let gSum = RSSExpandedReader.GSUM[group]; - let value = vOdd * tEven + vEven + gSum; + const group /* int */ = Math.trunc((13 - oddSum) / 2); + const oddWidest = RSSExpandedReader.SYMBOL_WIDEST[group]; + const evenWidest = 9 - oddWidest; + const vOdd = RSSUtils.getRSSvalue(oddCounts, oddWidest, true); + const vEven = RSSUtils.getRSSvalue(evenCounts, evenWidest, false); + const tEven = RSSExpandedReader.EVEN_TOTAL_SUBSET[group]; + const gSum = RSSExpandedReader.GSUM[group]; + const value = vOdd * tEven + vEven + gSum; return new DataCharacter(value, checksumPortion); } @@ -808,15 +839,15 @@ export default class RSSExpandedReader extends AbstractRSSReader { private static isNotA1left( pattern: FinderPattern, isOddPattern: boolean, - leftChar: boolean + leftChar: boolean, ): boolean { // A1: pattern.getValue is 0 (A), and it's an oddPattern, and it is a left char return !(pattern.getValue() === 0 && isOddPattern && leftChar); } - private adjustOddEvenCounts(numModules) { - let oddSum = MathUtils.sum(new Int32Array(this.getOddCounts())); - let evenSum = MathUtils.sum(new Int32Array(this.getEvenCounts())); + private adjustOddEvenCounts(numModules: number /* int */) { + const oddSum = MathUtils.sum(new Int32Array(this.getOddCounts())); + const evenSum = MathUtils.sum(new Int32Array(this.getEvenCounts())); let incrementOdd = false; let decrementOdd = false; diff --git a/src/core/oned/rss/expanded/decoders/AI01392xDecoder.ts b/src/core/oned/rss/expanded/decoders/AI01392xDecoder.ts index c36db80d..43f07e54 100644 --- a/src/core/oned/rss/expanded/decoders/AI01392xDecoder.ts +++ b/src/core/oned/rss/expanded/decoders/AI01392xDecoder.ts @@ -15,16 +15,16 @@ export default class AI01392xDecoder extends AI01decoder { if (this.getInformation().getSize() < AI01392xDecoder.HEADER_SIZE + AI01decoder.GTIN_SIZE) { throw new NotFoundException(); } - let buf = new StringBuilder(); + const buf = new StringBuilder(); this.encodeCompressedGtin(buf, AI01392xDecoder.HEADER_SIZE); - let lastAIdigit = this.getGeneralDecoder().extractNumericValueFromBitArray(AI01392xDecoder.HEADER_SIZE + AI01decoder.GTIN_SIZE, AI01392xDecoder.LAST_DIGIT_SIZE); + const lastAIdigit = this.getGeneralDecoder().extractNumericValueFromBitArray(AI01392xDecoder.HEADER_SIZE + AI01decoder.GTIN_SIZE, AI01392xDecoder.LAST_DIGIT_SIZE); buf.append('(392'); - buf.append(lastAIdigit); + buf.append('' + lastAIdigit); buf.append(')'); - let decodedInformation = this.getGeneralDecoder().decodeGeneralPurposeField(AI01392xDecoder.HEADER_SIZE + AI01decoder.GTIN_SIZE + AI01392xDecoder.LAST_DIGIT_SIZE, null); + const decodedInformation = this.getGeneralDecoder().decodeGeneralPurposeField(AI01392xDecoder.HEADER_SIZE + AI01decoder.GTIN_SIZE + AI01392xDecoder.LAST_DIGIT_SIZE, null); buf.append(decodedInformation.getNewString()); return buf.toString(); diff --git a/src/core/oned/rss/expanded/decoders/AI01393xDecoder.ts b/src/core/oned/rss/expanded/decoders/AI01393xDecoder.ts index 068f1f8d..445d4814 100644 --- a/src/core/oned/rss/expanded/decoders/AI01393xDecoder.ts +++ b/src/core/oned/rss/expanded/decoders/AI01393xDecoder.ts @@ -20,35 +20,36 @@ export default class AI01393xDecoder extends AI01decoder { throw new NotFoundException(); } - let buf = new StringBuilder(); + const buf = new StringBuilder(); this.encodeCompressedGtin(buf, AI01393xDecoder.HEADER_SIZE); - let lastAIdigit = this.getGeneralDecoder().extractNumericValueFromBitArray( + const lastAIdigit = this.getGeneralDecoder().extractNumericValueFromBitArray( AI01393xDecoder.HEADER_SIZE + AI01decoder.GTIN_SIZE, AI01393xDecoder.LAST_DIGIT_SIZE ); buf.append('(393'); - buf.append(lastAIdigit); + buf.append('' + lastAIdigit); buf.append(')'); - let firstThreeDigits = + const firstThreeDigits /* int */ = this.getGeneralDecoder().extractNumericValueFromBitArray( AI01393xDecoder.HEADER_SIZE + AI01decoder.GTIN_SIZE + AI01393xDecoder.LAST_DIGIT_SIZE, AI01393xDecoder.FIRST_THREE_DIGITS_SIZE ); - if (firstThreeDigits / 100 === 0) { + // Pad with leading zeroes. + if (firstThreeDigits < 100) { buf.append('0'); } - if (firstThreeDigits / 10 === 0) { + if (firstThreeDigits < 10) { buf.append('0'); } - buf.append(firstThreeDigits); + buf.append('' + firstThreeDigits); - let generalInformation = this.getGeneralDecoder().decodeGeneralPurposeField( + const generalInformation = this.getGeneralDecoder().decodeGeneralPurposeField( AI01393xDecoder.HEADER_SIZE + AI01decoder.GTIN_SIZE + AI01393xDecoder.LAST_DIGIT_SIZE + diff --git a/src/core/oned/rss/expanded/decoders/AI013x0x1xDecoder.ts b/src/core/oned/rss/expanded/decoders/AI013x0x1xDecoder.ts index 819fd9b5..881b46af 100644 --- a/src/core/oned/rss/expanded/decoders/AI013x0x1xDecoder.ts +++ b/src/core/oned/rss/expanded/decoders/AI013x0x1xDecoder.ts @@ -28,7 +28,7 @@ export default class AI013x0x1xDecoder extends AI01weightDecoder { throw new NotFoundException(); } - let buf = new StringBuilder(); + const buf = new StringBuilder(); this.encodeCompressedGtin(buf, AI013x0x1xDecoder.HEADER_SIZE); this.encodeCompressedWeight( @@ -59,30 +59,32 @@ export default class AI013x0x1xDecoder extends AI01weightDecoder { buf.append(this.dateCode); buf.append(')'); - let day = numericDate % 32; - numericDate /= 32; - let month = (numericDate % 12) + 1; - numericDate /= 12; - let year = numericDate; + const day /* int */ = numericDate % 32; + numericDate = Math.trunc(numericDate / 32); + const month /* int */ = (numericDate % 12) + 1; + numericDate = Math.trunc(numericDate / 12); + const year /* int */ = numericDate; - if (year / 10 === 0) { + // Division of an integer by 10 results in 0. + // The checks exist to add a leading 0. + if (year < 10) { buf.append('0'); } - buf.append(year); - if (month / 10 === 0) { + buf.append('' + year); + if (month < 10) { buf.append('0'); } - buf.append(month); - if (day / 10 === 0) { + buf.append('' + month); + if (day < 10) { buf.append('0'); } - buf.append(day); + buf.append('' + day); } - protected addWeightCode(buf: StringBuilder, weight: number): void { + protected addWeightCode(buf: StringBuilder, weight: number /* int */): void { buf.append('('); buf.append(this.firstAIdigits); - buf.append(weight / 100000); + buf.append('' + Math.trunc(weight / 100000)); buf.append(')'); } diff --git a/src/core/oned/rss/expanded/decoders/AI01AndOtherAIs.ts b/src/core/oned/rss/expanded/decoders/AI01AndOtherAIs.ts index fcd53496..f23916fa 100644 --- a/src/core/oned/rss/expanded/decoders/AI01AndOtherAIs.ts +++ b/src/core/oned/rss/expanded/decoders/AI01AndOtherAIs.ts @@ -12,13 +12,15 @@ export default class AI01AndOtherAIs extends AI01decoder { } public parseInformation(): string { + const buff = new StringBuilder(); - let buff = new StringBuilder(); buff.append('(01)'); - let initialGtinPosition = buff.length(); - let firstGtinDigit = this.getGeneralDecoder().extractNumericValueFromBitArray(AI01AndOtherAIs.HEADER_SIZE, 4); - buff.append(firstGtinDigit); + const initialGtinPosition = buff.length(); + const 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); } diff --git a/src/core/oned/rss/expanded/decoders/AI01decoder.ts b/src/core/oned/rss/expanded/decoders/AI01decoder.ts index 701cee55..bb48c1da 100644 --- a/src/core/oned/rss/expanded/decoders/AI01decoder.ts +++ b/src/core/oned/rss/expanded/decoders/AI01decoder.ts @@ -12,7 +12,7 @@ export default abstract class AI01decoder extends AbstractExpandedDecoder { encodeCompressedGtin(buf: StringBuilder, currentPos: number): void { buf.append('(01)'); - let initialPosition = buf.length(); + const initialPosition = buf.length(); buf.append('9'); this.encodeCompressedGtinWithoutAI(buf, currentPos, initialPosition); @@ -20,14 +20,15 @@ export default abstract class AI01decoder extends AbstractExpandedDecoder { encodeCompressedGtinWithoutAI(buf: StringBuilder, currentPos: number, initialBufferPosition: number): void { for (let i = 0; i < 4; ++i) { - let currentBlock = this.getGeneralDecoder().extractNumericValueFromBitArray(currentPos + 10 * i, 10); - if (currentBlock / 100 === 0) { + const currentBlock /* int */ = this.getGeneralDecoder().extractNumericValueFromBitArray(currentPos + 10 * i, 10); + // Pad with leading zeroes. + if (currentBlock < 100) { buf.append('0'); } - if (currentBlock / 10 === 0) { + if (currentBlock < 10) { buf.append('0'); } - buf.append(currentBlock); + buf.append('' + currentBlock); } AI01decoder.appendCheckDigit(buf, initialBufferPosition); @@ -36,9 +37,7 @@ export default abstract class AI01decoder extends AbstractExpandedDecoder { private static appendCheckDigit(buf: StringBuilder, currentPos: number): void { 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); + const digit = buf.charAt(i + currentPos).charCodeAt(0) - '0'.charCodeAt(0); checkDigit += (i & 0x01) === 0 ? 3 * digit : digit; } @@ -47,7 +46,7 @@ export default abstract class AI01decoder extends AbstractExpandedDecoder { checkDigit = 0; } - buf.append(checkDigit); + buf.append('' + checkDigit); } } diff --git a/src/core/oned/rss/expanded/decoders/AI01weightDecoder.ts b/src/core/oned/rss/expanded/decoders/AI01weightDecoder.ts index c36e7573..ab1be095 100644 --- a/src/core/oned/rss/expanded/decoders/AI01weightDecoder.ts +++ b/src/core/oned/rss/expanded/decoders/AI01weightDecoder.ts @@ -12,16 +12,17 @@ export default abstract class AI01weightDecoder extends AI01decoder { let originalWeightNumeric = this.getGeneralDecoder().extractNumericValueFromBitArray(currentPos, weightSize); this.addWeightCode(buf, originalWeightNumeric); - let weightNumeric = this.checkWeight(originalWeightNumeric); + const weightNumeric /* int */ = this.checkWeight(originalWeightNumeric); let currentDivisor = 100000; + // Pad with leading zeroes. for (let i = 0; i < 5; ++i) { - if (weightNumeric / currentDivisor === 0) { + if (weightNumeric < currentDivisor) { buf.append('0'); } - currentDivisor /= 10; + currentDivisor = Math.trunc(currentDivisor / 10); } - buf.append(weightNumeric); + buf.append('' + weightNumeric); } protected abstract addWeightCode(buf: StringBuilder, weight: number): void; diff --git a/src/core/oned/rss/expanded/decoders/AbstractExpandedDecoderComplement.ts b/src/core/oned/rss/expanded/decoders/AbstractExpandedDecoderComplement.ts index 184dad2a..8d4a92ed 100644 --- a/src/core/oned/rss/expanded/decoders/AbstractExpandedDecoderComplement.ts +++ b/src/core/oned/rss/expanded/decoders/AbstractExpandedDecoderComplement.ts @@ -12,43 +12,38 @@ import AbstractExpandedDecoder from './AbstractExpandedDecoder'; export function createDecoder(information: BitArray): AbstractExpandedDecoder { - 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); + if (information.get(1)) { + return new AI01AndOtherAIs(information); + + } + if (!information.get(2)) { + return new AnyAIDecoder(information); + } + + const fourBitEncodationMethod = GeneralAppIdDecoder.extractNumericValueFromBitArray(information, 1, 4); + + switch (fourBitEncodationMethod) { + case 4: return new AI013103decoder(information); + case 5: return new AI01320xDecoder(information); + } + + const fiveBitEncodationMethod = GeneralAppIdDecoder.extractNumericValueFromBitArray(information, 1, 5); + switch (fiveBitEncodationMethod) { + case 12: return new AI01392xDecoder(information); + case 13: return new AI01393xDecoder(information); } + const 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'); + } + throw new IllegalStateException('unknown decoder: ' + information); } diff --git a/src/core/oned/rss/expanded/decoders/BlockParsedResult.ts b/src/core/oned/rss/expanded/decoders/BlockParsedResult.ts index 669cbb77..42f6177f 100644 --- a/src/core/oned/rss/expanded/decoders/BlockParsedResult.ts +++ b/src/core/oned/rss/expanded/decoders/BlockParsedResult.ts @@ -2,19 +2,15 @@ import DecodedInformation from './DecodedInformation'; export default class BlockParsedResult { - private readonly decodedInformation: DecodedInformation; + private readonly decodedInformation: DecodedInformation | null; private readonly finished: boolean; - constructor(finished: boolean, decodedInformation?: DecodedInformation) { - if (decodedInformation) { - this.decodedInformation = null; - } else { - this.finished = finished; - this.decodedInformation = decodedInformation; - } + constructor(decodedInformation?: DecodedInformation | null, finished?: boolean) { + this.decodedInformation = decodedInformation ? decodedInformation : null; + this.finished = !!finished; } - getDecodedInformation(): DecodedInformation { + getDecodedInformation(): DecodedInformation | null { return this.decodedInformation; } diff --git a/src/core/oned/rss/expanded/decoders/CurrentParsingState.ts b/src/core/oned/rss/expanded/decoders/CurrentParsingState.ts index 68ff2c63..11976bbd 100644 --- a/src/core/oned/rss/expanded/decoders/CurrentParsingState.ts +++ b/src/core/oned/rss/expanded/decoders/CurrentParsingState.ts @@ -7,16 +7,20 @@ enum State { export default class CurrentParsingState { private position: number; private encoding: State; + constructor() { this.position = 0; this.encoding = State.NUMERIC; } + getPosition(): number { return this.position; } + setPosition(position: number): void { this.position = position; } + incrementPosition(delta: number): void { this.position += delta; } diff --git a/src/core/oned/rss/expanded/decoders/DecodedChar.ts b/src/core/oned/rss/expanded/decoders/DecodedChar.ts index c8286f2e..ab5ac4db 100644 --- a/src/core/oned/rss/expanded/decoders/DecodedChar.ts +++ b/src/core/oned/rss/expanded/decoders/DecodedChar.ts @@ -2,7 +2,7 @@ import DecodedObject from './DecodedObject'; export default class DecodedChar extends DecodedObject { private readonly value: string; - static readonly FNC1 = '$'; + static readonly FNC1 = '$'; // It's not in Alphanumeric neither in ISO/IEC 646 charset constructor(newPosition: number, value: string) { super(newPosition); diff --git a/src/core/oned/rss/expanded/decoders/DecodedInformation.ts b/src/core/oned/rss/expanded/decoders/DecodedInformation.ts index 17c27e23..a903fa95 100644 --- a/src/core/oned/rss/expanded/decoders/DecodedInformation.ts +++ b/src/core/oned/rss/expanded/decoders/DecodedInformation.ts @@ -8,14 +8,14 @@ export default class DecodedInformation extends DecodedObject { constructor(newPosition: number, newString: string, remainingValue?: number) { super(newPosition); - if (remainingValue) { - this.remaining = true; - this.remainingValue = this.remainingValue; - } else { + this.newString = newString; + if (remainingValue === undefined) { this.remaining = false; this.remainingValue = 0; + } else { + this.remaining = true; + this.remainingValue = remainingValue; } - this.newString = newString; } getNewString(): string { @@ -25,6 +25,7 @@ export default class DecodedInformation extends DecodedObject { isRemaining(): boolean { return this.remaining; } + getRemainingValue() { return this.remainingValue; } diff --git a/src/core/oned/rss/expanded/decoders/DecodedNumeric.ts b/src/core/oned/rss/expanded/decoders/DecodedNumeric.ts index 76f7068a..51b732a8 100644 --- a/src/core/oned/rss/expanded/decoders/DecodedNumeric.ts +++ b/src/core/oned/rss/expanded/decoders/DecodedNumeric.ts @@ -35,8 +35,4 @@ export default class DecodedNumeric extends DecodedObject { return this.secondDigit === DecodedNumeric.FNC1; } - isAnyFNC1(): boolean { - return this.firstDigit === DecodedNumeric.FNC1 || this.secondDigit === DecodedNumeric.FNC1; - } - } diff --git a/src/core/oned/rss/expanded/decoders/FieldParser.ts b/src/core/oned/rss/expanded/decoders/FieldParser.ts index 4171c1cf..31d634b6 100644 --- a/src/core/oned/rss/expanded/decoders/FieldParser.ts +++ b/src/core/oned/rss/expanded/decoders/FieldParser.ts @@ -1,213 +1,227 @@ import NotFoundException from '../../../../NotFoundException'; +type DataLengthProcessor = (rawInformation: string) => string; + export default class FieldParser { - private static readonly VARIABLE_LENGTH = []; - private static readonly 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], - ]; - private static readonly 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], - ]; - private static readonly 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], - ]; - private static readonly 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], - ]; - - constructor() { + private static fixed(aiSize: number, fieldSize: number): DataLengthProcessor { + return (rawInformation) => FieldParser.processFixedAI(aiSize, fieldSize, rawInformation); + } + private static variable(aiSize: number, fieldSize: number): DataLengthProcessor { + return (rawInformation) => FieldParser.processVariableAI(aiSize, fieldSize, rawInformation); } + + private static readonly TWO_DIGIT_DATA_LENGTH = (() => { + const out = new Map(); + + out.set('00', FieldParser.fixed(2, 18)); + out.set('01', FieldParser.fixed(2, 14)); + out.set('02', FieldParser.fixed(2, 14)); + out.set('10', FieldParser.variable(2, 20)); + out.set('11', FieldParser.fixed(2, 6)); + out.set('12', FieldParser.fixed(2, 6)); + out.set('13', FieldParser.fixed(2, 6)); + out.set('15', FieldParser.fixed(2, 6)); + out.set('16', FieldParser.fixed(2, 6)); + out.set('17', FieldParser.fixed(2, 6)); + out.set('20', FieldParser.fixed(2, 2)); + out.set('21', FieldParser.variable(2, 20)); + out.set('22', FieldParser.variable(2, 29)); // limited to 20 in latest versions of spec + out.set('30', FieldParser.variable(2, 8)); + out.set('37', FieldParser.variable(2, 8)); + //internal company codes + for (let i = 90; i <= 99; i++) { + out.set(String(i), FieldParser.variable(2, 30)); + } + + return out; + })(); + + private static readonly THREE_DIGIT_DATA_LENGTH = new Map([ + ['235', FieldParser.variable(3, 28)], + ['240', FieldParser.variable(3, 30)], + ['241', FieldParser.variable(3, 30)], + ['242', FieldParser.variable(3, 6)], + ['243', FieldParser.variable(3, 20)], + ['250', FieldParser.variable(3, 30)], + ['251', FieldParser.variable(3, 30)], + ['253', FieldParser.variable(3, 30)], + ['254', FieldParser.variable(3, 20)], + ['255', FieldParser.variable(3, 25)], + ['400', FieldParser.variable(3, 30)], + ['401', FieldParser.variable(3, 30)], + ['402', FieldParser.fixed(3, 17)], + ['403', FieldParser.variable(3, 30)], + ['410', FieldParser.fixed(3, 13)], + ['411', FieldParser.fixed(3, 13)], + ['412', FieldParser.fixed(3, 13)], + ['413', FieldParser.fixed(3, 13)], + ['414', FieldParser.fixed(3, 13)], + ['415', FieldParser.fixed(3, 13)], + ['416', FieldParser.fixed(3, 13)], + ['417', FieldParser.fixed(3, 13)], + ['420', FieldParser.variable(3, 20)], + ['421', FieldParser.variable(3, 15)], // limited to 12 in latest versions of spec + ['422', FieldParser.fixed(3, 3)], + ['423', FieldParser.variable(3, 15)], + ['424', FieldParser.fixed(3, 3)], + ['425', FieldParser.variable(3, 15)], + ['426', FieldParser.fixed(3, 3)], + ['427', FieldParser.variable(3, 3)], + ['710', FieldParser.variable(3, 20)], + ['711', FieldParser.variable(3, 20)], + ['712', FieldParser.variable(3, 20)], + ['713', FieldParser.variable(3, 20)], + ['714', FieldParser.variable(3, 20)], + ['715', FieldParser.variable(3, 20)], + ]); + + private static readonly THREE_DIGIT_PLUS_DIGIT_DATA_LENGTH = (() => { + const out = new Map(); + + for (let i = 310; i <= 316; i++) { + out.set(String(i), FieldParser.fixed(4, 6)); + } + for (let i = 320; i <= 337; i++) { + out.set(String(i), FieldParser.fixed(4, 6)); + } + for (let i = 340; i <= 357; i++) { + out.set(String(i), FieldParser.fixed(4, 6)); + } + for (let i = 360; i <= 369; i++) { + out.set(String(i), FieldParser.fixed(4, 6)); + } + out.set('390', FieldParser.variable(4, 15)); + out.set('391', FieldParser.variable(4, 18)); + out.set('392', FieldParser.variable(4, 15)); + out.set('393', FieldParser.variable(4, 18)); + out.set('394', FieldParser.fixed(4, 4)); + out.set('395', FieldParser.fixed(4, 6)); + out.set('703', FieldParser.variable(4, 30)); + out.set('723', FieldParser.variable(4, 30)); + + return out; + })(); + + private static readonly FOUR_DIGIT_DATA_LENGTH = (() => { + const out = new Map(); + + out.set('4300', FieldParser.variable(4, 35)); + out.set('4301', FieldParser.variable(4, 35)); + out.set('4302', FieldParser.variable(4, 70)); + out.set('4303', FieldParser.variable(4, 70)); + out.set('4304', FieldParser.variable(4, 70)); + out.set('4305', FieldParser.variable(4, 70)); + out.set('4306', FieldParser.variable(4, 70)); + out.set('4307', FieldParser.fixed(4, 2)); + out.set('4308', FieldParser.variable(4, 30)); + out.set('4309', FieldParser.fixed(4, 20)); + out.set('4310', FieldParser.variable(4, 35)); + out.set('4311', FieldParser.variable(4, 35)); + out.set('4312', FieldParser.variable(4, 70)); + out.set('4313', FieldParser.variable(4, 70)); + out.set('4314', FieldParser.variable(4, 70)); + out.set('4315', FieldParser.variable(4, 70)); + out.set('4316', FieldParser.variable(4, 70)); + out.set('4317', FieldParser.fixed(4, 2)); + out.set('4318', FieldParser.variable(4, 20)); + out.set('4319', FieldParser.variable(4, 30)); + out.set('4320', FieldParser.variable(4, 35)); + out.set('4321', FieldParser.fixed(4, 1)); + out.set('4322', FieldParser.fixed(4, 1)); + out.set('4323', FieldParser.fixed(4, 1)); + out.set('4324', FieldParser.fixed(4, 10)); + out.set('4325', FieldParser.fixed(4, 10)); + out.set('4326', FieldParser.fixed(4, 6)); + out.set('7001', FieldParser.fixed(4, 13)); + out.set('7002', FieldParser.variable(4, 30)); + out.set('7003', FieldParser.fixed(4, 10)); + out.set('7004', FieldParser.variable(4, 4)); + out.set('7005', FieldParser.variable(4, 12)); + out.set('7006', FieldParser.fixed(4, 6)); + out.set('7007', FieldParser.variable(4, 12)); + out.set('7008', FieldParser.variable(4, 3)); + out.set('7009', FieldParser.variable(4, 10)); + out.set('7010', FieldParser.variable(4, 2)); + out.set('7011', FieldParser.variable(4, 10)); + out.set('7020', FieldParser.variable(4, 20)); + out.set('7021', FieldParser.variable(4, 20)); + out.set('7022', FieldParser.variable(4, 20)); + out.set('7023', FieldParser.variable(4, 30)); + out.set('7040', FieldParser.fixed(4, 4)); + out.set('7240', FieldParser.variable(4, 20)); + out.set('8001', FieldParser.fixed(4, 14)); + out.set('8002', FieldParser.variable(4, 20)); + out.set('8003', FieldParser.variable(4, 30)); + out.set('8004', FieldParser.variable(4, 30)); + out.set('8005', FieldParser.fixed(4, 6)); + out.set('8006', FieldParser.fixed(4, 18)); + out.set('8007', FieldParser.variable(4, 34)); + out.set('8008', FieldParser.variable(4, 12)); + out.set('8009', FieldParser.variable(4, 50)); + out.set('8010', FieldParser.variable(4, 30)); + out.set('8011', FieldParser.variable(4, 12)); + out.set('8012', FieldParser.variable(4, 20)); + out.set('8013', FieldParser.variable(4, 25)); + out.set('8017', FieldParser.fixed(4, 18)); + out.set('8018', FieldParser.fixed(4, 18)); + out.set('8019', FieldParser.variable(4, 10)); + out.set('8020', FieldParser.variable(4, 25)); + out.set('8026', FieldParser.fixed(4, 18)); + out.set('8100', FieldParser.fixed(4, 6)); // removed from latest versions of spec + out.set('8101', FieldParser.fixed(4, 10)); // removed from latest versions of spec + out.set('8102', FieldParser.fixed(4, 2)); // removed from latest versions of spec + out.set('8110', FieldParser.variable(4, 70)); + out.set('8111', FieldParser.fixed(4, 4)); + out.set('8112', FieldParser.variable(4, 70)); + out.set('8200', FieldParser.variable(4, 70)); + + return out; + })(); + + private constructor() { + } + static parseFieldsInGeneralPurpose(rawInformation: string): string { 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); - } + const firstTwoDigits = rawInformation.substring(0, 2); + const twoDigitDataProcessor = this.TWO_DIGIT_DATA_LENGTH.get(firstTwoDigits); + if (twoDigitDataProcessor) { + return twoDigitDataProcessor(rawInformation); } if (rawInformation.length < 3) { throw new NotFoundException(); } - let firstThreeDigits = rawInformation.substring(0, 3); + const 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); - } + const threeDigitDataProcessor = this.THREE_DIGIT_DATA_LENGTH.get(firstThreeDigits); + if (threeDigitDataProcessor) { + return threeDigitDataProcessor(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); - } + const threeDigitPlusDigitDataProcessor = this.THREE_DIGIT_PLUS_DIGIT_DATA_LENGTH.get(firstThreeDigits); + if (threeDigitPlusDigitDataProcessor) { + return threeDigitPlusDigitDataProcessor(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); - } + const firstFourDigits = rawInformation.substring(0, 4); + const fourDigitDataProcessor = this.FOUR_DIGIT_DATA_LENGTH.get(firstFourDigits); + if (fourDigitDataProcessor) { + return fourDigitDataProcessor(rawInformation); } throw new NotFoundException(); @@ -218,33 +232,32 @@ export default class FieldParser { throw new NotFoundException(); } - let ai = rawInformation.substring(0, aiSize); + const 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; + const field = rawInformation.substring(aiSize, aiSize + fieldSize); + const remaining = rawInformation.substring(aiSize + fieldSize); + const result = '(' + ai + ')' + field; + const parsedAI = FieldParser.parseFieldsInGeneralPurpose(remaining); + return parsedAI === null ? result : result + parsedAI; } private static processVariableAI(aiSize: number, variableFieldSize: number, rawInformation: string): string { - let ai = rawInformation.substring(0, aiSize); - let maxSize; + const ai = rawInformation.substring(0, aiSize); + let maxSize: number; 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; + const field = rawInformation.substring(aiSize, maxSize); + const remaining = rawInformation.substring(maxSize); + const result = '(' + ai + ')' + field; + const parsedAI = FieldParser.parseFieldsInGeneralPurpose(remaining); + return parsedAI === null ? result : result + parsedAI; } - } diff --git a/src/core/oned/rss/expanded/decoders/GeneralAppIdDecoder.ts b/src/core/oned/rss/expanded/decoders/GeneralAppIdDecoder.ts index dbe4bc35..d35dae52 100644 --- a/src/core/oned/rss/expanded/decoders/GeneralAppIdDecoder.ts +++ b/src/core/oned/rss/expanded/decoders/GeneralAppIdDecoder.ts @@ -12,7 +12,7 @@ import FieldParser from './FieldParser'; export default class GeneralAppIdDecoder { private readonly information: BitArray; - private readonly current: CurrentParsingState; + private readonly current = new CurrentParsingState(); private readonly buffer = new StringBuilder(); constructor(information: BitArray) { @@ -21,11 +21,11 @@ export default class GeneralAppIdDecoder { decodeAllCodes(buff: StringBuilder, initialPosition: number): string { let currentPosition = initialPosition; - let remaining = null; + let remaining: string | null = null; do { - let info = this.decodeGeneralPurposeField(currentPosition, remaining); - let parsedFields = FieldParser.parseFieldsInGeneralPurpose(info.getNewString()); - if (parsedFields != null) { + const info = this.decodeGeneralPurposeField(currentPosition, remaining); + const parsedFields = FieldParser.parseFieldsInGeneralPurpose(info.getNewString()); + if (parsedFields !== null) { buff.append(parsedFields); } if (info.isRemaining()) { @@ -61,16 +61,16 @@ export default class GeneralAppIdDecoder { private decodeNumeric(pos: number): DecodedNumeric { if (pos + 7 > this.information.getSize()) { - let numeric = this.extractNumericValueFromBitArray(pos, 4); + const 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); + const numeric = this.extractNumericValueFromBitArray(pos, 7); - let digit1 = (numeric - 8) / 11; - let digit2 = (numeric - 8) % 11; + const digit1 = Math.trunc((numeric - 8) / 11); + const digit2 = (numeric - 8) % 11; return new DecodedNumeric(pos + 7, digit1, digit2); } @@ -94,24 +94,24 @@ export default class GeneralAppIdDecoder { // this.buffer.setLength(0); this.buffer.setLengthToZero(); - if (remaining != null) { + if (remaining !== null) { this.buffer.append(remaining); } this.current.setPosition(pos); - let lastDecoded = this.parseBlocks(); - if (lastDecoded != null && lastDecoded.isRemaining()) { + const 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()); } private parseBlocks(): DecodedInformation { - let isFinished: boolean; + let isFinished = false; let result: BlockParsedResult; do { - let initialPosition = this.current.getPosition(); + const initialPosition = this.current.getPosition(); if (this.current.isAlpha()) { result = this.parseAlphaBlock(); @@ -124,7 +124,7 @@ export default class GeneralAppIdDecoder { isFinished = result.isFinished(); } - let positionChanged: boolean = initialPosition !== this.current.getPosition(); + const positionChanged: boolean = initialPosition !== this.current.getPosition(); if (!positionChanged && !isFinished) { break; } @@ -135,7 +135,7 @@ export default class GeneralAppIdDecoder { private parseNumericBlock(): BlockParsedResult { while (this.isStillNumeric(this.current.getPosition())) { - let numeric: DecodedNumeric = this.decodeNumeric(this.current.getPosition()); + const numeric = this.decodeNumeric(this.current.getPosition()); this.current.setPosition(numeric.getNewPosition()); if (numeric.isFirstDigitFNC1()) { @@ -145,32 +145,32 @@ export default class GeneralAppIdDecoder { } else { information = new DecodedInformation(this.current.getPosition(), this.buffer.toString(), numeric.getSecondDigit()); } - return new BlockParsedResult(true, information); + return new BlockParsedResult(information, true); } - this.buffer.append(numeric.getFirstDigit()); + this.buffer.append('' + numeric.getFirstDigit()); if (numeric.isSecondDigitFNC1()) { - let information = new DecodedInformation(this.current.getPosition(), this.buffer.toString()); - return new BlockParsedResult(true, information); + const information = new DecodedInformation(this.current.getPosition(), this.buffer.toString()); + return new BlockParsedResult(information, true); } - this.buffer.append(numeric.getSecondDigit()); + this.buffer.append('' + numeric.getSecondDigit()); } if (this.isNumericToAlphaNumericLatch(this.current.getPosition())) { this.current.setAlpha(); this.current.incrementPosition(4); } - return new BlockParsedResult(false); + return new BlockParsedResult(); } private parseIsoIec646Block(): BlockParsedResult { while (this.isStillIsoIec646(this.current.getPosition())) { - let iso = this.decodeIsoIec646(this.current.getPosition()); + const 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); + const information = new DecodedInformation(this.current.getPosition(), this.buffer.toString()); + return new BlockParsedResult(information, true); } this.buffer.append(iso.getValue()); } @@ -187,17 +187,17 @@ export default class GeneralAppIdDecoder { this.current.setAlpha(); } - return new BlockParsedResult(false); + return new BlockParsedResult(); } private parseAlphaBlock(): BlockParsedResult { while (this.isStillAlpha(this.current.getPosition())) { - let alpha = this.decodeAlphanumeric(this.current.getPosition()); + const 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 + const information = new DecodedInformation(this.current.getPosition(), this.buffer.toString()); + return new BlockParsedResult(information, true); // end of the char block } this.buffer.append(alpha.getValue()); @@ -215,7 +215,7 @@ export default class GeneralAppIdDecoder { this.current.setIsoIec646(); } - return new BlockParsedResult(false); + return new BlockParsedResult(); } private isStillIsoIec646(pos: number): boolean { @@ -223,7 +223,7 @@ export default class GeneralAppIdDecoder { return false; } - let fiveBitValue = this.extractNumericValueFromBitArray(pos, 5); + const fiveBitValue = this.extractNumericValueFromBitArray(pos, 5); if (fiveBitValue >= 5 && fiveBitValue < 16) { return true; } @@ -232,7 +232,7 @@ export default class GeneralAppIdDecoder { return false; } - let sevenBitValue = this.extractNumericValueFromBitArray(pos, 7); + const sevenBitValue = this.extractNumericValueFromBitArray(pos, 7); if (sevenBitValue >= 64 && sevenBitValue < 116) { return true; } @@ -241,33 +241,33 @@ export default class GeneralAppIdDecoder { return false; } - let eightBitValue = this.extractNumericValueFromBitArray(pos, 8); + const eightBitValue = this.extractNumericValueFromBitArray(pos, 8); return eightBitValue >= 232 && eightBitValue < 253; } private decodeIsoIec646(pos: number): DecodedChar { - let fiveBitValue = this.extractNumericValueFromBitArray(pos, 5); + const 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))); + return new DecodedChar(pos + 5, String.fromCharCode('0'.charCodeAt(0) + fiveBitValue - 5)); } - let sevenBitValue = this.extractNumericValueFromBitArray(pos, 7); + const sevenBitValue = this.extractNumericValueFromBitArray(pos, 7); if (sevenBitValue >= 64 && sevenBitValue < 90) { - return new DecodedChar(pos + 7, ('' + (sevenBitValue + 1))); + return new DecodedChar(pos + 7, String.fromCharCode(sevenBitValue + 1)); } if (sevenBitValue >= 90 && sevenBitValue < 116) { - return new DecodedChar(pos + 7, ('' + (sevenBitValue + 7))); + return new DecodedChar(pos + 7, String.fromCharCode(sevenBitValue + 7)); } - let eightBitValue = this.extractNumericValueFromBitArray(pos, 8); - let c; + const eightBitValue = this.extractNumericValueFromBitArray(pos, 8); + let c: string; switch (eightBitValue) { case 232: c = '!'; @@ -344,7 +344,7 @@ export default class GeneralAppIdDecoder { } // We now check if it's a valid 5-bit value (0..9 and FNC1) - let fiveBitValue = this.extractNumericValueFromBitArray(pos, 5); + const fiveBitValue = this.extractNumericValueFromBitArray(pos, 5); if (fiveBitValue >= 5 && fiveBitValue < 16) { return true; } @@ -353,27 +353,27 @@ export default class GeneralAppIdDecoder { return false; } - let sixBitValue = this.extractNumericValueFromBitArray(pos, 6); + const sixBitValue = this.extractNumericValueFromBitArray(pos, 6); return sixBitValue >= 16 && sixBitValue < 63; // 63 not included } private decodeAlphanumeric(pos: number): DecodedChar { - let fiveBitValue = this.extractNumericValueFromBitArray(pos, 5); + const 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))); + return new DecodedChar(pos + 5, String.fromCharCode('0'.charCodeAt(0) + fiveBitValue - 5)); } let sixBitValue = this.extractNumericValueFromBitArray(pos, 6); if (sixBitValue >= 32 && sixBitValue < 58) { - return new DecodedChar(pos + 6, ('' + (sixBitValue + 33))); + return new DecodedChar(pos + 6, String.fromCharCode((sixBitValue + 33))); } - let c; + let c: string; switch (sixBitValue) { case 58: c = '*'; diff --git a/src/core/oned/rss/expanded/decoders/createDecoder.ts b/src/core/oned/rss/expanded/decoders/createDecoder.ts deleted file mode 100644 index 2c0efc4c..00000000 --- a/src/core/oned/rss/expanded/decoders/createDecoder.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { BitArray, IllegalStateException } from '../../../../..'; -import AbstractExpandedDecoder from './AbstractExpandedDecoder'; -import AI013103decoder from './AI013103decoder'; -import AI01320xDecoder from './AI01320xDecoder'; -import AI01392xDecoder from './AI01392xDecoder'; -import AI01393xDecoder from './AI01393xDecoder'; -import AI013x0x1xDecoder from './AI013x0x1xDecoder'; -import AI01AndOtherAIs from './AI01AndOtherAIs'; -import AnyAIDecoder from './AnyAIDecoder'; -import GeneralAppIdDecoder from './GeneralAppIdDecoder'; - - -export default function createDecoder(information: BitArray): AbstractExpandedDecoder { - 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); - } -} diff --git a/src/core/util/System.ts b/src/core/util/System.ts index ff8586d3..0dc9f280 100644 --- a/src/core/util/System.ts +++ b/src/core/util/System.ts @@ -5,9 +5,18 @@ export default class System { * Makes a copy of a array. */ public static arraycopy(src: any, srcPos: number, dest: any, destPos: number, length: number): void { - // TODO: better use split or set? - while (length--) { - dest[destPos++] = src[srcPos++]; + if (src === dest && srcPos < destPos) { + // Copy backwards to avoid writing into indices we're about to read from. + destPos += length - 1; + srcPos += length - 1; + while (length--) { + dest[destPos--] = src[srcPos--]; + } + } else { + // TODO: better use split or set? + while (length--) { + dest[destPos++] = src[srcPos++]; + } } } diff --git a/src/index.ts b/src/index.ts index 38ebd7bd..d967d281 100644 --- a/src/index.ts +++ b/src/index.ts @@ -123,3 +123,4 @@ export { default as AbstractExpandedDecoder } from './core/oned/rss/expanded/dec export { createDecoder as createAbstractExpandedDecoder } from './core/oned/rss/expanded/decoders/AbstractExpandedDecoderComplement'; export { default as MultiFormatOneDReader } from './core/oned/MultiFormatOneDReader'; export { default as CodaBarReader } from './core/oned/CodaBarReader'; +export { default as FinderPattern } from './core/oned/rss/FinderPattern'; diff --git a/src/test/core/oned/rss/expanded/RSSExpandedBlackBox1.spec.ts b/src/test/core/oned/rss/expanded/RSSExpandedBlackBox1.spec.ts index 9950e2d2..2f1f7d25 100644 --- a/src/test/core/oned/rss/expanded/RSSExpandedBlackBox1.spec.ts +++ b/src/test/core/oned/rss/expanded/RSSExpandedBlackBox1.spec.ts @@ -36,14 +36,14 @@ class RSSExpandedBlackBox1Spec extends AbstractBlackBoxSpec { public constructor() { super('src/test/resources/blackbox/rssexpanded-1', new MultiFormatReader(), BarcodeFormat.RSS_EXPANDED); - // this.addTest(32, 32, 0.0); - // this.addTest(32, 32, 180.0); + this.addTest(35, 35, 0.0); + this.addTest(35, 35, 180.0); } } -// describe('RSSExpandedBlackBox1Spec', () => { -// it('testBlackBox', async () => { -// const test = new RSSExpandedBlackBox1Spec(); -// await test.testBlackBox(); -// }); -// }); +describe('RSSExpandedBlackBox1Spec', () => { + it('testBlackBox', async () => { + const test = new RSSExpandedBlackBox1Spec(); + await test.testBlackBox(); + }); +}); diff --git a/src/test/core/oned/rss/expanded/RSSExpandedBlackBox2.spec.ts b/src/test/core/oned/rss/expanded/RSSExpandedBlackBox2.spec.ts index fe8b1582..5e599f7c 100644 --- a/src/test/core/oned/rss/expanded/RSSExpandedBlackBox2.spec.ts +++ b/src/test/core/oned/rss/expanded/RSSExpandedBlackBox2.spec.ts @@ -36,15 +36,14 @@ class RSSExpandedBlackBox2TestCase extends AbstractBlackBoxSpec { public constructor() { super('src/test/resources/blackbox/rssexpanded-2', new MultiFormatReader(), BarcodeFormat.RSS_EXPANDED); - // this.addTest(21, 23, 0.0); - // this.addTest(21, 23, 180.0); + this.addTest(21, 23, 0.0); + this.addTest(21, 23, 180.0); } } -// describe('RSSExpandedBlackBox2TestCase', () => { -// it('testBlackBox', async () => { -// const test = new RSSExpandedBlackBox2TestCase(); -// await test.testBlackBox(); -// }); -// }); -// +describe('RSSExpandedBlackBox2TestCase', () => { + it('testBlackBox', async () => { + const test = new RSSExpandedBlackBox2TestCase(); + await test.testBlackBox(); + }); +}); diff --git a/src/test/core/oned/rss/expanded/RSSExpandedBlackBox3.spec.ts b/src/test/core/oned/rss/expanded/RSSExpandedBlackBox3.spec.ts index f4a64659..0ff4fa4e 100644 --- a/src/test/core/oned/rss/expanded/RSSExpandedBlackBox3.spec.ts +++ b/src/test/core/oned/rss/expanded/RSSExpandedBlackBox3.spec.ts @@ -36,14 +36,14 @@ class RSSExpandedBlackBox3TestCase extends AbstractBlackBoxSpec { public constructor() { super('src/test/resources/blackbox/rssexpanded-3', new MultiFormatReader(), BarcodeFormat.RSS_EXPANDED); - // this.addTest(117, 117, 0.0); - // this.addTest(117, 117, 180.0); + this.addTest(117, 117, 0.0); + this.addTest(117, 117, 180.0); } } -// describe('RSSExpandedBlackBox3TestCase', () => { -// it('testBlackBox', async () => { -// const test = new RSSExpandedBlackBox3TestCase(); -// await test.testBlackBox(); -// }); -// }); +describe('RSSExpandedBlackBox3TestCase', () => { + it('testBlackBox', async () => { + const test = new RSSExpandedBlackBox3TestCase(); + await test.testBlackBox(); + }); +}); diff --git a/src/test/core/oned/rss/expanded/RSSExpandedInternal.spec.ts b/src/test/core/oned/rss/expanded/RSSExpandedInternal.spec.ts new file mode 100644 index 00000000..b14c48d3 --- /dev/null +++ b/src/test/core/oned/rss/expanded/RSSExpandedInternal.spec.ts @@ -0,0 +1,160 @@ +/* + * 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. + */ + +/* + * These authors would like to acknowledge the Spanish Ministry of Industry, + * Tourism and Trade, for the support in the project TSI020301-2008-2 + * "PIRAmIDE: Personalizable Interactions with Resources on AmI-enabled + * Mobile Dynamic Environments", led by Treelogic + * ( http://www.treelogic.com/ ): + * + * http://www.piramidepse.com/ + */ + +// package com.google.zxing.oned.rss.expanded; + +import { describe } from 'mocha'; +import * as path from 'path'; + +import { BinaryBitmap, FinderPattern, GlobalHistogramBinarizer, NotFoundException, RSSExpandedReader } from '@zxing/library'; +import AbstractBlackBoxSpec from '../../../common/AbstractBlackBox'; +import { assertEquals, assertNotNull } from '../../../util/AssertUtils'; +import SharpImage from '../../../util/SharpImage'; +import SharpImageLuminanceSource from '../../../SharpImageLuminanceSource'; + +/** + * @author Pablo Orduña, University of Deusto (pablo.orduna@deusto.es) + * @author Eduardo Castillejo, University of Deusto (eduardo.castillejo@deusto.es) + */ +class RSSExpandedInternalTestCase /* extends Assert */ { + public async testFindFinderPatterns(): Promise /* throws Exception */ { + const image = await RSSExpandedInternalTestCase.readImage('2.png'); + const binaryMap = new BinaryBitmap(new GlobalHistogramBinarizer(new SharpImageLuminanceSource(image))); + const rowNumber: /*int*/ number = Math.trunc(binaryMap.getHeight() / 2); + const row = binaryMap.getBlackRow(rowNumber, null); + const previousPairs: Array = []; + + const rssExpandedReader = new RSSExpandedReader(); + const pair1 = rssExpandedReader.retrieveNextPair(row, previousPairs, rowNumber); + previousPairs.push(pair1); + let finderPattern = pair1.getFinderPattern(); + assertNotNull(finderPattern); + assertEquals(0, finderPattern.getValue()); + + const pair2 = rssExpandedReader.retrieveNextPair(row, previousPairs, rowNumber); + previousPairs.push(pair2); + finderPattern = pair2.getFinderPattern(); + assertNotNull(finderPattern); + assertEquals(1, finderPattern.getValue()); + + const pair3 = rssExpandedReader.retrieveNextPair(row, previousPairs, rowNumber); + previousPairs.push(pair3); + finderPattern = pair3.getFinderPattern(); + assertNotNull(finderPattern); + assertEquals(1, finderPattern.getValue()); + + try { + rssExpandedReader.retrieveNextPair(row, previousPairs, rowNumber); + // the previous was the last pair + throw new Error('NotFoundException expected'); + } catch (ex) { + if (ex instanceof NotFoundException) { + // ok + } else { + throw new Error('NotFoundException expected'); + } + } + } + + public async testRetrieveNextPairPatterns(): Promise /* throws Exception */ { + const image = await RSSExpandedInternalTestCase.readImage('3.png'); + const binaryMap = new BinaryBitmap(new GlobalHistogramBinarizer(new SharpImageLuminanceSource(image))); + const rowNumber: /*int*/ number = Math.trunc(binaryMap.getHeight() / 2); + const row = binaryMap.getBlackRow(rowNumber, null); + const previousPairs: Array = []; + + const rssExpandedReader = new RSSExpandedReader(); + const pair1 = rssExpandedReader.retrieveNextPair(row, previousPairs, rowNumber); + previousPairs.push(pair1); + let finderPattern = pair1.getFinderPattern(); + assertNotNull(finderPattern); + assertEquals(0, finderPattern.getValue()); + + const pair2 = rssExpandedReader.retrieveNextPair(row, previousPairs, rowNumber); + previousPairs.push(pair2); + finderPattern = pair2.getFinderPattern(); + assertNotNull(finderPattern); + assertEquals(0, finderPattern.getValue()); + } + + public async testDecodeCheckCharacter(): Promise /* throws Exception */ { + const image = await RSSExpandedInternalTestCase.readImage('3.png'); + const binaryMap = new BinaryBitmap(new GlobalHistogramBinarizer(new SharpImageLuminanceSource(image))); + const row = binaryMap.getBlackRow(/* int */ Math.trunc(binaryMap.getHeight() / 2), null); + + const startEnd: Array = [145, 243]; // image pixels where the A1 pattern starts (at 124) and ends (at 214) + const value: /*int*/ number = 0; // A + const finderPatternA1 = new FinderPattern(value, startEnd, startEnd[0], startEnd[1], /* int */ Math.trunc(image.getHeight() / 2)); + //{1, 8, 4, 1, 1}; + const rssExpandedReader = new RSSExpandedReader(); + const dataCharacter = rssExpandedReader.decodeDataCharacter(row, finderPatternA1, true, true); + + assertEquals(98, dataCharacter.getValue()); + } + + public async testDecodeDataCharacter(): Promise /* throws Exception */ { + const image = await RSSExpandedInternalTestCase.readImage('3.png'); + const binaryMap = new BinaryBitmap(new GlobalHistogramBinarizer(new SharpImageLuminanceSource(image))); + const row = binaryMap.getBlackRow(/* int */ Math.trunc(binaryMap.getHeight() / 2), null); + + const startEnd: Array = [145, 243]; // image pixels where the A1 pattern starts (at 124) and ends (at 214) + const value: /*int*/ number = 0; // A + const finderPatternA1 = new FinderPattern(value, startEnd, startEnd[0], startEnd[1], /* int */ Math.trunc(image.getHeight() / 2)); + //{1, 8, 4, 1, 1}; + const rssExpandedReader = new RSSExpandedReader(); + const dataCharacter = rssExpandedReader.decodeDataCharacter(row, finderPatternA1, true, false); + + assertEquals(19, dataCharacter.getValue()); + assertEquals(1007, dataCharacter.getChecksumPortion()); + } + + private static readImage(fileName: string): Promise /* throws IOException */ { + // Image loading adapted from `AbstractBlackBoxSpec.testBlackBoxCountingResults`. + const basePath = AbstractBlackBoxSpec.buildTestBase("src/test/resources/blackbox/rssexpanded-1/"); + const imagePath = path.resolve(basePath, fileName); + const rotatedImage = SharpImage.loadWithRotation(imagePath, 0); + return rotatedImage; + } + +} + +describe('RSSExpandedInternalTestCase', () => { + it('testFindFinderPatterns', async () => { + await new RSSExpandedInternalTestCase().testFindFinderPatterns(); + }); + + it('testRetrieveNextPairPatterns', async () => { + await new RSSExpandedInternalTestCase().testRetrieveNextPairPatterns(); + }); + + it('testDecodeCheckCharacter', async () => { + await new RSSExpandedInternalTestCase().testDecodeCheckCharacter(); + }); + + it('testDecodeDataCharacter', async () => { + await new RSSExpandedInternalTestCase().testDecodeDataCharacter(); + }); +}); \ No newline at end of file diff --git a/src/test/core/oned/rss/expanded/RSSExpandedInternalTestCase.java b/src/test/core/oned/rss/expanded/RSSExpandedInternalTestCase.java deleted file mode 100644 index 34ccdc3e..00000000 --- a/src/test/core/oned/rss/expanded/RSSExpandedInternalTestCase.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * 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. - */ - -/* - * These authors would like to acknowledge the Spanish Ministry of Industry, - * Tourism and Trade, for the support in the project TSI020301-2008-2 - * "PIRAmIDE: Personalizable Interactions with Resources on AmI-enabled - * Mobile Dynamic Environments", led by Treelogic - * ( http://www.treelogic.com/ ): - * - * http://www.piramidepse.com/ - */ - -package com.google.zxing.oned.rss.expanded; - -import java.awt.image.BufferedImage; -import java.io.IOException; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; - -import javax.imageio.ImageIO; - -import com.google.zxing.BinaryBitmap; -import com.google.zxing.BufferedImageLuminanceSource; -import com.google.zxing.NotFoundException; -import com.google.zxing.common.AbstractBlackBoxTestCase; -import com.google.zxing.common.BitArray; -import com.google.zxing.common.GlobalHistogramBinarizer; -import com.google.zxing.oned.rss.DataCharacter; -import com.google.zxing.oned.rss.FinderPattern; - -import org.junit.Assert; -import org.junit.Test; - -/** - * @author Pablo Orduña, University of Deusto (pablo.orduna@deusto.es) - * @author Eduardo Castillejo, University of Deusto (eduardo.castillejo@deusto.es) - */ -public final class RSSExpandedInternalTestCase extends Assert { - - @Test - public void testFindFinderPatterns() throws Exception { - BufferedImage image = readImage("2.png"); - BinaryBitmap binaryMap = new BinaryBitmap(new GlobalHistogramBinarizer(new BufferedImageLuminanceSource(image))); - int rowNumber = binaryMap.getHeight() / 2; - BitArray row = binaryMap.getBlackRow(rowNumber, null); - List previousPairs = new ArrayList<>(); - - RSSExpandedReader rssExpandedReader = new RSSExpandedReader(); - ExpandedPair pair1 = rssExpandedReader.retrieveNextPair(row, previousPairs, rowNumber); - previousPairs.add(pair1); - FinderPattern finderPattern = pair1.getFinderPattern(); - assertNotNull(finderPattern); - assertEquals(0, finderPattern.getValue()); - - ExpandedPair pair2 = rssExpandedReader.retrieveNextPair(row, previousPairs, rowNumber); - previousPairs.add(pair2); - finderPattern = pair2.getFinderPattern(); - assertNotNull(finderPattern); - assertEquals(1, finderPattern.getValue()); - - ExpandedPair pair3 = rssExpandedReader.retrieveNextPair(row, previousPairs, rowNumber); - previousPairs.add(pair3); - finderPattern = pair3.getFinderPattern(); - assertNotNull(finderPattern); - assertEquals(1, finderPattern.getValue()); - - try { - rssExpandedReader.retrieveNextPair(row, previousPairs, rowNumber); - // the previous was the last pair - fail(NotFoundException.class.getName() + " expected"); - } catch (NotFoundException nfe) { - // ok - } - } - - @Test - public void testRetrieveNextPairPatterns() throws Exception { - BufferedImage image = readImage("3.png"); - BinaryBitmap binaryMap = new BinaryBitmap(new GlobalHistogramBinarizer(new BufferedImageLuminanceSource(image))); - int rowNumber = binaryMap.getHeight() / 2; - BitArray row = binaryMap.getBlackRow(rowNumber, null); - List previousPairs = new ArrayList<>(); - - RSSExpandedReader rssExpandedReader = new RSSExpandedReader(); - ExpandedPair pair1 = rssExpandedReader.retrieveNextPair(row, previousPairs, rowNumber); - previousPairs.add(pair1); - FinderPattern finderPattern = pair1.getFinderPattern(); - assertNotNull(finderPattern); - assertEquals(0, finderPattern.getValue()); - - ExpandedPair pair2 = rssExpandedReader.retrieveNextPair(row, previousPairs, rowNumber); - previousPairs.add(pair2); - finderPattern = pair2.getFinderPattern(); - assertNotNull(finderPattern); - assertEquals(0, finderPattern.getValue()); - } - - @Test - public void testDecodeCheckCharacter() throws Exception { - BufferedImage image = readImage("3.png"); - BinaryBitmap binaryMap = new BinaryBitmap(new GlobalHistogramBinarizer(new BufferedImageLuminanceSource(image))); - BitArray row = binaryMap.getBlackRow(binaryMap.getHeight() / 2, null); - - int[] startEnd = {145, 243}; //image pixels where the A1 pattern starts (at 124) and ends (at 214) - int value = 0; // A - FinderPattern finderPatternA1 = new FinderPattern(value, startEnd, startEnd[0], startEnd[1], image.getHeight() / 2); - //{1, 8, 4, 1, 1}; - RSSExpandedReader rssExpandedReader = new RSSExpandedReader(); - DataCharacter dataCharacter = rssExpandedReader.decodeDataCharacter(row, finderPatternA1, true, true); - - assertEquals(98, dataCharacter.getValue()); - } - - @Test - public void testDecodeDataCharacter() throws Exception { - BufferedImage image = readImage("3.png"); - BinaryBitmap binaryMap = new BinaryBitmap(new GlobalHistogramBinarizer(new BufferedImageLuminanceSource(image))); - BitArray row = binaryMap.getBlackRow(binaryMap.getHeight() / 2, null); - - int[] startEnd = {145, 243}; //image pixels where the A1 pattern starts (at 124) and ends (at 214) - int value = 0; // A - FinderPattern finderPatternA1 = new FinderPattern(value, startEnd, startEnd[0], startEnd[1], image.getHeight() / 2); - //{1, 8, 4, 1, 1}; - RSSExpandedReader rssExpandedReader = new RSSExpandedReader(); - DataCharacter dataCharacter = rssExpandedReader.decodeDataCharacter(row, finderPatternA1, true, false); - - assertEquals(19, dataCharacter.getValue()); - assertEquals(1007, dataCharacter.getChecksumPortion()); - } - - private static BufferedImage readImage(String fileName) throws IOException { - Path path = AbstractBlackBoxTestCase.buildTestBase("src/test/resources/blackbox/rssexpanded-1/").resolve(fileName); - return ImageIO.read(path.toFile()); - } - -} diff --git a/src/test/core/oned/rss/expanded/RSSExpandedStackedBlackBox1.spec.ts b/src/test/core/oned/rss/expanded/RSSExpandedStackedBlackBox1.spec.ts index f68386fd..1247cc5e 100644 --- a/src/test/core/oned/rss/expanded/RSSExpandedStackedBlackBox1.spec.ts +++ b/src/test/core/oned/rss/expanded/RSSExpandedStackedBlackBox1.spec.ts @@ -41,16 +41,15 @@ class RSSExpandedStackedBlackBox1TestCase extends AbstractBlackBoxSpec { public constructor() { super('src/test/resources/blackbox/rssexpandedstacked-1', new MultiFormatReader(), BarcodeFormat.RSS_EXPANDED); - // this.addTest(59, 64, 0.0); - // this.addTest(59, 64, 180.0); + this.addTest(60, 65, 0.0); + this.addTest(60, 65, 180.0); } } -// -// describe('RSSExpandedStackedBlackBox1TestCase', () => { -// it('testBlackBox', async () => { -// const test = new RSSExpandedStackedBlackBox1TestCase(); -// await test.testBlackBox(); -// }); -// }); +describe('RSSExpandedStackedBlackBox1TestCase', () => { + it('testBlackBox', async () => { + const test = new RSSExpandedStackedBlackBox1TestCase(); + await test.testBlackBox(); + }); +}); diff --git a/src/test/core/oned/rss/expanded/RSSExpandedStackedBlackBox2.spec.ts b/src/test/core/oned/rss/expanded/RSSExpandedStackedBlackBox2.spec.ts index 27939a06..a8439717 100644 --- a/src/test/core/oned/rss/expanded/RSSExpandedStackedBlackBox2.spec.ts +++ b/src/test/core/oned/rss/expanded/RSSExpandedStackedBlackBox2.spec.ts @@ -41,17 +41,15 @@ class RSSExpandedStackedBlackBox2TestCase extends AbstractBlackBoxSpec { public constructor() { super('src/test/resources/blackbox/rssexpandedstacked-2', new MultiFormatReader(), BarcodeFormat.RSS_EXPANDED); - // this.addTest(2, 7, 0.0); - // this.addTest(2, 7, 180.0); + this.addTest(2, 7, 0.0); + this.addTest(2, 7, 180.0); } } -// describe('RSSExpandedStackedBlackBox2TestCase', () => { -// it('testBlackBox', async () => { -// const test = new RSSExpandedStackedBlackBox2TestCase(); -// await test.testBlackBox(); -// }); -// }); - - +describe('RSSExpandedStackedBlackBox2TestCase', () => { + it('testBlackBox', async () => { + const test = new RSSExpandedStackedBlackBox2TestCase(); + await test.testBlackBox(); + }); +}); diff --git a/src/test/core/oned/rss/expanded/RSSExpandedStackedInternal.spec.ts b/src/test/core/oned/rss/expanded/RSSExpandedStackedInternal.spec.ts new file mode 100644 index 00000000..e8101d1d --- /dev/null +++ b/src/test/core/oned/rss/expanded/RSSExpandedStackedInternal.spec.ts @@ -0,0 +1,92 @@ +/* + * 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. + */ + +/* + * These authors would like to acknowledge the Spanish Ministry of Industry, + * Tourism and Trade, for the support in the project TSI020301-2008-2 + * "PIRAmIDE: Personalizable Interactions with Resources on AmI-enabled + * Mobile Dynamic Environments", led by Treelogic + * ( http://www.treelogic.com/ ): + * + * http://www.piramidepse.com/ + */ + +// package com.google.zxing.oned.rss.expanded; + +import { describe } from 'mocha'; +import { NotFoundException, RSSExpandedReader } from '@zxing/library'; +import TestCaseUtil from '../../../oned/rss/expanded/TestCaseUtil'; +import { assertEquals } from '../../../util/AssertUtils'; + +/** + * Tests {@link RSSExpandedReader} handling of stacked RSS barcodes. + */ +class RSSExpandedStackedInternalTestCase /* extends Assert */ { + public async testDecodingRowByRow(): Promise /* throws Exception */ { + const rssExpandedReader = new RSSExpandedReader(); + + const binaryMap = await TestCaseUtil.getBinaryBitmap('src/test/resources/blackbox/rssexpandedstacked-2/1000.png'); + + const firstRowNumber /* int */ = Math.trunc(binaryMap.getHeight() / 3); + const firstRow = binaryMap.getBlackRow(firstRowNumber, null); + try { + rssExpandedReader.decodeRow2pairs(firstRowNumber, firstRow); + throw new Error('NotFoundException expected'); + } catch (ex) { + if (ex instanceof NotFoundException) { + // ok + } else { + throw new Error('NotFoundException expected'); + } + } + + assertEquals(1, rssExpandedReader.getRows().length); + const firstExpandedRow = rssExpandedReader.getRows()[0]; + assertEquals(firstRowNumber, firstExpandedRow.getRowNumber()); + + assertEquals(2, firstExpandedRow.getPairs().length); + + firstExpandedRow.getPairs()[1].getFinderPattern().getStartEnd()[1] = 0; + + const secondRowNumber /* int */ = Math.trunc(2 * binaryMap.getHeight() / 3); + const secondRow = binaryMap.getBlackRow(secondRowNumber, null); + secondRow.reverse(); + + const totalPairs: Array = rssExpandedReader.decodeRow2pairs(secondRowNumber, secondRow); + + const result = RSSExpandedReader.constructResult(totalPairs); + assertEquals('(01)98898765432106(3202)012345(15)991231', result.getText()); + } + + public async testCompleteDecode(): Promise /* throws Exception */ { + const rssExpandedReader = new RSSExpandedReader(); + + const binaryMap = await TestCaseUtil.getBinaryBitmap('src/test/resources/blackbox/rssexpandedstacked-2/1000.png'); + + const result = rssExpandedReader.decode(binaryMap); + assertEquals('(01)98898765432106(3202)012345(15)991231', result.getText()); + } +} + +describe('RSSExpandedStackedInternalTestCase', () => { + it('testDecodingRowByRow', async () => { + await new RSSExpandedStackedInternalTestCase().testDecodingRowByRow(); + }); + + it('testCompleteDecode', async () => { + await new RSSExpandedStackedInternalTestCase().testCompleteDecode(); + }); +}); diff --git a/src/test/core/oned/rss/expanded/RSSExpandedStackedInternalTestCase.java b/src/test/core/oned/rss/expanded/RSSExpandedStackedInternalTestCase.java deleted file mode 100644 index 7b18046c..00000000 --- a/src/test/core/oned/rss/expanded/RSSExpandedStackedInternalTestCase.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * 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. - */ - -/* - * These authors would like to acknowledge the Spanish Ministry of Industry, - * Tourism and Trade, for the support in the project TSI020301-2008-2 - * "PIRAmIDE: Personalizable Interactions with Resources on AmI-enabled - * Mobile Dynamic Environments", led by Treelogic - * ( http://www.treelogic.com/ ): - * - * http://www.piramidepse.com/ - */ - -package com.google.zxing.oned.rss.expanded; - -import java.util.List; - -import com.google.zxing.oned.OneDReader; -import org.junit.Assert; -import org.junit.Test; - -import com.google.zxing.BinaryBitmap; -import com.google.zxing.NotFoundException; -import com.google.zxing.Result; -import com.google.zxing.common.BitArray; - -/** - * Tests {@link RSSExpandedReader} handling of stacked RSS barcodes. - */ -public final class RSSExpandedStackedInternalTestCase extends Assert { - - @Test - public void testDecodingRowByRow() throws Exception { - RSSExpandedReader rssExpandedReader = new RSSExpandedReader(); - - BinaryBitmap binaryMap = TestCaseUtil.getBinaryBitmap("src/test/resources/blackbox/rssexpandedstacked-2/1000.png"); - - int firstRowNumber = binaryMap.getHeight() / 3; - BitArray firstRow = binaryMap.getBlackRow(firstRowNumber, null); - try { - rssExpandedReader.decodeRow2pairs(firstRowNumber, firstRow); - fail(NotFoundException.class.getName() + " expected"); - } catch (NotFoundException nfe) { - // ok - } - - assertEquals(1, rssExpandedReader.getRows().size()); - ExpandedRow firstExpandedRow = rssExpandedReader.getRows().get(0); - assertEquals(firstRowNumber, firstExpandedRow.getRowNumber()); - - assertEquals(2, firstExpandedRow.getPairs().size()); - - firstExpandedRow.getPairs().get(1).getFinderPattern().getStartEnd()[1] = 0; - - int secondRowNumber = 2 * binaryMap.getHeight() / 3; - BitArray secondRow = binaryMap.getBlackRow(secondRowNumber, null); - secondRow.reverse(); - - List totalPairs = rssExpandedReader.decodeRow2pairs(secondRowNumber, secondRow); - - Result result = RSSExpandedReader.constructResult(totalPairs); - assertEquals("(01)98898765432106(3202)012345(15)991231", result.getText()); - } - - @Test - public void testCompleteDecode() throws Exception { - OneDReader rssExpandedReader = new RSSExpandedReader(); - - BinaryBitmap binaryMap = TestCaseUtil.getBinaryBitmap("src/test/resources/blackbox/rssexpandedstacked-2/1000.png"); - - Result result = rssExpandedReader.decode(binaryMap); - assertEquals("(01)98898765432106(3202)012345(15)991231", result.getText()); - } - - -} diff --git a/src/test/core/oned/rss/expanded/TestCaseUtil.ts b/src/test/core/oned/rss/expanded/TestCaseUtil.ts index 0a596030..66858f12 100644 --- a/src/test/core/oned/rss/expanded/TestCaseUtil.ts +++ b/src/test/core/oned/rss/expanded/TestCaseUtil.ts @@ -49,16 +49,16 @@ export default class TestCaseUtil { /** * @throws IOException */ - private static getBufferedImage(path: string): SharpImage { + private static getBufferedImage(path: string): Promise { let file = AbstractBlackBoxSpec.buildTestBase(path); - return SharpImage.load(file, 0); + return SharpImage.loadWithRotation(file, 0); } /** * @throws IOException */ - static getBinaryBitmap(path: string): BinaryBitmap { - let bufferedImage: SharpImage = TestCaseUtil.getBufferedImage(path); + static async getBinaryBitmap(path: string): Promise { + let bufferedImage: SharpImage = await TestCaseUtil.getBufferedImage(path); let luminanceSource: SharpImageLuminanceSource = new SharpImageLuminanceSource(bufferedImage); return new BinaryBitmap(new GlobalHistogramBinarizer(luminanceSource)); } diff --git a/src/test/core/util/SharpImage.ts b/src/test/core/util/SharpImage.ts index 91b29e7d..8e949f7d 100644 --- a/src/test/core/util/SharpImage.ts +++ b/src/test/core/util/SharpImage.ts @@ -44,6 +44,7 @@ export default class SharpImage { } public static load(path: string, rotation: number): SharpImage { + // FIXME: this returns a SharpImage which has no data nor size. Replaced with loadWithRotation where it caused problems. const wrapper = sharp(path).raw(); diff --git a/src/test/resources/blackbox/rssexpanded-1/33.png b/src/test/resources/blackbox/rssexpanded-1/33.png new file mode 100644 index 00000000..fd2ef698 Binary files /dev/null and b/src/test/resources/blackbox/rssexpanded-1/33.png differ diff --git a/src/test/resources/blackbox/rssexpanded-1/33.txt b/src/test/resources/blackbox/rssexpanded-1/33.txt new file mode 100644 index 00000000..c1741cbe --- /dev/null +++ b/src/test/resources/blackbox/rssexpanded-1/33.txt @@ -0,0 +1 @@ +(420)azaaaaa"agaa&a3 \ No newline at end of file diff --git a/src/test/resources/blackbox/rssexpanded-1/34.png b/src/test/resources/blackbox/rssexpanded-1/34.png new file mode 100644 index 00000000..a5554251 Binary files /dev/null and b/src/test/resources/blackbox/rssexpanded-1/34.png differ diff --git a/src/test/resources/blackbox/rssexpanded-1/34.txt b/src/test/resources/blackbox/rssexpanded-1/34.txt new file mode 100644 index 00000000..75cf858c --- /dev/null +++ b/src/test/resources/blackbox/rssexpanded-1/34.txt @@ -0,0 +1 @@ +(420)aaeaaa"-a23455a \ No newline at end of file diff --git a/src/test/resources/blackbox/rssexpanded-1/35.png b/src/test/resources/blackbox/rssexpanded-1/35.png new file mode 100644 index 00000000..70d30d05 Binary files /dev/null and b/src/test/resources/blackbox/rssexpanded-1/35.png differ diff --git a/src/test/resources/blackbox/rssexpanded-1/35.txt b/src/test/resources/blackbox/rssexpanded-1/35.txt new file mode 100644 index 00000000..82e37996 --- /dev/null +++ b/src/test/resources/blackbox/rssexpanded-1/35.txt @@ -0,0 +1 @@ +(420)WWWWWW3/a"Wa1WW \ No newline at end of file diff --git a/src/test/resources/blackbox/rssexpandedstacked-1/65.png b/src/test/resources/blackbox/rssexpandedstacked-1/65.png new file mode 100644 index 00000000..68deddbe Binary files /dev/null and b/src/test/resources/blackbox/rssexpandedstacked-1/65.png differ diff --git a/src/test/resources/blackbox/rssexpandedstacked-1/65.txt b/src/test/resources/blackbox/rssexpandedstacked-1/65.txt new file mode 100644 index 00000000..e88ae808 --- /dev/null +++ b/src/test/resources/blackbox/rssexpandedstacked-1/65.txt @@ -0,0 +1 @@ +(8110))2345672(1*813 \ No newline at end of file