@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.
Files changed (179) hide show
  1. package/LICENSE +5 -4
  2. package/NOTICE.md +2 -2
  3. package/README.md +61 -13
  4. package/bundle/sythos-barcode.esm.js +11658 -11244
  5. package/bundle/sythos-barcode.js +11658 -11244
  6. package/package.json +10 -3
  7. package/src/index.d.ts +1 -1
  8. package/src/index.js +508 -516
  9. package/src/js/aztec/decoder.js +208 -199
  10. package/src/js/aztec/detector.js +153 -126
  11. package/src/js/aztec/encoder.js +172 -153
  12. package/src/js/aztec/high-level.js +106 -101
  13. package/src/js/aztec/index.js +1 -12
  14. package/src/js/aztec/tables.js +106 -120
  15. package/src/js/aztecrune/decoder.js +76 -85
  16. package/src/js/aztecrune/detector.js +107 -93
  17. package/src/js/aztecrune/encoder.js +50 -68
  18. package/src/js/aztecrune/index.js +1 -16
  19. package/src/js/aztecrune/tables.js +59 -54
  20. package/src/js/compactpdf417/decoder.js +69 -74
  21. package/src/js/compactpdf417/detector.js +74 -75
  22. package/src/js/compactpdf417/encoder.js +68 -81
  23. package/src/js/compactpdf417/index.js +1 -13
  24. package/src/js/compactpdf417/tables.js +71 -80
  25. package/src/js/core/bit-buffer.js +106 -122
  26. package/src/js/core/bit-matrix.js +196 -194
  27. package/src/js/core/errors.js +13 -15
  28. package/src/js/core/galois-field.js +119 -133
  29. package/src/js/core/index.js +2 -19
  30. package/src/js/core/reed-solomon.js +195 -204
  31. package/src/js/databar/codec.js +93 -108
  32. package/src/js/databar/decoder.js +149 -140
  33. package/src/js/databar/encoder.js +47 -55
  34. package/src/js/databar/gs1.js +122 -123
  35. package/src/js/databar/index.js +4 -36
  36. package/src/js/databar/patterns.js +81 -85
  37. package/src/js/databar/tables.js +51 -52
  38. package/src/js/datamatrix/decoder.js +216 -182
  39. package/src/js/datamatrix/detector.js +161 -132
  40. package/src/js/datamatrix/encoder.js +192 -133
  41. package/src/js/datamatrix/index.js +1 -9
  42. package/src/js/datamatrix/tables.js +68 -69
  43. package/src/js/frameqr/decoder.js +102 -125
  44. package/src/js/frameqr/detector.js +101 -94
  45. package/src/js/frameqr/encoder.js +67 -80
  46. package/src/js/frameqr/index.js +1 -10
  47. package/src/js/frameqr/tables.js +145 -163
  48. package/src/js/image/binarizer.js +146 -162
  49. package/src/js/image/grid-sampler.js +63 -79
  50. package/src/js/image/index.js +0 -2
  51. package/src/js/image/luminance.js +129 -140
  52. package/src/js/image/perspective.js +112 -149
  53. package/src/js/micropdf417/compaction.js +64 -65
  54. package/src/js/micropdf417/decoder.js +106 -109
  55. package/src/js/micropdf417/detector.js +73 -70
  56. package/src/js/micropdf417/encoder.js +129 -154
  57. package/src/js/micropdf417/error-correction.js +6 -11
  58. package/src/js/micropdf417/index.js +2 -16
  59. package/src/js/micropdf417/tables.js +112 -99
  60. package/src/js/microqr/decoder.js +177 -182
  61. package/src/js/microqr/detector.js +240 -256
  62. package/src/js/microqr/encoder.js +239 -194
  63. package/src/js/microqr/index.js +0 -2
  64. package/src/js/microqr/tables.js +208 -200
  65. package/src/js/oned/addons.js +167 -191
  66. package/src/js/oned/index.js +27 -58
  67. package/src/js/oned/patterns.js +194 -213
  68. package/src/js/oned/reader.js +1003 -984
  69. package/src/js/oned/writers.js +411 -466
  70. package/src/js/pdf417/compaction.js +315 -222
  71. package/src/js/pdf417/decoder.js +71 -36
  72. package/src/js/pdf417/detector.js +377 -361
  73. package/src/js/pdf417/encoder.js +57 -46
  74. package/src/js/pdf417/error-correction.js +5 -8
  75. package/src/js/pdf417/tables.js +248 -250
  76. package/src/js/qr/decoder.js +311 -347
  77. package/src/js/qr/detector.js +378 -395
  78. package/src/js/qr/encoder.js +514 -599
  79. package/src/js/qr/index.js +0 -2
  80. package/src/js/qr/tables.js +375 -422
  81. package/src/js/render/image-data.js +57 -62
  82. package/src/js/render/index.js +21 -23
  83. package/src/js/render/options.js +82 -90
  84. package/src/js/render/png.js +144 -169
  85. package/src/js/render/svg.js +41 -44
  86. package/src/js/render/webgl.js +103 -109
  87. package/src/js/render/webgpu.js +205 -217
  88. package/src/js/rmqr/decoder.js +161 -53
  89. package/src/js/rmqr/detector.js +73 -41
  90. package/src/js/rmqr/encoder.js +253 -104
  91. package/src/js/rmqr/index.js +1 -5
  92. package/src/js/rmqr/tables.js +108 -85
  93. package/src/ts/aztec/decoder.ts +317 -0
  94. package/src/ts/aztec/detector.ts +224 -0
  95. package/src/ts/aztec/encoder.ts +257 -0
  96. package/src/ts/aztec/high-level.ts +211 -0
  97. package/src/ts/aztec/index.ts +45 -0
  98. package/src/ts/aztec/tables.ts +210 -0
  99. package/src/ts/aztecrune/decoder.ts +155 -0
  100. package/src/ts/aztecrune/detector.ts +166 -0
  101. package/src/ts/aztecrune/encoder.ts +121 -0
  102. package/src/ts/aztecrune/index.ts +49 -0
  103. package/src/ts/aztecrune/tables.ts +137 -0
  104. package/src/ts/compactpdf417/decoder.ts +128 -0
  105. package/src/ts/compactpdf417/detector.ts +139 -0
  106. package/src/ts/compactpdf417/encoder.ts +140 -0
  107. package/src/ts/compactpdf417/index.ts +17 -0
  108. package/src/ts/compactpdf417/tables.ts +173 -0
  109. package/src/ts/core/bit-buffer.ts +174 -0
  110. package/src/ts/core/bit-matrix.ts +241 -0
  111. package/src/ts/core/errors.ts +61 -0
  112. package/src/ts/core/galois-field.ts +207 -0
  113. package/src/ts/core/index.ts +56 -0
  114. package/src/ts/core/reed-solomon.ts +327 -0
  115. package/src/ts/databar/codec.ts +181 -0
  116. package/src/ts/databar/decoder.ts +215 -0
  117. package/src/ts/databar/encoder.ts +96 -0
  118. package/src/ts/databar/gs1.ts +177 -0
  119. package/src/ts/databar/index.ts +68 -0
  120. package/src/ts/databar/patterns.ts +143 -0
  121. package/src/ts/databar/tables.ts +124 -0
  122. package/src/ts/datamatrix/decoder.ts +262 -0
  123. package/src/ts/datamatrix/detector.ts +225 -0
  124. package/src/ts/datamatrix/encoder.ts +191 -0
  125. package/src/ts/datamatrix/index.ts +42 -0
  126. package/src/ts/datamatrix/tables.ts +123 -0
  127. package/src/ts/frameqr/decoder.ts +239 -0
  128. package/src/ts/frameqr/detector.ts +192 -0
  129. package/src/ts/frameqr/encoder.ts +156 -0
  130. package/src/ts/frameqr/index.ts +42 -0
  131. package/src/ts/frameqr/tables.ts +270 -0
  132. package/src/ts/image/binarizer.ts +270 -0
  133. package/src/ts/image/grid-sampler.ts +164 -0
  134. package/src/ts/image/index.ts +40 -0
  135. package/src/ts/image/luminance.ts +196 -0
  136. package/src/ts/image/perspective.ts +195 -0
  137. package/src/ts/index.d.ts +1 -1
  138. package/src/ts/index.ts +790 -0
  139. package/src/ts/micropdf417/compaction.ts +116 -0
  140. package/src/ts/micropdf417/decoder.ts +183 -0
  141. package/src/ts/micropdf417/detector.ts +149 -0
  142. package/src/ts/micropdf417/encoder.ts +209 -0
  143. package/src/ts/micropdf417/error-correction.ts +55 -0
  144. package/src/ts/micropdf417/index.ts +49 -0
  145. package/src/ts/micropdf417/tables.ts +184 -0
  146. package/src/ts/microqr/decoder.ts +245 -0
  147. package/src/ts/microqr/detector.ts +355 -0
  148. package/src/ts/microqr/encoder.ts +269 -0
  149. package/src/ts/microqr/index.ts +36 -0
  150. package/src/ts/microqr/tables.ts +316 -0
  151. package/src/ts/oned/addons.ts +420 -0
  152. package/src/ts/oned/index.ts +106 -0
  153. package/src/ts/oned/patterns.ts +384 -0
  154. package/src/ts/oned/reader.ts +1364 -0
  155. package/src/ts/oned/writers.ts +746 -0
  156. package/src/ts/pdf417/compaction.ts +298 -0
  157. package/src/ts/pdf417/decoder.ts +75 -0
  158. package/src/ts/pdf417/detector.ts +468 -0
  159. package/src/ts/pdf417/encoder.ts +91 -0
  160. package/src/ts/pdf417/error-correction.ts +47 -0
  161. package/src/ts/pdf417/index.ts +6 -0
  162. package/src/ts/pdf417/tables.ts +317 -0
  163. package/src/ts/qr/decoder.ts +575 -0
  164. package/src/ts/qr/detector.ts +630 -0
  165. package/src/ts/qr/encoder.ts +958 -0
  166. package/src/ts/qr/index.ts +44 -0
  167. package/src/ts/qr/tables.ts +737 -0
  168. package/src/ts/render/image-data.ts +125 -0
  169. package/src/ts/render/index.ts +130 -0
  170. package/src/ts/render/options.ts +160 -0
  171. package/src/ts/render/png.ts +295 -0
  172. package/src/ts/render/svg.ts +120 -0
  173. package/src/ts/render/webgl.ts +206 -0
  174. package/src/ts/render/webgpu.ts +369 -0
  175. package/src/ts/rmqr/decoder.ts +101 -0
  176. package/src/ts/rmqr/detector.ts +90 -0
  177. package/src/ts/rmqr/encoder.ts +172 -0
  178. package/src/ts/rmqr/index.ts +37 -0
  179. package/src/ts/rmqr/tables.ts +154 -0
@@ -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
- const codes = [];
80
- for (let l = 0; l < ECC_LEVELS.length; l++) {
81
- for (let mask = 0; mask < 8; mask++) {
82
- codes.push({ bits: formatInfoBits(ECC_LEVELS[l], mask), ecc: ECC_LEVELS[l], mask });
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
- const codes = [];
91
- for (let v = VERSION_INFO_MIN; v <= MAX_VERSION; v++) {
92
- codes.push({ bits: versionInfoBits(v), version: v });
93
- }
94
- return codes;
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
- let v = a ^ b;
103
- let n = 0;
104
- while (v !== 0) {
105
- v &= v - 1;
106
- n++;
107
- }
108
- return n;
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
- let best = null;
121
- let bestDistance = BCH_MAX_DISTANCE + 1;
122
- let ambiguous = false;
123
-
124
- for (let i = 0; i < codes.length; i++) {
125
- const d = hammingDistance(value, codes[i].bits);
126
- if (d < bestDistance) {
127
- bestDistance = d;
128
- best = codes[i];
129
- ambiguous = false;
130
- } else if (d === bestDistance) {
131
- ambiguous = true;
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
- if (!best || bestDistance > BCH_MAX_DISTANCE || ambiguous) return null;
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
- let value = 0;
148
- for (let i = 0; i < positions.length; i++) {
149
- if (m.get(positions[i][0], positions[i][1])) value |= 1 << i;
150
- }
151
- return value;
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
- const [a, b] = formatInfoPositions(size);
166
- const candidates = [nearestCode(readBits(m, a), FORMAT_CODES), nearestCode(readBits(m, b), FORMAT_CODES)];
167
-
168
- let best = null;
169
- for (let i = 0; i < candidates.length; i++) {
170
- const c = candidates[i];
171
- if (c && (!best || c.distance < best.distance)) best = c;
172
- }
173
- if (!best) {
174
- throw new FormatError('QR: format information is unreadable in both copies');
175
- }
176
- return { ecc: best.ecc, mask: best.mask };
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
- if (fromDimension < VERSION_INFO_MIN) return fromDimension;
192
-
193
- /** @type {Array<[number, number]>} */
194
- const bottomLeft = [];
195
- /** @type {Array<[number, number]>} */
196
- const topRight = [];
197
- for (let i = 0; i < 18; i++) {
198
- const major = Math.floor(i / 3);
199
- const minor = i % 3;
200
- bottomLeft.push([major, size - 11 + minor]);
201
- topRight.push([size - 11 + minor, major]);
202
- }
203
-
204
- const candidates = [
205
- nearestCode(readBits(m, bottomLeft), VERSION_CODES),
206
- nearestCode(readBits(m, topRight), VERSION_CODES),
207
- ];
208
-
209
- let best = null;
210
- for (let i = 0; i < candidates.length; i++) {
211
- const c = candidates[i];
212
- if (c && (!best || c.distance < best.distance)) best = c;
213
- }
214
-
215
- // Unreadable version information is survivable; contradictory version
216
- // information is not.
217
- if (!best) return fromDimension;
218
- if (best.version !== fromDimension) {
219
- throw new FormatError(
220
- `QR: version information says ${best.version} but the symbol is ` +
221
- `${size}x${size} modules (version ${fromDimension})`
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
- const order = dataModuleOrder(version);
236
- const out = new Uint8Array(totalCodewords);
237
- const available = totalCodewords * 8;
238
-
239
- for (let p = 0, bit = 0; p < order.length && bit < available; p += 2, bit++) {
240
- const x = order[p];
241
- const y = order[p + 1];
242
- let dark = m.get(x, y);
243
- if (maskBit(mask, x, y)) dark = !dark;
244
- if (dark) out[bit >> 3] |= 0x80 >> (bit & 7);
245
- }
246
-
247
- return out;
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
- const counts = new Array(layout.blockCount);
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
- if (i < counts[b]) blocks[b][i] = codewords[n++];
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
- blocks[b][counts[b] + i] = codewords[n++];
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
- const data = new Uint8Array(layout.totalDataCodewords);
282
- let offset = 0;
283
- let corrections = 0;
284
-
285
- for (let b = 0; b < layout.blockCount; b++) {
286
- corrections += rsDecode(blocks[b], layout.eccPerBlock, GF256_QR, 0);
287
- for (let i = 0; i < counts[b]; i++) data[offset + i] = blocks[b][i];
288
- offset += counts[b];
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
- const combined = (Math.floor(value / 0xc0) << 8) | (value % 0xc0);
307
- return combined + (combined + 0x8140 <= 0x9ffc ? 0x8140 : 0xc140);
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
- 0: 'iso-8859-1',
313
- 1: 'iso-8859-1',
314
- 2: 'iso-8859-1',
315
- 3: 'iso-8859-1',
316
- 4: 'iso-8859-2',
317
- 5: 'iso-8859-3',
318
- 6: 'iso-8859-4',
319
- 7: 'iso-8859-5',
320
- 8: 'iso-8859-6',
321
- 9: 'iso-8859-7',
322
- 10: 'iso-8859-8',
323
- 11: 'iso-8859-9',
324
- 12: 'iso-8859-10',
325
- 13: 'iso-8859-11',
326
- 15: 'iso-8859-13',
327
- 16: 'iso-8859-14',
328
- 17: 'iso-8859-15',
329
- 18: 'iso-8859-16',
330
- 20: 'shift_jis',
331
- 21: 'windows-1250',
332
- 22: 'windows-1251',
333
- 23: 'windows-1252',
334
- 24: 'windows-1256',
335
- 25: 'utf-16be',
336
- 26: 'utf-8',
337
- 27: 'us-ascii',
338
- 28: 'big5',
339
- 29: 'gb18030',
340
- 30: 'euc-kr',
341
- 170: 'us-ascii',
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
- if (eci !== null && eci !== undefined) {
358
- const label = ECI_LABELS[eci];
359
- if (label) {
360
- try {
361
- return new TextDecoder(label).decode(bytes);
362
- } catch (e) {
363
- /* Unsupported label on this platform; fall through. */
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
- } else {
367
- try {
368
- return new TextDecoder('utf-8', { fatal: true }).decode(bytes);
369
- } catch (e) {
370
- /* Not valid UTF-8; it is Latin-1. */
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
- let s = '';
382
- for (let i = 0; i < bytes.length; i++) s += String.fromCharCode(bytes[i]);
383
- return s;
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
- try {
392
- const decoder = new TextDecoder('shift_jis');
393
- const text = decoder.decode(bytes);
394
- if (text.indexOf('�') === -1) return text;
395
- } catch (e) {
396
- /* No Shift_JIS codec on this platform. */
397
- }
398
- // Graceful degradation: the payload is structurally intact but we cannot
399
- // name the characters, so mark them rather than failing the whole symbol.
400
- let s = '';
401
- for (let i = 0; i < bytes.length; i += 2) s += '�';
402
- return s;
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
- const first = reader.read(8);
414
- if ((first & 0x80) === 0) return first;
415
- if ((first & 0xc0) === 0x80) return ((first & 0x3f) << 8) | reader.read(8);
416
- if ((first & 0xe0) === 0xc0) return ((first & 0x1f) << 16) | reader.read(16);
417
- throw new FormatError(`QR: malformed ECI designator (first byte 0x${first.toString(16)})`);
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
- const reader = new BitReader(data);
429
- let text = '';
430
- /** @type {number[]} */
431
- const rawBytes = [];
432
- /** @type {number | null} */
433
- let eci = null;
434
-
435
- // A symbol whose payload ends exactly on a codeword boundary has no room for
436
- // a terminator, so running out of bits is a normal end, not an error.
437
- while (reader.available() >= 4) {
438
- const mode = reader.read(4);
439
- if (mode === MODE.TERMINATOR) break;
440
-
441
- if (mode === MODE.ECI) {
442
- eci = readEciDesignator(reader);
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 (count - i === 2) {
472
- const pair = reader.read(7);
473
- if (pair > 99) throw new FormatError(`QR: numeric pair ${pair} out of range`);
474
- text += String(pair).padStart(2, '0');
475
- } else if (count - i === 1) {
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
- break;
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
- if (i < count) {
492
- const single = reader.read(6);
493
- if (single >= 45) throw new FormatError(`QR: alphanumeric value ${single} out of range`);
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
- break;
497
- }
498
-
499
- case MODE.BYTE: {
500
- const bytes = new Uint8Array(count);
501
- for (let i = 0; i < count; i++) {
502
- bytes[i] = reader.read(8);
503
- rawBytes.push(bytes[i]);
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
- if (!matrix || !matrix.width) throw new FormatError('QR: no matrix supplied');
550
-
551
- const size = matrix.width;
552
- if (matrix.height !== size) {
553
- throw new FormatError(`QR: symbol must be square, got ${size}x${matrix.height}`);
554
- }
555
- if ((size - 17) % 4 !== 0) {
556
- throw new FormatError(`QR: ${size} modules is not a valid symbol size`);
557
- }
558
-
559
- const dimensionVersion = (size - 17) / 4;
560
- if (dimensionVersion < MIN_VERSION || dimensionVersion > MAX_VERSION) {
561
- throw new FormatError(`QR: ${size} modules implies version ${dimensionVersion}`);
562
- }
563
-
564
- const { ecc, mask } = readFormatInfo(matrix, size);
565
- const version = readVersionInfo(matrix, size, dimensionVersion);
566
-
567
- const layout = blockLayout(version, ecc);
568
- const codewords = readCodewords(matrix, version, mask, layout.totalCodewords);
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 };