@sythos/js_barcode_universal 0.1.0 → 1.1.0

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.
@@ -10,7 +10,9 @@ appendix carries the consolidated inventory these files expand on.
10
10
 
11
11
  | Format | File | `[TO VERIFY]` items |
12
12
  |---|---|---:|
13
+ | Aztec Code | [`aztec-code.license`](aztec-code.license) | 7 |
13
14
  | QR Code | [`qr-code.license`](qr-code.license) | 5 |
15
+ | Data Matrix ECC 200 | [`data-matrix.license`](data-matrix.license) | 9 |
14
16
  | EAN-13 | [`ean-13.license`](ean-13.license) | 5 |
15
17
  | EAN-8 | [`ean-8.license`](ean-8.license) | 5 |
16
18
  | UPC-A | [`upc-a.license`](upc-a.license) | 6 |
@@ -0,0 +1,74 @@
1
+ Aztec Code — format provenance and usability
2
+ ==========================================================================
3
+
4
+ This file records where the Aztec Code symbology comes from, what governs
5
+ it, and the basis on which this project implements and redistributes it.
6
+
7
+ It is an ENGINEERING INVENTORY, NOT LEGAL ADVICE. Items marked [TO VERIFY]
8
+ have not been independently confirmed by the author.
9
+
10
+
11
+ ORIGIN
12
+ --------------------------------------------------------------------------
13
+
14
+ Created by Andrew Longacre, Jr. of Welch Allyn in the 1990s. The exact
15
+ attribution, ownership history and release terms have not been
16
+ independently confirmed by the author. [TO VERIFY]
17
+
18
+
19
+ SPECIFICATION
20
+ --------------------------------------------------------------------------
21
+
22
+ ISO/IEC 24778:2024 — Aztec Code bar code symbology specification.
23
+ Published by ISO/IEC. The specification TEXT is copyrighted by its
24
+ publisher and is paywalled; it is not reproduced here.
25
+
26
+ Specification TEXT is copyrighted by its publisher. The SYMBOLOGY it
27
+ describes is not — an encoding scheme is a system, not a work of
28
+ authorship. This project implements the symbology; it does not reproduce,
29
+ redistribute or excerpt any specification document.
30
+
31
+
32
+ PATENT STATUS
33
+ --------------------------------------------------------------------------
34
+
35
+ The status of patents associated with Aztec Code, including any original
36
+ rights-holder statements and their current effect, has not been
37
+ independently confirmed by the author. [TO VERIFY]
38
+
39
+
40
+ TRADEMARK
41
+ --------------------------------------------------------------------------
42
+
43
+ The current registration status and ownership of the words "Aztec Code"
44
+ have not been independently established. [TO VERIFY] The name is used
45
+ descriptively here and does not imply endorsement by any rights holder.
46
+
47
+ A trademark does not restrict implementing a symbology. It restricts
48
+ branding — how a product names and presents itself.
49
+
50
+
51
+ IMPLEMENTATION BASIS IN THIS PROJECT
52
+ --------------------------------------------------------------------------
53
+
54
+ Aztec Code is implemented here from published descriptions of the format,
55
+ which are systems and facts rather than works of authorship. No source
56
+ code and no constant table from any other barcode implementation was used,
57
+ consulted or copied, under any licence.
58
+
59
+
60
+ CONCLUSION
61
+ --------------------------------------------------------------------------
62
+
63
+ This project's original implementation is distributed under the MIT
64
+ License. The ISO/IEC reference establishes the technical standard; this
65
+ engineering inventory does not claim to have resolved the patent or
66
+ trademark entries that remain marked [TO VERIFY].
67
+
68
+ References relied upon:
69
+ - ISO/IEC 24778:2024, ISO/IEC
70
+ - Aztec Code origin, patent position and trademark status [TO VERIFY]
71
+
72
+ This is an engineering assessment, not legal advice. Items marked
73
+ [TO VERIFY] have not been independently confirmed by the author. If your
74
+ use is commercially sensitive, obtain your own legal review.
@@ -0,0 +1,82 @@
1
+ Data Matrix ECC 200 — format provenance and usability
2
+ ==========================================================================
3
+
4
+ This file records where the Data Matrix ECC 200 symbology comes from, what governs
5
+ it, and the basis on which this project implements and redistributes it.
6
+
7
+ It is an ENGINEERING INVENTORY, NOT LEGAL ADVICE. Items marked [TO VERIFY]
8
+ have not been independently confirmed by the author.
9
+
10
+
11
+ ORIGIN
12
+ --------------------------------------------------------------------------
13
+
14
+ Data Matrix emerged from industrial automatic-identification work
15
+ associated with International Data Matrix and later RVSI/CI Matrix in the
16
+ late 1980s and early 1990s. The exact corporate chronology and attribution
17
+ have not been independently confirmed by the author. [TO VERIFY]
18
+
19
+
20
+ SPECIFICATION
21
+ --------------------------------------------------------------------------
22
+
23
+ ISO/IEC 16022:2024 — Data Matrix bar code symbology specification.
24
+ Published by ISO/IEC. The specification TEXT is copyrighted by its
25
+ publisher and is paywalled; it is not reproduced here.
26
+ GS1 General Specifications and the GS1 DataMatrix Guideline govern use
27
+ of ECC 200 with a leading FNC1 and GS1 Application Identifiers.
28
+
29
+ Specification TEXT is copyrighted by its publisher. The SYMBOLOGY it
30
+ describes is not — an encoding scheme is a system, not a work of
31
+ authorship. This project implements the symbology; it does not reproduce,
32
+ redistribute or excerpt any specification document.
33
+
34
+
35
+ PATENT STATUS
36
+ --------------------------------------------------------------------------
37
+
38
+ Patent rights associated with ECC 200 have been reported as released for
39
+ public use by an earlier rights holder, but the original patents, chain of
40
+ title and release terms have not been independently confirmed by the
41
+ author. [TO VERIFY]
42
+
43
+
44
+ TRADEMARK
45
+ --------------------------------------------------------------------------
46
+
47
+ The current registration status and ownership of the words "Data Matrix"
48
+ have not been independently established. [TO VERIFY] "GS1" and "GS1
49
+ DataMatrix" are GS1 designations; their use is descriptive here and does
50
+ not imply endorsement. [TO VERIFY]
51
+
52
+ A trademark does not restrict implementing a symbology. It restricts
53
+ branding — how a product names and presents itself.
54
+
55
+
56
+ IMPLEMENTATION BASIS IN THIS PROJECT
57
+ --------------------------------------------------------------------------
58
+
59
+ Data Matrix ECC 200 is implemented here from published descriptions of the format,
60
+ which are systems and facts rather than works of authorship. No source
61
+ code and no constant table from any other barcode implementation was used,
62
+ consulted or copied, under any licence.
63
+
64
+
65
+ CONCLUSION
66
+ --------------------------------------------------------------------------
67
+
68
+ This project's original implementation is distributed under the MIT
69
+ License. The ISO/IEC and GS1 references establish the technical standards;
70
+ this engineering inventory does not claim to have resolved the
71
+ patent-release or trademark entries that remain marked [TO VERIFY].
72
+
73
+ References relied upon:
74
+ - ISO/IEC 16022:2024, ISO/IEC
75
+ - GS1 General Specifications, GS1 AISBL
76
+ - GS1 DataMatrix Guideline, GS1 AISBL
77
+ - Reported ECC 200 patent-release position [TO VERIFY]
78
+ - Data Matrix trademark status and ownership [TO VERIFY]
79
+
80
+ This is an engineering assessment, not legal advice. Items marked
81
+ [TO VERIFY] have not been independently confirmed by the author. If your
82
+ use is commercially sensitive, obtain your own legal review.
package/package.json CHANGED
@@ -1,89 +1,95 @@
1
- {
2
- "name": "@sythos/js_barcode_universal",
3
- "version": "0.1.0",
4
- "description": "Read and write barcodes in JavaScript. 100% original code, zero runtime dependencies. QR Code, EAN/UPC, ISBN, Code 128, Code 39, Code 93, ITF, Codabar, MSI, Code 11 and Pharmacode.",
5
- "author": "Sythos",
6
- "license": "MIT",
7
- "type": "module",
8
- "main": "./src/index.js",
9
- "module": "./src/index.js",
10
- "browser": "./bundle/sythos-barcode.js",
11
- "unpkg": "./bundle/sythos-barcode.js",
12
- "jsdelivr": "./bundle/sythos-barcode.js",
13
- "sideEffects": false,
14
- "engines": {
15
- "node": ">=18"
16
- },
17
- "publishConfig": {
18
- "access": "public"
19
- },
20
- "exports": {
21
- ".": "./src/index.js",
22
- "./core": "./src/core/index.js",
23
- "./image": "./src/image/index.js",
24
- "./oned": "./src/oned/index.js",
1
+ {
2
+ "name": "@sythos/js_barcode_universal",
3
+ "version": "1.1.0",
4
+ "description": "Read and write barcodes in JavaScript. 100% original code, zero runtime dependencies. Aztec Code, Data Matrix ECC 200, QR Code, EAN/UPC, ISBN, Code 128, Code 39, Code 93, ITF, Codabar, MSI, Code 11 and Pharmacode.",
5
+ "author": "Sythos",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "main": "./src/index.js",
9
+ "module": "./src/index.js",
10
+ "browser": "./bundle/sythos-barcode.js",
11
+ "unpkg": "./bundle/sythos-barcode.js",
12
+ "jsdelivr": "./bundle/sythos-barcode.js",
13
+ "sideEffects": false,
14
+ "engines": {
15
+ "node": ">=18"
16
+ },
17
+ "publishConfig": {
18
+ "access": "public"
19
+ },
20
+ "exports": {
21
+ ".": "./src/index.js",
22
+ "./core": "./src/core/index.js",
23
+ "./image": "./src/image/index.js",
24
+ "./oned": "./src/oned/index.js",
25
25
  "./qr": "./src/qr/index.js",
26
- "./render": "./src/render/index.js",
27
- "./render/svg": "./src/render/svg.js",
28
- "./render/png": "./src/render/png.js",
29
- "./render/image-data": "./src/render/image-data.js",
30
- "./bundle": "./bundle/sythos-barcode.esm.js",
31
- "./bundle/iife": "./bundle/sythos-barcode.js",
32
- "./package.json": "./package.json"
33
- },
34
- "files": [
35
- "src",
36
- "bundle",
37
- "examples",
38
- "licenses",
39
- "LICENSE",
40
- "NOTICE.md",
41
- "README.md"
42
- ],
43
- "keywords": [
44
- "barcode",
45
- "barcode-generator",
46
- "barcode-scanner",
47
- "qrcode",
48
- "qr",
49
- "qr-code",
50
- "ean",
51
- "ean13",
52
- "ean8",
53
- "upc",
54
- "upca",
55
- "upce",
56
- "isbn",
57
- "code128",
58
- "gs1-128",
59
- "code39",
60
- "code93",
61
- "itf",
62
- "itf14",
63
- "codabar",
64
- "msi",
65
- "code11",
66
- "pharmacode",
67
- "scanner",
68
- "decoder",
69
- "encoder",
70
- "generator",
71
- "reader",
72
- "writer",
73
- "zero-dependencies",
74
- "no-dependencies",
75
- "browser",
76
- "nodejs",
77
- "esm"
78
- ],
79
- "repository": {
80
- "type": "git",
81
- "url": "git+https://github.com/Sythos/JS_Barcode_Universal.git"
82
- },
83
- "bugs": {
84
- "url": "https://github.com/Sythos/JS_Barcode_Universal/issues"
85
- },
86
- "homepage": "https://github.com/Sythos/JS_Barcode_Universal#readme",
87
- "dependencies": {},
88
- "devDependencies": {}
89
- }
26
+ "./datamatrix": "./src/datamatrix/index.js",
27
+ "./aztec": "./src/aztec/index.js",
28
+ "./render": "./src/render/index.js",
29
+ "./render/svg": "./src/render/svg.js",
30
+ "./render/png": "./src/render/png.js",
31
+ "./render/image-data": "./src/render/image-data.js",
32
+ "./bundle": "./bundle/sythos-barcode.esm.js",
33
+ "./bundle/iife": "./bundle/sythos-barcode.js",
34
+ "./package.json": "./package.json"
35
+ },
36
+ "files": [
37
+ "src",
38
+ "bundle",
39
+ "examples",
40
+ "licenses",
41
+ "LICENSE",
42
+ "NOTICE.md",
43
+ "README.md"
44
+ ],
45
+ "keywords": [
46
+ "barcode",
47
+ "datamatrix",
48
+ "ecc200",
49
+ "aztec",
50
+ "aztec-code",
51
+ "barcode-generator",
52
+ "barcode-scanner",
53
+ "qrcode",
54
+ "qr",
55
+ "qr-code",
56
+ "ean",
57
+ "ean13",
58
+ "ean8",
59
+ "upc",
60
+ "upca",
61
+ "upce",
62
+ "isbn",
63
+ "code128",
64
+ "gs1-128",
65
+ "code39",
66
+ "code93",
67
+ "itf",
68
+ "itf14",
69
+ "codabar",
70
+ "msi",
71
+ "code11",
72
+ "pharmacode",
73
+ "scanner",
74
+ "decoder",
75
+ "encoder",
76
+ "generator",
77
+ "reader",
78
+ "writer",
79
+ "zero-dependencies",
80
+ "no-dependencies",
81
+ "browser",
82
+ "nodejs",
83
+ "esm"
84
+ ],
85
+ "repository": {
86
+ "type": "git",
87
+ "url": "git+https://github.com/Sythos/JS_Barcode_Universal.git"
88
+ },
89
+ "bugs": {
90
+ "url": "https://github.com/Sythos/JS_Barcode_Universal/issues"
91
+ },
92
+ "homepage": "https://github.com/Sythos/JS_Barcode_Universal#readme",
93
+ "dependencies": {},
94
+ "devDependencies": {}
95
+ }
@@ -0,0 +1,317 @@
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
+ /**
32
+ * Decoder for a sampled Aztec symbol.
33
+ *
34
+ * This module deliberately accepts only a square, module-aligned BitMatrix.
35
+ * Locating a bull's-eye in a photograph and perspective sampling are detector
36
+ * concerns. Keeping the two stages apart makes all bit order and ECC rules
37
+ * testable without image-processing noise.
38
+ *
39
+ * Contract with tables.js:
40
+ * - aztecSymbolForLayers(compact, layers) returns the nominal symbol data;
41
+ * - aztecWordSizeForLayers(layers) returns 6, 8, 10 or 12;
42
+ * - aztecFieldForLayers(layers) returns the matching binary Galois field;
43
+ * - aztecMatrixSize(compact, layers) returns the rendered square size.
44
+ *
45
+ * @module aztec/decoder
46
+ */
47
+
48
+ import { FormatError } from '../core/errors.js';
49
+ import { rsDecode } from '../core/reed-solomon.js';
50
+ import {
51
+ aztecLayer as aztecSymbolForLayers,
52
+ wordSizeForLayers as aztecWordSizeForLayers,
53
+ fieldForLayers as aztecFieldForLayers,
54
+ fieldForWordSize,
55
+ aztecSymbolSize as aztecMatrixSize,
56
+ } from './tables.js';
57
+
58
+ const UPPER = ['CTRL_PS', ' ', ...'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'CTRL_LL', 'CTRL_ML', 'CTRL_DL', 'CTRL_BS'];
59
+ const LOWER = ['CTRL_PS', ' ', ...'abcdefghijklmnopqrstuvwxyz', 'CTRL_US', 'CTRL_ML', 'CTRL_DL', 'CTRL_BS'];
60
+ const MIXED = [
61
+ 'CTRL_PS', ' ', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\b', '\t', '\n', '\x0b', '\f', '\r', '\x1b',
62
+ '\x1c', '\x1d', '\x1e', '\x1f', '@', '\\', '^', '_', '`', '|', '~', '\x7f', 'CTRL_LL', 'CTRL_UL', 'CTRL_PL', 'CTRL_BS',
63
+ ];
64
+ const PUNCT = ['FLG(n)', '\r', '\r\n', '. ', ', ', ': ', '!', '"', '#', '$', '%', '&', "'", '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '[', ']', '{', '}', 'CTRL_UL'];
65
+ const DIGIT = ['CTRL_PS', ' ', ...'0123456789', ',', '.', 'CTRL_UL'];
66
+ const TABLES = { UPPER, LOWER, MIXED, PUNCT, DIGIT };
67
+
68
+ /** @param {boolean[]} bits @param {number} offset @param {number} count */
69
+ function readBits(bits, offset, count) {
70
+ if (offset + count > bits.length) throw new FormatError('Aztec: truncated high-level stream');
71
+ let value = 0;
72
+ for (let i = 0; i < count; i++) value = (value << 1) | (bits[offset + i] ? 1 : 0);
73
+ return value;
74
+ }
75
+
76
+ /** @param {number} value @param {number} count @param {boolean[]} out */
77
+ function appendBits(value, count, out) {
78
+ for (let i = count - 1; i >= 0; i--) out.push(((value >>> i) & 1) !== 0);
79
+ }
80
+
81
+ /**
82
+ * Decode an Aztec high-level bit stream to its exact byte payload.
83
+ *
84
+ * Text tables contribute their ISO-8859-1 byte values; Binary Shift appends
85
+ * raw bytes. ECI markers are consumed but intentionally not emitted: callers
86
+ * receive the transported byte payload and may select their own charset.
87
+ *
88
+ * @param {boolean[]} bits
89
+ * @returns {Uint8Array}
90
+ */
91
+ export function decodeHighLevelBits(bits) {
92
+ const output = [];
93
+ let latch = 'UPPER';
94
+ let shift = 'UPPER';
95
+ let offset = 0;
96
+
97
+ while (offset < bits.length) {
98
+ if (shift === 'BINARY') {
99
+ if (offset + 5 > bits.length) break; // legal trailing pad
100
+ let length = readBits(bits, offset, 5);
101
+ offset += 5;
102
+ if (length === 0) {
103
+ if (offset + 11 > bits.length) throw new FormatError('Aztec: truncated Binary Shift length');
104
+ length = readBits(bits, offset, 11) + 31;
105
+ offset += 11;
106
+ }
107
+ if (offset + length * 8 > bits.length) throw new FormatError('Aztec: truncated Binary Shift data');
108
+ for (let i = 0; i < length; i++) {
109
+ output.push(readBits(bits, offset, 8));
110
+ offset += 8;
111
+ }
112
+ shift = latch;
113
+ continue;
114
+ }
115
+
116
+ const size = shift === 'DIGIT' ? 4 : 5;
117
+ if (offset + size > bits.length) break; // trailing pad after unstuffing
118
+ const code = readBits(bits, offset, size);
119
+ offset += size;
120
+ const table = TABLES[shift];
121
+ const token = table[code];
122
+ if (token === undefined) throw new FormatError(`Aztec: invalid ${shift} code ${code}`);
123
+
124
+ if (token === 'FLG(n)') {
125
+ if (offset + 3 > bits.length) throw new FormatError('Aztec: truncated FLG(n)');
126
+ const count = readBits(bits, offset, 3);
127
+ offset += 3;
128
+ if (count === 0) output.push(0x1d); // FNC1 / GS
129
+ else if (count <= 6) {
130
+ // ECI assignment number, encoded as count decimal digits. It changes
131
+ // interpretation, not the wire bytes, so consume it without output.
132
+ for (let i = 0; i < count; i++) {
133
+ if (offset + 4 > bits.length) throw new FormatError('Aztec: truncated ECI');
134
+ const digit = readBits(bits, offset, 4);
135
+ offset += 4;
136
+ if (digit < 2 || digit > 11) throw new FormatError('Aztec: invalid ECI digit');
137
+ }
138
+ } else {
139
+ throw new FormatError(`Aztec: unsupported FLG(${count})`);
140
+ }
141
+ shift = latch;
142
+ continue;
143
+ }
144
+
145
+ if (token.startsWith('CTRL_')) {
146
+ const targetCode = token.slice(5, -1);
147
+ const latchMode = token.endsWith('L');
148
+ const target = ({ P: 'PUNCT', L: 'LOWER', M: 'MIXED', D: 'DIGIT', U: 'UPPER', B: 'BINARY' })[targetCode];
149
+ if (!target) throw new FormatError(`Aztec: invalid control ${token}`);
150
+ shift = target;
151
+ if (latchMode) latch = shift;
152
+ continue;
153
+ }
154
+
155
+ for (let i = 0; i < token.length; i++) output.push(token.charCodeAt(i));
156
+ shift = latch;
157
+ }
158
+ return Uint8Array.from(output);
159
+ }
160
+
161
+ /** @param {boolean} compact @param {number} layers */
162
+ function alignmentMap(compact, layers) {
163
+ const baseSize = (compact ? 11 : 14) + layers * 4;
164
+ if (compact) return Array.from({ length: baseSize }, (_, i) => i);
165
+ const size = aztecMatrixSize(layers, false);
166
+ const map = new Array(baseSize);
167
+ const baseCenter = baseSize >> 1;
168
+ const center = size >> 1;
169
+ for (let i = 0; i < baseCenter; i++) {
170
+ const offset = i + Math.floor(i / 15);
171
+ map[baseCenter - i - 1] = center - offset - 1;
172
+ map[baseCenter + i] = center + offset + 1;
173
+ }
174
+ return map;
175
+ }
176
+
177
+ /**
178
+ * Read the four sides of the parameter message. The order mirrors the
179
+ * clockwise write order and is independent of the data spiral.
180
+ *
181
+ * @param {import('../core/bit-matrix.js').BitMatrix} matrix
182
+ * @param {boolean} compact
183
+ * @returns {boolean[]}
184
+ */
185
+ function readModeBits(matrix, compact) {
186
+ const center = matrix.width >> 1;
187
+ const side = compact ? 7 : 10;
188
+ const offset = compact ? 5 : 7;
189
+ // Full symbols skip the reference grid line through the bull's-eye. This
190
+ // exact sequence is also used by drawModeMessage() in encoder.js.
191
+ const positions = Array.from(
192
+ { length: side },
193
+ (_, i) => compact ? center - 3 + i : center - 5 + i + Math.floor(i / 5),
194
+ );
195
+ const bits = [];
196
+ for (let i = 0; i < side; i++) bits.push(matrix.get(positions[i], center - offset));
197
+ for (let i = 0; i < side; i++) bits.push(matrix.get(center + offset, positions[i]));
198
+ for (let i = 0; i < side; i++) bits.push(matrix.get(positions[side - 1 - i], center + offset));
199
+ for (let i = 0; i < side; i++) bits.push(matrix.get(center - offset, positions[side - 1 - i]));
200
+ return bits;
201
+ }
202
+
203
+ /** @param {boolean[]} bits @param {boolean} compact */
204
+ function decodeModeMessage(bits, compact) {
205
+ const total = compact ? 7 : 10;
206
+ const dataWords = compact ? 2 : 4;
207
+ const words = new Array(total);
208
+ for (let i = 0; i < total; i++) words[i] = readBits(bits, i * 4, 4);
209
+ const corrections = rsDecode(words, total - dataWords, fieldForWordSize(4), 1);
210
+ let data = 0;
211
+ for (let i = 0; i < dataWords; i++) data = (data << 4) | words[i];
212
+ const layers = compact ? (data >>> 6) + 1 : (data >>> 11) + 1;
213
+ const dataCodewords = compact ? (data & 0x3f) + 1 : (data & 0x7ff) + 1;
214
+ return { layers, dataCodewords, corrections };
215
+ }
216
+
217
+ /** @param {boolean} compact @param {number} layers */
218
+ function totalBitsInLayers(compact, layers) {
219
+ return ((compact ? 88 : 112) + 16 * layers) * layers;
220
+ }
221
+
222
+ /**
223
+ * Extract raw, stuffed codeword bits in logical ring order.
224
+ * @param {import('../core/bit-matrix.js').BitMatrix} matrix
225
+ * @param {boolean} compact @param {number} layers
226
+ * @returns {boolean[]}
227
+ */
228
+ function extractBits(matrix, compact, layers) {
229
+ const baseSize = (compact ? 11 : 14) + layers * 4;
230
+ const map = alignmentMap(compact, layers);
231
+ const raw = new Array(totalBitsInLayers(compact, layers));
232
+ let offset = 0;
233
+ for (let layer = 0; layer < layers; layer++) {
234
+ const rowSize = (layers - layer) * 4 + (compact ? 9 : 12);
235
+ for (let j = 0; j < rowSize; j++) {
236
+ const col = j * 2;
237
+ for (let k = 0; k < 2; k++) {
238
+ raw[offset + col + k] = matrix.get(map[layer * 2 + k], map[layer * 2 + j]);
239
+ raw[offset + rowSize * 2 + col + k] = matrix.get(map[layer * 2 + j], map[baseSize - 1 - layer * 2 - k]);
240
+ raw[offset + rowSize * 4 + col + k] = matrix.get(map[baseSize - 1 - layer * 2 - k], map[baseSize - 1 - layer * 2 - j]);
241
+ raw[offset + rowSize * 6 + col + k] = matrix.get(map[baseSize - 1 - layer * 2 - j], map[layer * 2 + k]);
242
+ }
243
+ }
244
+ offset += rowSize * 8;
245
+ }
246
+ return raw;
247
+ }
248
+
249
+ /** @param {boolean[]} raw @param {number} layers @param {number} dataCodewords */
250
+ function correctAndUnstuff(raw, layers, dataCodewords) {
251
+ const wordSize = aztecWordSizeForLayers(layers);
252
+ const totalWords = Math.floor(raw.length / wordSize);
253
+ if (dataCodewords <= 0 || dataCodewords > totalWords) throw new FormatError('Aztec: invalid data word count');
254
+ const start = raw.length % wordSize;
255
+ const words = new Array(totalWords);
256
+ for (let i = 0; i < totalWords; i++) words[i] = readBits(raw, start + i * wordSize, wordSize);
257
+ const corrections = rsDecode(words, totalWords - dataCodewords, aztecFieldForLayers(layers), 1);
258
+ const mask = (1 << wordSize) - 1;
259
+ const corrected = [];
260
+ for (let i = 0; i < dataCodewords; i++) {
261
+ const word = words[i];
262
+ if (word === 0 || word === mask) throw new FormatError('Aztec: invalid stuffed codeword');
263
+ if (word === 1 || word === mask - 1) {
264
+ for (let j = 0; j < wordSize - 1; j++) corrected.push(word === mask - 1);
265
+ } else {
266
+ appendBits(word, wordSize, corrected);
267
+ }
268
+ }
269
+ return { bits: corrected, corrections };
270
+ }
271
+
272
+ /** @param {Uint8Array} bytes */
273
+ function bytesToText(bytes) {
274
+ try { return new TextDecoder('utf-8', { fatal: true }).decode(bytes); }
275
+ catch { return new TextDecoder('latin1').decode(bytes); }
276
+ }
277
+
278
+ /**
279
+ * Decode a square Aztec symbol with one bit per module and no quiet zone.
280
+ * The matrix must already be oriented with the mode message at the top.
281
+ *
282
+ * @param {import('../core/bit-matrix.js').BitMatrix} matrix
283
+ * @returns {{text: string, bytes: Uint8Array, compact: boolean, layers: number, corrections: number, eccPercent: number}}
284
+ */
285
+ export function decodeAztec(matrix) {
286
+ if (!matrix || matrix.width !== matrix.height) throw new FormatError('Aztec: expected a square BitMatrix');
287
+ let compact;
288
+ let mode;
289
+ // Compact and full dimensions are disjoint; trying both also makes malformed
290
+ // candidate handling deterministic for the future image detector.
291
+ for (const candidate of [true, false]) {
292
+ try {
293
+ const value = decodeModeMessage(readModeBits(matrix, candidate), candidate);
294
+ if (value.layers < 1 || value.layers > (candidate ? 4 : 32)) continue;
295
+ if (aztecMatrixSize(value.layers, candidate) !== matrix.width) continue;
296
+ compact = candidate;
297
+ mode = value;
298
+ break;
299
+ } catch { /* Try the other family. */ }
300
+ }
301
+ if (compact === undefined || !mode) throw new FormatError('Aztec: invalid mode message or dimensions');
302
+ // Ensure the declared layer data agrees with the table module, so a future
303
+ // tables refactor cannot silently make decoder capacity calculations stale.
304
+ aztecSymbolForLayers(mode.layers, compact);
305
+ const raw = extractBits(matrix, compact, mode.layers);
306
+ const payload = correctAndUnstuff(raw, mode.layers, mode.dataCodewords);
307
+ const bytes = decodeHighLevelBits(payload.bits);
308
+ const totalWords = Math.floor(raw.length / aztecWordSizeForLayers(mode.layers));
309
+ return {
310
+ text: bytesToText(bytes),
311
+ bytes,
312
+ compact,
313
+ layers: mode.layers,
314
+ corrections: mode.corrections + payload.corrections,
315
+ eccPercent: Math.round(((totalWords - mode.dataCodewords) * 100) / totalWords),
316
+ };
317
+ }