@sythos/js_barcode_universal 1.5.7 → 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 +50 -37
- package/bundle/sythos-barcode.esm.js +12051 -11484
- package/bundle/sythos-barcode.js +12051 -11484
- package/package.json +52 -45
- package/src/index.d.ts +363 -0
- package/src/index.js +573 -428
- package/src/js/aztec/decoder.js +326 -0
- package/src/js/aztec/detector.js +251 -0
- package/src/js/aztec/encoder.js +276 -0
- package/src/js/aztec/high-level.js +216 -0
- package/src/js/aztec/index.js +34 -0
- package/src/js/aztec/tables.js +196 -0
- package/src/js/aztecrune/decoder.js +146 -0
- package/src/js/aztecrune/detector.js +180 -0
- package/src/js/aztecrune/encoder.js +103 -0
- package/src/js/aztecrune/index.js +34 -0
- package/src/js/aztecrune/tables.js +142 -0
- package/src/js/compactpdf417/decoder.js +123 -0
- package/src/js/compactpdf417/detector.js +138 -0
- package/src/js/compactpdf417/encoder.js +127 -0
- package/src/js/compactpdf417/index.js +5 -0
- package/src/js/compactpdf417/tables.js +164 -0
- package/src/js/core/bit-buffer.js +158 -0
- package/src/js/core/bit-matrix.js +243 -0
- package/src/js/core/errors.js +59 -0
- package/src/js/core/galois-field.js +193 -0
- package/src/js/core/index.js +39 -0
- package/src/js/core/reed-solomon.js +318 -0
- package/src/js/databar/codec.js +166 -0
- package/src/js/databar/decoder.js +224 -0
- package/src/js/databar/encoder.js +88 -0
- package/src/js/databar/gs1.js +176 -0
- package/src/js/databar/index.js +36 -0
- package/src/js/databar/patterns.js +139 -0
- package/src/js/databar/tables.js +123 -0
- package/src/js/datamatrix/decoder.js +296 -0
- package/src/js/datamatrix/detector.js +254 -0
- package/src/js/datamatrix/encoder.js +250 -0
- package/src/js/datamatrix/index.js +34 -0
- package/src/js/datamatrix/tables.js +122 -0
- package/src/js/frameqr/decoder.js +216 -0
- package/src/js/frameqr/detector.js +199 -0
- package/src/js/frameqr/encoder.js +143 -0
- package/src/js/frameqr/index.js +33 -0
- package/src/js/frameqr/tables.js +252 -0
- package/src/js/image/binarizer.js +254 -0
- package/src/js/image/grid-sampler.js +148 -0
- package/src/js/image/index.js +38 -0
- package/src/js/image/luminance.js +185 -0
- package/src/js/image/perspective.js +158 -0
- package/src/js/micropdf417/compaction.js +115 -0
- package/src/js/micropdf417/decoder.js +180 -0
- package/src/js/micropdf417/detector.js +152 -0
- package/src/js/micropdf417/encoder.js +184 -0
- package/src/js/micropdf417/error-correction.js +50 -0
- package/src/js/micropdf417/index.js +35 -0
- package/src/js/micropdf417/tables.js +197 -0
- package/src/js/microqr/decoder.js +240 -0
- package/src/js/microqr/detector.js +339 -0
- package/src/js/microqr/encoder.js +314 -0
- package/src/js/microqr/index.js +34 -0
- package/src/js/microqr/tables.js +324 -0
- package/src/js/oned/addons.js +396 -0
- package/src/js/oned/index.js +75 -0
- package/src/js/oned/patterns.js +365 -0
- package/src/js/oned/reader.js +1383 -0
- package/src/js/oned/writers.js +691 -0
- package/src/js/pdf417/compaction.js +391 -0
- package/src/js/pdf417/decoder.js +110 -0
- package/src/js/pdf417/detector.js +484 -0
- package/src/js/pdf417/encoder.js +102 -0
- package/src/js/pdf417/error-correction.js +44 -0
- package/src/{pdf417 → js/pdf417}/index.js +0 -5
- package/src/js/pdf417/tables.js +315 -0
- package/src/js/qr/decoder.js +539 -0
- package/src/js/qr/detector.js +613 -0
- package/src/js/qr/encoder.js +873 -0
- package/src/js/qr/index.js +42 -0
- package/src/js/qr/tables.js +690 -0
- package/src/js/render/image-data.js +120 -0
- package/src/js/render/index.js +128 -0
- package/src/js/render/options.js +152 -0
- package/src/js/render/png.js +270 -0
- package/src/js/render/svg.js +117 -0
- package/src/js/render/webgl.js +200 -0
- package/src/js/render/webgpu.js +357 -0
- package/src/js/rmqr/decoder.js +209 -0
- package/src/js/rmqr/detector.js +122 -0
- package/src/js/rmqr/encoder.js +321 -0
- package/src/js/rmqr/index.js +33 -0
- package/src/js/rmqr/tables.js +177 -0
- package/src/{aztec/decoder.js → ts/aztec/decoder.ts} +0 -1
- package/{typings → src/ts}/aztec/detector.d.ts +2 -2
- package/src/{aztec/detector.js → ts/aztec/detector.ts} +0 -1
- package/src/{aztec/encoder.js → ts/aztec/encoder.ts} +0 -1
- package/src/{aztec/high-level.js → ts/aztec/high-level.ts} +0 -1
- package/src/{aztec/index.js → ts/aztec/index.ts} +0 -1
- package/src/{aztec/tables.js → ts/aztec/tables.ts} +0 -1
- package/src/{aztecrune/decoder.js → ts/aztecrune/decoder.ts} +0 -1
- package/src/{aztecrune/detector.js → ts/aztecrune/detector.ts} +0 -1
- package/src/{aztecrune/encoder.js → ts/aztecrune/encoder.ts} +0 -1
- package/src/{aztecrune/index.js → ts/aztecrune/index.ts} +0 -1
- package/src/{aztecrune/tables.js → ts/aztecrune/tables.ts} +0 -1
- package/src/{compactpdf417/decoder.js → ts/compactpdf417/decoder.ts} +0 -1
- package/src/{compactpdf417/detector.js → ts/compactpdf417/detector.ts} +0 -1
- package/src/{compactpdf417/encoder.js → ts/compactpdf417/encoder.ts} +0 -1
- package/src/{compactpdf417/index.js → ts/compactpdf417/index.ts} +0 -5
- package/src/{compactpdf417/tables.js → ts/compactpdf417/tables.ts} +0 -1
- package/src/{core/bit-buffer.js → ts/core/bit-buffer.ts} +0 -1
- package/src/{core/bit-matrix.js → ts/core/bit-matrix.ts} +0 -1
- package/src/{core/errors.js → ts/core/errors.ts} +0 -1
- package/src/{core/galois-field.js → ts/core/galois-field.ts} +0 -1
- package/src/{core/index.js → ts/core/index.ts} +0 -1
- package/src/{core/reed-solomon.js → ts/core/reed-solomon.ts} +0 -1
- package/src/{databar/codec.js → ts/databar/codec.ts} +0 -1
- package/src/{databar/decoder.js → ts/databar/decoder.ts} +0 -1
- package/src/{databar/encoder.js → ts/databar/encoder.ts} +0 -1
- package/src/{databar/gs1.js → ts/databar/gs1.ts} +0 -1
- package/src/{databar/index.js → ts/databar/index.ts} +0 -1
- package/src/{databar/patterns.js → ts/databar/patterns.ts} +0 -1
- package/src/{databar/tables.js → ts/databar/tables.ts} +0 -1
- package/src/{datamatrix/decoder.js → ts/datamatrix/decoder.ts} +0 -1
- package/{typings → src/ts}/datamatrix/detector.d.ts +2 -2
- package/src/{datamatrix/detector.js → ts/datamatrix/detector.ts} +0 -1
- package/src/{datamatrix/encoder.js → ts/datamatrix/encoder.ts} +0 -1
- package/src/{datamatrix/index.js → ts/datamatrix/index.ts} +0 -1
- package/src/{datamatrix/tables.js → ts/datamatrix/tables.ts} +0 -1
- package/src/{frameqr/decoder.js → ts/frameqr/decoder.ts} +15 -16
- package/src/{frameqr/detector.js → ts/frameqr/detector.ts} +2 -3
- package/src/{frameqr/encoder.js → ts/frameqr/encoder.ts} +7 -8
- package/src/{frameqr/index.js → ts/frameqr/index.ts} +0 -1
- package/src/{frameqr/tables.js → ts/frameqr/tables.ts} +10 -11
- package/src/{image/binarizer.js → ts/image/binarizer.ts} +0 -1
- package/src/{image/grid-sampler.js → ts/image/grid-sampler.ts} +0 -1
- package/src/{image/index.js → ts/image/index.ts} +0 -1
- package/src/{image/luminance.js → ts/image/luminance.ts} +0 -1
- package/src/{image/perspective.js → ts/image/perspective.ts} +0 -1
- package/{typings → src/ts}/index.d.ts +3 -3
- package/src/ts/index.ts +790 -0
- package/src/{micropdf417/compaction.js → ts/micropdf417/compaction.ts} +0 -1
- package/src/{micropdf417/decoder.js → ts/micropdf417/decoder.ts} +0 -1
- package/src/{micropdf417/detector.js → ts/micropdf417/detector.ts} +0 -1
- package/src/{micropdf417/encoder.js → ts/micropdf417/encoder.ts} +0 -1
- package/src/{micropdf417/error-correction.js → ts/micropdf417/error-correction.ts} +0 -1
- package/src/{micropdf417/index.js → ts/micropdf417/index.ts} +0 -1
- package/src/{micropdf417/tables.js → ts/micropdf417/tables.ts} +0 -1
- package/src/{microqr/decoder.js → ts/microqr/decoder.ts} +0 -1
- package/src/{microqr/detector.js → ts/microqr/detector.ts} +0 -1
- package/src/{microqr/encoder.js → ts/microqr/encoder.ts} +0 -1
- package/src/{microqr/index.js → ts/microqr/index.ts} +0 -1
- package/src/{microqr/tables.js → ts/microqr/tables.ts} +0 -1
- package/src/{oned/addons.js → ts/oned/addons.ts} +0 -1
- package/src/{oned/index.js → ts/oned/index.ts} +0 -1
- package/src/{oned/patterns.js → ts/oned/patterns.ts} +0 -1
- package/src/{oned/reader.js → ts/oned/reader.ts} +0 -1
- package/src/{oned/writers.js → ts/oned/writers.ts} +0 -1
- package/src/{pdf417/compaction.js → ts/pdf417/compaction.ts} +0 -1
- package/src/{pdf417/decoder.js → ts/pdf417/decoder.ts} +0 -1
- package/src/{pdf417/detector.js → ts/pdf417/detector.ts} +0 -1
- package/src/{pdf417/encoder.js → ts/pdf417/encoder.ts} +0 -1
- package/src/{pdf417/error-correction.js → ts/pdf417/error-correction.ts} +0 -1
- package/src/ts/pdf417/index.ts +6 -0
- package/src/{pdf417/tables.js → ts/pdf417/tables.ts} +0 -1
- package/src/{qr/decoder.js → ts/qr/decoder.ts} +0 -1
- package/src/{qr/detector.js → ts/qr/detector.ts} +0 -1
- package/src/{qr/encoder.js → ts/qr/encoder.ts} +0 -1
- package/src/{qr/index.js → ts/qr/index.ts} +0 -1
- package/src/{qr/tables.js → ts/qr/tables.ts} +0 -1
- package/src/{render/image-data.js → ts/render/image-data.ts} +0 -1
- package/src/{render/index.js → ts/render/index.ts} +0 -1
- package/src/{render/options.js → ts/render/options.ts} +0 -1
- package/src/{render/png.js → ts/render/png.ts} +0 -1
- package/src/{render/svg.js → ts/render/svg.ts} +0 -1
- package/src/{render/webgl.js → ts/render/webgl.ts} +0 -1
- package/src/{render/webgpu.js → ts/render/webgpu.ts} +0 -1
- package/src/{rmqr/decoder.js → ts/rmqr/decoder.ts} +0 -1
- package/src/{rmqr/detector.js → ts/rmqr/detector.ts} +0 -1
- package/src/{rmqr/encoder.js → ts/rmqr/encoder.ts} +0 -1
- package/src/{rmqr/index.js → ts/rmqr/index.ts} +0 -1
- package/src/{rmqr/tables.js → ts/rmqr/tables.ts} +0 -1
- /package/{typings → src/ts}/aztec/decoder.d.ts +0 -0
- /package/{typings → src/ts}/aztec/encoder.d.ts +0 -0
- /package/{typings → src/ts}/aztec/high-level.d.ts +0 -0
- /package/{typings → src/ts}/aztec/index.d.ts +0 -0
- /package/{typings → src/ts}/aztec/tables.d.ts +0 -0
- /package/{typings → src/ts}/aztecrune/decoder.d.ts +0 -0
- /package/{typings → src/ts}/aztecrune/detector.d.ts +0 -0
- /package/{typings → src/ts}/aztecrune/encoder.d.ts +0 -0
- /package/{typings → src/ts}/aztecrune/index.d.ts +0 -0
- /package/{typings → src/ts}/aztecrune/tables.d.ts +0 -0
- /package/{typings → src/ts}/bundle/sythos-barcode.d.ts +0 -0
- /package/{typings → src/ts}/bundle/sythos-barcode.esm.d.ts +0 -0
- /package/{typings → src/ts}/compactpdf417/decoder.d.ts +0 -0
- /package/{typings → src/ts}/compactpdf417/detector.d.ts +0 -0
- /package/{typings → src/ts}/compactpdf417/encoder.d.ts +0 -0
- /package/{typings → src/ts}/compactpdf417/index.d.ts +0 -0
- /package/{typings → src/ts}/compactpdf417/tables.d.ts +0 -0
- /package/{typings → src/ts}/core/bit-buffer.d.ts +0 -0
- /package/{typings → src/ts}/core/bit-matrix.d.ts +0 -0
- /package/{typings → src/ts}/core/errors.d.ts +0 -0
- /package/{typings → src/ts}/core/galois-field.d.ts +0 -0
- /package/{typings → src/ts}/core/index.d.ts +0 -0
- /package/{typings → src/ts}/core/reed-solomon.d.ts +0 -0
- /package/{typings → src/ts}/databar/codec.d.ts +0 -0
- /package/{typings → src/ts}/databar/decoder.d.ts +0 -0
- /package/{typings → src/ts}/databar/encoder.d.ts +0 -0
- /package/{typings → src/ts}/databar/gs1.d.ts +0 -0
- /package/{typings → src/ts}/databar/index.d.ts +0 -0
- /package/{typings → src/ts}/databar/patterns.d.ts +0 -0
- /package/{typings → src/ts}/databar/tables.d.ts +0 -0
- /package/{typings → src/ts}/datamatrix/decoder.d.ts +0 -0
- /package/{typings → src/ts}/datamatrix/encoder.d.ts +0 -0
- /package/{typings → src/ts}/datamatrix/index.d.ts +0 -0
- /package/{typings → src/ts}/datamatrix/tables.d.ts +0 -0
- /package/{typings → src/ts}/frameqr/decoder.d.ts +0 -0
- /package/{typings → src/ts}/frameqr/detector.d.ts +0 -0
- /package/{typings → src/ts}/frameqr/encoder.d.ts +0 -0
- /package/{typings → src/ts}/frameqr/index.d.ts +0 -0
- /package/{typings → src/ts}/frameqr/tables.d.ts +0 -0
- /package/{typings → src/ts}/image/binarizer.d.ts +0 -0
- /package/{typings → src/ts}/image/grid-sampler.d.ts +0 -0
- /package/{typings → src/ts}/image/index.d.ts +0 -0
- /package/{typings → src/ts}/image/luminance.d.ts +0 -0
- /package/{typings → src/ts}/image/perspective.d.ts +0 -0
- /package/{typings → src/ts}/micropdf417/compaction.d.ts +0 -0
- /package/{typings → src/ts}/micropdf417/decoder.d.ts +0 -0
- /package/{typings → src/ts}/micropdf417/detector.d.ts +0 -0
- /package/{typings → src/ts}/micropdf417/encoder.d.ts +0 -0
- /package/{typings → src/ts}/micropdf417/error-correction.d.ts +0 -0
- /package/{typings → src/ts}/micropdf417/index.d.ts +0 -0
- /package/{typings → src/ts}/micropdf417/tables.d.ts +0 -0
- /package/{typings → src/ts}/microqr/decoder.d.ts +0 -0
- /package/{typings → src/ts}/microqr/detector.d.ts +0 -0
- /package/{typings → src/ts}/microqr/encoder.d.ts +0 -0
- /package/{typings → src/ts}/microqr/index.d.ts +0 -0
- /package/{typings → src/ts}/microqr/tables.d.ts +0 -0
- /package/{typings → src/ts}/oned/addons.d.ts +0 -0
- /package/{typings → src/ts}/oned/index.d.ts +0 -0
- /package/{typings → src/ts}/oned/patterns.d.ts +0 -0
- /package/{typings → src/ts}/oned/reader.d.ts +0 -0
- /package/{typings → src/ts}/oned/writers.d.ts +0 -0
- /package/{typings → src/ts}/pdf417/compaction.d.ts +0 -0
- /package/{typings → src/ts}/pdf417/decoder.d.ts +0 -0
- /package/{typings → src/ts}/pdf417/detector.d.ts +0 -0
- /package/{typings → src/ts}/pdf417/encoder.d.ts +0 -0
- /package/{typings → src/ts}/pdf417/error-correction.d.ts +0 -0
- /package/{typings → src/ts}/pdf417/index.d.ts +0 -0
- /package/{typings → src/ts}/pdf417/tables.d.ts +0 -0
- /package/{typings → src/ts}/qr/decoder.d.ts +0 -0
- /package/{typings → src/ts}/qr/detector.d.ts +0 -0
- /package/{typings → src/ts}/qr/encoder.d.ts +0 -0
- /package/{typings → src/ts}/qr/index.d.ts +0 -0
- /package/{typings → src/ts}/qr/tables.d.ts +0 -0
- /package/{typings → src/ts}/render/image-data.d.ts +0 -0
- /package/{typings → src/ts}/render/index.d.ts +0 -0
- /package/{typings → src/ts}/render/options.d.ts +0 -0
- /package/{typings → src/ts}/render/png.d.ts +0 -0
- /package/{typings → src/ts}/render/svg.d.ts +0 -0
- /package/{typings → src/ts}/render/webgl.d.ts +0 -0
- /package/{typings → src/ts}/render/webgpu.d.ts +0 -0
- /package/{typings → src/ts}/rmqr/decoder.d.ts +0 -0
- /package/{typings → src/ts}/rmqr/detector.d.ts +0 -0
- /package/{typings → src/ts}/rmqr/encoder.d.ts +0 -0
- /package/{typings → src/ts}/rmqr/index.d.ts +0 -0
- /package/{typings → src/ts}/rmqr/tables.d.ts +0 -0
package/src/ts/index.ts
ADDED
|
@@ -0,0 +1,790 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Sythos Barcode Suite
|
|
3
|
+
*
|
|
4
|
+
* MIT License
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) 2026 Sythos
|
|
7
|
+
* SPDX-FileCopyrightText: 2026 Sythos (https://www.sythos.net)
|
|
8
|
+
*
|
|
9
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
10
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
11
|
+
* in the Software without restriction, including without limitation the rights
|
|
12
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
13
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
14
|
+
* furnished to do so, subject to the following conditions:
|
|
15
|
+
*
|
|
16
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
17
|
+
* copies or substantial portions of the Software.
|
|
18
|
+
*
|
|
19
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
20
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
21
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
22
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
23
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
24
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
25
|
+
* SOFTWARE.
|
|
26
|
+
*
|
|
27
|
+
* SPDX-License-Identifier: MIT
|
|
28
|
+
*
|
|
29
|
+
* Original work. No code from any other barcode implementation.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Sythos Barcode Suite — public API.
|
|
34
|
+
*
|
|
35
|
+
* Two functions carry the whole surface:
|
|
36
|
+
*
|
|
37
|
+
* encode(text, { format }) -> BitMatrix
|
|
38
|
+
* decode(image, { formats }) -> Result[]
|
|
39
|
+
*
|
|
40
|
+
* Everything else is a renderer or a format-specific escape hatch. The core is
|
|
41
|
+
* free of I/O and of any platform assumption: images go in as
|
|
42
|
+
* `{ data, width, height }` with RGBA bytes, which is exactly what `ImageData`
|
|
43
|
+
* is, so a canvas, an `OffscreenCanvas`, sharp, jimp and node-canvas all work
|
|
44
|
+
* without an adapter.
|
|
45
|
+
*
|
|
46
|
+
* @module @sythos/js_barcode_universal
|
|
47
|
+
*/
|
|
48
|
+
|
|
49
|
+
import { BitMatrix } from './core/bit-matrix.js';
|
|
50
|
+
import { EncodeError, NotFoundError } from './core/errors.js';
|
|
51
|
+
import { LuminanceSource } from './image/luminance.js';
|
|
52
|
+
import { binarize } from './image/binarizer.js';
|
|
53
|
+
import { ONED_FORMATS } from './oned/index.js';
|
|
54
|
+
import { decodeOneD } from './oned/reader.js';
|
|
55
|
+
import * as datamatrix from './datamatrix/index.js';
|
|
56
|
+
import * as qr from './qr/index.js';
|
|
57
|
+
import * as aztec from './aztec/index.js';
|
|
58
|
+
import * as pdf417 from './pdf417/index.js';
|
|
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';
|
|
66
|
+
|
|
67
|
+
export { BitMatrix };
|
|
68
|
+
export {
|
|
69
|
+
BarcodeError, EncodeError, NotFoundError, FormatError, ChecksumError,
|
|
70
|
+
} from './core/errors.js';
|
|
71
|
+
export { LuminanceSource } from './image/luminance.js';
|
|
72
|
+
export { binarize, binarizeGlobal, binarizeHybrid } from './image/binarizer.js';
|
|
73
|
+
export * from './oned/index.js';
|
|
74
|
+
export { toSVG, toSVGDataURI } from './render/svg.js';
|
|
75
|
+
export { toImageData, toCanvas } from './render/image-data.js';
|
|
76
|
+
export { toPNG, toPNGDataURI } from './render/png.js';
|
|
77
|
+
export { renderToCanvasAuto, isWebGL2Available } from './render/index.js';
|
|
78
|
+
export { renderToCanvasAutoAsync, isWebGPUAvailable } from './render/index.js';
|
|
79
|
+
export { encodeQR, decodeQR, detectQR, detectAndDecodeQR } from './qr/index.js';
|
|
80
|
+
export {
|
|
81
|
+
encodeDataMatrix, decodeDataMatrix, detectDataMatrix, detectAndDecodeDataMatrix,
|
|
82
|
+
} from './datamatrix/index.js';
|
|
83
|
+
export { encodeAztec, decodeAztec, detectAztec, detectAndDecodeAztec } from './aztec/index.js';
|
|
84
|
+
export * from './aztecrune/index.js';
|
|
85
|
+
export { encodePDF417, decodePDF417, detectPDF417, detectAndDecodePDF417 } from './pdf417/index.js';
|
|
86
|
+
export * from './compactpdf417/index.js';
|
|
87
|
+
// DataBar exports include both the verified GTIN/data layer and the
|
|
88
|
+
// Omnidirectional/Truncated physical image path.
|
|
89
|
+
export * from './databar/index.js';
|
|
90
|
+
export {
|
|
91
|
+
encodeMicroPDF417, decodeMicroPDF417, detectMicroPDF417, detectAndDecodeMicroPDF417,
|
|
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';
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* @typedef {object} FormatInfo
|
|
101
|
+
* @property {string} id
|
|
102
|
+
* @property {string} label
|
|
103
|
+
* @property {boolean} canWrite
|
|
104
|
+
* @property {boolean} canRead
|
|
105
|
+
* @property {'1D' | '2D'} kind
|
|
106
|
+
* @property {'supplement'} [role]
|
|
107
|
+
*/
|
|
108
|
+
|
|
109
|
+
// Writing and reading a format are separate capabilities that can land at
|
|
110
|
+
// different times, so they are reported separately rather than collapsed into
|
|
111
|
+
// one "supported" flag that would be wrong in one direction or the other.
|
|
112
|
+
//
|
|
113
|
+
// Capability is probed rather than declared, so this stays correct whether the
|
|
114
|
+
// QR module is the full implementation or a stand-in: a module may opt out
|
|
115
|
+
// explicitly with QR_CAN_ENCODE/QR_CAN_DECODE, and is otherwise taken at face
|
|
116
|
+
// value.
|
|
117
|
+
const qrPresent = qr.QR_PLACEHOLDER !== true;
|
|
118
|
+
const qrCanEncode = qrPresent &&
|
|
119
|
+
typeof qr.encodeQR === 'function' && qr.QR_CAN_ENCODE !== false;
|
|
120
|
+
const qrCanDecode = qrPresent &&
|
|
121
|
+
typeof qr.detectAndDecodeQR === 'function' && qr.QR_CAN_DECODE !== false;
|
|
122
|
+
const dataMatrixCanEncode = typeof datamatrix.encodeDataMatrix === 'function';
|
|
123
|
+
const dataMatrixCanDecode = typeof datamatrix.detectAndDecodeDataMatrix === 'function';
|
|
124
|
+
const aztecCanEncode = typeof aztec.encodeAztec === 'function';
|
|
125
|
+
const aztecCanDecode = typeof aztec.detectAndDecodeAztec === 'function';
|
|
126
|
+
const pdf417CanEncode = typeof pdf417.encodePDF417 === 'function';
|
|
127
|
+
// The matrix decoder is complete, but automatic image localization is still
|
|
128
|
+
// limited to clean module-aligned symbols or an application-supplied
|
|
129
|
+
// quadrilateral. Keep the generic scanner capability opt-in until a
|
|
130
|
+
// perspective/noise corpus is passed.
|
|
131
|
+
const pdf417CanDecode = typeof pdf417.detectAndDecodePDF417 === 'function';
|
|
132
|
+
const microPdf417CanEncode = typeof micropdf417.encodeMicroPDF417 === 'function';
|
|
133
|
+
const microPdf417CanDecode = typeof micropdf417.detectAndDecodeMicroPDF417 === 'function';
|
|
134
|
+
const microQrCanEncode = typeof microqr.encodeMicroQR === 'function';
|
|
135
|
+
const microQrCanDecode = typeof microqr.detectAndDecodeMicroQR === 'function';
|
|
136
|
+
const rmqrCanEncode = typeof rmqr.encodeRMQR === 'function';
|
|
137
|
+
const rmqrCanDecode = typeof rmqr.detectAndDecodeRMQR === 'function';
|
|
138
|
+
const frameQrCanEncode = typeof frameqr.encodeFrameQR === 'function';
|
|
139
|
+
const frameQrCanDecode = typeof frameqr.detectAndDecodeFrameQR === 'function';
|
|
140
|
+
const aztecRuneCanEncode = typeof aztecRune.encodeAztecRune === 'function';
|
|
141
|
+
const aztecRuneCanDecode = typeof aztecRune.detectAndDecodeAztecRune === 'function';
|
|
142
|
+
const compactPdf417CanEncode = typeof compactPdf417.encodeCompactPDF417 === 'function';
|
|
143
|
+
const compactPdf417CanDecode = typeof compactPdf417.detectAndDecodeCompactPDF417 === 'function';
|
|
144
|
+
const dataBarCanEncode = typeof databar.encodeDataBar14 === 'function';
|
|
145
|
+
const dataBarCanDecode = typeof databar.decodeDataBar14Scanline === 'function';
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Every format this build supports.
|
|
149
|
+
*
|
|
150
|
+
* Writing and reading are listed separately on purpose. Writing a symbology is
|
|
151
|
+
* a table lookup; reading one needs a detector that finds it in a photograph,
|
|
152
|
+
* which is far more work. The two lists legitimately differ, and saying so
|
|
153
|
+
* here is better than failing at call time.
|
|
154
|
+
*
|
|
155
|
+
* @returns {FormatInfo[]}
|
|
156
|
+
*/
|
|
157
|
+
export function listFormats() {
|
|
158
|
+
const formats = Object.entries(ONED_FORMATS).map(([id, info]) => ({
|
|
159
|
+
id,
|
|
160
|
+
label: info.label,
|
|
161
|
+
canWrite: true,
|
|
162
|
+
canRead: info.readable,
|
|
163
|
+
kind: /** @type {'1D'} */ ('1D'),
|
|
164
|
+
...(info.role ? { role: info.role } : {}),
|
|
165
|
+
}));
|
|
166
|
+
|
|
167
|
+
formats.push({
|
|
168
|
+
id: 'qr',
|
|
169
|
+
label: 'QR Code',
|
|
170
|
+
canWrite: qrCanEncode,
|
|
171
|
+
canRead: qrCanDecode,
|
|
172
|
+
kind: /** @type {'2D'} */ ('2D'),
|
|
173
|
+
});
|
|
174
|
+
formats.push({
|
|
175
|
+
id: 'datamatrix',
|
|
176
|
+
label: 'Data Matrix ECC 200',
|
|
177
|
+
canWrite: dataMatrixCanEncode,
|
|
178
|
+
canRead: dataMatrixCanDecode,
|
|
179
|
+
kind: /** @type {'2D'} */ ('2D'),
|
|
180
|
+
});
|
|
181
|
+
formats.push({
|
|
182
|
+
id: 'aztec',
|
|
183
|
+
label: 'Aztec Code',
|
|
184
|
+
canWrite: aztecCanEncode,
|
|
185
|
+
canRead: aztecCanDecode,
|
|
186
|
+
kind: /** @type {'2D'} */ ('2D'),
|
|
187
|
+
});
|
|
188
|
+
formats.push({
|
|
189
|
+
id: 'aztecrune',
|
|
190
|
+
label: 'Aztec Rune',
|
|
191
|
+
canWrite: aztecRuneCanEncode,
|
|
192
|
+
canRead: aztecRuneCanDecode,
|
|
193
|
+
kind: /** @type {'2D'} */ ('2D'),
|
|
194
|
+
});
|
|
195
|
+
formats.push({
|
|
196
|
+
id: 'pdf417',
|
|
197
|
+
label: 'PDF417',
|
|
198
|
+
canWrite: pdf417CanEncode,
|
|
199
|
+
canRead: pdf417CanDecode,
|
|
200
|
+
kind: /** @type {'2D'} */ ('2D'),
|
|
201
|
+
});
|
|
202
|
+
formats.push({
|
|
203
|
+
id: 'compactpdf417',
|
|
204
|
+
label: 'Compact PDF417',
|
|
205
|
+
canWrite: compactPdf417CanEncode,
|
|
206
|
+
canRead: compactPdf417CanDecode,
|
|
207
|
+
kind: /** @type {'2D'} */ ('2D'),
|
|
208
|
+
});
|
|
209
|
+
formats.push({
|
|
210
|
+
id: 'micropdf417',
|
|
211
|
+
label: 'MicroPDF417',
|
|
212
|
+
canWrite: microPdf417CanEncode,
|
|
213
|
+
canRead: microPdf417CanDecode,
|
|
214
|
+
kind: /** @type {'2D'} */ ('2D'),
|
|
215
|
+
});
|
|
216
|
+
formats.push({
|
|
217
|
+
id: 'microqr',
|
|
218
|
+
label: 'Micro QR Code',
|
|
219
|
+
canWrite: microQrCanEncode,
|
|
220
|
+
canRead: microQrCanDecode,
|
|
221
|
+
kind: /** @type {'2D'} */ ('2D'),
|
|
222
|
+
});
|
|
223
|
+
formats.push({
|
|
224
|
+
id: 'rmqr',
|
|
225
|
+
label: 'rMQR Code',
|
|
226
|
+
canWrite: rmqrCanEncode,
|
|
227
|
+
canRead: rmqrCanDecode,
|
|
228
|
+
kind: /** @type {'2D'} */ ('2D'),
|
|
229
|
+
});
|
|
230
|
+
formats.push({
|
|
231
|
+
id: 'frameqr',
|
|
232
|
+
label: 'Sythos Canvas QR profile',
|
|
233
|
+
canWrite: frameQrCanEncode,
|
|
234
|
+
canRead: frameQrCanDecode,
|
|
235
|
+
kind: /** @type {'2D'} */ ('2D'),
|
|
236
|
+
});
|
|
237
|
+
formats.push({
|
|
238
|
+
id: 'gs1databar14',
|
|
239
|
+
label: 'GS1 DataBar Omnidirectional / Truncated',
|
|
240
|
+
canWrite: dataBarCanEncode,
|
|
241
|
+
canRead: dataBarCanDecode,
|
|
242
|
+
kind: /** @type {'1D'} */ ('1D'),
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
return formats;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Encode a payload into a barcode matrix.
|
|
250
|
+
*
|
|
251
|
+
* The result is a `BitMatrix` where a set bit is a dark module, with no quiet
|
|
252
|
+
* zone — the renderers add that, because the right margin depends on the
|
|
253
|
+
* output medium. Linear symbols come back one module tall; height is a
|
|
254
|
+
* rendering decision, not an encoding one.
|
|
255
|
+
*
|
|
256
|
+
* @param {string | number} text
|
|
257
|
+
* @param {object} [options]
|
|
258
|
+
* @param {string} [options.format] Format id. Default 'qr'.
|
|
259
|
+
* @param {'L'|'M'|'Q'|'H'} [options.ecc] QR error-correction level.
|
|
260
|
+
* @param {number} [options.version] QR version, 1-40. Auto if omitted.
|
|
261
|
+
* @param {boolean} [options.checkDigit] Append a check digit, where optional.
|
|
262
|
+
* @param {boolean} [options.fullAscii] Code 39 extended encoding.
|
|
263
|
+
* @param {boolean} [options.gs1] Emit a leading FNC1.
|
|
264
|
+
* @param {number} [options.layers] Aztec layer count; automatic if omitted.
|
|
265
|
+
* @param {boolean} [options.compact] Force an Aztec Compact or Full symbol.
|
|
266
|
+
* @param {number} [options.eccPercent] Requested Aztec error-correction percentage.
|
|
267
|
+
* @param {number} [options.eccLevel] PDF417 error-correction level, 0-8.
|
|
268
|
+
* @param {number} [options.columns] PDF417 columns, 1-30.
|
|
269
|
+
* @param {number} [options.rows] PDF417 rows, 3-90.
|
|
270
|
+
* @param {number} [options.rowHeight] PDF417 row height in modules.
|
|
271
|
+
* @param {'auto'|'text'|'byte'|'numeric'} [options.compaction] PDF417 compaction mode.
|
|
272
|
+
* @param {number} [options.eci] MicroPDF417 byte-compaction ECI assignment (3 or 26).
|
|
273
|
+
* @param {number} [options.aspectRatio] Preferred MicroPDF417 symbol aspect ratio.
|
|
274
|
+
* @param {object} [options.canvas] Sythos Canvas QR artwork reservation.
|
|
275
|
+
* @param {'square'|'circle'|'diamond'} [options.canvas.shape] Canvas shape.
|
|
276
|
+
* @param {number} [options.canvas.size] Odd canvas size in QR modules.
|
|
277
|
+
* @param {number} [options.canvas.width] Canvas width in QR modules.
|
|
278
|
+
* @param {number} [options.canvas.height] Canvas height in QR modules.
|
|
279
|
+
* @param {number} [options.canvas.centerX] Canvas centre X in QR modules.
|
|
280
|
+
* @param {number} [options.canvas.centerY] Canvas centre Y in QR modules.
|
|
281
|
+
* @param {0|90|180|270} [options.canvas.angle] Canvas quarter-turn.
|
|
282
|
+
* @returns {BitMatrix}
|
|
283
|
+
*/
|
|
284
|
+
export function encode(text, options = {}) {
|
|
285
|
+
const format = String(options.format ?? 'qr').toLowerCase();
|
|
286
|
+
const value = typeof text === 'number' ? String(text) : text;
|
|
287
|
+
|
|
288
|
+
if (format === 'qr' || format === 'qrcode') {
|
|
289
|
+
return qr.encodeQR(value, options);
|
|
290
|
+
}
|
|
291
|
+
if (format === 'datamatrix' || format === 'data-matrix') {
|
|
292
|
+
return datamatrix.encodeDataMatrix(value, options);
|
|
293
|
+
}
|
|
294
|
+
if (format === 'aztec' || format === 'aztec-code') {
|
|
295
|
+
return aztec.encodeAztec(value, options);
|
|
296
|
+
}
|
|
297
|
+
if (format === 'aztecrune' || format === 'aztec-rune' || format === 'rune') {
|
|
298
|
+
return aztecRune.encodeAztecRune(value, options);
|
|
299
|
+
}
|
|
300
|
+
if (format === 'pdf417' || format === 'pdf-417') {
|
|
301
|
+
return pdf417.encodePDF417(value, options);
|
|
302
|
+
}
|
|
303
|
+
if (format === 'compactpdf417' || format === 'compact-pdf417' || format === 'compact-pdf-417') {
|
|
304
|
+
return compactPdf417.encodeCompactPDF417(value, options);
|
|
305
|
+
}
|
|
306
|
+
if (format === 'micropdf417' || format === 'micro-pdf417' || format === 'micro-pdf-417') {
|
|
307
|
+
return micropdf417.encodeMicroPDF417(value, options);
|
|
308
|
+
}
|
|
309
|
+
if (format === 'microqr' || format === 'micro-qr') {
|
|
310
|
+
return microqr.encodeMicroQR(value, options);
|
|
311
|
+
}
|
|
312
|
+
if (format === 'rmqr' || format === 'r-mqr' || format === 'rectangular-micro-qr') {
|
|
313
|
+
return rmqr.encodeRMQR(value, options);
|
|
314
|
+
}
|
|
315
|
+
if (format === 'frameqr' || format === 'frame-qr' || format === 'canvas-qr') {
|
|
316
|
+
return frameqr.encodeFrameQR(value, options);
|
|
317
|
+
}
|
|
318
|
+
if (format === 'gs1databar14' || format === 'gs1-databar14' || format === 'databar') {
|
|
319
|
+
return databar.encodeDataBar14(value, options);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const entry = ONED_FORMATS[format];
|
|
323
|
+
if (!entry) {
|
|
324
|
+
const known = [...Object.keys(ONED_FORMATS), 'qr', 'datamatrix', 'aztec', 'aztecrune', 'pdf417', 'compactpdf417', 'micropdf417', 'microqr', 'rmqr', 'frameqr', 'gs1databar14'].join(', ');
|
|
325
|
+
throw new EncodeError(`Unknown format "${format}". Known formats: ${known}`);
|
|
326
|
+
}
|
|
327
|
+
return entry.encode(value, options);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* @typedef {object} DecodeResult
|
|
332
|
+
* @property {string} text
|
|
333
|
+
* @property {string} format
|
|
334
|
+
* @property {Uint8Array} [bytes] Raw octets exposed by byte-oriented payload modes, before text decoding.
|
|
335
|
+
* @property {{mode: 'text'|'byte'|'numeric', text: string, bytes: Uint8Array, eci: number, latch: number|null, codewordStart: number, codewordEnd: number}[]} [segments] PDF417 compaction segments in source order.
|
|
336
|
+
* @property {number} [version] QR version.
|
|
337
|
+
* @property {string} [ecc] QR error-correction level.
|
|
338
|
+
* @property {number} [layers] Aztec layer count.
|
|
339
|
+
* @property {boolean} [compact] Whether an Aztec symbol is Compact.
|
|
340
|
+
* @property {number} [corrections] Reed–Solomon corrections applied by an Aztec decode.
|
|
341
|
+
* @property {number} [rows] PDF417 row count.
|
|
342
|
+
* @property {number} [columns] PDF417 column count.
|
|
343
|
+
* @property {number} [eccLevel] PDF417 error-correction level.
|
|
344
|
+
* @property {number} [rowHeight] PDF417 row height in modules.
|
|
345
|
+
* @property {number} [variant] MicroPDF417 predefined variant number.
|
|
346
|
+
* @property {number} [eccCodewords] MicroPDF417 fixed error-correction codewords.
|
|
347
|
+
* @property {string} [profile] Sythos Canvas QR profile identifier.
|
|
348
|
+
* @property {boolean} [certified] Whether the profile is certified by its originator.
|
|
349
|
+
* @property {object} [canvas] Canvas reservation metadata for the Sythos profile.
|
|
350
|
+
* @property {{format:'ean2'|'ean5', text:string, parity:string, checksum?:number}} [addon] Attached EAN/UPC supplement.
|
|
351
|
+
* @property {number} [confidence] Camera-profile confidence from 0 to 1.
|
|
352
|
+
* @property {{x:number,y:number,width:number,height:number}} [bounds] Camera-profile bounds in the scanned orientation.
|
|
353
|
+
* @property {0|45|90|135|180|225|270|315} [rotation] Camera-profile orientation in degrees.
|
|
354
|
+
* @property {{quietZone:boolean,checksum:boolean|null,rows:number|null,consistency:number|null}} [quality] Camera-profile validation evidence.
|
|
355
|
+
* @property {boolean} [gs1] Whether the physical symbol is classified as GS1.
|
|
356
|
+
* @property {string} [symbologyIdentifier] GS1 symbology identifier.
|
|
357
|
+
* @property {Array<{ai:string,value:string,fixed?:boolean}>} [elements] Parsed GS1 Application Identifier fields.
|
|
358
|
+
* @property {string} [gs1ParseError] Semantic GS1 parsing error after a valid physical read.
|
|
359
|
+
* @property {string} [gtin] GS1 DataBar GTIN-14 payload.
|
|
360
|
+
* @property {boolean} [linkage] GS1 DataBar linkage flag.
|
|
361
|
+
*/
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Find and decode every barcode in an image.
|
|
365
|
+
*
|
|
366
|
+
* Returns an array, empty when nothing is found — an image with no barcode is
|
|
367
|
+
* an ordinary outcome for a camera frame, not an error, and throwing would
|
|
368
|
+
* make the common scanning loop a try/catch.
|
|
369
|
+
*
|
|
370
|
+
* @param {{data: Uint8ClampedArray|Uint8Array|number[], width: number, height: number}} image
|
|
371
|
+
* @param {object} [options]
|
|
372
|
+
* @param {string[]} [options.formats] Restrict to these format ids.
|
|
373
|
+
* @param {boolean} [options.tryHarder] Retry inverted and rotated. Default true.
|
|
374
|
+
* @param {'global'|'hybrid'|'auto'} [options.binarizer]
|
|
375
|
+
* @param {'camera'} [options.profile] Opt-in strict camera profile for validated reads.
|
|
376
|
+
* @param {object} [options.frameqr] Sythos Canvas QR detector options when
|
|
377
|
+
* the profile marker is not preserved through image rendering.
|
|
378
|
+
* @returns {DecodeResult[]}
|
|
379
|
+
*/
|
|
380
|
+
export function decode(image, options = {}) {
|
|
381
|
+
const { formats = null, tryHarder = true, binarizer = 'auto', profile = null } = options;
|
|
382
|
+
const want = formats ? new Set(formats.map((f) => f.toLowerCase())) : null;
|
|
383
|
+
const wantQR = !want || want.has('qr') || want.has('qrcode');
|
|
384
|
+
const wantDataMatrix = !want || want.has('datamatrix') || want.has('data-matrix');
|
|
385
|
+
const wantAztec = !want || want.has('aztec') || want.has('aztec-code');
|
|
386
|
+
const wantAztecRune = !want || want.has('aztecrune') || want.has('aztec-rune') || want.has('rune');
|
|
387
|
+
const wantPDF417 = !want || want.has('pdf417') || want.has('pdf-417');
|
|
388
|
+
const wantCompactPDF417 = !want || want.has('compactpdf417') || want.has('compact-pdf417') || want.has('compact-pdf-417');
|
|
389
|
+
const wantMicroPDF417 = !want || want.has('micropdf417') || want.has('micro-pdf417') || want.has('micro-pdf-417');
|
|
390
|
+
const wantMicroQR = !want || want.has('microqr') || want.has('micro-qr');
|
|
391
|
+
const wantRMQR = !want || want.has('rmqr') || want.has('r-mqr') || want.has('rectangular-micro-qr');
|
|
392
|
+
const wantFrameQR = !want || want.has('frameqr') || want.has('frame-qr') || want.has('canvas-qr');
|
|
393
|
+
const oneDAliases = new Set(['gs1databar14', 'databar', 'gs1-databar14']);
|
|
394
|
+
const wantOneD = !want || [...want].some((f) => f in ONED_FORMATS || oneDAliases.has(f));
|
|
395
|
+
const wantTwoD = wantQR || wantDataMatrix || wantAztec || wantAztecRune || wantPDF417
|
|
396
|
+
|| wantCompactPDF417 || wantMicroPDF417 || wantMicroQR || wantRMQR || wantFrameQR;
|
|
397
|
+
|
|
398
|
+
const source = LuminanceSource.fromImageData(image);
|
|
399
|
+
const results = [];
|
|
400
|
+
|
|
401
|
+
// Light-on-dark symbols are common on screens and packaging, so a second
|
|
402
|
+
// inverted pass is worth the cost when the first finds nothing.
|
|
403
|
+
const passes = tryHarder ? [source, source.invert()] : [source];
|
|
404
|
+
|
|
405
|
+
for (const pass of passes) {
|
|
406
|
+
const bits = binarize(pass, binarizer);
|
|
407
|
+
|
|
408
|
+
// Keep all two-dimensional detector calls in one helper so camera-only
|
|
409
|
+
// orientation retries use exactly the same validation path as the native
|
|
410
|
+
// orientation. Arbitrary-angle retries remain opt-in to the camera
|
|
411
|
+
// profile because resampling is intentionally not part of ordinary decode.
|
|
412
|
+
const readTwoD = (candidateBits, cameraRotation = 0, candidateSource = pass) => {
|
|
413
|
+
const before = results.length;
|
|
414
|
+
const add = (found, format) => {
|
|
415
|
+
if (!found) return;
|
|
416
|
+
const publicFound = { ...found, format };
|
|
417
|
+
if (profile === 'camera' && cameraRotation !== 0) publicFound.rotation = cameraRotation;
|
|
418
|
+
results.push(publicFound);
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
if (wantQR && qrCanDecode) {
|
|
422
|
+
try {
|
|
423
|
+
for (const found of qr.detectAndDecodeQR(candidateBits)) add(found, 'qr');
|
|
424
|
+
} catch {
|
|
425
|
+
/* no QR in this pass */
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
if (wantDataMatrix && dataMatrixCanDecode) {
|
|
430
|
+
// Hybrid thresholding can erase the interior of very large, perfectly
|
|
431
|
+
// uniform modules. In auto mode keep the local-threshold attempt, then
|
|
432
|
+
// retry Data Matrix once with the global threshold before giving up.
|
|
433
|
+
const dataMatrixBits = binarizer === 'auto'
|
|
434
|
+
? [candidateBits, binarize(candidateSource, 'global')]
|
|
435
|
+
: [candidateBits];
|
|
436
|
+
for (const thresholdBits of dataMatrixBits) {
|
|
437
|
+
try {
|
|
438
|
+
const found = datamatrix.detectAndDecodeDataMatrix(thresholdBits);
|
|
439
|
+
if (found) { add(found, 'datamatrix'); break; }
|
|
440
|
+
} catch {
|
|
441
|
+
/* no Data Matrix with this threshold */
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
if (wantAztec && aztecCanDecode) {
|
|
447
|
+
// The central bull's-eye is a small, high-contrast target. Hybrid
|
|
448
|
+
// thresholding can flatten it on clean rendered symbols, so mirror the
|
|
449
|
+
// Data Matrix global fallback in auto mode.
|
|
450
|
+
const aztecBits = binarizer === 'auto'
|
|
451
|
+
? [candidateBits, binarize(candidateSource, 'global')]
|
|
452
|
+
: [candidateBits];
|
|
453
|
+
for (const thresholdBits of aztecBits) {
|
|
454
|
+
try {
|
|
455
|
+
const found = aztec.detectAndDecodeAztec(thresholdBits);
|
|
456
|
+
if (found) { add(found, 'aztec'); break; }
|
|
457
|
+
} catch {
|
|
458
|
+
/* no Aztec code with this threshold */
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
if (wantAztecRune && aztecRuneCanDecode) {
|
|
464
|
+
try {
|
|
465
|
+
const found = aztecRune.detectAndDecodeAztecRune(candidateBits);
|
|
466
|
+
if (found) add(found, 'aztecrune');
|
|
467
|
+
} catch {
|
|
468
|
+
/* no Aztec Rune in this pass */
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
if (wantPDF417 && pdf417CanDecode) {
|
|
473
|
+
try {
|
|
474
|
+
const found = pdf417.detectAndDecodePDF417(candidateBits);
|
|
475
|
+
if (found) add(found, 'pdf417');
|
|
476
|
+
} catch {
|
|
477
|
+
/* no PDF417 in this pass */
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
if (wantCompactPDF417 && compactPdf417CanDecode) {
|
|
482
|
+
try {
|
|
483
|
+
const found = compactPdf417.detectAndDecodeCompactPDF417(candidateBits);
|
|
484
|
+
if (found) add(found, 'compactpdf417');
|
|
485
|
+
} catch {
|
|
486
|
+
/* no Compact PDF417 in this pass */
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
if (wantMicroPDF417 && microPdf417CanDecode) {
|
|
491
|
+
// MicroPDF417 detection measures runs across the whole raster. Hybrid
|
|
492
|
+
// thresholding can alter uniform modules near local-window boundaries,
|
|
493
|
+
// so retry the global threshold in auto mode as for the other 2D codes.
|
|
494
|
+
const microPdf417Bits = binarizer === 'auto'
|
|
495
|
+
? [candidateBits, binarize(candidateSource, 'global')]
|
|
496
|
+
: [candidateBits];
|
|
497
|
+
for (const thresholdBits of microPdf417Bits) {
|
|
498
|
+
try {
|
|
499
|
+
const found = micropdf417.detectAndDecodeMicroPDF417(thresholdBits);
|
|
500
|
+
if (found) { add(found, 'micropdf417'); break; }
|
|
501
|
+
} catch {
|
|
502
|
+
/* no MicroPDF417 with this threshold */
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
if (wantMicroQR && microQrCanDecode) {
|
|
508
|
+
try {
|
|
509
|
+
for (const found of microqr.detectAndDecodeMicroQR(candidateBits)) add(found, 'microqr');
|
|
510
|
+
} catch {
|
|
511
|
+
/* no Micro QR in this pass */
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
if (wantRMQR && rmqrCanDecode) {
|
|
516
|
+
try {
|
|
517
|
+
const found = rmqr.detectAndDecodeRMQR(candidateBits);
|
|
518
|
+
if (found) add(found, 'rmqr');
|
|
519
|
+
} catch {
|
|
520
|
+
/* no rMQR in this pass */
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
if (wantFrameQR && frameQrCanDecode) {
|
|
525
|
+
try {
|
|
526
|
+
for (const found of frameqr.detectAndDecodeFrameQR(candidateBits, options.frameqr ?? {})) add(found, 'frameqr');
|
|
527
|
+
} catch {
|
|
528
|
+
/* no Sythos Canvas QR profile in this pass */
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
return results.length > before;
|
|
533
|
+
};
|
|
534
|
+
|
|
535
|
+
const twoDFound = readTwoD(bits);
|
|
536
|
+
if (profile === 'camera' && wantTwoD && !twoDFound) {
|
|
537
|
+
// Normalize a camera frame through the full eight-angle set only after
|
|
538
|
+
// the native orientation has failed. This preserves the fast path while
|
|
539
|
+
// limiting resampling to frames that need it.
|
|
540
|
+
const cameraRotations = [45, 90, 135, 180, 225, 270, 315];
|
|
541
|
+
for (const rotation of cameraRotations) {
|
|
542
|
+
const inverse = (360 - rotation) % 360;
|
|
543
|
+
const orientedSource = inverse % 90 === 0
|
|
544
|
+
? inverse === 0
|
|
545
|
+
? pass
|
|
546
|
+
: inverse === 90
|
|
547
|
+
? pass.rotate90()
|
|
548
|
+
: inverse === 180
|
|
549
|
+
? rotateLuminanceSourceByDegrees(pass, 180)
|
|
550
|
+
: pass.rotate90().rotate90().rotate90()
|
|
551
|
+
: rotateLuminanceSourceByDegrees(pass, inverse);
|
|
552
|
+
if (readTwoD(binarize(orientedSource, binarizer), rotation, orientedSource)) break;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
if (wantOneD) {
|
|
557
|
+
const oneDFormats = want
|
|
558
|
+
? [...want].filter((f) => f in ONED_FORMATS || oneDAliases.has(f))
|
|
559
|
+
: null;
|
|
560
|
+
const oneDPasses = [{ bits, rotation: 0 }];
|
|
561
|
+
// A linear symbol rotated away from the horizontal has no usable
|
|
562
|
+
// horizontal scanline. The strict camera profile adds normalized
|
|
563
|
+
// orientations only when the native orientation found no validated 1D
|
|
564
|
+
// result. Keep the two quarter-turns first: they are the established
|
|
565
|
+
// path and avoid paying for diagonal resampling on the common case.
|
|
566
|
+
const readOneD = (candidateBits, rotation) => decodeOneD(candidateBits, {
|
|
567
|
+
...options, formats: oneDFormats, tryHarder, profile, cameraRotation: rotation,
|
|
568
|
+
});
|
|
569
|
+
let oneDResults = readOneD(bits, 0);
|
|
570
|
+
if (profile === 'camera' && oneDResults.length === 0) {
|
|
571
|
+
// `rotation` describes the supplied raster, clockwise from the
|
|
572
|
+
// canonical horizontal orientation. To normalize it, apply the
|
|
573
|
+
// inverse rotation. Include 180 explicitly so the full eight-angle
|
|
574
|
+
// set still works when tryHarder is disabled (the normal reversed-row
|
|
575
|
+
// pass also handles it without a second raster transform).
|
|
576
|
+
const cameraRotations = [90, 270, 180, 45, 135, 225, 315];
|
|
577
|
+
for (const rotation of cameraRotations) {
|
|
578
|
+
const inverse = (360 - rotation) % 360;
|
|
579
|
+
const oriented = inverse % 90 === 0
|
|
580
|
+
? inverse === 0
|
|
581
|
+
? bits.clone()
|
|
582
|
+
: inverse === 180
|
|
583
|
+
? (() => { const copy = bits.clone(); copy.rotate180(); return copy; })()
|
|
584
|
+
: rotateBitMatrix90(bits, inverse === 90)
|
|
585
|
+
: rotateBitMatrixByDegrees(bits, inverse);
|
|
586
|
+
oneDPasses.push({ bits: oriented, rotation });
|
|
587
|
+
}
|
|
588
|
+
for (let i = 1; i < oneDPasses.length && oneDResults.length === 0; i++) {
|
|
589
|
+
oneDResults = readOneD(oneDPasses[i].bits, oneDPasses[i].rotation);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
for (const found of oneDResults) {
|
|
593
|
+
const { row, ...publicFound } = found;
|
|
594
|
+
void row;
|
|
595
|
+
if (publicFound.gs1) {
|
|
596
|
+
const semanticText = publicFound.format === 'gs1databar14'
|
|
597
|
+
? `01${publicFound.gtin ?? publicFound.text}`
|
|
598
|
+
: publicFound.text;
|
|
599
|
+
try {
|
|
600
|
+
publicFound.elements = databar.decodeGS1ElementString(semanticText);
|
|
601
|
+
} catch (error) {
|
|
602
|
+
publicFound.gs1ParseError = error instanceof Error ? error.message : String(error);
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
results.push(publicFound);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
if (results.length > 0) break;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// De-duplicate: the same symbol is often read on several scan rows.
|
|
613
|
+
const seen = new Set();
|
|
614
|
+
const unique = results.filter((r) => {
|
|
615
|
+
const key = `${r.format}:${r.text}`;
|
|
616
|
+
if (seen.has(key)) return false;
|
|
617
|
+
seen.add(key);
|
|
618
|
+
return true;
|
|
619
|
+
});
|
|
620
|
+
|
|
621
|
+
// On large clean rasters, hybrid thresholding can erase otherwise uniform
|
|
622
|
+
// QR/PDF417 modules. Keep auto/hybrid as the primary strategy, then make one
|
|
623
|
+
// focused global retry only when the complete primary pass found nothing.
|
|
624
|
+
// This deliberately leaves an explicit global request single-pass.
|
|
625
|
+
const retryFormats = formats
|
|
626
|
+
? formats.filter((format) => {
|
|
627
|
+
const id = String(format).toLowerCase();
|
|
628
|
+
return id === 'qr' || id === 'qrcode'
|
|
629
|
+
|| id === 'pdf417' || id === 'pdf-417'
|
|
630
|
+
|| id === 'compactpdf417' || id === 'compact-pdf417' || id === 'compact-pdf-417';
|
|
631
|
+
})
|
|
632
|
+
: ['qr', 'pdf417', 'compactpdf417'];
|
|
633
|
+
const shouldRetryGlobal = unique.length === 0
|
|
634
|
+
&& (binarizer === 'auto' || binarizer === 'hybrid')
|
|
635
|
+
&& retryFormats.length > 0;
|
|
636
|
+
|
|
637
|
+
if (!shouldRetryGlobal) {
|
|
638
|
+
return profile === 'camera'
|
|
639
|
+
? unique.sort((a, b) => (b.confidence ?? 0) - (a.confidence ?? 0))
|
|
640
|
+
: unique;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
const fallback = decode(image, {
|
|
644
|
+
...options,
|
|
645
|
+
formats: retryFormats,
|
|
646
|
+
binarizer: 'global',
|
|
647
|
+
});
|
|
648
|
+
const fallbackSeen = new Set();
|
|
649
|
+
const merged = [...unique, ...fallback].filter((r) => {
|
|
650
|
+
const key = `${r.format}:${r.text}`;
|
|
651
|
+
if (fallbackSeen.has(key)) return false;
|
|
652
|
+
fallbackSeen.add(key);
|
|
653
|
+
return true;
|
|
654
|
+
});
|
|
655
|
+
return profile === 'camera'
|
|
656
|
+
? merged.sort((a, b) => (b.confidence ?? 0) - (a.confidence ?? 0))
|
|
657
|
+
: merged;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
/**
|
|
661
|
+
* @param {BitMatrix} matrix
|
|
662
|
+
* @param {boolean} clockwise
|
|
663
|
+
* @returns {BitMatrix}
|
|
664
|
+
*/
|
|
665
|
+
function rotateBitMatrix90(matrix, clockwise) {
|
|
666
|
+
const rotated = new BitMatrix(matrix.height, matrix.width);
|
|
667
|
+
for (let y = 0; y < matrix.height; y++) {
|
|
668
|
+
for (let x = 0; x < matrix.width; x++) {
|
|
669
|
+
if (!matrix.get(x, y)) continue;
|
|
670
|
+
if (clockwise) rotated.set(matrix.height - 1 - y, x);
|
|
671
|
+
else rotated.set(y, matrix.width - 1 - x);
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
return rotated;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
/**
|
|
678
|
+
* Rotate a binarized raster by an arbitrary clockwise angle without changing
|
|
679
|
+
* the source matrix. This is intentionally used only by the strict camera
|
|
680
|
+
* profile: arbitrary-angle resampling is useful for linear symbols, but is
|
|
681
|
+
* too permissive to become a default retry for every detector.
|
|
682
|
+
*
|
|
683
|
+
* @param {BitMatrix} matrix
|
|
684
|
+
* @param {number} degrees Clockwise angle, normally one of 45/135/225/315.
|
|
685
|
+
* @returns {BitMatrix}
|
|
686
|
+
*/
|
|
687
|
+
function rotateBitMatrixByDegrees(matrix, degrees) {
|
|
688
|
+
const angle = ((degrees % 360) + 360) % 360;
|
|
689
|
+
if (angle === 0) return matrix.clone();
|
|
690
|
+
if (angle === 90) return rotateBitMatrix90(matrix, true);
|
|
691
|
+
if (angle === 180) {
|
|
692
|
+
const rotated = matrix.clone();
|
|
693
|
+
rotated.rotate180();
|
|
694
|
+
return rotated;
|
|
695
|
+
}
|
|
696
|
+
if (angle === 270) return rotateBitMatrix90(matrix, false);
|
|
697
|
+
|
|
698
|
+
const radians = angle * Math.PI / 180;
|
|
699
|
+
const sin = Math.sin(radians);
|
|
700
|
+
const cos = Math.cos(radians);
|
|
701
|
+
const width = Math.ceil(Math.abs(matrix.width * cos) + Math.abs(matrix.height * sin));
|
|
702
|
+
const height = Math.ceil(Math.abs(matrix.width * sin) + Math.abs(matrix.height * cos));
|
|
703
|
+
const rotated = new BitMatrix(width, height);
|
|
704
|
+
const sourceCenterX = (matrix.width - 1) / 2;
|
|
705
|
+
const sourceCenterY = (matrix.height - 1) / 2;
|
|
706
|
+
const destinationCenterX = (width - 1) / 2;
|
|
707
|
+
const destinationCenterY = (height - 1) / 2;
|
|
708
|
+
|
|
709
|
+
// Inverse-map destination pixels into the source. Sampling the already
|
|
710
|
+
// binarized raster keeps the operation deterministic and leaves the caller's
|
|
711
|
+
// original image and threshold result untouched.
|
|
712
|
+
for (let y = 0; y < height; y++) {
|
|
713
|
+
const dy = y - destinationCenterY;
|
|
714
|
+
for (let x = 0; x < width; x++) {
|
|
715
|
+
const dx = x - destinationCenterX;
|
|
716
|
+
const sourceX = Math.round(cos * dx + sin * dy + sourceCenterX);
|
|
717
|
+
const sourceY = Math.round(-sin * dx + cos * dy + sourceCenterY);
|
|
718
|
+
if (sourceX >= 0 && sourceX < matrix.width && sourceY >= 0 && sourceY < matrix.height &&
|
|
719
|
+
matrix.get(sourceX, sourceY)) {
|
|
720
|
+
rotated.set(x, y);
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
return rotated;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
/**
|
|
728
|
+
* Rotate the greyscale source before thresholding. Resampling luminance rather
|
|
729
|
+
* than an already-binarized matrix preserves module contrast at diagonal
|
|
730
|
+
* orientations and gives the format-specific detectors the same input quality
|
|
731
|
+
* as the native camera frame.
|
|
732
|
+
*
|
|
733
|
+
* @param {LuminanceSource} source
|
|
734
|
+
* @param {number} degrees Clockwise angle.
|
|
735
|
+
* @returns {LuminanceSource}
|
|
736
|
+
*/
|
|
737
|
+
function rotateLuminanceSourceByDegrees(source, degrees) {
|
|
738
|
+
const angle = ((degrees % 360) + 360) % 360;
|
|
739
|
+
if (angle === 0) return source;
|
|
740
|
+
if (angle === 90) return source.rotate90();
|
|
741
|
+
if (angle === 180) return source.rotate90().rotate90();
|
|
742
|
+
if (angle === 270) return source.rotate90().rotate90().rotate90();
|
|
743
|
+
|
|
744
|
+
const radians = angle * Math.PI / 180;
|
|
745
|
+
const sin = Math.sin(radians);
|
|
746
|
+
const cos = Math.cos(radians);
|
|
747
|
+
const width = Math.ceil(Math.abs(source.width * cos) + Math.abs(source.height * sin));
|
|
748
|
+
const height = Math.ceil(Math.abs(source.width * sin) + Math.abs(source.height * cos));
|
|
749
|
+
const rotated = new Uint8Array(width * height);
|
|
750
|
+
rotated.fill(255);
|
|
751
|
+
const sourceCenterX = (source.width - 1) / 2;
|
|
752
|
+
const sourceCenterY = (source.height - 1) / 2;
|
|
753
|
+
const destinationCenterX = (width - 1) / 2;
|
|
754
|
+
const destinationCenterY = (height - 1) / 2;
|
|
755
|
+
|
|
756
|
+
for (let y = 0; y < height; y++) {
|
|
757
|
+
const dy = y - destinationCenterY;
|
|
758
|
+
for (let x = 0; x < width; x++) {
|
|
759
|
+
const dx = x - destinationCenterX;
|
|
760
|
+
const sourceX = cos * dx + sin * dy + sourceCenterX;
|
|
761
|
+
const sourceY = -sin * dx + cos * dy + sourceCenterY;
|
|
762
|
+
if (sourceX >= 0 && sourceX < source.width && sourceY >= 0 && sourceY < source.height) {
|
|
763
|
+
const left = Math.floor(sourceX), top = Math.floor(sourceY);
|
|
764
|
+
const right = Math.min(source.width - 1, left + 1);
|
|
765
|
+
const bottom = Math.min(source.height - 1, top + 1);
|
|
766
|
+
const fx = sourceX - left, fy = sourceY - top;
|
|
767
|
+
const topValue = source.get(left, top) * (1 - fx) + source.get(right, top) * fx;
|
|
768
|
+
const bottomValue = source.get(left, bottom) * (1 - fx) + source.get(right, bottom) * fx;
|
|
769
|
+
rotated[y * width + x] = Math.round(topValue * (1 - fy) + bottomValue * fy);
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
return LuminanceSource.fromGrey(rotated, width, height);
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
/**
|
|
777
|
+
* Decode, or throw if nothing is found.
|
|
778
|
+
*
|
|
779
|
+
* @param {{data: Uint8ClampedArray|Uint8Array|number[], width: number, height: number}} image
|
|
780
|
+
* @param {object} [options]
|
|
781
|
+
* @returns {DecodeResult}
|
|
782
|
+
*/
|
|
783
|
+
export function decodeStrict(image, options) {
|
|
784
|
+
const results = decode(image, options);
|
|
785
|
+
if (results.length === 0) throw new NotFoundError('No barcode found in image');
|
|
786
|
+
return results[0];
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
/** Library version, matching package.json. */
|
|
790
|
+
export const VERSION = '1.5.9';
|