@sythos/js_barcode_universal 1.2.5 → 1.4.1
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/AI_USAGE.md +33 -0
- package/LICENSE +11 -2
- package/NOTICE.md +10 -4
- package/README.md +601 -465
- package/bundle/sythos-barcode.esm.js +4669 -74
- package/bundle/sythos-barcode.js +4575 -72
- package/examples/create.html +1010 -730
- package/examples/read.html +1 -0
- package/licenses/README.md +40 -58
- package/licenses/aztec-code.license +15 -13
- package/licenses/aztec-rune.license +76 -0
- package/licenses/codabar.license +18 -15
- package/licenses/code-11.license +11 -9
- package/licenses/code-128.license +10 -8
- package/licenses/code-39.license +10 -8
- package/licenses/code-93.license +16 -14
- package/licenses/compact-pdf417.license +76 -0
- package/licenses/data-matrix.license +15 -13
- package/licenses/ean-13.license +10 -8
- package/licenses/ean-2.license +74 -0
- package/licenses/ean-5.license +74 -0
- package/licenses/ean-8.license +10 -8
- package/licenses/frameqr.license +84 -0
- package/licenses/gs1-128.license +10 -8
- package/licenses/gs1-databar.license +81 -0
- package/licenses/isbn.license +10 -8
- package/licenses/itf-14.license +10 -8
- package/licenses/itf.license +9 -7
- package/licenses/micro-qr.license +79 -0
- package/licenses/micropdf417.license +52 -67
- package/licenses/msi-plessey.license +13 -11
- package/licenses/pdf417.license +78 -37
- package/licenses/pharmacode.license +14 -12
- package/licenses/qr-code.license +9 -7
- package/licenses/rmqr.license +79 -0
- package/licenses/upc-a.license +12 -10
- package/licenses/upc-e.license +10 -8
- package/package.json +109 -88
- package/src/aztec/decoder.js +1 -0
- package/src/aztec/detector.js +1 -0
- package/src/aztec/encoder.js +1 -0
- package/src/aztec/high-level.js +1 -0
- package/src/aztec/index.js +1 -0
- package/src/aztec/tables.js +1 -0
- package/src/aztecrune/decoder.js +156 -0
- package/src/aztecrune/detector.js +167 -0
- package/src/aztecrune/encoder.js +122 -0
- package/src/aztecrune/index.js +50 -0
- package/src/aztecrune/tables.js +138 -0
- package/src/compactpdf417/decoder.js +129 -0
- package/src/compactpdf417/detector.js +140 -0
- package/src/compactpdf417/encoder.js +141 -0
- package/src/compactpdf417/index.js +22 -0
- package/src/compactpdf417/tables.js +174 -0
- package/src/core/bit-buffer.js +1 -0
- package/src/core/bit-matrix.js +1 -0
- package/src/core/errors.js +1 -0
- package/src/core/galois-field.js +1 -0
- package/src/core/index.js +1 -0
- package/src/core/reed-solomon.js +1 -0
- package/src/databar/codec.js +182 -0
- package/src/databar/decoder.js +115 -0
- package/src/databar/encoder.js +97 -0
- package/src/databar/gs1.js +178 -0
- package/src/databar/index.js +69 -0
- package/src/databar/patterns.js +144 -0
- package/src/databar/tables.js +125 -0
- package/src/datamatrix/decoder.js +263 -262
- package/src/datamatrix/detector.js +226 -225
- package/src/datamatrix/encoder.js +192 -191
- package/src/datamatrix/index.js +43 -42
- package/src/datamatrix/tables.js +124 -123
- package/src/frameqr/decoder.js +240 -0
- package/src/frameqr/detector.js +193 -0
- package/src/frameqr/encoder.js +157 -0
- package/src/frameqr/index.js +43 -0
- package/src/frameqr/tables.js +271 -0
- package/src/image/binarizer.js +1 -0
- package/src/image/grid-sampler.js +1 -0
- package/src/image/index.js +1 -0
- package/src/image/luminance.js +1 -0
- package/src/image/perspective.js +1 -0
- package/src/index.js +155 -2
- package/src/micropdf417/compaction.js +1 -0
- package/src/micropdf417/decoder.js +1 -0
- package/src/micropdf417/detector.js +1 -0
- package/src/micropdf417/encoder.js +1 -0
- package/src/micropdf417/error-correction.js +1 -0
- package/src/micropdf417/index.js +1 -0
- package/src/micropdf417/tables.js +1 -0
- package/src/microqr/decoder.js +246 -0
- package/src/microqr/detector.js +356 -0
- package/src/microqr/encoder.js +270 -0
- package/src/microqr/index.js +37 -0
- package/src/microqr/tables.js +317 -0
- package/src/oned/addons.js +421 -0
- package/src/oned/index.js +77 -59
- package/src/oned/patterns.js +1 -0
- package/src/oned/reader.js +1 -0
- package/src/oned/writers.js +1 -0
- package/src/pdf417/compaction.js +1 -0
- package/src/pdf417/decoder.js +1 -0
- package/src/pdf417/detector.js +1 -0
- package/src/pdf417/encoder.js +1 -0
- package/src/pdf417/error-correction.js +1 -0
- package/src/pdf417/index.js +5 -0
- package/src/pdf417/tables.js +1 -0
- package/src/qr/decoder.js +1 -0
- package/src/qr/detector.js +1 -0
- package/src/qr/encoder.js +1 -0
- package/src/qr/index.js +1 -0
- package/src/qr/tables.js +1 -0
- package/src/render/image-data.js +1 -0
- package/src/render/index.js +1 -0
- package/src/render/options.js +1 -0
- package/src/render/png.js +1 -0
- package/src/render/svg.js +1 -0
- package/src/render/webgl.js +1 -0
- package/src/render/webgpu.js +1 -0
- package/src/rmqr/decoder.js +102 -0
- package/src/rmqr/detector.js +91 -0
- package/src/rmqr/encoder.js +173 -0
- package/src/rmqr/index.js +38 -0
- package/src/rmqr/tables.js +155 -0
package/src/image/luminance.js
CHANGED
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
24
24
|
* SOFTWARE.
|
|
25
25
|
*
|
|
26
|
+
* SPDX-FileCopyrightText: 2026 Sythos (https://www.sythos.net)
|
|
26
27
|
* SPDX-License-Identifier: MIT
|
|
27
28
|
*
|
|
28
29
|
* Original work. No code from any other barcode implementation.
|
package/src/image/perspective.js
CHANGED
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
24
24
|
* SOFTWARE.
|
|
25
25
|
*
|
|
26
|
+
* SPDX-FileCopyrightText: 2026 Sythos (https://www.sythos.net)
|
|
26
27
|
* SPDX-License-Identifier: MIT
|
|
27
28
|
*
|
|
28
29
|
* Original work. No code from any other barcode implementation.
|
package/src/index.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* MIT License
|
|
5
5
|
*
|
|
6
6
|
* Copyright (c) 2026 Sythos
|
|
7
|
+
* SPDX-FileCopyrightText: 2026 Sythos (https://www.sythos.net)
|
|
7
8
|
*
|
|
8
9
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
9
10
|
* of this software and associated documentation files (the "Software"), to deal
|
|
@@ -56,6 +57,12 @@ import * as qr from './qr/index.js';
|
|
|
56
57
|
import * as aztec from './aztec/index.js';
|
|
57
58
|
import * as pdf417 from './pdf417/index.js';
|
|
58
59
|
import * as micropdf417 from './micropdf417/index.js';
|
|
60
|
+
import * as microqr from './microqr/index.js';
|
|
61
|
+
import * as rmqr from './rmqr/index.js';
|
|
62
|
+
import * as frameqr from './frameqr/index.js';
|
|
63
|
+
import * as aztecRune from './aztecrune/index.js';
|
|
64
|
+
import * as compactPdf417 from './compactpdf417/index.js';
|
|
65
|
+
import * as databar from './databar/index.js';
|
|
59
66
|
|
|
60
67
|
export { BitMatrix };
|
|
61
68
|
export {
|
|
@@ -74,10 +81,20 @@ export {
|
|
|
74
81
|
encodeDataMatrix, decodeDataMatrix, detectDataMatrix, detectAndDecodeDataMatrix,
|
|
75
82
|
} from './datamatrix/index.js';
|
|
76
83
|
export { encodeAztec, decodeAztec, detectAztec, detectAndDecodeAztec } from './aztec/index.js';
|
|
84
|
+
export * from './aztecrune/index.js';
|
|
77
85
|
export { encodePDF417, decodePDF417, detectPDF417, detectAndDecodePDF417 } from './pdf417/index.js';
|
|
86
|
+
export * from './compactpdf417/index.js';
|
|
87
|
+
// DataBar currently exposes verified GS1 data-layer codecs only; physical
|
|
88
|
+
// symbol rendering and image detection remain deliberately out of scope.
|
|
89
|
+
export * from './databar/index.js';
|
|
78
90
|
export {
|
|
79
91
|
encodeMicroPDF417, decodeMicroPDF417, detectMicroPDF417, detectAndDecodeMicroPDF417,
|
|
80
92
|
} from './micropdf417/index.js';
|
|
93
|
+
export { encodeMicroQR, decodeMicroQR, detectMicroQR, detectAndDecodeMicroQR } from './microqr/index.js';
|
|
94
|
+
export { encodeRMQR, decodeRMQR, detectRMQR, detectAndDecodeRMQR } from './rmqr/index.js';
|
|
95
|
+
export {
|
|
96
|
+
encodeFrameQR, decodeFrameQR, detectFrameQR, detectAndDecodeFrameQR,
|
|
97
|
+
} from './frameqr/index.js';
|
|
81
98
|
|
|
82
99
|
/**
|
|
83
100
|
* @typedef {object} FormatInfo
|
|
@@ -113,6 +130,17 @@ const pdf417CanEncode = typeof pdf417.encodePDF417 === 'function';
|
|
|
113
130
|
const pdf417CanDecode = typeof pdf417.detectAndDecodePDF417 === 'function';
|
|
114
131
|
const microPdf417CanEncode = typeof micropdf417.encodeMicroPDF417 === 'function';
|
|
115
132
|
const microPdf417CanDecode = typeof micropdf417.detectAndDecodeMicroPDF417 === 'function';
|
|
133
|
+
const microQrCanEncode = typeof microqr.encodeMicroQR === 'function';
|
|
134
|
+
const microQrCanDecode = typeof microqr.detectAndDecodeMicroQR === 'function';
|
|
135
|
+
const rmqrCanEncode = typeof rmqr.encodeRMQR === 'function';
|
|
136
|
+
const rmqrCanDecode = typeof rmqr.detectAndDecodeRMQR === 'function';
|
|
137
|
+
const frameQrCanEncode = typeof frameqr.encodeFrameQR === 'function';
|
|
138
|
+
const frameQrCanDecode = typeof frameqr.detectAndDecodeFrameQR === 'function';
|
|
139
|
+
const aztecRuneCanEncode = typeof aztecRune.encodeAztecRune === 'function';
|
|
140
|
+
const aztecRuneCanDecode = typeof aztecRune.detectAndDecodeAztecRune === 'function';
|
|
141
|
+
const compactPdf417CanEncode = typeof compactPdf417.encodeCompactPDF417 === 'function';
|
|
142
|
+
const compactPdf417CanDecode = typeof compactPdf417.detectAndDecodeCompactPDF417 === 'function';
|
|
143
|
+
const dataBarCanEncode = typeof databar.encodeDataBar14 === 'function';
|
|
116
144
|
|
|
117
145
|
/**
|
|
118
146
|
* Every format this build supports.
|
|
@@ -154,6 +182,13 @@ export function listFormats() {
|
|
|
154
182
|
canRead: aztecCanDecode,
|
|
155
183
|
kind: /** @type {'2D'} */ ('2D'),
|
|
156
184
|
});
|
|
185
|
+
formats.push({
|
|
186
|
+
id: 'aztecrune',
|
|
187
|
+
label: 'Aztec Rune',
|
|
188
|
+
canWrite: aztecRuneCanEncode,
|
|
189
|
+
canRead: aztecRuneCanDecode,
|
|
190
|
+
kind: /** @type {'2D'} */ ('2D'),
|
|
191
|
+
});
|
|
157
192
|
formats.push({
|
|
158
193
|
id: 'pdf417',
|
|
159
194
|
label: 'PDF417',
|
|
@@ -161,6 +196,13 @@ export function listFormats() {
|
|
|
161
196
|
canRead: pdf417CanDecode,
|
|
162
197
|
kind: /** @type {'2D'} */ ('2D'),
|
|
163
198
|
});
|
|
199
|
+
formats.push({
|
|
200
|
+
id: 'compactpdf417',
|
|
201
|
+
label: 'Compact PDF417',
|
|
202
|
+
canWrite: compactPdf417CanEncode,
|
|
203
|
+
canRead: compactPdf417CanDecode,
|
|
204
|
+
kind: /** @type {'2D'} */ ('2D'),
|
|
205
|
+
});
|
|
164
206
|
formats.push({
|
|
165
207
|
id: 'micropdf417',
|
|
166
208
|
label: 'MicroPDF417',
|
|
@@ -168,6 +210,34 @@ export function listFormats() {
|
|
|
168
210
|
canRead: microPdf417CanDecode,
|
|
169
211
|
kind: /** @type {'2D'} */ ('2D'),
|
|
170
212
|
});
|
|
213
|
+
formats.push({
|
|
214
|
+
id: 'microqr',
|
|
215
|
+
label: 'Micro QR Code',
|
|
216
|
+
canWrite: microQrCanEncode,
|
|
217
|
+
canRead: microQrCanDecode,
|
|
218
|
+
kind: /** @type {'2D'} */ ('2D'),
|
|
219
|
+
});
|
|
220
|
+
formats.push({
|
|
221
|
+
id: 'rmqr',
|
|
222
|
+
label: 'rMQR Code',
|
|
223
|
+
canWrite: rmqrCanEncode,
|
|
224
|
+
canRead: rmqrCanDecode,
|
|
225
|
+
kind: /** @type {'2D'} */ ('2D'),
|
|
226
|
+
});
|
|
227
|
+
formats.push({
|
|
228
|
+
id: 'frameqr',
|
|
229
|
+
label: 'FrameQR Code',
|
|
230
|
+
canWrite: frameQrCanEncode,
|
|
231
|
+
canRead: frameQrCanDecode,
|
|
232
|
+
kind: /** @type {'2D'} */ ('2D'),
|
|
233
|
+
});
|
|
234
|
+
formats.push({
|
|
235
|
+
id: 'gs1databar14',
|
|
236
|
+
label: 'GS1 DataBar Omnidirectional / Truncated',
|
|
237
|
+
canWrite: dataBarCanEncode,
|
|
238
|
+
canRead: false,
|
|
239
|
+
kind: /** @type {'1D'} */ ('1D'),
|
|
240
|
+
});
|
|
171
241
|
|
|
172
242
|
return formats;
|
|
173
243
|
}
|
|
@@ -198,6 +268,14 @@ export function listFormats() {
|
|
|
198
268
|
* @param {'auto'|'text'|'byte'|'numeric'} [options.compaction] PDF417 compaction mode.
|
|
199
269
|
* @param {number} [options.eci] MicroPDF417 byte-compaction ECI assignment (3 or 26).
|
|
200
270
|
* @param {number} [options.aspectRatio] Preferred MicroPDF417 symbol aspect ratio.
|
|
271
|
+
* @param {object} [options.canvas] FrameQR Code artwork reservation.
|
|
272
|
+
* @param {'square'|'circle'|'diamond'} [options.canvas.shape] Canvas shape.
|
|
273
|
+
* @param {number} [options.canvas.size] Odd canvas size in QR modules.
|
|
274
|
+
* @param {number} [options.canvas.width] Canvas width in QR modules.
|
|
275
|
+
* @param {number} [options.canvas.height] Canvas height in QR modules.
|
|
276
|
+
* @param {number} [options.canvas.centerX] Canvas centre X in QR modules.
|
|
277
|
+
* @param {number} [options.canvas.centerY] Canvas centre Y in QR modules.
|
|
278
|
+
* @param {0|90|180|270} [options.canvas.angle] Canvas quarter-turn.
|
|
201
279
|
* @returns {BitMatrix}
|
|
202
280
|
*/
|
|
203
281
|
export function encode(text, options = {}) {
|
|
@@ -213,16 +291,34 @@ export function encode(text, options = {}) {
|
|
|
213
291
|
if (format === 'aztec' || format === 'aztec-code') {
|
|
214
292
|
return aztec.encodeAztec(value, options);
|
|
215
293
|
}
|
|
294
|
+
if (format === 'aztecrune' || format === 'aztec-rune' || format === 'rune') {
|
|
295
|
+
return aztecRune.encodeAztecRune(value, options);
|
|
296
|
+
}
|
|
216
297
|
if (format === 'pdf417' || format === 'pdf-417') {
|
|
217
298
|
return pdf417.encodePDF417(value, options);
|
|
218
299
|
}
|
|
300
|
+
if (format === 'compactpdf417' || format === 'compact-pdf417' || format === 'compact-pdf-417') {
|
|
301
|
+
return compactPdf417.encodeCompactPDF417(value, options);
|
|
302
|
+
}
|
|
219
303
|
if (format === 'micropdf417' || format === 'micro-pdf417' || format === 'micro-pdf-417') {
|
|
220
304
|
return micropdf417.encodeMicroPDF417(value, options);
|
|
221
305
|
}
|
|
306
|
+
if (format === 'microqr' || format === 'micro-qr') {
|
|
307
|
+
return microqr.encodeMicroQR(value, options);
|
|
308
|
+
}
|
|
309
|
+
if (format === 'rmqr' || format === 'r-mqr' || format === 'rectangular-micro-qr') {
|
|
310
|
+
return rmqr.encodeRMQR(value, options);
|
|
311
|
+
}
|
|
312
|
+
if (format === 'frameqr' || format === 'frame-qr' || format === 'canvas-qr') {
|
|
313
|
+
return frameqr.encodeFrameQR(value, options);
|
|
314
|
+
}
|
|
315
|
+
if (format === 'gs1databar14' || format === 'gs1-databar14' || format === 'databar') {
|
|
316
|
+
return databar.encodeDataBar14(value, options);
|
|
317
|
+
}
|
|
222
318
|
|
|
223
319
|
const entry = ONED_FORMATS[format];
|
|
224
320
|
if (!entry) {
|
|
225
|
-
const known = [...Object.keys(ONED_FORMATS), 'qr', 'datamatrix', 'aztec', 'pdf417', 'micropdf417'].join(', ');
|
|
321
|
+
const known = [...Object.keys(ONED_FORMATS), 'qr', 'datamatrix', 'aztec', 'aztecrune', 'pdf417', 'compactpdf417', 'micropdf417', 'microqr', 'rmqr', 'frameqr', 'gs1databar14'].join(', ');
|
|
226
322
|
throw new EncodeError(`Unknown format "${format}". Known formats: ${known}`);
|
|
227
323
|
}
|
|
228
324
|
return entry.encode(value, options);
|
|
@@ -245,6 +341,9 @@ export function encode(text, options = {}) {
|
|
|
245
341
|
* @property {number} [rowHeight] PDF417 row height in modules.
|
|
246
342
|
* @property {number} [variant] MicroPDF417 predefined variant number.
|
|
247
343
|
* @property {number} [eccCodewords] MicroPDF417 fixed error-correction codewords.
|
|
344
|
+
* @property {string} [profile] FrameQR Code profile identifier.
|
|
345
|
+
* @property {boolean} [certified] Whether the profile is certified by its originator.
|
|
346
|
+
* @property {object} [canvas] Canvas reservation metadata for the FrameQR Code profile.
|
|
248
347
|
*/
|
|
249
348
|
|
|
250
349
|
/**
|
|
@@ -259,6 +358,8 @@ export function encode(text, options = {}) {
|
|
|
259
358
|
* @param {string[]} [options.formats] Restrict to these format ids.
|
|
260
359
|
* @param {boolean} [options.tryHarder] Retry inverted and rotated. Default true.
|
|
261
360
|
* @param {'global'|'hybrid'|'auto'} [options.binarizer]
|
|
361
|
+
* @param {object} [options.frameqr] FrameQR Code detector options when
|
|
362
|
+
* the profile marker is not preserved through image rendering.
|
|
262
363
|
* @returns {DecodeResult[]}
|
|
263
364
|
*/
|
|
264
365
|
export function decode(image, options = {}) {
|
|
@@ -267,8 +368,13 @@ export function decode(image, options = {}) {
|
|
|
267
368
|
const wantQR = !want || want.has('qr') || want.has('qrcode');
|
|
268
369
|
const wantDataMatrix = !want || want.has('datamatrix') || want.has('data-matrix');
|
|
269
370
|
const wantAztec = !want || want.has('aztec') || want.has('aztec-code');
|
|
371
|
+
const wantAztecRune = !want || want.has('aztecrune') || want.has('aztec-rune') || want.has('rune');
|
|
270
372
|
const wantPDF417 = !want || want.has('pdf417') || want.has('pdf-417');
|
|
373
|
+
const wantCompactPDF417 = !want || want.has('compactpdf417') || want.has('compact-pdf417') || want.has('compact-pdf-417');
|
|
271
374
|
const wantMicroPDF417 = !want || want.has('micropdf417') || want.has('micro-pdf417') || want.has('micro-pdf-417');
|
|
375
|
+
const wantMicroQR = !want || want.has('microqr') || want.has('micro-qr');
|
|
376
|
+
const wantRMQR = !want || want.has('rmqr') || want.has('r-mqr') || want.has('rectangular-micro-qr');
|
|
377
|
+
const wantFrameQR = !want || want.has('frameqr') || want.has('frame-qr') || want.has('canvas-qr');
|
|
272
378
|
const wantOneD = !want || [...want].some((f) => f in ONED_FORMATS);
|
|
273
379
|
|
|
274
380
|
const source = LuminanceSource.fromImageData(image);
|
|
@@ -321,6 +427,15 @@ export function decode(image, options = {}) {
|
|
|
321
427
|
}
|
|
322
428
|
}
|
|
323
429
|
|
|
430
|
+
if (wantAztecRune && aztecRuneCanDecode) {
|
|
431
|
+
try {
|
|
432
|
+
const found = aztecRune.detectAndDecodeAztecRune(bits);
|
|
433
|
+
if (found) results.push({ ...found, format: 'aztecrune' });
|
|
434
|
+
} catch {
|
|
435
|
+
/* no Aztec Rune in this pass */
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
324
439
|
if (wantPDF417 && pdf417CanDecode) {
|
|
325
440
|
try {
|
|
326
441
|
const found = pdf417.detectAndDecodePDF417(bits);
|
|
@@ -330,6 +445,15 @@ export function decode(image, options = {}) {
|
|
|
330
445
|
}
|
|
331
446
|
}
|
|
332
447
|
|
|
448
|
+
if (wantCompactPDF417 && compactPdf417CanDecode) {
|
|
449
|
+
try {
|
|
450
|
+
const found = compactPdf417.detectAndDecodeCompactPDF417(bits);
|
|
451
|
+
if (found) results.push({ ...found, format: 'compactpdf417' });
|
|
452
|
+
} catch {
|
|
453
|
+
/* no Compact PDF417 in this pass */
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
333
457
|
if (wantMicroPDF417 && microPdf417CanDecode) {
|
|
334
458
|
// MicroPDF417 detection measures runs across the whole raster. Hybrid
|
|
335
459
|
// thresholding can alter uniform modules near local-window boundaries,
|
|
@@ -345,6 +469,35 @@ export function decode(image, options = {}) {
|
|
|
345
469
|
}
|
|
346
470
|
}
|
|
347
471
|
|
|
472
|
+
if (wantMicroQR && microQrCanDecode) {
|
|
473
|
+
try {
|
|
474
|
+
for (const found of microqr.detectAndDecodeMicroQR(bits)) {
|
|
475
|
+
results.push({ ...found, format: 'microqr' });
|
|
476
|
+
}
|
|
477
|
+
} catch {
|
|
478
|
+
/* no Micro QR in this pass */
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
if (wantRMQR && rmqrCanDecode) {
|
|
483
|
+
try {
|
|
484
|
+
const found = rmqr.detectAndDecodeRMQR(bits);
|
|
485
|
+
if (found) results.push({ ...found, format: 'rmqr' });
|
|
486
|
+
} catch {
|
|
487
|
+
/* no rMQR in this pass */
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
if (wantFrameQR && frameQrCanDecode) {
|
|
492
|
+
try {
|
|
493
|
+
for (const found of frameqr.detectAndDecodeFrameQR(bits, options.frameqr ?? {})) {
|
|
494
|
+
results.push({ ...found, format: 'frameqr' });
|
|
495
|
+
}
|
|
496
|
+
} catch {
|
|
497
|
+
/* no FrameQR Code in this pass */
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
348
501
|
if (wantOneD) {
|
|
349
502
|
const oneDFormats = want ? [...want].filter((f) => f in ONED_FORMATS) : null;
|
|
350
503
|
for (const found of decodeOneD(bits, { formats: oneDFormats, tryHarder })) {
|
|
@@ -379,4 +532,4 @@ export function decodeStrict(image, options) {
|
|
|
379
532
|
}
|
|
380
533
|
|
|
381
534
|
/** Library version, matching package.json. */
|
|
382
|
-
export const VERSION = '1.
|
|
535
|
+
export const VERSION = '1.4.1';
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
24
24
|
* SOFTWARE.
|
|
25
25
|
*
|
|
26
|
+
* SPDX-FileCopyrightText: 2026 Sythos (https://www.sythos.net)
|
|
26
27
|
* SPDX-License-Identifier: MIT
|
|
27
28
|
*
|
|
28
29
|
* Original work. No code from any other barcode implementation.
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
24
24
|
* SOFTWARE.
|
|
25
25
|
*
|
|
26
|
+
* SPDX-FileCopyrightText: 2026 Sythos (https://www.sythos.net)
|
|
26
27
|
* SPDX-License-Identifier: MIT
|
|
27
28
|
*
|
|
28
29
|
* Original work. No code from any other barcode implementation.
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
24
24
|
* SOFTWARE.
|
|
25
25
|
*
|
|
26
|
+
* SPDX-FileCopyrightText: 2026 Sythos (https://www.sythos.net)
|
|
26
27
|
* SPDX-License-Identifier: MIT
|
|
27
28
|
*
|
|
28
29
|
* Original work. No code from any other barcode implementation.
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
24
24
|
* SOFTWARE.
|
|
25
25
|
*
|
|
26
|
+
* SPDX-FileCopyrightText: 2026 Sythos (https://www.sythos.net)
|
|
26
27
|
* SPDX-License-Identifier: MIT
|
|
27
28
|
*
|
|
28
29
|
* Original work. No code from any other barcode implementation.
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
24
24
|
* SOFTWARE.
|
|
25
25
|
*
|
|
26
|
+
* SPDX-FileCopyrightText: 2026 Sythos (https://www.sythos.net)
|
|
26
27
|
* SPDX-License-Identifier: MIT
|
|
27
28
|
*
|
|
28
29
|
* Original work. No code from any other barcode implementation.
|
package/src/micropdf417/index.js
CHANGED
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
24
24
|
* SOFTWARE.
|
|
25
25
|
*
|
|
26
|
+
* SPDX-FileCopyrightText: 2026 Sythos (https://www.sythos.net)
|
|
26
27
|
* SPDX-License-Identifier: MIT
|
|
27
28
|
*
|
|
28
29
|
* Original work. No code from any other barcode implementation.
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
24
24
|
* SOFTWARE.
|
|
25
25
|
*
|
|
26
|
+
* SPDX-FileCopyrightText: 2026 Sythos (https://www.sythos.net)
|
|
26
27
|
* SPDX-License-Identifier: MIT
|
|
27
28
|
*
|
|
28
29
|
* Original work. No code from any other barcode implementation.
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Sythos Barcode Suite
|
|
3
|
+
*
|
|
4
|
+
* MIT License
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) 2026 Sythos
|
|
7
|
+
*
|
|
8
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
9
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
10
|
+
* in the Software without restriction, including without limitation the rights
|
|
11
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
12
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
13
|
+
* furnished to do so, subject to the following conditions:
|
|
14
|
+
*
|
|
15
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
16
|
+
* copies or substantial portions of the Software.
|
|
17
|
+
*
|
|
18
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
19
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
20
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
21
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
22
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
23
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
24
|
+
* SOFTWARE.
|
|
25
|
+
*
|
|
26
|
+
* SPDX-FileCopyrightText: 2026 Sythos (https://www.sythos.net)
|
|
27
|
+
* SPDX-License-Identifier: MIT
|
|
28
|
+
*
|
|
29
|
+
* Original work. No code from any other barcode implementation.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Micro QR decoder for an already sampled M1-M4 module matrix.
|
|
34
|
+
*
|
|
35
|
+
* @module microqr/decoder
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
import { ChecksumError, FormatError } from '../core/errors.js';
|
|
39
|
+
import { GF256_QR } from '../core/galois-field.js';
|
|
40
|
+
import { rsDecode } from '../core/reed-solomon.js';
|
|
41
|
+
import {
|
|
42
|
+
microQrBlockLayout,
|
|
43
|
+
microQrDataModuleOrder,
|
|
44
|
+
microQrDecodeFormatInfo,
|
|
45
|
+
microQrFormatInfoPositions,
|
|
46
|
+
microQrMaskBit,
|
|
47
|
+
} from './tables.js';
|
|
48
|
+
|
|
49
|
+
const ALPHANUMERIC = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:';
|
|
50
|
+
const MODE_NAMES = ['numeric', 'alphanumeric', 'byte', 'kanji'];
|
|
51
|
+
const COUNT_BITS = {
|
|
52
|
+
numeric: [0, 3, 4, 5, 6],
|
|
53
|
+
alphanumeric: [0, 0, 3, 4, 5],
|
|
54
|
+
byte: [0, 0, 0, 4, 5],
|
|
55
|
+
kanji: [0, 0, 0, 3, 4],
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
class LimitedBitReader {
|
|
59
|
+
constructor(bytes, limit) {
|
|
60
|
+
this.bytes = bytes;
|
|
61
|
+
this.limit = limit;
|
|
62
|
+
this.offset = 0;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
available() { return this.limit - this.offset; }
|
|
66
|
+
|
|
67
|
+
read(count) {
|
|
68
|
+
if (!Number.isInteger(count) || count < 1 || count > 32 || count > this.available()) {
|
|
69
|
+
throw new FormatError(`Micro QR: needed ${count} bits, ${Math.max(0, this.available())} remain`);
|
|
70
|
+
}
|
|
71
|
+
let value = 0;
|
|
72
|
+
for (let i = 0; i < count; i++, this.offset++) {
|
|
73
|
+
value = (value << 1) | ((this.bytes[this.offset >>> 3] >>> (7 - (this.offset & 7))) & 1);
|
|
74
|
+
}
|
|
75
|
+
return value >>> 0;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function moduleAt(matrix, x, y, mirrored) {
|
|
80
|
+
return mirrored ? matrix.get(y, x) : matrix.get(x, y);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function readFormat(matrix, expectedVersion, mirrored) {
|
|
84
|
+
let bits = 0;
|
|
85
|
+
const positions = microQrFormatInfoPositions(matrix.width);
|
|
86
|
+
for (let i = 0; i < positions.length; i++) {
|
|
87
|
+
const [x, y] = positions[i];
|
|
88
|
+
if (moduleAt(matrix, x, y, mirrored)) bits |= 1 << i;
|
|
89
|
+
}
|
|
90
|
+
const format = microQrDecodeFormatInfo(bits);
|
|
91
|
+
if (!format) throw new FormatError('Micro QR: format information is unreadable');
|
|
92
|
+
if (format.version !== expectedVersion) {
|
|
93
|
+
throw new FormatError(
|
|
94
|
+
`Micro QR: format identifies ${format.version}, but the matrix dimension identifies ${expectedVersion}`,
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
return format;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function readCodewords(matrix, layout, mask, mirrored) {
|
|
101
|
+
const order = microQrDataModuleOrder(layout.version);
|
|
102
|
+
const data = new Array(layout.dataCodewords).fill(0);
|
|
103
|
+
const ecc = new Array(layout.eccCodewords).fill(0);
|
|
104
|
+
let streamOffset = 0;
|
|
105
|
+
|
|
106
|
+
const readBit = () => {
|
|
107
|
+
const x = order[streamOffset * 2];
|
|
108
|
+
const y = order[streamOffset * 2 + 1];
|
|
109
|
+
if (x === undefined || y === undefined) throw new FormatError('Micro QR: encoding region is truncated');
|
|
110
|
+
streamOffset++;
|
|
111
|
+
return moduleAt(matrix, x, y, mirrored) !== microQrMaskBit(mask, x, y) ? 1 : 0;
|
|
112
|
+
};
|
|
113
|
+
const readInto = (target, index, count, highBit = 7) => {
|
|
114
|
+
for (let bit = highBit; bit > highBit - count; bit--) target[index] |= readBit() << bit;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const fullData = layout.shortDataCodewordBits === 4 ? layout.dataCodewords - 1 : layout.dataCodewords;
|
|
118
|
+
for (let i = 0; i < fullData; i++) readInto(data, i, 8);
|
|
119
|
+
if (layout.shortDataCodewordBits === 4) readInto(data, data.length - 1, 4);
|
|
120
|
+
for (let i = 0; i < ecc.length; i++) readInto(ecc, i, 8);
|
|
121
|
+
|
|
122
|
+
if (streamOffset !== order.length / 2) {
|
|
123
|
+
throw new FormatError(`Micro QR: read ${streamOffset} of ${order.length / 2} encoding modules`);
|
|
124
|
+
}
|
|
125
|
+
return data.concat(ecc);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function correctCodewords(received, layout) {
|
|
129
|
+
const corrections = rsDecode(received, layout.eccCodewords, GF256_QR, 0);
|
|
130
|
+
if (layout.version === 'M1' && corrections !== 0) {
|
|
131
|
+
throw new ChecksumError('Micro QR: M1 provides error detection only');
|
|
132
|
+
}
|
|
133
|
+
return { data: Uint8Array.from(received.slice(0, layout.dataCodewords)), corrections };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function decodeKanjiValue(value) {
|
|
137
|
+
const combined = (Math.floor(value / 0xc0) << 8) | (value % 0xc0);
|
|
138
|
+
const sjis = combined + (combined < 0x1f00 ? 0x8140 : 0xc140);
|
|
139
|
+
const bytes = Uint8Array.of(sjis >>> 8, sjis & 0xff);
|
|
140
|
+
try {
|
|
141
|
+
return new TextDecoder('shift_jis', { fatal: true }).decode(bytes);
|
|
142
|
+
} catch {
|
|
143
|
+
throw new FormatError(`Micro QR: invalid Kanji value ${value}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function parsePayload(data, version, dataBits) {
|
|
148
|
+
const reader = new LimitedBitReader(data, dataBits);
|
|
149
|
+
const modeValue = version === 1 ? 0 : reader.read(version - 1);
|
|
150
|
+
if (modeValue > 3 || (version === 2 && modeValue > 1)) {
|
|
151
|
+
throw new FormatError(`Micro QR: mode indicator ${modeValue} is unavailable in M${version}`);
|
|
152
|
+
}
|
|
153
|
+
const mode = MODE_NAMES[modeValue];
|
|
154
|
+
const countWidth = COUNT_BITS[mode][version];
|
|
155
|
+
if (!countWidth) throw new FormatError(`Micro QR: ${mode} mode is unavailable in M${version}`);
|
|
156
|
+
const count = reader.read(countWidth);
|
|
157
|
+
if (count === 0) throw new FormatError('Micro QR: zero-length data segment');
|
|
158
|
+
|
|
159
|
+
let text = '';
|
|
160
|
+
const rawBytes = [];
|
|
161
|
+
if (mode === 'numeric') {
|
|
162
|
+
let remaining = count;
|
|
163
|
+
while (remaining >= 3) {
|
|
164
|
+
const value = reader.read(10);
|
|
165
|
+
if (value >= 1000) throw new FormatError(`Micro QR: invalid numeric triplet ${value}`);
|
|
166
|
+
text += String(value).padStart(3, '0');
|
|
167
|
+
remaining -= 3;
|
|
168
|
+
}
|
|
169
|
+
if (remaining === 2) {
|
|
170
|
+
const value = reader.read(7);
|
|
171
|
+
if (value >= 100) throw new FormatError(`Micro QR: invalid numeric pair ${value}`);
|
|
172
|
+
text += String(value).padStart(2, '0');
|
|
173
|
+
} else if (remaining === 1) {
|
|
174
|
+
const value = reader.read(4);
|
|
175
|
+
if (value >= 10) throw new FormatError(`Micro QR: invalid numeric digit ${value}`);
|
|
176
|
+
text += String(value);
|
|
177
|
+
}
|
|
178
|
+
} else if (mode === 'alphanumeric') {
|
|
179
|
+
let remaining = count;
|
|
180
|
+
while (remaining >= 2) {
|
|
181
|
+
const value = reader.read(11);
|
|
182
|
+
if (value >= 45 * 45) throw new FormatError(`Micro QR: invalid alphanumeric pair ${value}`);
|
|
183
|
+
text += ALPHANUMERIC[Math.floor(value / 45)] + ALPHANUMERIC[value % 45];
|
|
184
|
+
remaining -= 2;
|
|
185
|
+
}
|
|
186
|
+
if (remaining === 1) {
|
|
187
|
+
const value = reader.read(6);
|
|
188
|
+
if (value >= 45) throw new FormatError(`Micro QR: invalid alphanumeric value ${value}`);
|
|
189
|
+
text += ALPHANUMERIC[value];
|
|
190
|
+
}
|
|
191
|
+
} else if (mode === 'byte') {
|
|
192
|
+
for (let i = 0; i < count; i++) {
|
|
193
|
+
const value = reader.read(8);
|
|
194
|
+
rawBytes.push(value);
|
|
195
|
+
text += String.fromCharCode(value);
|
|
196
|
+
}
|
|
197
|
+
} else {
|
|
198
|
+
for (let i = 0; i < count; i++) text += decodeKanjiValue(reader.read(13));
|
|
199
|
+
}
|
|
200
|
+
return { text, bytes: Uint8Array.from(rawBytes), mode };
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function decodeOrientation(matrix, expectedVersion, mirrored) {
|
|
204
|
+
const format = readFormat(matrix, expectedVersion, mirrored);
|
|
205
|
+
const layout = microQrBlockLayout(format.version, format.ecc);
|
|
206
|
+
const received = readCodewords(matrix, layout, format.mask, mirrored);
|
|
207
|
+
const { data, corrections } = correctCodewords(received, layout);
|
|
208
|
+
const payload = parsePayload(data, Number(format.version.slice(1)), layout.dataBits);
|
|
209
|
+
return {
|
|
210
|
+
text: payload.text,
|
|
211
|
+
bytes: payload.bytes,
|
|
212
|
+
mode: payload.mode,
|
|
213
|
+
version: format.version,
|
|
214
|
+
ecc: format.ecc,
|
|
215
|
+
mask: format.mask,
|
|
216
|
+
corrections,
|
|
217
|
+
formatCorrections: format.correctedBits,
|
|
218
|
+
mirrored,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/** Decode a sampled Micro QR Code symbol without its quiet zone. */
|
|
223
|
+
export function decodeMicroQR(matrix) {
|
|
224
|
+
if (!matrix || !Number.isInteger(matrix.width) || typeof matrix.get !== 'function') {
|
|
225
|
+
throw new FormatError('Micro QR: no matrix supplied');
|
|
226
|
+
}
|
|
227
|
+
if (matrix.height !== matrix.width) {
|
|
228
|
+
throw new FormatError(`Micro QR: symbol must be square, got ${matrix.width}x${matrix.height}`);
|
|
229
|
+
}
|
|
230
|
+
const version = (matrix.width - 9) / 2;
|
|
231
|
+
if (!Number.isInteger(version) || version < 1 || version > 4) {
|
|
232
|
+
throw new FormatError(`Micro QR: ${matrix.width} modules is not a valid M1-M4 symbol size`);
|
|
233
|
+
}
|
|
234
|
+
const expectedVersion = `M${version}`;
|
|
235
|
+
try {
|
|
236
|
+
return decodeOrientation(matrix, expectedVersion, false);
|
|
237
|
+
} catch (primaryError) {
|
|
238
|
+
try {
|
|
239
|
+
return decodeOrientation(matrix, expectedVersion, true);
|
|
240
|
+
} catch {
|
|
241
|
+
throw primaryError;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export { ChecksumError, FormatError };
|