@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.
- package/LICENSE +5 -4
- package/NOTICE.md +2 -2
- package/README.md +17 -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/rmqr/decoder.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
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
|
-
|
|
76
|
-
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
mode
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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 };
|
package/src/js/rmqr/detector.js
CHANGED
|
@@ -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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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++)
|
|
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
|
-
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
let
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const
|
|
64
|
-
for (let
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
-
|
|
86
|
-
|
|
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; }
|