|
| 1 | +import BarcodeReader from './barcode_reader'; |
| 2 | + |
| 3 | +function TwoOfFiveReader(opts) { |
| 4 | + BarcodeReader.call(this, opts); |
| 5 | + this.barSpaceRatio = [1, 1]; |
| 6 | +} |
| 7 | + |
| 8 | +var N = 1, |
| 9 | + W = 3, |
| 10 | + properties = { |
| 11 | + START_PATTERN: {value: [W, N, W, N, N, N]}, |
| 12 | + STOP_PATTERN: {value: [W, N, N, N, W]}, |
| 13 | + CODE_PATTERN: {value: [ |
| 14 | + [N, N, W, W, N], |
| 15 | + [W, N, N, N, W], |
| 16 | + [N, W, N, N, W], |
| 17 | + [W, W, N, N, N], |
| 18 | + [N, N, W, N, W], |
| 19 | + [W, N, W, N, N], |
| 20 | + [N, W, W, N, N], |
| 21 | + [N, N, N, W, W], |
| 22 | + [W, N, N, W, N], |
| 23 | + [N, W, N, W, N] |
| 24 | + ]}, |
| 25 | + SINGLE_CODE_ERROR: {value: 0.78, writable: true}, |
| 26 | + AVG_CODE_ERROR: {value: 0.30, writable: true}, |
| 27 | + MAX_CORRECTION_FACTOR: {value: 5}, |
| 28 | + FORMAT: {value: "2of5"} |
| 29 | + }; |
| 30 | + |
| 31 | +const startPatternLength = properties.START_PATTERN.value.reduce((sum, val) => sum + val, 0); |
| 32 | + |
| 33 | +TwoOfFiveReader.prototype = Object.create(BarcodeReader.prototype, properties); |
| 34 | +TwoOfFiveReader.prototype.constructor = TwoOfFiveReader; |
| 35 | + |
| 36 | +TwoOfFiveReader.prototype._findPattern = function(pattern, offset, isWhite, tryHarder) { |
| 37 | + var counter = [], |
| 38 | + self = this, |
| 39 | + i, |
| 40 | + counterPos = 0, |
| 41 | + bestMatch = { |
| 42 | + error: Number.MAX_VALUE, |
| 43 | + code: -1, |
| 44 | + start: 0, |
| 45 | + end: 0 |
| 46 | + }, |
| 47 | + error, |
| 48 | + j, |
| 49 | + sum, |
| 50 | + normalized, |
| 51 | + epsilon = self.AVG_CODE_ERROR; |
| 52 | + |
| 53 | + isWhite = isWhite || false; |
| 54 | + tryHarder = tryHarder || false; |
| 55 | + |
| 56 | + if (!offset) { |
| 57 | + offset = self._nextSet(self._row); |
| 58 | + } |
| 59 | + |
| 60 | + for ( i = 0; i < pattern.length; i++) { |
| 61 | + counter[i] = 0; |
| 62 | + } |
| 63 | + |
| 64 | + for ( i = offset; i < self._row.length; i++) { |
| 65 | + if (self._row[i] ^ isWhite) { |
| 66 | + counter[counterPos]++; |
| 67 | + } else { |
| 68 | + if (counterPos === counter.length - 1) { |
| 69 | + sum = 0; |
| 70 | + for ( j = 0; j < counter.length; j++) { |
| 71 | + sum += counter[j]; |
| 72 | + } |
| 73 | + error = self._matchPattern(counter, pattern); |
| 74 | + if (error < epsilon) { |
| 75 | + bestMatch.error = error; |
| 76 | + bestMatch.start = i - sum; |
| 77 | + bestMatch.end = i; |
| 78 | + return bestMatch; |
| 79 | + } |
| 80 | + if (tryHarder) { |
| 81 | + for (j = 0; j < counter.length - 2; j++) { |
| 82 | + counter[j] = counter[j + 2]; |
| 83 | + } |
| 84 | + counter[counter.length - 2] = 0; |
| 85 | + counter[counter.length - 1] = 0; |
| 86 | + counterPos--; |
| 87 | + } else { |
| 88 | + return null; |
| 89 | + } |
| 90 | + } else { |
| 91 | + counterPos++; |
| 92 | + } |
| 93 | + counter[counterPos] = 1; |
| 94 | + isWhite = !isWhite; |
| 95 | + } |
| 96 | + } |
| 97 | + return null; |
| 98 | +}; |
| 99 | + |
| 100 | +TwoOfFiveReader.prototype._findStart = function() { |
| 101 | + var self = this, |
| 102 | + leadingWhitespaceStart, |
| 103 | + offset = self._nextSet(self._row), |
| 104 | + startInfo, |
| 105 | + narrowBarWidth = 1; |
| 106 | + |
| 107 | + while (!startInfo) { |
| 108 | + startInfo = self._findPattern(self.START_PATTERN, offset, false, true); |
| 109 | + if (!startInfo) { |
| 110 | + return null; |
| 111 | + } |
| 112 | + narrowBarWidth = Math.floor((startInfo.end - startInfo.start) / startPatternLength); |
| 113 | + leadingWhitespaceStart = startInfo.start - narrowBarWidth * 5; |
| 114 | + if (leadingWhitespaceStart >= 0) { |
| 115 | + if (self._matchRange(leadingWhitespaceStart, startInfo.start, 0)) { |
| 116 | + return startInfo; |
| 117 | + } |
| 118 | + } |
| 119 | + offset = startInfo.end; |
| 120 | + startInfo = null; |
| 121 | + } |
| 122 | +}; |
| 123 | + |
| 124 | +TwoOfFiveReader.prototype._verifyTrailingWhitespace = function(endInfo) { |
| 125 | + var self = this, |
| 126 | + trailingWhitespaceEnd; |
| 127 | + |
| 128 | + trailingWhitespaceEnd = endInfo.end + ((endInfo.end - endInfo.start) / 2); |
| 129 | + if (trailingWhitespaceEnd < self._row.length) { |
| 130 | + if (self._matchRange(endInfo.end, trailingWhitespaceEnd, 0)) { |
| 131 | + return endInfo; |
| 132 | + } |
| 133 | + } |
| 134 | + return null; |
| 135 | +}; |
| 136 | + |
| 137 | +TwoOfFiveReader.prototype._findEnd = function() { |
| 138 | + var self = this, |
| 139 | + endInfo, |
| 140 | + tmp, |
| 141 | + offset; |
| 142 | + |
| 143 | + self._row.reverse(); |
| 144 | + offset = self._nextSet(self._row); |
| 145 | + endInfo = self._findPattern(self.STOP_PATTERN, offset, false, true); |
| 146 | + self._row.reverse(); |
| 147 | + |
| 148 | + if (endInfo === null) { |
| 149 | + return null; |
| 150 | + } |
| 151 | + |
| 152 | + // reverse numbers |
| 153 | + tmp = endInfo.start; |
| 154 | + endInfo.start = self._row.length - endInfo.end; |
| 155 | + endInfo.end = self._row.length - tmp; |
| 156 | + |
| 157 | + return endInfo !== null ? self._verifyTrailingWhitespace(endInfo) : null; |
| 158 | +}; |
| 159 | + |
| 160 | +TwoOfFiveReader.prototype._decodeCode = function(counter) { |
| 161 | + var j, |
| 162 | + self = this, |
| 163 | + sum = 0, |
| 164 | + normalized, |
| 165 | + error, |
| 166 | + epsilon = self.AVG_CODE_ERROR, |
| 167 | + code, |
| 168 | + bestMatch = { |
| 169 | + error: Number.MAX_VALUE, |
| 170 | + code: -1, |
| 171 | + start: 0, |
| 172 | + end: 0 |
| 173 | + }; |
| 174 | + |
| 175 | + for ( j = 0; j < counter.length; j++) { |
| 176 | + sum += counter[j]; |
| 177 | + } |
| 178 | + for (code = 0; code < self.CODE_PATTERN.length; code++) { |
| 179 | + error = self._matchPattern(counter, self.CODE_PATTERN[code]); |
| 180 | + if (error < bestMatch.error) { |
| 181 | + bestMatch.code = code; |
| 182 | + bestMatch.error = error; |
| 183 | + } |
| 184 | + } |
| 185 | + if (bestMatch.error < epsilon) { |
| 186 | + return bestMatch; |
| 187 | + } |
| 188 | +}; |
| 189 | + |
| 190 | +TwoOfFiveReader.prototype._decodePayload = function(counters, result, decodedCodes) { |
| 191 | + var i, |
| 192 | + self = this, |
| 193 | + pos = 0, |
| 194 | + counterLength = counters.length, |
| 195 | + counter = [0, 0, 0, 0, 0], |
| 196 | + code; |
| 197 | + |
| 198 | + while (pos < counterLength) { |
| 199 | + for (i = 0; i < 5; i++) { |
| 200 | + counter[i] = counters[pos] * this.barSpaceRatio[0]; |
| 201 | + pos += 2; |
| 202 | + } |
| 203 | + code = self._decodeCode(counter); |
| 204 | + if (!code) { |
| 205 | + return null; |
| 206 | + } |
| 207 | + result.push(code.code + ""); |
| 208 | + decodedCodes.push(code); |
| 209 | + } |
| 210 | + return code; |
| 211 | +}; |
| 212 | + |
| 213 | +TwoOfFiveReader.prototype._verifyCounterLength = function(counters) { |
| 214 | + return (counters.length % 10 === 0); |
| 215 | +}; |
| 216 | + |
| 217 | +TwoOfFiveReader.prototype._decode = function() { |
| 218 | + var startInfo, |
| 219 | + endInfo, |
| 220 | + self = this, |
| 221 | + code, |
| 222 | + result = [], |
| 223 | + decodedCodes = [], |
| 224 | + counters; |
| 225 | + |
| 226 | + startInfo = self._findStart(); |
| 227 | + if (!startInfo) { |
| 228 | + return null; |
| 229 | + } |
| 230 | + decodedCodes.push(startInfo); |
| 231 | + |
| 232 | + endInfo = self._findEnd(); |
| 233 | + if (!endInfo) { |
| 234 | + return null; |
| 235 | + } |
| 236 | + |
| 237 | + counters = self._fillCounters(startInfo.end, endInfo.start, false); |
| 238 | + if (!self._verifyCounterLength(counters)) { |
| 239 | + return null; |
| 240 | + } |
| 241 | + code = self._decodePayload(counters, result, decodedCodes); |
| 242 | + if (!code) { |
| 243 | + return null; |
| 244 | + } |
| 245 | + if (result.length % 2 !== 0 || |
| 246 | + result.length < 6) { |
| 247 | + return null; |
| 248 | + } |
| 249 | + |
| 250 | + decodedCodes.push(endInfo); |
| 251 | + return { |
| 252 | + code: result.join(""), |
| 253 | + start: startInfo.start, |
| 254 | + end: endInfo.end, |
| 255 | + startInfo: startInfo, |
| 256 | + decodedCodes: decodedCodes |
| 257 | + }; |
| 258 | +}; |
| 259 | + |
| 260 | +export default TwoOfFiveReader; |
0 commit comments