@sythos/js_barcode_universal 1.5.8 → 1.5.9

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 +17 -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,75 +27,183 @@
27
27
  *
28
28
  * Original work. No code from any other barcode implementation.
29
29
  */
30
-
31
30
  import { BitReader } from '../core/bit-buffer.js';
32
31
  import { ChecksumError, FormatError } from '../core/errors.js';
33
32
  import { GF256_QR } from '../core/galois-field.js';
34
33
  import { rsDecode } from '../core/reed-solomon.js';
35
- import {
36
- FORMAT_MASK_FINDER, FORMAT_MASK_SUB, dataModuleOrder, functionModules, maskBit,
37
- versionForSize, versionInfo, formatBits,
38
- } from './tables.js';
34
+ import { FORMAT_MASK_FINDER, FORMAT_MASK_SUB, dataModuleOrder, maskBit, versionForSize, formatBits, } from './tables.js';
39
35
  import { ALPHANUMERIC_CHARS, MODE } from './encoder.js';
40
-
41
- function hamming(a, b) { let v = a ^ b, n = 0; while (v) { v &= v - 1; n++; } return n; }
36
+ function hamming(a, b) { let v = a ^ b, n = 0; while (v) {
37
+ v &= v - 1;
38
+ n++;
39
+ } return n; }
42
40
  function readFormat(matrix, v) {
43
- let a = 0; for (let n = 0; n < 18; n++) if (matrix.get(8 + Math.floor(n / 5), 1 + (n % 5))) a |= 1 << n;
44
- let b = 0; for (let n = 0; n < 15; n++) if (matrix.get(v.width - 8 + Math.floor(n / 5), v.height - 6 + (n % 5))) b |= 1 << n;
45
- for (let n = 15; n < 18; n++) if (matrix.get(v.width - 5 + (n - 15), v.height - 6)) b |= 1 << n;
46
- const candidates = [];
47
- for (let version = 1; version <= 32; version++) for (const ecc of ['M', 'H']) {
48
- candidates.push({ version, ecc, finder: formatBits(version, ecc) ^ FORMAT_MASK_FINDER, sub: formatBits(version, ecc) ^ FORMAT_MASK_SUB });
49
- }
50
- let best = null;
51
- for (const c of candidates) for (const [value, expected] of [[a, c.finder], [b, c.sub]]) { const distance = hamming(value, expected); if (!best || distance < best.distance) best = { ...c, distance }; }
52
- if (!best || best.distance > 3) throw new FormatError('rMQR: format information is unreadable');
53
- return best;
41
+ let a = 0;
42
+ for (let n = 0; n < 18; n++)
43
+ if (matrix.get(8 + Math.floor(n / 5), 1 + (n % 5)))
44
+ a |= 1 << n;
45
+ let b = 0;
46
+ for (let n = 0; n < 15; n++)
47
+ if (matrix.get(v.width - 8 + Math.floor(n / 5), v.height - 6 + (n % 5)))
48
+ b |= 1 << n;
49
+ for (let n = 15; n < 18; n++)
50
+ if (matrix.get(v.width - 5 + (n - 15), v.height - 6))
51
+ b |= 1 << n;
52
+ const candidates = [];
53
+ for (let version = 1; version <= 32; version++)
54
+ for (const ecc of ['M', 'H']) {
55
+ candidates.push({ version, ecc, finder: formatBits(version, ecc) ^ FORMAT_MASK_FINDER, sub: formatBits(version, ecc) ^ FORMAT_MASK_SUB });
56
+ }
57
+ let best = null;
58
+ for (const c of candidates)
59
+ for (const [value, expected] of [[a, c.finder], [b, c.sub]]) {
60
+ const distance = hamming(value, expected);
61
+ if (!best || distance < best.distance)
62
+ best = { ...c, distance };
63
+ }
64
+ if (!best || best.distance > 3)
65
+ throw new FormatError('rMQR: format information is unreadable');
66
+ return best;
54
67
  }
55
-
56
68
  function readCodewords(matrix, v, mask, total) {
57
- const order = dataModuleOrder(v.version); const out = new Uint8Array(total); let bit = 0;
58
- for (const [x, y] of order) { if (bit >= total * 8) break; let on = matrix.get(x, y); if (maskBit(x, y)) on = !on; if (on) out[bit >>> 3] |= 0x80 >>> (bit & 7); bit++; }
59
- return out;
69
+ const order = dataModuleOrder(v.version);
70
+ const out = new Uint8Array(total);
71
+ let bit = 0;
72
+ for (const [x, y] of order) {
73
+ if (bit >= total * 8)
74
+ break;
75
+ let on = matrix.get(x, y);
76
+ if (maskBit(x, y))
77
+ on = !on;
78
+ if (on)
79
+ out[bit >>> 3] |= 0x80 >>> (bit & 7);
80
+ bit++;
81
+ }
82
+ return out;
60
83
  }
61
-
62
84
  function deinterleave(codewords, v, ecc) {
63
- const blocks = v.blockLayout(ecc).blocks; const arrays = blocks.map((b) => new Array(b.total).fill(0)); let offset = 0;
64
- const maxData = Math.max(...blocks.map((b) => b.data)); for (let i = 0; i < maxData; i++) for (let b = 0; b < blocks.length; b++) if (i < blocks[b].data) arrays[b][i] = codewords[offset++];
65
- const maxEcc = Math.max(...blocks.map((b) => b.ecc)); for (let i = 0; i < maxEcc; i++) for (let b = 0; b < blocks.length; b++) if (i < blocks[b].ecc) arrays[b][blocks[b].data + i] = codewords[offset++];
66
- const data = []; let corrections = 0;
67
- for (let b = 0; b < arrays.length; b++) { corrections += rsDecode(arrays[b], blocks[b].ecc, GF256_QR, 0); data.push(...arrays[b].slice(0, blocks[b].data)); }
68
- return { data: Uint8Array.from(data), corrections };
85
+ const blocks = v.blockLayout(ecc).blocks;
86
+ const arrays = blocks.map((b) => new Array(b.total).fill(0));
87
+ let offset = 0;
88
+ const maxData = Math.max(...blocks.map((b) => b.data));
89
+ for (let i = 0; i < maxData; i++)
90
+ for (let b = 0; b < blocks.length; b++)
91
+ if (i < blocks[b].data)
92
+ arrays[b][i] = codewords[offset++];
93
+ const maxEcc = Math.max(...blocks.map((b) => b.ecc));
94
+ for (let i = 0; i < maxEcc; i++)
95
+ for (let b = 0; b < blocks.length; b++)
96
+ if (i < blocks[b].ecc)
97
+ arrays[b][blocks[b].data + i] = codewords[offset++];
98
+ const data = [];
99
+ let corrections = 0;
100
+ for (let b = 0; b < arrays.length; b++) {
101
+ corrections += rsDecode(arrays[b], blocks[b].ecc, GF256_QR, 0);
102
+ data.push(...arrays[b].slice(0, blocks[b].data));
103
+ }
104
+ return { data: Uint8Array.from(data), corrections };
69
105
  }
70
-
71
106
  function decodeBytes(bytes, eci) {
72
- try { return new TextDecoder(eci === 26 ? 'utf-8' : 'iso-8859-1', { fatal: false }).decode(bytes); } catch { return String.fromCharCode(...bytes); }
107
+ try {
108
+ return new TextDecoder(eci === 26 ? 'utf-8' : 'iso-8859-1', { fatal: false }).decode(bytes);
109
+ }
110
+ catch {
111
+ return String.fromCharCode(...bytes);
112
+ }
73
113
  }
74
114
  function parseSegments(data, v) {
75
- const reader = new BitReader(data); let text = ''; const raw = []; let eci = 3; let mode;
76
- while (reader.available() >= 3) {
77
- const peek = (() => { const save = { byteOffset: reader.byteOffset, bitOffset: reader.bitOffset }; const n = reader.read(3); reader.byteOffset = save.byteOffset; reader.bitOffset = save.bitOffset; return n; })();
78
- if (peek === 0) break;
79
- mode = reader.read(3);
80
- if (mode === MODE.ECI) { const first = reader.read(8); let value; if (!(first & 0x80)) value = first; else if ((first & 0xc0) === 0x80) value = ((first & 0x3f) << 8) | reader.read(8); else if ((first & 0xe0) === 0xc0) value = ((first & 0x1f) << 16) | reader.read(16); else throw new FormatError('rMQR: invalid ECI'); eci = value; continue; }
81
- const kind = mode === MODE.NUMERIC ? 'numeric' : mode === MODE.ALPHANUMERIC ? 'alphanumeric' : mode === MODE.BYTE ? 'byte' : mode === MODE.KANJI ? 'kanji' : null;
82
- if (!kind) throw new FormatError(`rMQR: unsupported mode ${mode}`);
83
- const count = reader.read(v.countBits(kind));
84
- if (kind === 'numeric') { let remaining = count; while (remaining >= 3) { const n = reader.read(10).toString().padStart(3, '0'); text += n; remaining -= 3; } if (remaining === 2) text += reader.read(7).toString().padStart(2, '0'); else if (remaining === 1) text += reader.read(4).toString(); }
85
- else if (kind === 'alphanumeric') { let remaining = count; while (remaining >= 2) { const n = reader.read(11); text += ALPHANUMERIC_CHARS[Math.floor(n / 45)] + ALPHANUMERIC_CHARS[n % 45]; remaining -= 2; } if (remaining) text += ALPHANUMERIC_CHARS[reader.read(6)]; }
86
- else if (kind === 'byte') { const b = new Uint8Array(count); for (let i = 0; i < count; i++) { b[i] = reader.read(8); raw.push(b[i]); } text += decodeBytes(b, eci); }
87
- else { const bytes = new Uint8Array(count * 2); for (let i = 0; i < count; i++) { const n = reader.read(13); const v2 = n; const high = Math.floor(v2 / 0xc0); const low = v2 % 0xc0; const sjis = high < 0x1f ? 0x8140 + (high << 8) + low : 0xc140 + (high << 8) + low; bytes[i * 2] = sjis >> 8; bytes[i * 2 + 1] = sjis & 255; } try { text += new TextDecoder('shift_jis').decode(bytes); } catch { text += String.fromCharCode(...bytes); } }
88
- }
89
- return { text, bytes: Uint8Array.from(raw) };
115
+ const reader = new BitReader(data);
116
+ let text = '';
117
+ const raw = [];
118
+ let eci = 3;
119
+ let mode;
120
+ while (reader.available() >= 3) {
121
+ const peek = (() => { const save = { byteOffset: reader.byteOffset, bitOffset: reader.bitOffset }; const n = reader.read(3); reader.byteOffset = save.byteOffset; reader.bitOffset = save.bitOffset; return n; })();
122
+ if (peek === 0)
123
+ break;
124
+ mode = reader.read(3);
125
+ if (mode === MODE.ECI) {
126
+ const first = reader.read(8);
127
+ let value;
128
+ if (!(first & 0x80))
129
+ value = first;
130
+ else if ((first & 0xc0) === 0x80)
131
+ value = ((first & 0x3f) << 8) | reader.read(8);
132
+ else if ((first & 0xe0) === 0xc0)
133
+ value = ((first & 0x1f) << 16) | reader.read(16);
134
+ else
135
+ throw new FormatError('rMQR: invalid ECI');
136
+ eci = value;
137
+ continue;
138
+ }
139
+ const kind = mode === MODE.NUMERIC ? 'numeric' : mode === MODE.ALPHANUMERIC ? 'alphanumeric' : mode === MODE.BYTE ? 'byte' : mode === MODE.KANJI ? 'kanji' : null;
140
+ if (!kind)
141
+ throw new FormatError(`rMQR: unsupported mode ${mode}`);
142
+ const count = reader.read(v.countBits(kind));
143
+ if (kind === 'numeric') {
144
+ let remaining = count;
145
+ while (remaining >= 3) {
146
+ const n = reader.read(10).toString().padStart(3, '0');
147
+ text += n;
148
+ remaining -= 3;
149
+ }
150
+ if (remaining === 2)
151
+ text += reader.read(7).toString().padStart(2, '0');
152
+ else if (remaining === 1)
153
+ text += reader.read(4).toString();
154
+ }
155
+ else if (kind === 'alphanumeric') {
156
+ let remaining = count;
157
+ while (remaining >= 2) {
158
+ const n = reader.read(11);
159
+ text += ALPHANUMERIC_CHARS[Math.floor(n / 45)] + ALPHANUMERIC_CHARS[n % 45];
160
+ remaining -= 2;
161
+ }
162
+ if (remaining)
163
+ text += ALPHANUMERIC_CHARS[reader.read(6)];
164
+ }
165
+ else if (kind === 'byte') {
166
+ const b = new Uint8Array(count);
167
+ for (let i = 0; i < count; i++) {
168
+ b[i] = reader.read(8);
169
+ raw.push(b[i]);
170
+ }
171
+ text += decodeBytes(b, eci);
172
+ }
173
+ else {
174
+ const bytes = new Uint8Array(count * 2);
175
+ for (let i = 0; i < count; i++) {
176
+ const n = reader.read(13);
177
+ const v2 = n;
178
+ const high = Math.floor(v2 / 0xc0);
179
+ const low = v2 % 0xc0;
180
+ const sjis = high < 0x1f ? 0x8140 + (high << 8) + low : 0xc140 + (high << 8) + low;
181
+ bytes[i * 2] = sjis >> 8;
182
+ bytes[i * 2 + 1] = sjis & 255;
183
+ }
184
+ try {
185
+ text += new TextDecoder('shift_jis').decode(bytes);
186
+ }
187
+ catch {
188
+ text += String.fromCharCode(...bytes);
189
+ }
190
+ }
191
+ }
192
+ return { text, bytes: Uint8Array.from(raw) };
90
193
  }
91
-
92
194
  /** Decode an exact rMQR module matrix (without quiet zone). */
93
195
  export function decodeRMQR(matrix) {
94
- if (!matrix || !matrix.width || !matrix.height) throw new FormatError('rMQR: no matrix supplied');
95
- const v = versionForSize(matrix.width, matrix.height); if (!v) throw new FormatError(`rMQR: unsupported symbol size ${matrix.width}x${matrix.height}`);
96
- const info = readFormat(matrix, v); if (info.version !== v.version) throw new FormatError('rMQR: format/version mismatch');
97
- const codewords = readCodewords(matrix, v, 4, v.totalCodewords); const corrected = deinterleave(codewords, v, info.ecc); const parsed = parseSegments(corrected.data, v);
98
- return { ...parsed, version: v.version, name: v.name, ecc: info.ecc, mask: 4, corrections: corrected.corrections };
196
+ if (!matrix || !matrix.width || !matrix.height)
197
+ throw new FormatError('rMQR: no matrix supplied');
198
+ const v = versionForSize(matrix.width, matrix.height);
199
+ if (!v)
200
+ throw new FormatError(`rMQR: unsupported symbol size ${matrix.width}x${matrix.height}`);
201
+ const info = readFormat(matrix, v);
202
+ if (info.version !== v.version)
203
+ throw new FormatError('rMQR: format/version mismatch');
204
+ const codewords = readCodewords(matrix, v, 4, v.totalCodewords);
205
+ const corrected = deinterleave(codewords, v, info.ecc);
206
+ const parsed = parseSegments(corrected.data, v);
207
+ return { ...parsed, version: v.version, name: v.name, ecc: info.ecc, mask: 4, corrections: corrected.corrections };
99
208
  }
100
-
101
209
  export { ChecksumError };
@@ -27,64 +27,96 @@
27
27
  *
28
28
  * Original work. No code from any other barcode implementation.
29
29
  */
30
-
31
30
  import { BitMatrix } from '../core/bit-matrix.js';
32
31
  import { NotFoundError } from '../core/errors.js';
33
32
  import { decodeRMQR } from './decoder.js';
34
33
  import { versionForSize } from './tables.js';
35
-
36
34
  function rotate90(source) {
37
- const out = new BitMatrix(source.height, source.width);
38
- for (let y = 0; y < source.height; y++) for (let x = 0; x < source.width; x++) if (source.get(x, y)) out.set(source.height - 1 - y, x);
39
- return out;
35
+ const out = new BitMatrix(source.height, source.width);
36
+ for (let y = 0; y < source.height; y++)
37
+ for (let x = 0; x < source.width; x++)
38
+ if (source.get(x, y))
39
+ out.set(source.height - 1 - y, x);
40
+ return out;
40
41
  }
41
- function rotate180(source) { const out = new BitMatrix(source.width, source.height); for (let y = 0; y < source.height; y++) for (let x = 0; x < source.width; x++) if (source.get(x, y)) out.set(source.width - 1 - x, source.height - 1 - y); return out; }
42
+ function rotate180(source) { const out = new BitMatrix(source.width, source.height); for (let y = 0; y < source.height; y++)
43
+ for (let x = 0; x < source.width; x++)
44
+ if (source.get(x, y))
45
+ out.set(source.width - 1 - x, source.height - 1 - y); return out; }
42
46
  function rotate270(source) { return rotate90(rotate180(source)); }
43
47
  function crop(source, box) {
44
- const out = new BitMatrix(box.width, box.height); for (let y = 0; y < box.height; y++) for (let x = 0; x < box.width; x++) if (source.get(box.x + x, box.y + y)) out.set(x, y); return out;
48
+ const out = new BitMatrix(box.width, box.height);
49
+ for (let y = 0; y < box.height; y++)
50
+ for (let x = 0; x < box.width; x++)
51
+ if (source.get(box.x + x, box.y + y))
52
+ out.set(x, y);
53
+ return out;
45
54
  }
46
55
  function sample(matrix, width, height, scale, offsetX, offsetY) {
47
- const out = new BitMatrix(width, height); const half = Math.max(0, Math.floor(scale / 2));
48
- for (let y = 0; y < height; y++) for (let x = 0; x < width; x++) {
49
- let dark = 0, count = 0; const x0 = offsetX + x * scale, y0 = offsetY + y * scale;
50
- for (let yy = 0; yy < scale; yy++) for (let xx = 0; xx < scale; xx++) { if (matrix.get(x0 + xx, y0 + yy)) dark++; count++; }
51
- if (dark * 2 >= count) out.set(x, y);
52
- }
53
- return out;
56
+ const out = new BitMatrix(width, height);
57
+ const half = Math.max(0, Math.floor(scale / 2));
58
+ for (let y = 0; y < height; y++)
59
+ for (let x = 0; x < width; x++) {
60
+ let dark = 0, count = 0;
61
+ const x0 = offsetX + x * scale, y0 = offsetY + y * scale;
62
+ for (let yy = 0; yy < scale; yy++)
63
+ for (let xx = 0; xx < scale; xx++) {
64
+ if (matrix.get(x0 + xx, y0 + yy))
65
+ dark++;
66
+ count++;
67
+ }
68
+ if (dark * 2 >= count)
69
+ out.set(x, y);
70
+ }
71
+ return out;
54
72
  }
55
73
  function bounds(matrix) { return matrix.getBounds(); }
56
-
57
74
  /** Detect an axis-aligned, clean rMQR raster and return the exact module matrix. */
58
75
  export function detectRMQR(image, options = {}) {
59
- if (!image || !image.width || !image.height) throw new NotFoundError('rMQR: no raster supplied');
60
- if (options.perspective) throw new NotFoundError('rMQR: perspective detection is not available for clean-raster mode');
61
- const orientations = [image, rotate90(image), rotate180(image), rotate270(image)];
62
- for (let rotation = 0; rotation < orientations.length; rotation++) {
63
- const source = orientations[rotation]; const box = bounds(source); if (!box) continue;
64
- for (let version = 1; version <= 32; version++) {
65
- const v = versionForSize(box.width, box.height); // exact modules, scale 1
66
- if (v && v.version === version) {
67
- try { const matrix = crop(source, box); const result = decodeRMQR(matrix); return { matrix, result, rotation, corners: { x: box.x, y: box.y, width: box.width, height: box.height } }; } catch { /* try next orientation/candidate */ }
68
- }
69
- // Integer nearest-neighbour scale. The dark bounding box must still be an
70
- // exact multiple of the standard geometry; this rejects arbitrary text.
71
- const candidate = versionForSize(box.width, box.height);
72
- if (candidate) continue;
73
- const canonical = (awaitableVersion(version));
74
- const wScale = box.width / canonical.width;
75
- const hScale = box.height / canonical.height;
76
- if (!Number.isInteger(wScale) || wScale < 1 || wScale !== hScale) continue;
77
- const info = canonical;
78
- try { const matrix = sample(source, info.width, info.height, wScale, box.x, box.y); const result = decodeRMQR(matrix); return { matrix, result, rotation, scale: wScale, corners: { x: box.x, y: box.y, width: box.width, height: box.height } }; } catch { /* continue */ }
76
+ if (!image || !image.width || !image.height)
77
+ throw new NotFoundError('rMQR: no raster supplied');
78
+ if (options.perspective)
79
+ throw new NotFoundError('rMQR: perspective detection is not available for clean-raster mode');
80
+ const orientations = [image, rotate90(image), rotate180(image), rotate270(image)];
81
+ for (let rotation = 0; rotation < orientations.length; rotation++) {
82
+ const source = orientations[rotation];
83
+ const box = bounds(source);
84
+ if (!box)
85
+ continue;
86
+ for (let version = 1; version <= 32; version++) {
87
+ const v = versionForSize(box.width, box.height); // exact modules, scale 1
88
+ if (v && v.version === version) {
89
+ try {
90
+ const matrix = crop(source, box);
91
+ const result = decodeRMQR(matrix);
92
+ return { matrix, result, rotation, corners: { x: box.x, y: box.y, width: box.width, height: box.height } };
93
+ }
94
+ catch { /* try next orientation/candidate */ }
95
+ }
96
+ // Integer nearest-neighbour scale. The dark bounding box must still be an
97
+ // exact multiple of the standard geometry; this rejects arbitrary text.
98
+ const candidate = versionForSize(box.width, box.height);
99
+ if (candidate)
100
+ continue;
101
+ const canonical = (awaitableVersion(version));
102
+ const wScale = box.width / canonical.width;
103
+ const hScale = box.height / canonical.height;
104
+ if (!Number.isInteger(wScale) || wScale < 1 || wScale !== hScale)
105
+ continue;
106
+ const info = canonical;
107
+ try {
108
+ const matrix = sample(source, info.width, info.height, wScale, box.x, box.y);
109
+ const result = decodeRMQR(matrix);
110
+ return { matrix, result, rotation, scale: wScale, corners: { x: box.x, y: box.y, width: box.width, height: box.height } };
111
+ }
112
+ catch { /* continue */ }
113
+ }
79
114
  }
80
- }
81
- throw new NotFoundError('rMQR: no clean axis-aligned symbol found');
115
+ throw new NotFoundError('rMQR: no clean axis-aligned symbol found');
82
116
  }
83
-
84
117
  function awaitableVersion(version) {
85
- const sizes = [[43, 7], [59, 7], [77, 7], [99, 7], [139, 7], [43, 9], [59, 9], [77, 9], [99, 9], [139, 9], [27, 11], [43, 11], [59, 11], [77, 11], [99, 11], [139, 11], [27, 13], [43, 13], [59, 13], [77, 13], [99, 13], [139, 13], [43, 15], [59, 15], [77, 15], [99, 15], [139, 15], [43, 17], [59, 17], [77, 17], [99, 17], [139, 17]];
86
- return { version, width: sizes[version - 1][0], height: sizes[version - 1][1] };
118
+ const sizes = [[43, 7], [59, 7], [77, 7], [99, 7], [139, 7], [43, 9], [59, 9], [77, 9], [99, 9], [139, 9], [27, 11], [43, 11], [59, 11], [77, 11], [99, 11], [139, 11], [27, 13], [43, 13], [59, 13], [77, 13], [99, 13], [139, 13], [43, 15], [59, 15], [77, 15], [99, 15], [139, 15], [43, 17], [59, 17], [77, 17], [99, 17], [139, 17]];
119
+ return { version, width: sizes[version - 1][0], height: sizes[version - 1][1] };
87
120
  }
88
-
89
121
  /** Detect and decode a raster in one call. */
90
122
  export function detectAndDecodeRMQR(image, options = {}) { return detectRMQR(image, options).result; }