@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/tables.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 structural tables.
|
|
33
32
|
*
|
|
@@ -57,40 +56,32 @@
|
|
|
57
56
|
*
|
|
58
57
|
* @module qr/tables
|
|
59
58
|
*/
|
|
60
|
-
|
|
61
59
|
import { BitMatrix } from '../core/bit-matrix.js';
|
|
62
|
-
|
|
63
60
|
/** Error correction levels, weakest to strongest. */
|
|
64
61
|
export const ECC_LEVELS = ['L', 'M', 'Q', 'H'];
|
|
65
|
-
|
|
66
62
|
/**
|
|
67
63
|
* Two-bit level indicator used in the format information.
|
|
68
64
|
* Note this is *not* the L/M/Q/H ordering — the spec assigns them out of order.
|
|
69
65
|
*/
|
|
70
66
|
export const ECC_LEVEL_BITS = { L: 0b01, M: 0b00, Q: 0b11, H: 0b10 };
|
|
71
|
-
|
|
72
67
|
/** Inverse of {@link ECC_LEVEL_BITS}, indexed by the 2-bit value. */
|
|
73
68
|
export const ECC_LEVEL_BY_BITS = ['M', 'L', 'H', 'Q'];
|
|
74
|
-
|
|
75
69
|
export const MIN_VERSION = 1;
|
|
76
70
|
export const MAX_VERSION = 40;
|
|
77
|
-
|
|
78
71
|
/** Version at and above which an 18-bit version information block is carried. */
|
|
79
72
|
export const VERSION_INFO_MIN = 7;
|
|
80
|
-
|
|
81
73
|
/** Mode indicator nibbles. */
|
|
82
74
|
export const MODE = {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
75
|
+
TERMINATOR: 0x0,
|
|
76
|
+
NUMERIC: 0x1,
|
|
77
|
+
ALPHANUMERIC: 0x2,
|
|
78
|
+
STRUCTURED_APPEND: 0x3,
|
|
79
|
+
BYTE: 0x4,
|
|
80
|
+
FNC1_FIRST: 0x5,
|
|
81
|
+
ECI: 0x7,
|
|
82
|
+
KANJI: 0x8,
|
|
83
|
+
FNC1_SECOND: 0x9,
|
|
92
84
|
};
|
|
93
|
-
|
|
94
85
|
/**
|
|
95
86
|
* Character count indicator width, in bits, by mode and version band.
|
|
96
87
|
*
|
|
@@ -99,20 +90,18 @@ export const MODE = {
|
|
|
99
90
|
* field can push a payload over a version boundary, which widens it again.
|
|
100
91
|
*/
|
|
101
92
|
const COUNT_BITS = {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
93
|
+
[MODE.NUMERIC]: [10, 12, 14],
|
|
94
|
+
[MODE.ALPHANUMERIC]: [9, 11, 13],
|
|
95
|
+
[MODE.BYTE]: [8, 16, 16],
|
|
96
|
+
[MODE.KANJI]: [8, 10, 12],
|
|
106
97
|
};
|
|
107
|
-
|
|
108
98
|
/**
|
|
109
99
|
* @param {number} version 1-40
|
|
110
100
|
* @returns {number} Modules per side.
|
|
111
101
|
*/
|
|
112
102
|
export function versionSize(version) {
|
|
113
|
-
|
|
103
|
+
return 17 + 4 * version;
|
|
114
104
|
}
|
|
115
|
-
|
|
116
105
|
/**
|
|
117
106
|
* Bits in the character count indicator.
|
|
118
107
|
*
|
|
@@ -121,13 +110,15 @@ export function versionSize(version) {
|
|
|
121
110
|
* @returns {number}
|
|
122
111
|
*/
|
|
123
112
|
export function countBits(mode, version) {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
113
|
+
const widths = COUNT_BITS[mode];
|
|
114
|
+
if (!widths)
|
|
115
|
+
return 0;
|
|
116
|
+
if (version <= 9)
|
|
117
|
+
return widths[0];
|
|
118
|
+
if (version <= 26)
|
|
119
|
+
return widths[1];
|
|
120
|
+
return widths[2];
|
|
129
121
|
}
|
|
130
|
-
|
|
131
122
|
/**
|
|
132
123
|
* Centre coordinates of the alignment patterns for a version.
|
|
133
124
|
*
|
|
@@ -141,24 +132,22 @@ export function countBits(mode, version) {
|
|
|
141
132
|
* @returns {number[]} Ascending centres. Empty for version 1.
|
|
142
133
|
*/
|
|
143
134
|
export function alignmentCoordinates(version) {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
return coords;
|
|
135
|
+
if (version < 2)
|
|
136
|
+
return [];
|
|
137
|
+
const size = versionSize(version);
|
|
138
|
+
const count = Math.floor(version / 7) + 2;
|
|
139
|
+
const last = size - 7;
|
|
140
|
+
// Spacing is rounded up to an even number of modules so every centre lands on
|
|
141
|
+
// the same parity as the timing pattern, which is what keeps the patterns
|
|
142
|
+
// aligned with the module grid rather than straddling it.
|
|
143
|
+
const step = Math.ceil((size - 13) / (2 * count - 2)) * 2;
|
|
144
|
+
const coords = [6];
|
|
145
|
+
// Walk backwards from the final centre so the slack lands in the first gap.
|
|
146
|
+
for (let i = count - 1; i >= 1; i--)
|
|
147
|
+
coords.push(last - (count - 1 - i) * step);
|
|
148
|
+
coords.sort((a, b) => a - b);
|
|
149
|
+
return coords;
|
|
160
150
|
}
|
|
161
|
-
|
|
162
151
|
/**
|
|
163
152
|
* Centres of the alignment patterns actually drawn, as [x, y] pairs.
|
|
164
153
|
*
|
|
@@ -168,29 +157,30 @@ export function alignmentCoordinates(version) {
|
|
|
168
157
|
* @returns {Array<[number, number]>}
|
|
169
158
|
*/
|
|
170
159
|
export function alignmentCentres(version) {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
160
|
+
const coords = alignmentCoordinates(version);
|
|
161
|
+
if (coords.length === 0)
|
|
162
|
+
return [];
|
|
163
|
+
const size = versionSize(version);
|
|
164
|
+
const lo = 6;
|
|
165
|
+
const hi = size - 7;
|
|
166
|
+
const out = [];
|
|
167
|
+
for (let i = 0; i < coords.length; i++) {
|
|
168
|
+
for (let j = 0; j < coords.length; j++) {
|
|
169
|
+
const x = coords[j];
|
|
170
|
+
const y = coords[i];
|
|
171
|
+
// Skip the three finder corners.
|
|
172
|
+
if (x === lo && y === lo)
|
|
173
|
+
continue;
|
|
174
|
+
if (x === lo && y === hi)
|
|
175
|
+
continue;
|
|
176
|
+
if (x === hi && y === lo)
|
|
177
|
+
continue;
|
|
178
|
+
out.push([x, y]);
|
|
179
|
+
}
|
|
187
180
|
}
|
|
188
|
-
|
|
189
|
-
return out;
|
|
181
|
+
return out;
|
|
190
182
|
}
|
|
191
|
-
|
|
192
183
|
const reservedCache = new Map();
|
|
193
|
-
|
|
194
184
|
/**
|
|
195
185
|
* Map of modules that carry function patterns rather than payload.
|
|
196
186
|
*
|
|
@@ -209,51 +199,44 @@ const reservedCache = new Map();
|
|
|
209
199
|
* @returns {BitMatrix} Shared, cached — treat as immutable.
|
|
210
200
|
*/
|
|
211
201
|
export function reservedModules(version) {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
m.setRegion(size - 11, 0, 3, 6);
|
|
250
|
-
m.setRegion(0, size - 11, 6, 3);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
reservedCache.set(version, m);
|
|
254
|
-
return m;
|
|
202
|
+
const cached = reservedCache.get(version);
|
|
203
|
+
if (cached)
|
|
204
|
+
return cached;
|
|
205
|
+
const size = versionSize(version);
|
|
206
|
+
const m = new BitMatrix(size, size);
|
|
207
|
+
// Finder patterns with their separators: an 8x8 reserved block at each of
|
|
208
|
+
// three corners (7x7 pattern plus a one-module light border on the inner
|
|
209
|
+
// sides, which the corner blocks absorb).
|
|
210
|
+
m.setRegion(0, 0, 8, 8);
|
|
211
|
+
m.setRegion(size - 8, 0, 8, 8);
|
|
212
|
+
m.setRegion(0, size - 8, 8, 8);
|
|
213
|
+
// Timing patterns, spanning the gap between the separators.
|
|
214
|
+
for (let i = 8; i < size - 8; i++) {
|
|
215
|
+
m.set(i, 6);
|
|
216
|
+
m.set(6, i);
|
|
217
|
+
}
|
|
218
|
+
// Alignment patterns, 5x5 each.
|
|
219
|
+
const centres = alignmentCentres(version);
|
|
220
|
+
for (let i = 0; i < centres.length; i++) {
|
|
221
|
+
m.setRegion(centres[i][0] - 2, centres[i][1] - 2, 5, 5);
|
|
222
|
+
}
|
|
223
|
+
// Format information: two copies plus the dark module. The copies partly
|
|
224
|
+
// fall inside the 8x8 finder blocks already reserved; setting them again is
|
|
225
|
+
// harmless and keeps the intent explicit.
|
|
226
|
+
const [copyA, copyB] = formatInfoPositions(size);
|
|
227
|
+
for (let i = 0; i < 15; i++) {
|
|
228
|
+
m.set(copyA[i][0], copyA[i][1]);
|
|
229
|
+
m.set(copyB[i][0], copyB[i][1]);
|
|
230
|
+
}
|
|
231
|
+
m.set(8, size - 8); // dark module
|
|
232
|
+
// Version information, two 6x3 blocks.
|
|
233
|
+
if (version >= VERSION_INFO_MIN) {
|
|
234
|
+
m.setRegion(size - 11, 0, 3, 6);
|
|
235
|
+
m.setRegion(0, size - 11, 6, 3);
|
|
236
|
+
}
|
|
237
|
+
reservedCache.set(version, m);
|
|
238
|
+
return m;
|
|
255
239
|
}
|
|
256
|
-
|
|
257
240
|
/**
|
|
258
241
|
* Module positions of the two format information copies.
|
|
259
242
|
*
|
|
@@ -271,29 +254,32 @@ export function reservedModules(version) {
|
|
|
271
254
|
* @returns {[Array<[number, number]>, Array<[number, number]>]} [copyA, copyB]
|
|
272
255
|
*/
|
|
273
256
|
export function formatInfoPositions(size) {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
257
|
+
/** @type {Array<[number, number]>} */
|
|
258
|
+
const a = [];
|
|
259
|
+
/** @type {Array<[number, number]>} */
|
|
260
|
+
const b = [];
|
|
261
|
+
for (let i = 0; i < 15; i++) {
|
|
262
|
+
// Copy A wraps the top-left finder: down column 8, then left along row 8,
|
|
263
|
+
// stepping over the two timing modules.
|
|
264
|
+
if (i < 6)
|
|
265
|
+
a.push([8, i]);
|
|
266
|
+
else if (i === 6)
|
|
267
|
+
a.push([8, 7]);
|
|
268
|
+
else if (i === 7)
|
|
269
|
+
a.push([8, 8]);
|
|
270
|
+
else if (i === 8)
|
|
271
|
+
a.push([7, 8]);
|
|
272
|
+
else
|
|
273
|
+
a.push([14 - i, 8]);
|
|
274
|
+
// Copy B is split: the low bits run right-to-left along row 8 beside the
|
|
275
|
+
// top-right finder, the high bits run bottom-up beside the bottom-left one.
|
|
276
|
+
if (i < 8)
|
|
277
|
+
b.push([size - 1 - i, 8]);
|
|
278
|
+
else
|
|
279
|
+
b.push([8, size - 15 + i]);
|
|
280
|
+
}
|
|
281
|
+
return [a, b];
|
|
295
282
|
}
|
|
296
|
-
|
|
297
283
|
/**
|
|
298
284
|
* Modules available to data and error correction, counted off the grid.
|
|
299
285
|
*
|
|
@@ -301,17 +287,17 @@ export function formatInfoPositions(size) {
|
|
|
301
287
|
* @returns {number}
|
|
302
288
|
*/
|
|
303
289
|
export function freeModuleCount(version) {
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
290
|
+
const reserved = reservedModules(version);
|
|
291
|
+
const size = versionSize(version);
|
|
292
|
+
let free = 0;
|
|
293
|
+
for (let y = 0; y < size; y++) {
|
|
294
|
+
for (let x = 0; x < size; x++) {
|
|
295
|
+
if (!reserved.get(x, y))
|
|
296
|
+
free++;
|
|
297
|
+
}
|
|
310
298
|
}
|
|
311
|
-
|
|
312
|
-
return free;
|
|
299
|
+
return free;
|
|
313
300
|
}
|
|
314
|
-
|
|
315
301
|
/**
|
|
316
302
|
* Total codewords (data + error correction) a version holds.
|
|
317
303
|
*
|
|
@@ -322,9 +308,8 @@ export function freeModuleCount(version) {
|
|
|
322
308
|
* @returns {number}
|
|
323
309
|
*/
|
|
324
310
|
export function geometricTotalCodewords(version) {
|
|
325
|
-
|
|
311
|
+
return Math.floor(freeModuleCount(version) / 8);
|
|
326
312
|
}
|
|
327
|
-
|
|
328
313
|
/**
|
|
329
314
|
* Bits left over after the last whole codeword, written as zeroes.
|
|
330
315
|
*
|
|
@@ -332,11 +317,9 @@ export function geometricTotalCodewords(version) {
|
|
|
332
317
|
* @returns {number} 0, 3, 4 or 7.
|
|
333
318
|
*/
|
|
334
319
|
export function remainderBits(version) {
|
|
335
|
-
|
|
320
|
+
return freeModuleCount(version) % 8;
|
|
336
321
|
}
|
|
337
|
-
|
|
338
322
|
const orderCache = new Map();
|
|
339
|
-
|
|
340
323
|
/**
|
|
341
324
|
* Module positions in bitstream order, as interleaved x, y pairs.
|
|
342
325
|
*
|
|
@@ -354,38 +337,37 @@ const orderCache = new Map();
|
|
|
354
337
|
* `2 * freeModuleCount(version)`.
|
|
355
338
|
*/
|
|
356
339
|
export function dataModuleOrder(version) {
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
340
|
+
const cached = orderCache.get(version);
|
|
341
|
+
if (cached)
|
|
342
|
+
return cached;
|
|
343
|
+
const size = versionSize(version);
|
|
344
|
+
const reserved = reservedModules(version);
|
|
345
|
+
const out = new Int32Array(freeModuleCount(version) * 2);
|
|
346
|
+
let n = 0;
|
|
347
|
+
let upward = true;
|
|
348
|
+
for (let col = size - 1; col > 0; col -= 2) {
|
|
349
|
+
// Column 6 is the vertical timing pattern. Stepping over it shifts the
|
|
350
|
+
// whole remaining schedule left by one, so the loop variable itself has to
|
|
351
|
+
// move — adjusting only the current pair would visit column 4 twice and
|
|
352
|
+
// column 0 never, which is self-consistent between encoder and decoder and
|
|
353
|
+
// therefore invisible to a round-trip test.
|
|
354
|
+
if (col === 6)
|
|
355
|
+
col--;
|
|
356
|
+
for (let i = 0; i < size; i++) {
|
|
357
|
+
const y = upward ? size - 1 - i : i;
|
|
358
|
+
for (let c = 0; c < 2; c++) {
|
|
359
|
+
const x = col - c;
|
|
360
|
+
if (reserved.get(x, y))
|
|
361
|
+
continue;
|
|
362
|
+
out[n++] = x;
|
|
363
|
+
out[n++] = y;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
upward = !upward;
|
|
381
367
|
}
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
orderCache.set(version, out);
|
|
386
|
-
return out;
|
|
368
|
+
orderCache.set(version, out);
|
|
369
|
+
return out;
|
|
387
370
|
}
|
|
388
|
-
|
|
389
371
|
/**
|
|
390
372
|
* The eight data mask predicates.
|
|
391
373
|
*
|
|
@@ -399,20 +381,19 @@ export function dataModuleOrder(version) {
|
|
|
399
381
|
* @returns {boolean}
|
|
400
382
|
*/
|
|
401
383
|
export function maskBit(mask, x, y) {
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
384
|
+
switch (mask) {
|
|
385
|
+
case 0: return ((y + x) & 1) === 0;
|
|
386
|
+
case 1: return (y & 1) === 0;
|
|
387
|
+
case 2: return x % 3 === 0;
|
|
388
|
+
case 3: return (y + x) % 3 === 0;
|
|
389
|
+
case 4: return (((y >> 1) + Math.floor(x / 3)) & 1) === 0;
|
|
390
|
+
case 5: return ((y * x) & 1) + ((y * x) % 3) === 0;
|
|
391
|
+
case 6: return ((((y * x) & 1) + ((y * x) % 3)) & 1) === 0;
|
|
392
|
+
case 7: return ((((y + x) & 1) + ((y * x) % 3)) & 1) === 0;
|
|
393
|
+
default:
|
|
394
|
+
throw new RangeError(`QR: mask must be 0-7, got ${mask}`);
|
|
395
|
+
}
|
|
414
396
|
}
|
|
415
|
-
|
|
416
397
|
/**
|
|
417
398
|
* Error correction parameters, indexed `[version - 1]` then by level.
|
|
418
399
|
*
|
|
@@ -425,48 +406,47 @@ export function maskBit(mask, x, y) {
|
|
|
425
406
|
* @type {Array<{L: number[], M: number[], Q: number[], H: number[]}>}
|
|
426
407
|
*/
|
|
427
408
|
const ECC_TABLE = [
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
409
|
+
/* 1 */ { L: [7, 1, 19], M: [10, 1, 16], Q: [13, 1, 13], H: [17, 1, 9] },
|
|
410
|
+
/* 2 */ { L: [10, 1, 34], M: [16, 1, 28], Q: [22, 1, 22], H: [28, 1, 16] },
|
|
411
|
+
/* 3 */ { L: [15, 1, 55], M: [26, 1, 44], Q: [18, 2, 34], H: [22, 2, 26] },
|
|
412
|
+
/* 4 */ { L: [20, 1, 80], M: [18, 2, 64], Q: [26, 2, 48], H: [16, 4, 36] },
|
|
413
|
+
/* 5 */ { L: [26, 1, 108], M: [24, 2, 86], Q: [18, 4, 62], H: [22, 4, 46] },
|
|
414
|
+
/* 6 */ { L: [18, 2, 136], M: [16, 4, 108], Q: [24, 4, 76], H: [28, 4, 60] },
|
|
415
|
+
/* 7 */ { L: [20, 2, 156], M: [18, 4, 124], Q: [18, 6, 88], H: [26, 5, 66] },
|
|
416
|
+
/* 8 */ { L: [24, 2, 194], M: [22, 4, 154], Q: [22, 6, 110], H: [26, 6, 86] },
|
|
417
|
+
/* 9 */ { L: [30, 2, 232], M: [22, 5, 182], Q: [20, 8, 132], H: [24, 8, 100] },
|
|
418
|
+
/* 10 */ { L: [18, 4, 274], M: [26, 5, 216], Q: [24, 8, 154], H: [28, 8, 122] },
|
|
419
|
+
/* 11 */ { L: [20, 4, 324], M: [30, 5, 254], Q: [28, 8, 180], H: [24, 11, 140] },
|
|
420
|
+
/* 12 */ { L: [24, 4, 370], M: [22, 8, 290], Q: [26, 10, 206], H: [28, 11, 158] },
|
|
421
|
+
/* 13 */ { L: [26, 4, 428], M: [22, 9, 334], Q: [24, 12, 244], H: [22, 16, 180] },
|
|
422
|
+
/* 14 */ { L: [30, 4, 461], M: [24, 9, 365], Q: [20, 16, 261], H: [24, 16, 197] },
|
|
423
|
+
/* 15 */ { L: [22, 6, 523], M: [24, 10, 415], Q: [30, 12, 295], H: [24, 18, 223] },
|
|
424
|
+
/* 16 */ { L: [24, 6, 589], M: [28, 10, 453], Q: [24, 17, 325], H: [30, 16, 253] },
|
|
425
|
+
/* 17 */ { L: [28, 6, 647], M: [28, 11, 507], Q: [28, 16, 367], H: [28, 19, 283] },
|
|
426
|
+
/* 18 */ { L: [30, 6, 721], M: [26, 13, 563], Q: [28, 18, 397], H: [28, 21, 313] },
|
|
427
|
+
/* 19 */ { L: [28, 7, 795], M: [26, 14, 627], Q: [26, 21, 445], H: [26, 25, 341] },
|
|
428
|
+
/* 20 */ { L: [28, 8, 861], M: [26, 16, 669], Q: [30, 20, 485], H: [28, 25, 385] },
|
|
429
|
+
/* 21 */ { L: [28, 8, 932], M: [26, 17, 714], Q: [28, 23, 512], H: [30, 25, 406] },
|
|
430
|
+
/* 22 */ { L: [28, 9, 1006], M: [28, 17, 782], Q: [30, 23, 568], H: [24, 34, 442] },
|
|
431
|
+
/* 23 */ { L: [30, 9, 1094], M: [28, 18, 860], Q: [30, 25, 614], H: [30, 30, 464] },
|
|
432
|
+
/* 24 */ { L: [30, 10, 1174], M: [28, 20, 914], Q: [30, 27, 664], H: [30, 32, 514] },
|
|
433
|
+
/* 25 */ { L: [26, 12, 1276], M: [28, 21, 1000], Q: [30, 29, 718], H: [30, 35, 538] },
|
|
434
|
+
/* 26 */ { L: [28, 12, 1370], M: [28, 23, 1062], Q: [28, 34, 754], H: [30, 37, 596] },
|
|
435
|
+
/* 27 */ { L: [30, 12, 1468], M: [28, 25, 1128], Q: [30, 34, 808], H: [30, 40, 628] },
|
|
436
|
+
/* 28 */ { L: [30, 13, 1531], M: [28, 26, 1193], Q: [30, 35, 871], H: [30, 42, 661] },
|
|
437
|
+
/* 29 */ { L: [30, 14, 1631], M: [28, 28, 1267], Q: [30, 38, 911], H: [30, 45, 701] },
|
|
438
|
+
/* 30 */ { L: [30, 15, 1735], M: [28, 29, 1373], Q: [30, 40, 985], H: [30, 48, 745] },
|
|
439
|
+
/* 31 */ { L: [30, 16, 1843], M: [28, 31, 1455], Q: [30, 43, 1033], H: [30, 51, 793] },
|
|
440
|
+
/* 32 */ { L: [30, 17, 1955], M: [28, 33, 1541], Q: [30, 45, 1115], H: [30, 54, 845] },
|
|
441
|
+
/* 33 */ { L: [30, 18, 2071], M: [28, 35, 1631], Q: [30, 48, 1171], H: [30, 57, 901] },
|
|
442
|
+
/* 34 */ { L: [30, 19, 2191], M: [28, 37, 1725], Q: [30, 51, 1231], H: [30, 60, 961] },
|
|
443
|
+
/* 35 */ { L: [30, 19, 2306], M: [28, 38, 1812], Q: [30, 53, 1286], H: [30, 63, 986] },
|
|
444
|
+
/* 36 */ { L: [30, 20, 2434], M: [28, 40, 1914], Q: [30, 56, 1354], H: [30, 66, 1054] },
|
|
445
|
+
/* 37 */ { L: [30, 21, 2566], M: [28, 43, 1992], Q: [30, 59, 1426], H: [30, 70, 1096] },
|
|
446
|
+
/* 38 */ { L: [30, 22, 2702], M: [28, 45, 2102], Q: [30, 62, 1502], H: [30, 74, 1142] },
|
|
447
|
+
/* 39 */ { L: [30, 24, 2812], M: [28, 47, 2216], Q: [30, 65, 1582], H: [30, 77, 1222] },
|
|
448
|
+
/* 40 */ { L: [30, 25, 2956], M: [28, 49, 2334], Q: [30, 68, 1666], H: [30, 81, 1276] },
|
|
468
449
|
];
|
|
469
|
-
|
|
470
450
|
/**
|
|
471
451
|
* @typedef {object} BlockLayout
|
|
472
452
|
* @property {number} version
|
|
@@ -481,7 +461,6 @@ const ECC_TABLE = [
|
|
|
481
461
|
* @property {number} group2DataCount
|
|
482
462
|
* @property {number} remainderBits
|
|
483
463
|
*/
|
|
484
|
-
|
|
485
464
|
/**
|
|
486
465
|
* Block structure for a (version, level).
|
|
487
466
|
*
|
|
@@ -495,34 +474,31 @@ const ECC_TABLE = [
|
|
|
495
474
|
* @returns {BlockLayout}
|
|
496
475
|
*/
|
|
497
476
|
export function blockLayout(version, ecc) {
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
remainderBits: remainderBits(version),
|
|
523
|
-
};
|
|
477
|
+
if (version < MIN_VERSION || version > MAX_VERSION || (version | 0) !== version) {
|
|
478
|
+
throw new RangeError(`QR: version must be an integer 1-40, got ${version}`);
|
|
479
|
+
}
|
|
480
|
+
const entry = ECC_TABLE[version - 1][ecc];
|
|
481
|
+
if (!entry)
|
|
482
|
+
throw new RangeError(`QR: unknown error correction level "${ecc}"`);
|
|
483
|
+
const eccPerBlock = entry[0];
|
|
484
|
+
const blockCount = entry[1];
|
|
485
|
+
const totalDataCodewords = entry[2];
|
|
486
|
+
const base = Math.floor(totalDataCodewords / blockCount);
|
|
487
|
+
const extra = totalDataCodewords % blockCount;
|
|
488
|
+
return {
|
|
489
|
+
version,
|
|
490
|
+
ecc,
|
|
491
|
+
totalCodewords: totalDataCodewords + eccPerBlock * blockCount,
|
|
492
|
+
totalDataCodewords,
|
|
493
|
+
eccPerBlock,
|
|
494
|
+
blockCount,
|
|
495
|
+
group1Blocks: blockCount - extra,
|
|
496
|
+
group1DataCount: base,
|
|
497
|
+
group2Blocks: extra,
|
|
498
|
+
group2DataCount: base + 1,
|
|
499
|
+
remainderBits: remainderBits(version),
|
|
500
|
+
};
|
|
524
501
|
}
|
|
525
|
-
|
|
526
502
|
/**
|
|
527
503
|
* Data capacity in codewords.
|
|
528
504
|
*
|
|
@@ -531,9 +507,8 @@ export function blockLayout(version, ecc) {
|
|
|
531
507
|
* @returns {number}
|
|
532
508
|
*/
|
|
533
509
|
export function dataCodewords(version, ecc) {
|
|
534
|
-
|
|
510
|
+
return ECC_TABLE[version - 1][ecc][2];
|
|
535
511
|
}
|
|
536
|
-
|
|
537
512
|
/**
|
|
538
513
|
* Data capacity in bits.
|
|
539
514
|
*
|
|
@@ -542,12 +517,10 @@ export function dataCodewords(version, ecc) {
|
|
|
542
517
|
* @returns {number}
|
|
543
518
|
*/
|
|
544
519
|
export function dataBitCapacity(version, ecc) {
|
|
545
|
-
|
|
520
|
+
return dataCodewords(version, ecc) * 8;
|
|
546
521
|
}
|
|
547
|
-
|
|
548
522
|
/** Error correction codewords per block are drawn from this set and no other. */
|
|
549
523
|
const VALID_ECC_PER_BLOCK = [7, 10, 13, 15, 16, 17, 18, 20, 22, 24, 26, 28, 30];
|
|
550
|
-
|
|
551
524
|
/**
|
|
552
525
|
* Remainder bits by version band, from the spec. Independent of anything the
|
|
553
526
|
* ECC table says, so it validates the *geometry* — chiefly the alignment
|
|
@@ -557,15 +530,20 @@ const VALID_ECC_PER_BLOCK = [7, 10, 13, 15, 16, 17, 18, 20, 22, 24, 26, 28, 30];
|
|
|
557
530
|
* @returns {number}
|
|
558
531
|
*/
|
|
559
532
|
function expectedRemainderBits(version) {
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
533
|
+
if (version === 1)
|
|
534
|
+
return 0;
|
|
535
|
+
if (version <= 6)
|
|
536
|
+
return 7;
|
|
537
|
+
if (version <= 13)
|
|
538
|
+
return 0;
|
|
539
|
+
if (version <= 20)
|
|
540
|
+
return 3;
|
|
541
|
+
if (version <= 27)
|
|
542
|
+
return 4;
|
|
543
|
+
if (version <= 34)
|
|
544
|
+
return 3;
|
|
545
|
+
return 0;
|
|
567
546
|
}
|
|
568
|
-
|
|
569
547
|
/**
|
|
570
548
|
* Self-check every table in this file.
|
|
571
549
|
*
|
|
@@ -578,160 +556,135 @@ function expectedRemainderBits(version) {
|
|
|
578
556
|
* @returns {string[]} Human-readable problems; empty means everything holds.
|
|
579
557
|
*/
|
|
580
558
|
export function validateTables() {
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
problems.push(
|
|
591
|
-
`v${version}: remainder bits ${rem}, expected ${expectedRemainderBits(version)} ` +
|
|
592
|
-
`(free modules ${freeModuleCount(version)}) — check the alignment spacing rule`
|
|
593
|
-
);
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
// --- Placement order ----------------------------------------------
|
|
597
|
-
// Every free module must be visited exactly once. A placement order that
|
|
598
|
-
// skips one module and visits another twice is still perfectly
|
|
599
|
-
// self-consistent — the encoder writes and the decoder reads the same
|
|
600
|
-
// wrong sequence — so a round-trip test cannot see it. What it does is
|
|
601
|
-
// silently burn error correction budget on every symbol produced. This is
|
|
602
|
-
// the only check that catches it.
|
|
603
|
-
const order = dataModuleOrder(version);
|
|
604
|
-
const free = freeModuleCount(version);
|
|
605
|
-
if (order.length !== free * 2) {
|
|
606
|
-
problems.push(
|
|
607
|
-
`v${version}: placement order visits ${order.length / 2} modules, expected ${free}`
|
|
608
|
-
);
|
|
609
|
-
} else {
|
|
610
|
-
const seen = new Uint8Array(size * size);
|
|
611
|
-
let duplicates = 0;
|
|
612
|
-
let reservedHits = 0;
|
|
613
|
-
const reserved = reservedModules(version);
|
|
614
|
-
for (let p = 0; p < order.length; p += 2) {
|
|
615
|
-
const x = order[p];
|
|
616
|
-
const y = order[p + 1];
|
|
617
|
-
if (reserved.get(x, y)) reservedHits++;
|
|
618
|
-
if (seen[y * size + x]++) duplicates++;
|
|
619
|
-
}
|
|
620
|
-
if (duplicates > 0) {
|
|
621
|
-
problems.push(`v${version}: placement order visits ${duplicates} modules more than once`);
|
|
622
|
-
}
|
|
623
|
-
if (reservedHits > 0) {
|
|
624
|
-
problems.push(`v${version}: placement order includes ${reservedHits} function modules`);
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
// --- Alignment coordinates ----------------------------------------
|
|
629
|
-
const coords = alignmentCoordinates(version);
|
|
630
|
-
if (version === 1) {
|
|
631
|
-
if (coords.length !== 0) problems.push(`v1: expected no alignment coordinates, got ${coords.length}`);
|
|
632
|
-
} else {
|
|
633
|
-
const expectedCount = Math.floor(version / 7) + 2;
|
|
634
|
-
if (coords.length !== expectedCount) {
|
|
635
|
-
problems.push(`v${version}: ${coords.length} alignment coordinates, expected ${expectedCount}`);
|
|
636
|
-
}
|
|
637
|
-
if (coords[0] !== 6) {
|
|
638
|
-
problems.push(`v${version}: first alignment coordinate ${coords[0]}, expected 6`);
|
|
639
|
-
}
|
|
640
|
-
if (coords[coords.length - 1] !== size - 7) {
|
|
641
|
-
problems.push(
|
|
642
|
-
`v${version}: last alignment coordinate ${coords[coords.length - 1]}, expected ${size - 7}`
|
|
643
|
-
);
|
|
644
|
-
}
|
|
645
|
-
for (let i = 1; i < coords.length; i++) {
|
|
646
|
-
if (coords[i] <= coords[i - 1]) {
|
|
647
|
-
problems.push(`v${version}: alignment coordinates not strictly increasing at index ${i}`);
|
|
559
|
+
const problems = [];
|
|
560
|
+
for (let version = MIN_VERSION; version <= MAX_VERSION; version++) {
|
|
561
|
+
const size = versionSize(version);
|
|
562
|
+
const total = geometricTotalCodewords(version);
|
|
563
|
+
// --- Geometry ------------------------------------------------------
|
|
564
|
+
const rem = remainderBits(version);
|
|
565
|
+
if (rem !== expectedRemainderBits(version)) {
|
|
566
|
+
problems.push(`v${version}: remainder bits ${rem}, expected ${expectedRemainderBits(version)} ` +
|
|
567
|
+
`(free modules ${freeModuleCount(version)}) — check the alignment spacing rule`);
|
|
648
568
|
}
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
//
|
|
652
|
-
//
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
569
|
+
// --- Placement order ----------------------------------------------
|
|
570
|
+
// Every free module must be visited exactly once. A placement order that
|
|
571
|
+
// skips one module and visits another twice is still perfectly
|
|
572
|
+
// self-consistent — the encoder writes and the decoder reads the same
|
|
573
|
+
// wrong sequence — so a round-trip test cannot see it. What it does is
|
|
574
|
+
// silently burn error correction budget on every symbol produced. This is
|
|
575
|
+
// the only check that catches it.
|
|
576
|
+
const order = dataModuleOrder(version);
|
|
577
|
+
const free = freeModuleCount(version);
|
|
578
|
+
if (order.length !== free * 2) {
|
|
579
|
+
problems.push(`v${version}: placement order visits ${order.length / 2} modules, expected ${free}`);
|
|
660
580
|
}
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
581
|
+
else {
|
|
582
|
+
const seen = new Uint8Array(size * size);
|
|
583
|
+
let duplicates = 0;
|
|
584
|
+
let reservedHits = 0;
|
|
585
|
+
const reserved = reservedModules(version);
|
|
586
|
+
for (let p = 0; p < order.length; p += 2) {
|
|
587
|
+
const x = order[p];
|
|
588
|
+
const y = order[p + 1];
|
|
589
|
+
if (reserved.get(x, y))
|
|
590
|
+
reservedHits++;
|
|
591
|
+
if (seen[y * size + x]++)
|
|
592
|
+
duplicates++;
|
|
593
|
+
}
|
|
594
|
+
if (duplicates > 0) {
|
|
595
|
+
problems.push(`v${version}: placement order visits ${duplicates} modules more than once`);
|
|
596
|
+
}
|
|
597
|
+
if (reservedHits > 0) {
|
|
598
|
+
problems.push(`v${version}: placement order includes ${reservedHits} function modules`);
|
|
599
|
+
}
|
|
665
600
|
}
|
|
666
|
-
|
|
667
|
-
|
|
601
|
+
// --- Alignment coordinates ----------------------------------------
|
|
602
|
+
const coords = alignmentCoordinates(version);
|
|
603
|
+
if (version === 1) {
|
|
604
|
+
if (coords.length !== 0)
|
|
605
|
+
problems.push(`v1: expected no alignment coordinates, got ${coords.length}`);
|
|
668
606
|
}
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
layout.group2Blocks * layout.group2DataCount !== layout.totalDataCodewords
|
|
702
|
-
) {
|
|
703
|
-
problems.push(`${tag}: group sizes do not sum to the data codeword count`);
|
|
704
|
-
}
|
|
705
|
-
// Reed-Solomon over GF(256) cannot address a codeword longer than 255.
|
|
706
|
-
if (layout.group2DataCount + layout.eccPerBlock > 255) {
|
|
707
|
-
problems.push(
|
|
708
|
-
`${tag}: block length ${layout.group2DataCount + layout.eccPerBlock} exceeds GF(256)`
|
|
709
|
-
);
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
// Stronger correction must cost capacity, never gain it.
|
|
713
|
-
if (l > 0) {
|
|
714
|
-
const weaker = dataCodewords(version, ECC_LEVELS[l - 1]);
|
|
715
|
-
if (layout.totalDataCodewords >= weaker) {
|
|
716
|
-
problems.push(
|
|
717
|
-
`${tag}: ${layout.totalDataCodewords} data codewords is not less than ` +
|
|
718
|
-
`${ECC_LEVELS[l - 1]}'s ${weaker}`
|
|
719
|
-
);
|
|
607
|
+
else {
|
|
608
|
+
const expectedCount = Math.floor(version / 7) + 2;
|
|
609
|
+
if (coords.length !== expectedCount) {
|
|
610
|
+
problems.push(`v${version}: ${coords.length} alignment coordinates, expected ${expectedCount}`);
|
|
611
|
+
}
|
|
612
|
+
if (coords[0] !== 6) {
|
|
613
|
+
problems.push(`v${version}: first alignment coordinate ${coords[0]}, expected 6`);
|
|
614
|
+
}
|
|
615
|
+
if (coords[coords.length - 1] !== size - 7) {
|
|
616
|
+
problems.push(`v${version}: last alignment coordinate ${coords[coords.length - 1]}, expected ${size - 7}`);
|
|
617
|
+
}
|
|
618
|
+
for (let i = 1; i < coords.length; i++) {
|
|
619
|
+
if (coords[i] <= coords[i - 1]) {
|
|
620
|
+
problems.push(`v${version}: alignment coordinates not strictly increasing at index ${i}`);
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
if (coords.length >= 3) {
|
|
624
|
+
// Every gap after the first must be identical, and the first gap must
|
|
625
|
+
// not exceed it — the slack is absorbed at the start, never the end.
|
|
626
|
+
const step = coords[2] - coords[1];
|
|
627
|
+
for (let i = 3; i < coords.length; i++) {
|
|
628
|
+
if (coords[i] - coords[i - 1] !== step) {
|
|
629
|
+
problems.push(`v${version}: alignment gap ${coords[i] - coords[i - 1]} at index ${i}, expected ${step}`);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
if (coords[1] - coords[0] > step) {
|
|
633
|
+
problems.push(`v${version}: first alignment gap ${coords[1] - coords[0]} exceeds step ${step}`);
|
|
634
|
+
}
|
|
635
|
+
if (step % 2 !== 0) {
|
|
636
|
+
problems.push(`v${version}: alignment step ${step} is odd`);
|
|
637
|
+
}
|
|
638
|
+
}
|
|
720
639
|
}
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
640
|
+
// --- Error correction table ---------------------------------------
|
|
641
|
+
for (let l = 0; l < ECC_LEVELS.length; l++) {
|
|
642
|
+
const ecc = ECC_LEVELS[l];
|
|
643
|
+
const layout = blockLayout(version, ecc);
|
|
644
|
+
const tag = `v${version}-${ecc}`;
|
|
645
|
+
// THE identity. Everything else is a supporting check.
|
|
646
|
+
if (layout.totalCodewords !== total) {
|
|
647
|
+
problems.push(`${tag}: ${layout.blockCount} blocks x ${layout.eccPerBlock} ECC + ` +
|
|
648
|
+
`${layout.totalDataCodewords} data = ${layout.totalCodewords} codewords, ` +
|
|
649
|
+
`but the grid holds ${total}`);
|
|
650
|
+
}
|
|
651
|
+
if (VALID_ECC_PER_BLOCK.indexOf(layout.eccPerBlock) === -1) {
|
|
652
|
+
problems.push(`${tag}: ${layout.eccPerBlock} ECC codewords per block is not a valid value`);
|
|
653
|
+
}
|
|
654
|
+
if (layout.blockCount < 1) {
|
|
655
|
+
problems.push(`${tag}: block count ${layout.blockCount}`);
|
|
656
|
+
}
|
|
657
|
+
if (layout.group1DataCount < 1) {
|
|
658
|
+
problems.push(`${tag}: ${layout.totalDataCodewords} data codewords across ${layout.blockCount} blocks`);
|
|
659
|
+
}
|
|
660
|
+
if (layout.group1Blocks + layout.group2Blocks !== layout.blockCount) {
|
|
661
|
+
problems.push(`${tag}: group split does not sum to the block count`);
|
|
662
|
+
}
|
|
663
|
+
if (layout.group1Blocks * layout.group1DataCount +
|
|
664
|
+
layout.group2Blocks * layout.group2DataCount !== layout.totalDataCodewords) {
|
|
665
|
+
problems.push(`${tag}: group sizes do not sum to the data codeword count`);
|
|
666
|
+
}
|
|
667
|
+
// Reed-Solomon over GF(256) cannot address a codeword longer than 255.
|
|
668
|
+
if (layout.group2DataCount + layout.eccPerBlock > 255) {
|
|
669
|
+
problems.push(`${tag}: block length ${layout.group2DataCount + layout.eccPerBlock} exceeds GF(256)`);
|
|
670
|
+
}
|
|
671
|
+
// Stronger correction must cost capacity, never gain it.
|
|
672
|
+
if (l > 0) {
|
|
673
|
+
const weaker = dataCodewords(version, ECC_LEVELS[l - 1]);
|
|
674
|
+
if (layout.totalDataCodewords >= weaker) {
|
|
675
|
+
problems.push(`${tag}: ${layout.totalDataCodewords} data codewords is not less than ` +
|
|
676
|
+
`${ECC_LEVELS[l - 1]}'s ${weaker}`);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
// Capacity must grow with version.
|
|
680
|
+
if (version > MIN_VERSION) {
|
|
681
|
+
const smaller = dataCodewords(version - 1, ecc);
|
|
682
|
+
if (layout.totalDataCodewords <= smaller) {
|
|
683
|
+
problems.push(`${tag}: ${layout.totalDataCodewords} data codewords is not more than ` +
|
|
684
|
+
`v${version - 1}-${ecc}'s ${smaller}`);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
731
687
|
}
|
|
732
|
-
}
|
|
733
688
|
}
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
return problems;
|
|
689
|
+
return problems;
|
|
737
690
|
}
|