@sythos/js_barcode_universal 1.5.8 → 1.5.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +5 -4
- package/NOTICE.md +2 -2
- package/README.md +61 -13
- package/bundle/sythos-barcode.esm.js +11658 -11244
- package/bundle/sythos-barcode.js +11658 -11244
- package/package.json +10 -3
- package/src/index.d.ts +1 -1
- package/src/index.js +508 -516
- package/src/js/aztec/decoder.js +208 -199
- package/src/js/aztec/detector.js +153 -126
- package/src/js/aztec/encoder.js +172 -153
- package/src/js/aztec/high-level.js +106 -101
- package/src/js/aztec/index.js +1 -12
- package/src/js/aztec/tables.js +106 -120
- package/src/js/aztecrune/decoder.js +76 -85
- package/src/js/aztecrune/detector.js +107 -93
- package/src/js/aztecrune/encoder.js +50 -68
- package/src/js/aztecrune/index.js +1 -16
- package/src/js/aztecrune/tables.js +59 -54
- package/src/js/compactpdf417/decoder.js +69 -74
- package/src/js/compactpdf417/detector.js +74 -75
- package/src/js/compactpdf417/encoder.js +68 -81
- package/src/js/compactpdf417/index.js +1 -13
- package/src/js/compactpdf417/tables.js +71 -80
- package/src/js/core/bit-buffer.js +106 -122
- package/src/js/core/bit-matrix.js +196 -194
- package/src/js/core/errors.js +13 -15
- package/src/js/core/galois-field.js +119 -133
- package/src/js/core/index.js +2 -19
- package/src/js/core/reed-solomon.js +195 -204
- package/src/js/databar/codec.js +93 -108
- package/src/js/databar/decoder.js +149 -140
- package/src/js/databar/encoder.js +47 -55
- package/src/js/databar/gs1.js +122 -123
- package/src/js/databar/index.js +4 -36
- package/src/js/databar/patterns.js +81 -85
- package/src/js/databar/tables.js +51 -52
- package/src/js/datamatrix/decoder.js +216 -182
- package/src/js/datamatrix/detector.js +161 -132
- package/src/js/datamatrix/encoder.js +192 -133
- package/src/js/datamatrix/index.js +1 -9
- package/src/js/datamatrix/tables.js +68 -69
- package/src/js/frameqr/decoder.js +102 -125
- package/src/js/frameqr/detector.js +101 -94
- package/src/js/frameqr/encoder.js +67 -80
- package/src/js/frameqr/index.js +1 -10
- package/src/js/frameqr/tables.js +145 -163
- package/src/js/image/binarizer.js +146 -162
- package/src/js/image/grid-sampler.js +63 -79
- package/src/js/image/index.js +0 -2
- package/src/js/image/luminance.js +129 -140
- package/src/js/image/perspective.js +112 -149
- package/src/js/micropdf417/compaction.js +64 -65
- package/src/js/micropdf417/decoder.js +106 -109
- package/src/js/micropdf417/detector.js +73 -70
- package/src/js/micropdf417/encoder.js +129 -154
- package/src/js/micropdf417/error-correction.js +6 -11
- package/src/js/micropdf417/index.js +2 -16
- package/src/js/micropdf417/tables.js +112 -99
- package/src/js/microqr/decoder.js +177 -182
- package/src/js/microqr/detector.js +240 -256
- package/src/js/microqr/encoder.js +239 -194
- package/src/js/microqr/index.js +0 -2
- package/src/js/microqr/tables.js +208 -200
- package/src/js/oned/addons.js +167 -191
- package/src/js/oned/index.js +27 -58
- package/src/js/oned/patterns.js +194 -213
- package/src/js/oned/reader.js +1003 -984
- package/src/js/oned/writers.js +411 -466
- package/src/js/pdf417/compaction.js +315 -222
- package/src/js/pdf417/decoder.js +71 -36
- package/src/js/pdf417/detector.js +377 -361
- package/src/js/pdf417/encoder.js +57 -46
- package/src/js/pdf417/error-correction.js +5 -8
- package/src/js/pdf417/tables.js +248 -250
- package/src/js/qr/decoder.js +311 -347
- package/src/js/qr/detector.js +378 -395
- package/src/js/qr/encoder.js +514 -599
- package/src/js/qr/index.js +0 -2
- package/src/js/qr/tables.js +375 -422
- package/src/js/render/image-data.js +57 -62
- package/src/js/render/index.js +21 -23
- package/src/js/render/options.js +82 -90
- package/src/js/render/png.js +144 -169
- package/src/js/render/svg.js +41 -44
- package/src/js/render/webgl.js +103 -109
- package/src/js/render/webgpu.js +205 -217
- package/src/js/rmqr/decoder.js +161 -53
- package/src/js/rmqr/detector.js +73 -41
- package/src/js/rmqr/encoder.js +253 -104
- package/src/js/rmqr/index.js +1 -5
- package/src/js/rmqr/tables.js +108 -85
- package/src/ts/aztec/decoder.ts +317 -0
- package/src/ts/aztec/detector.ts +224 -0
- package/src/ts/aztec/encoder.ts +257 -0
- package/src/ts/aztec/high-level.ts +211 -0
- package/src/ts/aztec/index.ts +45 -0
- package/src/ts/aztec/tables.ts +210 -0
- package/src/ts/aztecrune/decoder.ts +155 -0
- package/src/ts/aztecrune/detector.ts +166 -0
- package/src/ts/aztecrune/encoder.ts +121 -0
- package/src/ts/aztecrune/index.ts +49 -0
- package/src/ts/aztecrune/tables.ts +137 -0
- package/src/ts/compactpdf417/decoder.ts +128 -0
- package/src/ts/compactpdf417/detector.ts +139 -0
- package/src/ts/compactpdf417/encoder.ts +140 -0
- package/src/ts/compactpdf417/index.ts +17 -0
- package/src/ts/compactpdf417/tables.ts +173 -0
- package/src/ts/core/bit-buffer.ts +174 -0
- package/src/ts/core/bit-matrix.ts +241 -0
- package/src/ts/core/errors.ts +61 -0
- package/src/ts/core/galois-field.ts +207 -0
- package/src/ts/core/index.ts +56 -0
- package/src/ts/core/reed-solomon.ts +327 -0
- package/src/ts/databar/codec.ts +181 -0
- package/src/ts/databar/decoder.ts +215 -0
- package/src/ts/databar/encoder.ts +96 -0
- package/src/ts/databar/gs1.ts +177 -0
- package/src/ts/databar/index.ts +68 -0
- package/src/ts/databar/patterns.ts +143 -0
- package/src/ts/databar/tables.ts +124 -0
- package/src/ts/datamatrix/decoder.ts +262 -0
- package/src/ts/datamatrix/detector.ts +225 -0
- package/src/ts/datamatrix/encoder.ts +191 -0
- package/src/ts/datamatrix/index.ts +42 -0
- package/src/ts/datamatrix/tables.ts +123 -0
- package/src/ts/frameqr/decoder.ts +239 -0
- package/src/ts/frameqr/detector.ts +192 -0
- package/src/ts/frameqr/encoder.ts +156 -0
- package/src/ts/frameqr/index.ts +42 -0
- package/src/ts/frameqr/tables.ts +270 -0
- package/src/ts/image/binarizer.ts +270 -0
- package/src/ts/image/grid-sampler.ts +164 -0
- package/src/ts/image/index.ts +40 -0
- package/src/ts/image/luminance.ts +196 -0
- package/src/ts/image/perspective.ts +195 -0
- package/src/ts/index.d.ts +1 -1
- package/src/ts/index.ts +790 -0
- package/src/ts/micropdf417/compaction.ts +116 -0
- package/src/ts/micropdf417/decoder.ts +183 -0
- package/src/ts/micropdf417/detector.ts +149 -0
- package/src/ts/micropdf417/encoder.ts +209 -0
- package/src/ts/micropdf417/error-correction.ts +55 -0
- package/src/ts/micropdf417/index.ts +49 -0
- package/src/ts/micropdf417/tables.ts +184 -0
- package/src/ts/microqr/decoder.ts +245 -0
- package/src/ts/microqr/detector.ts +355 -0
- package/src/ts/microqr/encoder.ts +269 -0
- package/src/ts/microqr/index.ts +36 -0
- package/src/ts/microqr/tables.ts +316 -0
- package/src/ts/oned/addons.ts +420 -0
- package/src/ts/oned/index.ts +106 -0
- package/src/ts/oned/patterns.ts +384 -0
- package/src/ts/oned/reader.ts +1364 -0
- package/src/ts/oned/writers.ts +746 -0
- package/src/ts/pdf417/compaction.ts +298 -0
- package/src/ts/pdf417/decoder.ts +75 -0
- package/src/ts/pdf417/detector.ts +468 -0
- package/src/ts/pdf417/encoder.ts +91 -0
- package/src/ts/pdf417/error-correction.ts +47 -0
- package/src/ts/pdf417/index.ts +6 -0
- package/src/ts/pdf417/tables.ts +317 -0
- package/src/ts/qr/decoder.ts +575 -0
- package/src/ts/qr/detector.ts +630 -0
- package/src/ts/qr/encoder.ts +958 -0
- package/src/ts/qr/index.ts +44 -0
- package/src/ts/qr/tables.ts +737 -0
- package/src/ts/render/image-data.ts +125 -0
- package/src/ts/render/index.ts +130 -0
- package/src/ts/render/options.ts +160 -0
- package/src/ts/render/png.ts +295 -0
- package/src/ts/render/svg.ts +120 -0
- package/src/ts/render/webgl.ts +206 -0
- package/src/ts/render/webgpu.ts +369 -0
- package/src/ts/rmqr/decoder.ts +101 -0
- package/src/ts/rmqr/detector.ts +90 -0
- package/src/ts/rmqr/encoder.ts +172 -0
- package/src/ts/rmqr/index.ts +37 -0
- package/src/ts/rmqr/tables.ts +154 -0
package/src/js/qr/decoder.js
CHANGED
|
@@ -27,7 +27,6 @@
|
|
|
27
27
|
*
|
|
28
28
|
* Original work. No code from any other barcode implementation.
|
|
29
29
|
*/
|
|
30
|
-
|
|
31
30
|
/**
|
|
32
31
|
* QR Code decoder.
|
|
33
32
|
*
|
|
@@ -44,26 +43,12 @@
|
|
|
44
43
|
*
|
|
45
44
|
* @module qr/decoder
|
|
46
45
|
*/
|
|
47
|
-
|
|
48
46
|
import { BitReader } from '../core/bit-buffer.js';
|
|
49
47
|
import { ChecksumError, FormatError } from '../core/errors.js';
|
|
50
48
|
import { GF256_QR } from '../core/galois-field.js';
|
|
51
49
|
import { rsDecode } from '../core/reed-solomon.js';
|
|
52
|
-
import {
|
|
53
|
-
ECC_LEVELS,
|
|
54
|
-
MAX_VERSION,
|
|
55
|
-
MIN_VERSION,
|
|
56
|
-
MODE,
|
|
57
|
-
VERSION_INFO_MIN,
|
|
58
|
-
blockLayout,
|
|
59
|
-
countBits,
|
|
60
|
-
dataModuleOrder,
|
|
61
|
-
formatInfoPositions,
|
|
62
|
-
maskBit,
|
|
63
|
-
versionSize,
|
|
64
|
-
} from './tables.js';
|
|
50
|
+
import { ECC_LEVELS, MAX_VERSION, MIN_VERSION, MODE, VERSION_INFO_MIN, blockLayout, countBits, dataModuleOrder, formatInfoPositions, maskBit, } from './tables.js';
|
|
65
51
|
import { ALPHANUMERIC_CHARS, formatInfoBits, versionInfoBits } from './encoder.js';
|
|
66
|
-
|
|
67
52
|
/**
|
|
68
53
|
* Maximum bit errors tolerated when matching a BCH field.
|
|
69
54
|
*
|
|
@@ -73,41 +58,37 @@ import { ALPHANUMERIC_CHARS, formatInfoBits, versionInfoBits } from './encoder.j
|
|
|
73
58
|
* would be caught downstream by Reed-Solomon anyway.
|
|
74
59
|
*/
|
|
75
60
|
const BCH_MAX_DISTANCE = 3;
|
|
76
|
-
|
|
77
61
|
/** Every legal masked format value, with what it means. */
|
|
78
62
|
const FORMAT_CODES = (() => {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
63
|
+
const codes = [];
|
|
64
|
+
for (let l = 0; l < ECC_LEVELS.length; l++) {
|
|
65
|
+
for (let mask = 0; mask < 8; mask++) {
|
|
66
|
+
codes.push({ bits: formatInfoBits(ECC_LEVELS[l], mask), ecc: ECC_LEVELS[l], mask });
|
|
67
|
+
}
|
|
83
68
|
}
|
|
84
|
-
|
|
85
|
-
return codes;
|
|
69
|
+
return codes;
|
|
86
70
|
})();
|
|
87
|
-
|
|
88
71
|
/** Every legal version information value. */
|
|
89
72
|
const VERSION_CODES = (() => {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
73
|
+
const codes = [];
|
|
74
|
+
for (let v = VERSION_INFO_MIN; v <= MAX_VERSION; v++) {
|
|
75
|
+
codes.push({ bits: versionInfoBits(v), version: v });
|
|
76
|
+
}
|
|
77
|
+
return codes;
|
|
95
78
|
})();
|
|
96
|
-
|
|
97
79
|
/**
|
|
98
80
|
* @param {number} a @param {number} b
|
|
99
81
|
* @returns {number} Number of differing bits.
|
|
100
82
|
*/
|
|
101
83
|
function hammingDistance(a, b) {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
84
|
+
let v = a ^ b;
|
|
85
|
+
let n = 0;
|
|
86
|
+
while (v !== 0) {
|
|
87
|
+
v &= v - 1;
|
|
88
|
+
n++;
|
|
89
|
+
}
|
|
90
|
+
return n;
|
|
109
91
|
}
|
|
110
|
-
|
|
111
92
|
/**
|
|
112
93
|
* Nearest legal codeword, or null when nothing is close enough.
|
|
113
94
|
*
|
|
@@ -117,25 +98,24 @@ function hammingDistance(a, b) {
|
|
|
117
98
|
* @returns {(T & {distance: number}) | null}
|
|
118
99
|
*/
|
|
119
100
|
function nearestCode(value, codes) {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
101
|
+
let best = null;
|
|
102
|
+
let bestDistance = BCH_MAX_DISTANCE + 1;
|
|
103
|
+
let ambiguous = false;
|
|
104
|
+
for (let i = 0; i < codes.length; i++) {
|
|
105
|
+
const d = hammingDistance(value, codes[i].bits);
|
|
106
|
+
if (d < bestDistance) {
|
|
107
|
+
bestDistance = d;
|
|
108
|
+
best = codes[i];
|
|
109
|
+
ambiguous = false;
|
|
110
|
+
}
|
|
111
|
+
else if (d === bestDistance) {
|
|
112
|
+
ambiguous = true;
|
|
113
|
+
}
|
|
132
114
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
return Object.assign({ distance: bestDistance }, best);
|
|
115
|
+
if (!best || bestDistance > BCH_MAX_DISTANCE || ambiguous)
|
|
116
|
+
return null;
|
|
117
|
+
return Object.assign({ distance: bestDistance }, best);
|
|
137
118
|
}
|
|
138
|
-
|
|
139
119
|
/**
|
|
140
120
|
* Read a run of module positions as a little-endian bit value.
|
|
141
121
|
*
|
|
@@ -144,13 +124,13 @@ function nearestCode(value, codes) {
|
|
|
144
124
|
* @returns {number}
|
|
145
125
|
*/
|
|
146
126
|
function readBits(m, positions) {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
127
|
+
let value = 0;
|
|
128
|
+
for (let i = 0; i < positions.length; i++) {
|
|
129
|
+
if (m.get(positions[i][0], positions[i][1]))
|
|
130
|
+
value |= 1 << i;
|
|
131
|
+
}
|
|
132
|
+
return value;
|
|
152
133
|
}
|
|
153
|
-
|
|
154
134
|
/**
|
|
155
135
|
* Recover the error correction level and mask.
|
|
156
136
|
*
|
|
@@ -162,20 +142,19 @@ function readBits(m, positions) {
|
|
|
162
142
|
* @returns {{ecc: string, mask: number}}
|
|
163
143
|
*/
|
|
164
144
|
function readFormatInfo(m, size) {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
145
|
+
const [a, b] = formatInfoPositions(size);
|
|
146
|
+
const candidates = [nearestCode(readBits(m, a), FORMAT_CODES), nearestCode(readBits(m, b), FORMAT_CODES)];
|
|
147
|
+
let best = null;
|
|
148
|
+
for (let i = 0; i < candidates.length; i++) {
|
|
149
|
+
const c = candidates[i];
|
|
150
|
+
if (c && (!best || c.distance < best.distance))
|
|
151
|
+
best = c;
|
|
152
|
+
}
|
|
153
|
+
if (!best) {
|
|
154
|
+
throw new FormatError('QR: format information is unreadable in both copies');
|
|
155
|
+
}
|
|
156
|
+
return { ecc: best.ecc, mask: best.mask };
|
|
177
157
|
}
|
|
178
|
-
|
|
179
158
|
/**
|
|
180
159
|
* Cross-check the version information against the symbol's dimension.
|
|
181
160
|
*
|
|
@@ -188,42 +167,38 @@ function readFormatInfo(m, size) {
|
|
|
188
167
|
* @returns {number}
|
|
189
168
|
*/
|
|
190
169
|
function readVersionInfo(m, size, fromDimension) {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
);
|
|
223
|
-
}
|
|
224
|
-
return best.version;
|
|
170
|
+
if (fromDimension < VERSION_INFO_MIN)
|
|
171
|
+
return fromDimension;
|
|
172
|
+
/** @type {Array<[number, number]>} */
|
|
173
|
+
const bottomLeft = [];
|
|
174
|
+
/** @type {Array<[number, number]>} */
|
|
175
|
+
const topRight = [];
|
|
176
|
+
for (let i = 0; i < 18; i++) {
|
|
177
|
+
const major = Math.floor(i / 3);
|
|
178
|
+
const minor = i % 3;
|
|
179
|
+
bottomLeft.push([major, size - 11 + minor]);
|
|
180
|
+
topRight.push([size - 11 + minor, major]);
|
|
181
|
+
}
|
|
182
|
+
const candidates = [
|
|
183
|
+
nearestCode(readBits(m, bottomLeft), VERSION_CODES),
|
|
184
|
+
nearestCode(readBits(m, topRight), VERSION_CODES),
|
|
185
|
+
];
|
|
186
|
+
let best = null;
|
|
187
|
+
for (let i = 0; i < candidates.length; i++) {
|
|
188
|
+
const c = candidates[i];
|
|
189
|
+
if (c && (!best || c.distance < best.distance))
|
|
190
|
+
best = c;
|
|
191
|
+
}
|
|
192
|
+
// Unreadable version information is survivable; contradictory version
|
|
193
|
+
// information is not.
|
|
194
|
+
if (!best)
|
|
195
|
+
return fromDimension;
|
|
196
|
+
if (best.version !== fromDimension) {
|
|
197
|
+
throw new FormatError(`QR: version information says ${best.version} but the symbol is ` +
|
|
198
|
+
`${size}x${size} modules (version ${fromDimension})`);
|
|
199
|
+
}
|
|
200
|
+
return best.version;
|
|
225
201
|
}
|
|
226
|
-
|
|
227
202
|
/**
|
|
228
203
|
* Unmask and read the interleaved codewords out of the module grid.
|
|
229
204
|
*
|
|
@@ -232,21 +207,20 @@ function readVersionInfo(m, size, fromDimension) {
|
|
|
232
207
|
* @returns {Uint8Array}
|
|
233
208
|
*/
|
|
234
209
|
function readCodewords(m, version, mask, totalCodewords) {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
210
|
+
const order = dataModuleOrder(version);
|
|
211
|
+
const out = new Uint8Array(totalCodewords);
|
|
212
|
+
const available = totalCodewords * 8;
|
|
213
|
+
for (let p = 0, bit = 0; p < order.length && bit < available; p += 2, bit++) {
|
|
214
|
+
const x = order[p];
|
|
215
|
+
const y = order[p + 1];
|
|
216
|
+
let dark = m.get(x, y);
|
|
217
|
+
if (maskBit(mask, x, y))
|
|
218
|
+
dark = !dark;
|
|
219
|
+
if (dark)
|
|
220
|
+
out[bit >> 3] |= 0x80 >> (bit & 7);
|
|
221
|
+
}
|
|
222
|
+
return out;
|
|
248
223
|
}
|
|
249
|
-
|
|
250
224
|
/**
|
|
251
225
|
* Undo the block interleaving and repair each block.
|
|
252
226
|
*
|
|
@@ -255,46 +229,41 @@ function readCodewords(m, version, mask, totalCodewords) {
|
|
|
255
229
|
* @returns {{data: Uint8Array, corrections: number}}
|
|
256
230
|
*/
|
|
257
231
|
function deinterleaveAndCorrect(codewords, layout) {
|
|
258
|
-
|
|
259
|
-
for (let b = 0; b < layout.blockCount; b++) {
|
|
260
|
-
counts[b] = b < layout.group1Blocks ? layout.group1DataCount : layout.group2DataCount;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
const blocks = [];
|
|
264
|
-
for (let b = 0; b < layout.blockCount; b++) {
|
|
265
|
-
blocks.push(new Array(counts[b] + layout.eccPerBlock).fill(0));
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
let n = 0;
|
|
269
|
-
const maxData = layout.group2Blocks > 0 ? layout.group2DataCount : layout.group1DataCount;
|
|
270
|
-
for (let i = 0; i < maxData; i++) {
|
|
232
|
+
const counts = new Array(layout.blockCount);
|
|
271
233
|
for (let b = 0; b < layout.blockCount; b++) {
|
|
272
|
-
|
|
234
|
+
counts[b] = b < layout.group1Blocks ? layout.group1DataCount : layout.group2DataCount;
|
|
273
235
|
}
|
|
274
|
-
|
|
275
|
-
for (let i = 0; i < layout.eccPerBlock; i++) {
|
|
236
|
+
const blocks = [];
|
|
276
237
|
for (let b = 0; b < layout.blockCount; b++) {
|
|
277
|
-
|
|
238
|
+
blocks.push(new Array(counts[b] + layout.eccPerBlock).fill(0));
|
|
239
|
+
}
|
|
240
|
+
let n = 0;
|
|
241
|
+
const maxData = layout.group2Blocks > 0 ? layout.group2DataCount : layout.group1DataCount;
|
|
242
|
+
for (let i = 0; i < maxData; i++) {
|
|
243
|
+
for (let b = 0; b < layout.blockCount; b++) {
|
|
244
|
+
if (i < counts[b])
|
|
245
|
+
blocks[b][i] = codewords[n++];
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
for (let i = 0; i < layout.eccPerBlock; i++) {
|
|
249
|
+
for (let b = 0; b < layout.blockCount; b++) {
|
|
250
|
+
blocks[b][counts[b] + i] = codewords[n++];
|
|
251
|
+
}
|
|
278
252
|
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
return { data, corrections };
|
|
253
|
+
const data = new Uint8Array(layout.totalDataCodewords);
|
|
254
|
+
let offset = 0;
|
|
255
|
+
let corrections = 0;
|
|
256
|
+
for (let b = 0; b < layout.blockCount; b++) {
|
|
257
|
+
corrections += rsDecode(blocks[b], layout.eccPerBlock, GF256_QR, 0);
|
|
258
|
+
for (let i = 0; i < counts[b]; i++)
|
|
259
|
+
data[offset + i] = blocks[b][i];
|
|
260
|
+
offset += counts[b];
|
|
261
|
+
}
|
|
262
|
+
return { data, corrections };
|
|
292
263
|
}
|
|
293
|
-
|
|
294
264
|
/* ------------------------------------------------------------------ *
|
|
295
265
|
* Bitstream interpretation
|
|
296
266
|
* ------------------------------------------------------------------ */
|
|
297
|
-
|
|
298
267
|
/**
|
|
299
268
|
* Unpack a 13-bit kanji value back to Shift_JIS. Inverse of the encoder's
|
|
300
269
|
* `sjisToThirteenBits`; the round-trip is asserted by the test suite.
|
|
@@ -303,44 +272,42 @@ function deinterleaveAndCorrect(codewords, layout) {
|
|
|
303
272
|
* @returns {number} 16-bit Shift_JIS value.
|
|
304
273
|
*/
|
|
305
274
|
function thirteenBitsToSjis(value) {
|
|
306
|
-
|
|
307
|
-
|
|
275
|
+
const combined = (Math.floor(value / 0xc0) << 8) | (value % 0xc0);
|
|
276
|
+
return combined + (combined + 0x8140 <= 0x9ffc ? 0x8140 : 0xc140);
|
|
308
277
|
}
|
|
309
|
-
|
|
310
278
|
/** ECI assignment numbers this decoder maps to a concrete codec label. */
|
|
311
279
|
const ECI_LABELS = {
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
280
|
+
0: 'iso-8859-1',
|
|
281
|
+
1: 'iso-8859-1',
|
|
282
|
+
2: 'iso-8859-1',
|
|
283
|
+
3: 'iso-8859-1',
|
|
284
|
+
4: 'iso-8859-2',
|
|
285
|
+
5: 'iso-8859-3',
|
|
286
|
+
6: 'iso-8859-4',
|
|
287
|
+
7: 'iso-8859-5',
|
|
288
|
+
8: 'iso-8859-6',
|
|
289
|
+
9: 'iso-8859-7',
|
|
290
|
+
10: 'iso-8859-8',
|
|
291
|
+
11: 'iso-8859-9',
|
|
292
|
+
12: 'iso-8859-10',
|
|
293
|
+
13: 'iso-8859-11',
|
|
294
|
+
15: 'iso-8859-13',
|
|
295
|
+
16: 'iso-8859-14',
|
|
296
|
+
17: 'iso-8859-15',
|
|
297
|
+
18: 'iso-8859-16',
|
|
298
|
+
20: 'shift_jis',
|
|
299
|
+
21: 'windows-1250',
|
|
300
|
+
22: 'windows-1251',
|
|
301
|
+
23: 'windows-1252',
|
|
302
|
+
24: 'windows-1256',
|
|
303
|
+
25: 'utf-16be',
|
|
304
|
+
26: 'utf-8',
|
|
305
|
+
27: 'us-ascii',
|
|
306
|
+
28: 'big5',
|
|
307
|
+
29: 'gb18030',
|
|
308
|
+
30: 'euc-kr',
|
|
309
|
+
170: 'us-ascii',
|
|
342
310
|
};
|
|
343
|
-
|
|
344
311
|
/**
|
|
345
312
|
* Turn a byte segment into text.
|
|
346
313
|
*
|
|
@@ -354,54 +321,58 @@ const ECI_LABELS = {
|
|
|
354
321
|
* @returns {string}
|
|
355
322
|
*/
|
|
356
323
|
function decodeBytes(bytes, eci) {
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
324
|
+
if (eci !== null && eci !== undefined) {
|
|
325
|
+
const label = ECI_LABELS[eci];
|
|
326
|
+
if (label) {
|
|
327
|
+
try {
|
|
328
|
+
return new TextDecoder(label).decode(bytes);
|
|
329
|
+
}
|
|
330
|
+
catch (e) {
|
|
331
|
+
/* Unsupported label on this platform; fall through. */
|
|
332
|
+
}
|
|
333
|
+
}
|
|
365
334
|
}
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
335
|
+
else {
|
|
336
|
+
try {
|
|
337
|
+
return new TextDecoder('utf-8', { fatal: true }).decode(bytes);
|
|
338
|
+
}
|
|
339
|
+
catch (e) {
|
|
340
|
+
/* Not valid UTF-8; it is Latin-1. */
|
|
341
|
+
}
|
|
371
342
|
}
|
|
372
|
-
|
|
373
|
-
return latin1(bytes);
|
|
343
|
+
return latin1(bytes);
|
|
374
344
|
}
|
|
375
|
-
|
|
376
345
|
/**
|
|
377
346
|
* @param {Uint8Array} bytes
|
|
378
347
|
* @returns {string}
|
|
379
348
|
*/
|
|
380
349
|
function latin1(bytes) {
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
350
|
+
let s = '';
|
|
351
|
+
for (let i = 0; i < bytes.length; i++)
|
|
352
|
+
s += String.fromCharCode(bytes[i]);
|
|
353
|
+
return s;
|
|
384
354
|
}
|
|
385
|
-
|
|
386
355
|
/**
|
|
387
356
|
* @param {Uint8Array} bytes Shift_JIS double bytes.
|
|
388
357
|
* @returns {string}
|
|
389
358
|
*/
|
|
390
359
|
function decodeKanji(bytes) {
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
360
|
+
try {
|
|
361
|
+
const decoder = new TextDecoder('shift_jis');
|
|
362
|
+
const text = decoder.decode(bytes);
|
|
363
|
+
if (text.indexOf('�') === -1)
|
|
364
|
+
return text;
|
|
365
|
+
}
|
|
366
|
+
catch (e) {
|
|
367
|
+
/* No Shift_JIS codec on this platform. */
|
|
368
|
+
}
|
|
369
|
+
// Graceful degradation: the payload is structurally intact but we cannot
|
|
370
|
+
// name the characters, so mark them rather than failing the whole symbol.
|
|
371
|
+
let s = '';
|
|
372
|
+
for (let i = 0; i < bytes.length; i += 2)
|
|
373
|
+
s += '�';
|
|
374
|
+
return s;
|
|
403
375
|
}
|
|
404
|
-
|
|
405
376
|
/**
|
|
406
377
|
* Read the ECI designator, which is 1, 2 or 3 bytes depending on its leading
|
|
407
378
|
* bits.
|
|
@@ -410,13 +381,15 @@ function decodeKanji(bytes) {
|
|
|
410
381
|
* @returns {number}
|
|
411
382
|
*/
|
|
412
383
|
function readEciDesignator(reader) {
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
384
|
+
const first = reader.read(8);
|
|
385
|
+
if ((first & 0x80) === 0)
|
|
386
|
+
return first;
|
|
387
|
+
if ((first & 0xc0) === 0x80)
|
|
388
|
+
return ((first & 0x3f) << 8) | reader.read(8);
|
|
389
|
+
if ((first & 0xe0) === 0xc0)
|
|
390
|
+
return ((first & 0x1f) << 16) | reader.read(16);
|
|
391
|
+
throw new FormatError(`QR: malformed ECI designator (first byte 0x${first.toString(16)})`);
|
|
418
392
|
}
|
|
419
|
-
|
|
420
393
|
/**
|
|
421
394
|
* Walk the mode segments and rebuild the payload.
|
|
422
395
|
*
|
|
@@ -425,106 +398,103 @@ function readEciDesignator(reader) {
|
|
|
425
398
|
* @returns {{text: string, bytes: Uint8Array}}
|
|
426
399
|
*/
|
|
427
400
|
function parseSegments(data, version) {
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
continue;
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
if (mode === MODE.FNC1_FIRST) continue;
|
|
447
|
-
if (mode === MODE.FNC1_SECOND) {
|
|
448
|
-
reader.read(8); // application indicator
|
|
449
|
-
continue;
|
|
450
|
-
}
|
|
451
|
-
if (mode === MODE.STRUCTURED_APPEND) {
|
|
452
|
-
reader.read(16); // sequence position, total, parity
|
|
453
|
-
continue;
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
const width = countBits(mode, version);
|
|
457
|
-
if (width === 0) {
|
|
458
|
-
throw new FormatError(`QR: unsupported mode indicator 0x${mode.toString(16)}`);
|
|
459
|
-
}
|
|
460
|
-
const count = reader.read(width);
|
|
461
|
-
|
|
462
|
-
switch (mode) {
|
|
463
|
-
case MODE.NUMERIC: {
|
|
464
|
-
let i = 0;
|
|
465
|
-
while (i + 3 <= count) {
|
|
466
|
-
const triple = reader.read(10);
|
|
467
|
-
if (triple > 999) throw new FormatError(`QR: numeric triple ${triple} out of range`);
|
|
468
|
-
text += String(triple).padStart(3, '0');
|
|
469
|
-
i += 3;
|
|
401
|
+
const reader = new BitReader(data);
|
|
402
|
+
let text = '';
|
|
403
|
+
/** @type {number[]} */
|
|
404
|
+
const rawBytes = [];
|
|
405
|
+
/** @type {number | null} */
|
|
406
|
+
let eci = null;
|
|
407
|
+
// A symbol whose payload ends exactly on a codeword boundary has no room for
|
|
408
|
+
// a terminator, so running out of bits is a normal end, not an error.
|
|
409
|
+
while (reader.available() >= 4) {
|
|
410
|
+
const mode = reader.read(4);
|
|
411
|
+
if (mode === MODE.TERMINATOR)
|
|
412
|
+
break;
|
|
413
|
+
if (mode === MODE.ECI) {
|
|
414
|
+
eci = readEciDesignator(reader);
|
|
415
|
+
continue;
|
|
470
416
|
}
|
|
471
|
-
if (
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
const single = reader.read(4);
|
|
477
|
-
if (single > 9) throw new FormatError(`QR: numeric digit ${single} out of range`);
|
|
478
|
-
text += String(single);
|
|
417
|
+
if (mode === MODE.FNC1_FIRST)
|
|
418
|
+
continue;
|
|
419
|
+
if (mode === MODE.FNC1_SECOND) {
|
|
420
|
+
reader.read(8); // application indicator
|
|
421
|
+
continue;
|
|
479
422
|
}
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
case MODE.ALPHANUMERIC: {
|
|
484
|
-
let i = 0;
|
|
485
|
-
while (i + 2 <= count) {
|
|
486
|
-
const pair = reader.read(11);
|
|
487
|
-
if (pair >= 45 * 45) throw new FormatError(`QR: alphanumeric pair ${pair} out of range`);
|
|
488
|
-
text += ALPHANUMERIC_CHARS[Math.floor(pair / 45)] + ALPHANUMERIC_CHARS[pair % 45];
|
|
489
|
-
i += 2;
|
|
423
|
+
if (mode === MODE.STRUCTURED_APPEND) {
|
|
424
|
+
reader.read(16); // sequence position, total, parity
|
|
425
|
+
continue;
|
|
490
426
|
}
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
text += ALPHANUMERIC_CHARS[single];
|
|
427
|
+
const width = countBits(mode, version);
|
|
428
|
+
if (width === 0) {
|
|
429
|
+
throw new FormatError(`QR: unsupported mode indicator 0x${mode.toString(16)}`);
|
|
495
430
|
}
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
431
|
+
const count = reader.read(width);
|
|
432
|
+
switch (mode) {
|
|
433
|
+
case MODE.NUMERIC: {
|
|
434
|
+
let i = 0;
|
|
435
|
+
while (i + 3 <= count) {
|
|
436
|
+
const triple = reader.read(10);
|
|
437
|
+
if (triple > 999)
|
|
438
|
+
throw new FormatError(`QR: numeric triple ${triple} out of range`);
|
|
439
|
+
text += String(triple).padStart(3, '0');
|
|
440
|
+
i += 3;
|
|
441
|
+
}
|
|
442
|
+
if (count - i === 2) {
|
|
443
|
+
const pair = reader.read(7);
|
|
444
|
+
if (pair > 99)
|
|
445
|
+
throw new FormatError(`QR: numeric pair ${pair} out of range`);
|
|
446
|
+
text += String(pair).padStart(2, '0');
|
|
447
|
+
}
|
|
448
|
+
else if (count - i === 1) {
|
|
449
|
+
const single = reader.read(4);
|
|
450
|
+
if (single > 9)
|
|
451
|
+
throw new FormatError(`QR: numeric digit ${single} out of range`);
|
|
452
|
+
text += String(single);
|
|
453
|
+
}
|
|
454
|
+
break;
|
|
455
|
+
}
|
|
456
|
+
case MODE.ALPHANUMERIC: {
|
|
457
|
+
let i = 0;
|
|
458
|
+
while (i + 2 <= count) {
|
|
459
|
+
const pair = reader.read(11);
|
|
460
|
+
if (pair >= 45 * 45)
|
|
461
|
+
throw new FormatError(`QR: alphanumeric pair ${pair} out of range`);
|
|
462
|
+
text += ALPHANUMERIC_CHARS[Math.floor(pair / 45)] + ALPHANUMERIC_CHARS[pair % 45];
|
|
463
|
+
i += 2;
|
|
464
|
+
}
|
|
465
|
+
if (i < count) {
|
|
466
|
+
const single = reader.read(6);
|
|
467
|
+
if (single >= 45)
|
|
468
|
+
throw new FormatError(`QR: alphanumeric value ${single} out of range`);
|
|
469
|
+
text += ALPHANUMERIC_CHARS[single];
|
|
470
|
+
}
|
|
471
|
+
break;
|
|
472
|
+
}
|
|
473
|
+
case MODE.BYTE: {
|
|
474
|
+
const bytes = new Uint8Array(count);
|
|
475
|
+
for (let i = 0; i < count; i++) {
|
|
476
|
+
bytes[i] = reader.read(8);
|
|
477
|
+
rawBytes.push(bytes[i]);
|
|
478
|
+
}
|
|
479
|
+
text += decodeBytes(bytes, eci);
|
|
480
|
+
break;
|
|
481
|
+
}
|
|
482
|
+
case MODE.KANJI: {
|
|
483
|
+
const bytes = new Uint8Array(count * 2);
|
|
484
|
+
for (let i = 0; i < count; i++) {
|
|
485
|
+
const sjis = thirteenBitsToSjis(reader.read(13));
|
|
486
|
+
bytes[i * 2] = sjis >> 8;
|
|
487
|
+
bytes[i * 2 + 1] = sjis & 0xff;
|
|
488
|
+
}
|
|
489
|
+
text += decodeKanji(bytes);
|
|
490
|
+
break;
|
|
491
|
+
}
|
|
492
|
+
default:
|
|
493
|
+
throw new FormatError(`QR: unsupported mode indicator 0x${mode.toString(16)}`);
|
|
504
494
|
}
|
|
505
|
-
text += decodeBytes(bytes, eci);
|
|
506
|
-
break;
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
case MODE.KANJI: {
|
|
510
|
-
const bytes = new Uint8Array(count * 2);
|
|
511
|
-
for (let i = 0; i < count; i++) {
|
|
512
|
-
const sjis = thirteenBitsToSjis(reader.read(13));
|
|
513
|
-
bytes[i * 2] = sjis >> 8;
|
|
514
|
-
bytes[i * 2 + 1] = sjis & 0xff;
|
|
515
|
-
}
|
|
516
|
-
text += decodeKanji(bytes);
|
|
517
|
-
break;
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
default:
|
|
521
|
-
throw new FormatError(`QR: unsupported mode indicator 0x${mode.toString(16)}`);
|
|
522
495
|
}
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
return { text, bytes: Uint8Array.from(rawBytes) };
|
|
496
|
+
return { text, bytes: Uint8Array.from(rawBytes) };
|
|
526
497
|
}
|
|
527
|
-
|
|
528
498
|
/**
|
|
529
499
|
* @typedef {object} DecodeResult
|
|
530
500
|
* @property {string} text Decoded payload.
|
|
@@ -535,7 +505,6 @@ function parseSegments(data, version) {
|
|
|
535
505
|
* @property {number} mask 0-7.
|
|
536
506
|
* @property {number} corrections Symbols repaired by Reed-Solomon.
|
|
537
507
|
*/
|
|
538
|
-
|
|
539
508
|
/**
|
|
540
509
|
* Decode a sampled QR Code symbol.
|
|
541
510
|
*
|
|
@@ -546,30 +515,25 @@ function parseSegments(data, version) {
|
|
|
546
515
|
* @throws {ChecksumError} If error correction cannot repair the symbol.
|
|
547
516
|
*/
|
|
548
517
|
export function decodeQR(matrix) {
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
const { data, corrections } = deinterleaveAndCorrect(codewords, layout);
|
|
570
|
-
const { text, bytes } = parseSegments(data, version);
|
|
571
|
-
|
|
572
|
-
return { text, bytes, version, ecc, mask, corrections };
|
|
518
|
+
if (!matrix || !matrix.width)
|
|
519
|
+
throw new FormatError('QR: no matrix supplied');
|
|
520
|
+
const size = matrix.width;
|
|
521
|
+
if (matrix.height !== size) {
|
|
522
|
+
throw new FormatError(`QR: symbol must be square, got ${size}x${matrix.height}`);
|
|
523
|
+
}
|
|
524
|
+
if ((size - 17) % 4 !== 0) {
|
|
525
|
+
throw new FormatError(`QR: ${size} modules is not a valid symbol size`);
|
|
526
|
+
}
|
|
527
|
+
const dimensionVersion = (size - 17) / 4;
|
|
528
|
+
if (dimensionVersion < MIN_VERSION || dimensionVersion > MAX_VERSION) {
|
|
529
|
+
throw new FormatError(`QR: ${size} modules implies version ${dimensionVersion}`);
|
|
530
|
+
}
|
|
531
|
+
const { ecc, mask } = readFormatInfo(matrix, size);
|
|
532
|
+
const version = readVersionInfo(matrix, size, dimensionVersion);
|
|
533
|
+
const layout = blockLayout(version, ecc);
|
|
534
|
+
const codewords = readCodewords(matrix, version, mask, layout.totalCodewords);
|
|
535
|
+
const { data, corrections } = deinterleaveAndCorrect(codewords, layout);
|
|
536
|
+
const { text, bytes } = parseSegments(data, version);
|
|
537
|
+
return { text, bytes, version, ecc, mask, corrections };
|
|
573
538
|
}
|
|
574
|
-
|
|
575
539
|
export { ChecksumError };
|