@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
|
@@ -0,0 +1,196 @@
|
|
|
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-License-Identifier: MIT
|
|
27
|
+
*
|
|
28
|
+
* Original work. No code from any other barcode implementation.
|
|
29
|
+
*/
|
|
30
|
+
/**
|
|
31
|
+
* Aztec Code layer geometry and Reed-Solomon parameters.
|
|
32
|
+
*
|
|
33
|
+
* `totalBits` counts the payload ring before its leading pad bits are added;
|
|
34
|
+
* consequently only `usableBits` can be partitioned into codewords. Compact
|
|
35
|
+
* symbols have no reference grid. Full symbols insert alternating reference
|
|
36
|
+
* rows and columns every 16 modules around the centre.
|
|
37
|
+
*
|
|
38
|
+
* The five data fields use generator base 1. GF(256)/DataMatrix is also the
|
|
39
|
+
* Aztec 8-bit field: both use primitive polynomial 0x12d.
|
|
40
|
+
*
|
|
41
|
+
* @module aztec/tables
|
|
42
|
+
*/
|
|
43
|
+
import { GF16, GF64, GF256_AZTEC, GF1024, GF4096 } from '../core/galois-field.js';
|
|
44
|
+
/** Reed-Solomon generator base defined for Aztec parameter and data fields. */
|
|
45
|
+
export const AZTEC_RS_GENERATOR_BASE = 1;
|
|
46
|
+
/** Minimum recommended error correction: 23 percent plus three codewords. */
|
|
47
|
+
export const AZTEC_DEFAULT_ECC_PERCENT = 23;
|
|
48
|
+
export const AZTEC_MIN_ECC_WORDS = 3;
|
|
49
|
+
/** Word size selected solely by the number of layers. */
|
|
50
|
+
export function wordSizeForLayers(layers) {
|
|
51
|
+
if (!Number.isInteger(layers) || layers < 1 || layers > 32) {
|
|
52
|
+
throw new RangeError(`Aztec: layers must be an integer from 1 to 32 (got ${layers})`);
|
|
53
|
+
}
|
|
54
|
+
if (layers <= 2)
|
|
55
|
+
return 6;
|
|
56
|
+
if (layers <= 8)
|
|
57
|
+
return 8;
|
|
58
|
+
if (layers <= 22)
|
|
59
|
+
return 10;
|
|
60
|
+
return 12;
|
|
61
|
+
}
|
|
62
|
+
/** Return the field used by Aztec codewords of `wordSize` bits. */
|
|
63
|
+
export function fieldForWordSize(wordSize) {
|
|
64
|
+
switch (wordSize) {
|
|
65
|
+
case 4: return GF16; // Mode message only.
|
|
66
|
+
case 6: return GF64;
|
|
67
|
+
case 8: return GF256_AZTEC;
|
|
68
|
+
case 10: return GF1024;
|
|
69
|
+
case 12: return GF4096;
|
|
70
|
+
default: throw new RangeError(`Aztec: unsupported codeword size ${wordSize}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/** Return the data field selected for a symbol with `layers` layers. */
|
|
74
|
+
export function fieldForLayers(layers) {
|
|
75
|
+
return fieldForWordSize(wordSizeForLayers(layers));
|
|
76
|
+
}
|
|
77
|
+
/** Matrix side length, including Full-mode reference grid lines. */
|
|
78
|
+
export function aztecSymbolSize(layers, compact = false) {
|
|
79
|
+
if (!Number.isInteger(layers) || layers < 1 || layers > (compact ? 4 : 32)) {
|
|
80
|
+
throw new RangeError(`Aztec: ${compact ? 'Compact' : 'Full'} layers out of range: ${layers}`);
|
|
81
|
+
}
|
|
82
|
+
if (compact)
|
|
83
|
+
return 11 + 4 * layers;
|
|
84
|
+
const baseMatrixSize = 14 + 4 * layers;
|
|
85
|
+
return baseMatrixSize + 1 + 2 * Math.floor((baseMatrixSize / 2 - 1) / 15);
|
|
86
|
+
}
|
|
87
|
+
function layer(layers, compact) {
|
|
88
|
+
const wordSize = wordSizeForLayers(layers);
|
|
89
|
+
const totalBits = ((compact ? 88 : 112) + 16 * layers) * layers;
|
|
90
|
+
const usableBits = totalBits - totalBits % wordSize;
|
|
91
|
+
const totalCodewords = usableBits / wordSize;
|
|
92
|
+
const baseMatrixSize = (compact ? 11 : 14) + 4 * layers;
|
|
93
|
+
return Object.freeze({
|
|
94
|
+
compact,
|
|
95
|
+
layers,
|
|
96
|
+
wordSize,
|
|
97
|
+
totalBits,
|
|
98
|
+
usableBits,
|
|
99
|
+
totalCodewords,
|
|
100
|
+
// Compact mode encodes the count in six bits and can therefore hold no
|
|
101
|
+
// more than 64 data codewords even where the ring itself is larger.
|
|
102
|
+
maxDataCodewords: compact ? Math.min(totalCodewords, 64) : totalCodewords,
|
|
103
|
+
baseMatrixSize,
|
|
104
|
+
symbolSize: aztecSymbolSize(layers, compact),
|
|
105
|
+
modeMessageDataWords: compact ? 2 : 4,
|
|
106
|
+
modeMessageWords: compact ? 7 : 10,
|
|
107
|
+
modeMessageBits: compact ? 28 : 40,
|
|
108
|
+
rsGeneratorBase: AZTEC_RS_GENERATOR_BASE,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
/** Compact Aztec layers 1 through 4, in encoding preference order. */
|
|
112
|
+
export const AZTEC_COMPACT_LAYERS = Object.freeze(Array.from({ length: 4 }, (_, i) => layer(i + 1, true)));
|
|
113
|
+
/** Full Aztec layers 1 through 32, in ascending layer order. */
|
|
114
|
+
export const AZTEC_FULL_LAYERS = Object.freeze(Array.from({ length: 32 }, (_, i) => layer(i + 1, false)));
|
|
115
|
+
/** All allowed symbols. Compact entries precede Full entries for automatic selection. */
|
|
116
|
+
export const AZTEC_LAYERS = Object.freeze([
|
|
117
|
+
...AZTEC_COMPACT_LAYERS,
|
|
118
|
+
...AZTEC_FULL_LAYERS,
|
|
119
|
+
]);
|
|
120
|
+
/** Return one immutable layer record. */
|
|
121
|
+
export function aztecLayer(layers, compact = false) {
|
|
122
|
+
if (!Number.isInteger(layers) || layers < 1 || layers > (compact ? 4 : 32)) {
|
|
123
|
+
throw new RangeError(`Aztec: ${compact ? 'Compact' : 'Full'} layers out of range: ${layers}`);
|
|
124
|
+
}
|
|
125
|
+
return (compact ? AZTEC_COMPACT_LAYERS : AZTEC_FULL_LAYERS)[layers - 1];
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Calculate the minimum parity count for a data word count.
|
|
129
|
+
*
|
|
130
|
+
* The percentage is rounded up because a fractional codeword cannot be
|
|
131
|
+
* emitted. The mandatory three words protect short payloads, where a bare
|
|
132
|
+
* percentage would otherwise round to zero.
|
|
133
|
+
*/
|
|
134
|
+
export function eccCodewordsFor(dataCodewords, eccPercent = AZTEC_DEFAULT_ECC_PERCENT) {
|
|
135
|
+
if (!Number.isInteger(dataCodewords) || dataCodewords < 0) {
|
|
136
|
+
throw new RangeError(`Aztec: data codewords must be a non-negative integer (got ${dataCodewords})`);
|
|
137
|
+
}
|
|
138
|
+
if (!Number.isFinite(eccPercent) || eccPercent < 0 || eccPercent > 100) {
|
|
139
|
+
throw new RangeError(`Aztec: ECC percent must be between 0 and 100 (got ${eccPercent})`);
|
|
140
|
+
}
|
|
141
|
+
return Math.ceil(dataCodewords * eccPercent / 100) + AZTEC_MIN_ECC_WORDS;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Choose the first symbol which holds an already stuffed payload.
|
|
145
|
+
*
|
|
146
|
+
* `dataBits` must be a multiple of the candidate word size; callers which
|
|
147
|
+
* start from high-level bits must stuff separately per candidate word size.
|
|
148
|
+
*/
|
|
149
|
+
export function selectAztecLayer(dataBits, { eccPercent = AZTEC_DEFAULT_ECC_PERCENT, layers = null, compact = null, } = {}) {
|
|
150
|
+
if (!Number.isInteger(dataBits) || dataBits < 0) {
|
|
151
|
+
throw new RangeError(`Aztec: data bits must be a non-negative integer (got ${dataBits})`);
|
|
152
|
+
}
|
|
153
|
+
if (compact !== null && typeof compact !== 'boolean') {
|
|
154
|
+
throw new TypeError('Aztec: compact must be true, false or null');
|
|
155
|
+
}
|
|
156
|
+
let candidates;
|
|
157
|
+
if (layers !== null) {
|
|
158
|
+
if (compact === null)
|
|
159
|
+
throw new TypeError('Aztec: compact must be specified when layers is specified');
|
|
160
|
+
candidates = [aztecLayer(layers, compact)];
|
|
161
|
+
}
|
|
162
|
+
else if (compact === null) {
|
|
163
|
+
candidates = AZTEC_LAYERS;
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
candidates = compact ? AZTEC_COMPACT_LAYERS : AZTEC_FULL_LAYERS;
|
|
167
|
+
}
|
|
168
|
+
for (const candidate of candidates) {
|
|
169
|
+
if (dataBits % candidate.wordSize !== 0)
|
|
170
|
+
continue;
|
|
171
|
+
const dataCodewords = dataBits / candidate.wordSize;
|
|
172
|
+
const eccCodewords = eccCodewordsFor(dataCodewords, eccPercent);
|
|
173
|
+
if (dataCodewords <= candidate.maxDataCodewords &&
|
|
174
|
+
dataCodewords + eccCodewords <= candidate.totalCodewords) {
|
|
175
|
+
return Object.freeze({ ...candidate, dataCodewords, eccCodewords });
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
throw new RangeError('Aztec: payload and requested error correction do not fit an available symbol');
|
|
179
|
+
}
|
|
180
|
+
/** Check static identities so table corruption fails explicitly in tests. */
|
|
181
|
+
export function validateAztecTables() {
|
|
182
|
+
const issues = [];
|
|
183
|
+
for (const entry of AZTEC_LAYERS) {
|
|
184
|
+
if (entry.usableBits % entry.wordSize !== 0)
|
|
185
|
+
issues.push(`${entry.compact ? 'C' : 'F'}${entry.layers}: unaligned usable bits`);
|
|
186
|
+
if (entry.totalCodewords !== entry.usableBits / entry.wordSize)
|
|
187
|
+
issues.push(`${entry.compact ? 'C' : 'F'}${entry.layers}: codeword mismatch`);
|
|
188
|
+
if (entry.symbolSize !== aztecSymbolSize(entry.layers, entry.compact))
|
|
189
|
+
issues.push(`${entry.compact ? 'C' : 'F'}${entry.layers}: matrix size mismatch`);
|
|
190
|
+
if (entry.rsGeneratorBase !== AZTEC_RS_GENERATOR_BASE)
|
|
191
|
+
issues.push(`${entry.compact ? 'C' : 'F'}${entry.layers}: generator base mismatch`);
|
|
192
|
+
if (entry.compact && entry.maxDataCodewords > 64)
|
|
193
|
+
issues.push(`C${entry.layers}: Compact data-word limit exceeded`);
|
|
194
|
+
}
|
|
195
|
+
return issues;
|
|
196
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
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-License-Identifier: MIT
|
|
27
|
+
*
|
|
28
|
+
* Original work. No code from any other barcode implementation.
|
|
29
|
+
*/
|
|
30
|
+
/**
|
|
31
|
+
* Aztec Rune decoder.
|
|
32
|
+
*
|
|
33
|
+
* The input is a square 11x11 module matrix. Structural modules are checked
|
|
34
|
+
* before the seven masked GF(16) codewords are Reed-Solomon corrected. The
|
|
35
|
+
* decoder tries the four in-plane quarter turns and both polarities, allowing
|
|
36
|
+
* it to consume an oriented sampled image while still rejecting ordinary
|
|
37
|
+
* Aztec/QR matrices.
|
|
38
|
+
*
|
|
39
|
+
* @module aztecrune/decoder
|
|
40
|
+
*/
|
|
41
|
+
import { FormatError } from '../core/errors.js';
|
|
42
|
+
import { rsDecode } from '../core/reed-solomon.js';
|
|
43
|
+
import { AZTEC_RUNE_DATA_POSITIONS, AZTEC_RUNE_ECC_CODEWORDS, AZTEC_RUNE_MASK, AZTEC_RUNE_SIZE, aztecRuneField, aztecRuneStructuralValue, } from './tables.js';
|
|
44
|
+
/** @param {import('../core/bit-matrix.js').BitMatrix} source @param {number} turns */
|
|
45
|
+
function rotateClockwise(source, turns) {
|
|
46
|
+
let current = source.clone();
|
|
47
|
+
for (let turn = 0; turn < turns; turn++) {
|
|
48
|
+
const out = new source.constructor(current.height, current.width);
|
|
49
|
+
for (let y = 0; y < current.height; y++)
|
|
50
|
+
for (let x = 0; x < current.width; x++) {
|
|
51
|
+
if (current.get(x, y))
|
|
52
|
+
out.set(current.height - 1 - y, x);
|
|
53
|
+
}
|
|
54
|
+
current = out;
|
|
55
|
+
}
|
|
56
|
+
return current;
|
|
57
|
+
}
|
|
58
|
+
/** @param {import('../core/bit-matrix.js').BitMatrix} matrix */
|
|
59
|
+
function invert(matrix) {
|
|
60
|
+
const out = matrix.clone();
|
|
61
|
+
for (let y = 0; y < out.height; y++)
|
|
62
|
+
for (let x = 0; x < out.width; x++)
|
|
63
|
+
out.flip(x, y);
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
66
|
+
/** @param {import('../core/bit-matrix.js').BitMatrix} matrix @param {boolean} inverted */
|
|
67
|
+
function structureMatches(matrix, inverted) {
|
|
68
|
+
for (let y = 0; y < AZTEC_RUNE_SIZE; y++)
|
|
69
|
+
for (let x = 0; x < AZTEC_RUNE_SIZE; x++) {
|
|
70
|
+
const expected = aztecRuneStructuralValue(x, y);
|
|
71
|
+
if (expected === null)
|
|
72
|
+
continue;
|
|
73
|
+
if (matrix.get(x, y) !== (inverted ? !expected : expected))
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
/** @param {import('../core/bit-matrix.js').BitMatrix} matrix @param {boolean} inverted */
|
|
79
|
+
function readCodewords(matrix, inverted) {
|
|
80
|
+
const words = new Array(2 + AZTEC_RUNE_ECC_CODEWORDS).fill(0);
|
|
81
|
+
for (let at = 0; at < AZTEC_RUNE_DATA_POSITIONS.length; at++) {
|
|
82
|
+
const [x, y] = AZTEC_RUNE_DATA_POSITIONS[at];
|
|
83
|
+
const bit = matrix.get(x, y) !== inverted;
|
|
84
|
+
words[at >>> 2] = (words[at >>> 2] << 1) | (bit ? 1 : 0);
|
|
85
|
+
}
|
|
86
|
+
return words.map((word) => word ^ AZTEC_RUNE_MASK);
|
|
87
|
+
}
|
|
88
|
+
/** @param {number} value @param {boolean} inverted @param {number} rotation @param {number} corrections */
|
|
89
|
+
function result(value, inverted, rotation, corrections) {
|
|
90
|
+
return {
|
|
91
|
+
format: 'aztecrune',
|
|
92
|
+
value,
|
|
93
|
+
text: String(value).padStart(3, '0'),
|
|
94
|
+
bytes: Uint8Array.of(value),
|
|
95
|
+
dimension: AZTEC_RUNE_SIZE,
|
|
96
|
+
inverted,
|
|
97
|
+
rotation,
|
|
98
|
+
corrections,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Decode a square Aztec Rune matrix.
|
|
103
|
+
*
|
|
104
|
+
* @param {import('../core/bit-matrix.js').BitMatrix} matrix
|
|
105
|
+
* @param {{inverted?: boolean|'auto', rotation?: number|'auto'}} [options]
|
|
106
|
+
* @returns {{format:'aztecrune',value:number,text:string,bytes:Uint8Array,dimension:number,inverted:boolean,rotation:number,corrections:number}}
|
|
107
|
+
* @throws {FormatError} For non-Rune geometry, structure or uncorrectable data.
|
|
108
|
+
*/
|
|
109
|
+
export function decodeAztecRune(matrix, options = {}) {
|
|
110
|
+
if (!matrix || matrix.width !== AZTEC_RUNE_SIZE || matrix.height !== AZTEC_RUNE_SIZE) {
|
|
111
|
+
throw new FormatError(`Aztec Rune: expected an ${AZTEC_RUNE_SIZE}x${AZTEC_RUNE_SIZE} module matrix`);
|
|
112
|
+
}
|
|
113
|
+
const invertedModes = options.inverted === true ? [true] : options.inverted === false ? [false] : [false, true];
|
|
114
|
+
const rotations = Number.isInteger(options.rotation)
|
|
115
|
+
? [((options.rotation % 360) + 360) % 360]
|
|
116
|
+
: [0, 90, 180, 270];
|
|
117
|
+
let checksumError = null;
|
|
118
|
+
for (const requestedRotation of rotations) {
|
|
119
|
+
if (requestedRotation % 90 !== 0)
|
|
120
|
+
continue;
|
|
121
|
+
// To canonicalize an input rotated clockwise by k degrees, rotate it
|
|
122
|
+
// counter-clockwise by k. `turns` is the clockwise operation we apply.
|
|
123
|
+
const turns = (4 - requestedRotation / 90) % 4;
|
|
124
|
+
const canonicalRotation = requestedRotation;
|
|
125
|
+
const rotated = turns === 0 ? matrix.clone() : rotateClockwise(matrix, turns);
|
|
126
|
+
for (const inverted of invertedModes) {
|
|
127
|
+
const canonical = inverted ? invert(rotated) : rotated;
|
|
128
|
+
if (!structureMatches(canonical, false))
|
|
129
|
+
continue;
|
|
130
|
+
const words = readCodewords(canonical, false);
|
|
131
|
+
try {
|
|
132
|
+
const corrections = rsDecode(words, AZTEC_RUNE_ECC_CODEWORDS, aztecRuneField(), 1);
|
|
133
|
+
const value = (words[0] << 4) | words[1];
|
|
134
|
+
return result(value, inverted, canonicalRotation, corrections);
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
checksumError = error;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (checksumError) {
|
|
142
|
+
throw new FormatError(`Aztec Rune: Reed-Solomon check failed: ${checksumError.message}`);
|
|
143
|
+
}
|
|
144
|
+
throw new FormatError('Aztec Rune: structural pattern or orientation is invalid');
|
|
145
|
+
}
|
|
146
|
+
export { readCodewords, structureMatches };
|
|
@@ -0,0 +1,180 @@
|
|
|
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-License-Identifier: MIT
|
|
27
|
+
*
|
|
28
|
+
* Original work. No code from any other barcode implementation.
|
|
29
|
+
*/
|
|
30
|
+
/**
|
|
31
|
+
* Detector for Aztec Rune symbols.
|
|
32
|
+
*
|
|
33
|
+
* Rune has a fixed 11x11 geometry and a distinctive five-ring bull's-eye. This
|
|
34
|
+
* detector targets binarized, module-aligned rasters (including an integer
|
|
35
|
+
* scale, quiet zone and quarter turns). Arbitrary perspective and severe blur
|
|
36
|
+
* remain detector-level limitations; callers needing those conditions should
|
|
37
|
+
* pass a rectified 11x11 matrix to decodeAztecRune.
|
|
38
|
+
*
|
|
39
|
+
* @module aztecrune/detector
|
|
40
|
+
*/
|
|
41
|
+
import { NotFoundError } from '../core/errors.js';
|
|
42
|
+
import { BitMatrix } from '../core/bit-matrix.js';
|
|
43
|
+
import { decodeAztecRune } from './decoder.js';
|
|
44
|
+
import { AZTEC_RUNE_SIZE } from './tables.js';
|
|
45
|
+
/** @typedef {{x:number,y:number}} Point */
|
|
46
|
+
/**
|
|
47
|
+
* Find connected components of a polarity. Components touching the image
|
|
48
|
+
* border are ignored when they exceed the plausible module area; this avoids
|
|
49
|
+
* returning the light background as a candidate while retaining an inverted
|
|
50
|
+
* Rune's isolated light centre module.
|
|
51
|
+
*/
|
|
52
|
+
function components(image, value) {
|
|
53
|
+
const width = image.width;
|
|
54
|
+
const height = image.height;
|
|
55
|
+
const seen = new Uint8Array(width * height);
|
|
56
|
+
const limit = Math.max(64, Math.floor(width * height * 0.04));
|
|
57
|
+
const found = [];
|
|
58
|
+
for (let y = 0; y < height; y++)
|
|
59
|
+
for (let x = 0; x < width; x++) {
|
|
60
|
+
const start = y * width + x;
|
|
61
|
+
if (seen[start] || image.get(x, y) !== value)
|
|
62
|
+
continue;
|
|
63
|
+
const queue = [[x, y]];
|
|
64
|
+
seen[start] = 1;
|
|
65
|
+
let head = 0;
|
|
66
|
+
let minX = x;
|
|
67
|
+
let maxX = x;
|
|
68
|
+
let minY = y;
|
|
69
|
+
let maxY = y;
|
|
70
|
+
let count = 0;
|
|
71
|
+
let touchesBorder = false;
|
|
72
|
+
while (head < queue.length) {
|
|
73
|
+
const [px, py] = queue[head++];
|
|
74
|
+
count++;
|
|
75
|
+
minX = Math.min(minX, px);
|
|
76
|
+
maxX = Math.max(maxX, px);
|
|
77
|
+
minY = Math.min(minY, py);
|
|
78
|
+
maxY = Math.max(maxY, py);
|
|
79
|
+
if (px === 0 || py === 0 || px === width - 1 || py === height - 1)
|
|
80
|
+
touchesBorder = true;
|
|
81
|
+
for (const [nx, ny] of [[px - 1, py], [px + 1, py], [px, py - 1], [px, py + 1]]) {
|
|
82
|
+
if (nx < 0 || ny < 0 || nx >= width || ny >= height)
|
|
83
|
+
continue;
|
|
84
|
+
const at = ny * width + nx;
|
|
85
|
+
if (!seen[at] && image.get(nx, ny) === value) {
|
|
86
|
+
seen[at] = 1;
|
|
87
|
+
queue.push([nx, ny]);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
const boxWidth = maxX - minX + 1;
|
|
92
|
+
const boxHeight = maxY - minY + 1;
|
|
93
|
+
if (count <= limit && !touchesBorder &&
|
|
94
|
+
Math.abs(boxWidth - boxHeight) <= Math.max(1, Math.ceil(Math.max(boxWidth, boxHeight) * 0.35)) &&
|
|
95
|
+
count >= boxWidth * boxHeight * 0.45) {
|
|
96
|
+
found.push({ x: (minX + maxX) / 2, y: (minY + maxY) / 2, width: boxWidth, height: boxHeight, count });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return found.sort((a, b) => b.count - a.count);
|
|
100
|
+
}
|
|
101
|
+
/** @param {import('../core/bit-matrix.js').BitMatrix} image @param {Point} center @param {number} pitch */
|
|
102
|
+
function sample(image, center, pitch) {
|
|
103
|
+
const matrix = new BitMatrix(AZTEC_RUNE_SIZE);
|
|
104
|
+
for (let y = 0; y < AZTEC_RUNE_SIZE; y++)
|
|
105
|
+
for (let x = 0; x < AZTEC_RUNE_SIZE; x++) {
|
|
106
|
+
const px = Math.round(center.x + (x - 5) * pitch);
|
|
107
|
+
const py = Math.round(center.y + (y - 5) * pitch);
|
|
108
|
+
if (px >= 0 && py >= 0 && px < image.width && py < image.height && image.get(px, py))
|
|
109
|
+
matrix.set(x, y);
|
|
110
|
+
}
|
|
111
|
+
return matrix;
|
|
112
|
+
}
|
|
113
|
+
function corners(center, pitch) {
|
|
114
|
+
const half = AZTEC_RUNE_SIZE * pitch / 2;
|
|
115
|
+
return [
|
|
116
|
+
{ x: center.x - half, y: center.y - half },
|
|
117
|
+
{ x: center.x + half, y: center.y - half },
|
|
118
|
+
{ x: center.x + half, y: center.y + half },
|
|
119
|
+
{ x: center.x - half, y: center.y + half },
|
|
120
|
+
];
|
|
121
|
+
}
|
|
122
|
+
function sameCandidate(left, right) {
|
|
123
|
+
return Math.hypot(left.center.x - right.center.x, left.center.y - right.center.y) <= Math.max(left.moduleSize, right.moduleSize) * 2;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Detect the most prominent Aztec Rune in a binarized image.
|
|
127
|
+
*
|
|
128
|
+
* @param {import('../core/bit-matrix.js').BitMatrix} binaryImage
|
|
129
|
+
* @returns {{corners:Point[],dimension:11,moduleSize:number,matrix:BitMatrix,result:object}|null}
|
|
130
|
+
*/
|
|
131
|
+
export function detectAztecRune(binaryImage) {
|
|
132
|
+
if (!binaryImage || !binaryImage.width || !binaryImage.height) {
|
|
133
|
+
throw new NotFoundError('detectAztecRune: no image supplied');
|
|
134
|
+
}
|
|
135
|
+
const candidates = [];
|
|
136
|
+
for (const value of [true, false]) {
|
|
137
|
+
for (const component of components(binaryImage, value)) {
|
|
138
|
+
const pitch = (component.width + component.height) / 2;
|
|
139
|
+
if (pitch < 0.8)
|
|
140
|
+
continue;
|
|
141
|
+
const matrix = sample(binaryImage, component, pitch);
|
|
142
|
+
let decoded;
|
|
143
|
+
try {
|
|
144
|
+
decoded = decodeAztecRune(matrix);
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
const candidate = {
|
|
150
|
+
center: component,
|
|
151
|
+
corners: corners(component, pitch),
|
|
152
|
+
dimension: AZTEC_RUNE_SIZE,
|
|
153
|
+
moduleSize: pitch,
|
|
154
|
+
matrix,
|
|
155
|
+
result: decoded,
|
|
156
|
+
};
|
|
157
|
+
if (!candidates.some((entry) => sameCandidate(entry, candidate)))
|
|
158
|
+
candidates.push(candidate);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
candidates.sort((a, b) => b.moduleSize - a.moduleSize);
|
|
162
|
+
const best = candidates[0];
|
|
163
|
+
if (!best)
|
|
164
|
+
return null;
|
|
165
|
+
delete best.center;
|
|
166
|
+
return best;
|
|
167
|
+
}
|
|
168
|
+
/** Detect and decode one Aztec Rune, or return `null` when none is verified. */
|
|
169
|
+
export function detectAndDecodeAztecRune(binaryImage) {
|
|
170
|
+
let detection;
|
|
171
|
+
try {
|
|
172
|
+
detection = detectAztecRune(binaryImage);
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
if (!detection)
|
|
178
|
+
return null;
|
|
179
|
+
return { ...detection.result, corners: detection.corners, moduleSize: detection.moduleSize };
|
|
180
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
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-License-Identifier: MIT
|
|
27
|
+
*
|
|
28
|
+
* Original work. No code from any other barcode implementation.
|
|
29
|
+
*/
|
|
30
|
+
import { EncodeError } from '../core/errors.js';
|
|
31
|
+
import { rsEncode } from '../core/reed-solomon.js';
|
|
32
|
+
import { AZTEC_RUNE_DATA_POSITIONS, AZTEC_RUNE_ECC_CODEWORDS, AZTEC_RUNE_MASK, AZTEC_RUNE_SIZE, aztecRuneField, buildAztecRuneStructure, } from './tables.js';
|
|
33
|
+
/** @param {unknown} value @returns {number} */
|
|
34
|
+
export function normalizeAztecRuneValue(value) {
|
|
35
|
+
if (typeof value === 'number') {
|
|
36
|
+
if (Number.isInteger(value) && value >= 0 && value <= 255)
|
|
37
|
+
return value;
|
|
38
|
+
throw new EncodeError('Aztec Rune: numeric value must be an integer from 0 to 255');
|
|
39
|
+
}
|
|
40
|
+
if (typeof value === 'string') {
|
|
41
|
+
if (!/^\d{1,3}$/.test(value))
|
|
42
|
+
throw new EncodeError('Aztec Rune: text input must contain one to three decimal digits');
|
|
43
|
+
const numeric = Number(value);
|
|
44
|
+
if (numeric > 255)
|
|
45
|
+
throw new EncodeError('Aztec Rune: decimal value must be from 000 to 255');
|
|
46
|
+
return numeric;
|
|
47
|
+
}
|
|
48
|
+
let bytes = null;
|
|
49
|
+
if (value instanceof ArrayBuffer)
|
|
50
|
+
bytes = new Uint8Array(value);
|
|
51
|
+
else if (ArrayBuffer.isView(value))
|
|
52
|
+
bytes = new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
|
|
53
|
+
if (bytes) {
|
|
54
|
+
if (bytes.length !== 1)
|
|
55
|
+
throw new EncodeError('Aztec Rune: binary input must contain exactly one byte');
|
|
56
|
+
return bytes[0];
|
|
57
|
+
}
|
|
58
|
+
throw new EncodeError('Aztec Rune: expected a number, decimal string or one-byte binary value');
|
|
59
|
+
}
|
|
60
|
+
/** @param {number} value @returns {number[]} */
|
|
61
|
+
function codewordsForValue(value) {
|
|
62
|
+
const data = [value >>> 4, value & 0x0f];
|
|
63
|
+
const parity = rsEncode(data, AZTEC_RUNE_ECC_CODEWORDS, aztecRuneField(), 1);
|
|
64
|
+
return data.concat(parity).map((word) => word ^ AZTEC_RUNE_MASK);
|
|
65
|
+
}
|
|
66
|
+
/** @param {BitMatrix} matrix @param {number[]} codewords */
|
|
67
|
+
function writeDataRing(matrix, codewords) {
|
|
68
|
+
for (let word = 0; word < codewords.length; word++) {
|
|
69
|
+
for (let bit = 0; bit < 4; bit++) {
|
|
70
|
+
const at = word * 4 + bit;
|
|
71
|
+
const [x, y] = AZTEC_RUNE_DATA_POSITIONS[at];
|
|
72
|
+
matrix.setValue(x, y, ((codewords[word] >>> (3 - bit)) & 1) !== 0);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Encode one byte as an Aztec Rune.
|
|
78
|
+
*
|
|
79
|
+
* @param {number|string|ArrayBuffer|ArrayBufferView} value
|
|
80
|
+
* @param {{inverted?: boolean}} [options]
|
|
81
|
+
* @returns {import('../core/bit-matrix.js').BitMatrix & {
|
|
82
|
+
* format: 'aztecrune', value: number, inverted: boolean
|
|
83
|
+
* }}
|
|
84
|
+
*/
|
|
85
|
+
export function encodeAztecRune(value, options = {}) {
|
|
86
|
+
const numeric = normalizeAztecRuneValue(value);
|
|
87
|
+
if (options.inverted !== undefined && typeof options.inverted !== 'boolean') {
|
|
88
|
+
throw new EncodeError('Aztec Rune: inverted must be boolean');
|
|
89
|
+
}
|
|
90
|
+
const inverted = options.inverted === true;
|
|
91
|
+
const matrix = buildAztecRuneStructure();
|
|
92
|
+
writeDataRing(matrix, codewordsForValue(numeric));
|
|
93
|
+
if (inverted) {
|
|
94
|
+
for (let y = 0; y < AZTEC_RUNE_SIZE; y++)
|
|
95
|
+
for (let x = 0; x < AZTEC_RUNE_SIZE; x++)
|
|
96
|
+
matrix.flip(x, y);
|
|
97
|
+
}
|
|
98
|
+
matrix.format = 'aztecrune';
|
|
99
|
+
matrix.value = numeric;
|
|
100
|
+
matrix.inverted = inverted;
|
|
101
|
+
return matrix;
|
|
102
|
+
}
|
|
103
|
+
export { codewordsForValue };
|
|
@@ -0,0 +1,34 @@
|
|
|
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-License-Identifier: MIT
|
|
27
|
+
*
|
|
28
|
+
* Original work. No code from any other barcode implementation.
|
|
29
|
+
*/
|
|
30
|
+
/** Aztec Rune entry points. @module aztecrune */
|
|
31
|
+
export { encodeAztecRune, normalizeAztecRuneValue } from './encoder.js';
|
|
32
|
+
export { decodeAztecRune } from './decoder.js';
|
|
33
|
+
export { detectAztecRune, detectAndDecodeAztecRune } from './detector.js';
|
|
34
|
+
export { AZTEC_RUNE_SIZE, AZTEC_RUNE_DATA_BITS, AZTEC_RUNE_WORD_SIZE, AZTEC_RUNE_DATA_CODEWORDS, AZTEC_RUNE_ECC_CODEWORDS, AZTEC_RUNE_TOTAL_CODEWORDS, AZTEC_RUNE_MASK, AZTEC_RUNE_DATA_POSITIONS, aztecRuneStructuralValue, buildAztecRuneStructure, aztecRuneField, validateAztecRuneTables, } from './tables.js';
|