@sythos/js_barcode_universal 1.0.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.
@@ -1,123 +1,123 @@
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
- * Data Matrix ECC 200 symbol parameters.
33
- *
34
- * Width and height include finder borders. `regionWidth` and `regionHeight`
35
- * describe the usable modules inside one data region. The last three columns
36
- * make the Reed-Solomon block split explicit instead of hiding the 144x144
37
- * exception in encoder control flow.
38
- *
39
- * @module datamatrix/tables
40
- */
41
-
42
- function symbol(width, height, dataRegionWidth, dataRegionHeight, dataCodewords, errorCodewords, dataBlockLengths) {
43
- const blockCount = dataBlockLengths.length;
44
- return Object.freeze({
45
- width, height, rows: height, columns: width,
46
- // Region dimensions include their one-module finder border on each side;
47
- // dataRegion* expose the inner placement lattice explicitly.
48
- regionWidth: dataRegionWidth + 2, regionHeight: dataRegionHeight + 2,
49
- dataRegionWidth, dataRegionHeight,
50
- dataRegionRows: dataRegionHeight, dataRegionColumns: dataRegionWidth,
51
- dataCodewords, errorCodewords, blockCount,
52
- eccPerBlock: errorCodewords / blockCount,
53
- dataBlockLengths: Object.freeze(dataBlockLengths),
54
- });
55
- }
56
-
57
- /** Classic ISO/IEC 16022 ECC 200 symbols; DMRE is deliberately excluded. */
58
- export const DATAMATRIX_SYMBOLS = Object.freeze([
59
- symbol(10, 10, 8, 8, 3, 5, [3]),
60
- symbol(12, 12, 10, 10, 5, 7, [5]),
61
- symbol(14, 14, 12, 12, 8, 10, [8]),
62
- symbol(16, 16, 14, 14, 12, 12, [12]),
63
- symbol(18, 18, 16, 16, 18, 14, [18]),
64
- symbol(20, 20, 18, 18, 22, 18, [22]),
65
- symbol(22, 22, 20, 20, 30, 20, [30]),
66
- symbol(24, 24, 22, 22, 36, 24, [36]),
67
- symbol(26, 26, 24, 24, 44, 28, [44]),
68
- symbol(32, 32, 14, 14, 62, 36, [62]),
69
- symbol(36, 36, 16, 16, 86, 42, [86]),
70
- symbol(40, 40, 18, 18, 114, 48, [114]),
71
- symbol(44, 44, 20, 20, 144, 56, [144]),
72
- symbol(48, 48, 22, 22, 174, 68, [174]),
73
- symbol(52, 52, 24, 24, 204, 84, [102, 102]),
74
- symbol(64, 64, 14, 14, 280, 112, [140, 140]),
75
- symbol(72, 72, 16, 16, 368, 144, [92, 92, 92, 92]),
76
- symbol(80, 80, 18, 18, 456, 192, [114, 114, 114, 114]),
77
- symbol(88, 88, 20, 20, 576, 224, [144, 144, 144, 144]),
78
- symbol(96, 96, 22, 22, 696, 272, [174, 174, 174, 174]),
79
- symbol(104, 104, 24, 24, 816, 336, [136, 136, 136, 136, 136, 136]),
80
- symbol(120, 120, 18, 18, 1050, 408, [175, 175, 175, 175, 175, 175]),
81
- symbol(132, 132, 20, 20, 1304, 496, [163, 163, 163, 163, 163, 163, 163, 163]),
82
- symbol(144, 144, 22, 22, 1558, 620, [156, 156, 156, 156, 156, 156, 156, 156, 155, 155]),
83
- symbol(18, 8, 16, 6, 5, 7, [5]),
84
- symbol(32, 8, 14, 6, 10, 11, [10]),
85
- symbol(26, 12, 24, 10, 16, 14, [16]),
86
- symbol(36, 12, 16, 10, 22, 18, [22]),
87
- symbol(36, 16, 16, 14, 32, 24, [32]),
88
- symbol(48, 16, 22, 14, 49, 28, [49]),
89
- ]);
90
-
91
- /** Compatibility alias. */
92
- export const SYMBOLS = DATAMATRIX_SYMBOLS;
93
-
94
- /** Return the smallest permitted symbol that holds `count` data codewords. */
95
- export function symbolForDataCodewords(count, shape = 'any') {
96
- for (const s of DATAMATRIX_SYMBOLS) {
97
- const rectangular = s.width !== s.height;
98
- if ((shape === 'square' && rectangular) || (shape === 'rectangular' && !rectangular)) continue;
99
- if (count <= s.dataCodewords) return s;
100
- }
101
- throw new RangeError(`Data Matrix: ${count} data codewords do not fit an ECC 200 ${shape} symbol`);
102
- }
103
-
104
- /** Check redundant geometry and block identities in the static table. */
105
- export function validateDataMatrixTables() {
106
- const issues = [];
107
- for (const s of DATAMATRIX_SYMBOLS) {
108
- const regionsX = s.width / s.regionWidth;
109
- const regionsY = s.height / s.regionHeight;
110
- if (!Number.isInteger(regionsX) || !Number.isInteger(regionsY)) issues.push(`${s.width}x${s.height}: non-integral regions`);
111
- const modules = regionsX * regionsY * s.dataRegionWidth * s.dataRegionHeight;
112
- // Annex F reserves four terminal modules on a few lattice dimensions.
113
- // They are set to dark after codeword placement and do not carry data.
114
- const unused = modules - (s.dataCodewords + s.errorCodewords) * 8;
115
- if (unused !== 0 && unused !== 4) issues.push(`${s.width}x${s.height}: geometry/codeword mismatch`);
116
- if (s.dataBlockLengths.reduce((a, b) => a + b, 0) !== s.dataCodewords) issues.push(`${s.width}x${s.height}: data block mismatch`);
117
- if (s.eccPerBlock * s.blockCount !== s.errorCodewords) issues.push(`${s.width}x${s.height}: ecc block mismatch`);
118
- }
119
- return issues;
120
- }
121
-
122
- /** Compatibility alias. */
123
- export const validateTables = validateDataMatrixTables;
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
+ * Data Matrix ECC 200 symbol parameters.
33
+ *
34
+ * Width and height include finder borders. `regionWidth` and `regionHeight`
35
+ * describe the usable modules inside one data region. The last three columns
36
+ * make the Reed-Solomon block split explicit instead of hiding the 144x144
37
+ * exception in encoder control flow.
38
+ *
39
+ * @module datamatrix/tables
40
+ */
41
+
42
+ function symbol(width, height, dataRegionWidth, dataRegionHeight, dataCodewords, errorCodewords, dataBlockLengths) {
43
+ const blockCount = dataBlockLengths.length;
44
+ return Object.freeze({
45
+ width, height, rows: height, columns: width,
46
+ // Region dimensions include their one-module finder border on each side;
47
+ // dataRegion* expose the inner placement lattice explicitly.
48
+ regionWidth: dataRegionWidth + 2, regionHeight: dataRegionHeight + 2,
49
+ dataRegionWidth, dataRegionHeight,
50
+ dataRegionRows: dataRegionHeight, dataRegionColumns: dataRegionWidth,
51
+ dataCodewords, errorCodewords, blockCount,
52
+ eccPerBlock: errorCodewords / blockCount,
53
+ dataBlockLengths: Object.freeze(dataBlockLengths),
54
+ });
55
+ }
56
+
57
+ /** Classic ISO/IEC 16022 ECC 200 symbols; DMRE is deliberately excluded. */
58
+ export const DATAMATRIX_SYMBOLS = Object.freeze([
59
+ symbol(10, 10, 8, 8, 3, 5, [3]),
60
+ symbol(12, 12, 10, 10, 5, 7, [5]),
61
+ symbol(14, 14, 12, 12, 8, 10, [8]),
62
+ symbol(16, 16, 14, 14, 12, 12, [12]),
63
+ symbol(18, 18, 16, 16, 18, 14, [18]),
64
+ symbol(20, 20, 18, 18, 22, 18, [22]),
65
+ symbol(22, 22, 20, 20, 30, 20, [30]),
66
+ symbol(24, 24, 22, 22, 36, 24, [36]),
67
+ symbol(26, 26, 24, 24, 44, 28, [44]),
68
+ symbol(32, 32, 14, 14, 62, 36, [62]),
69
+ symbol(36, 36, 16, 16, 86, 42, [86]),
70
+ symbol(40, 40, 18, 18, 114, 48, [114]),
71
+ symbol(44, 44, 20, 20, 144, 56, [144]),
72
+ symbol(48, 48, 22, 22, 174, 68, [174]),
73
+ symbol(52, 52, 24, 24, 204, 84, [102, 102]),
74
+ symbol(64, 64, 14, 14, 280, 112, [140, 140]),
75
+ symbol(72, 72, 16, 16, 368, 144, [92, 92, 92, 92]),
76
+ symbol(80, 80, 18, 18, 456, 192, [114, 114, 114, 114]),
77
+ symbol(88, 88, 20, 20, 576, 224, [144, 144, 144, 144]),
78
+ symbol(96, 96, 22, 22, 696, 272, [174, 174, 174, 174]),
79
+ symbol(104, 104, 24, 24, 816, 336, [136, 136, 136, 136, 136, 136]),
80
+ symbol(120, 120, 18, 18, 1050, 408, [175, 175, 175, 175, 175, 175]),
81
+ symbol(132, 132, 20, 20, 1304, 496, [163, 163, 163, 163, 163, 163, 163, 163]),
82
+ symbol(144, 144, 22, 22, 1558, 620, [156, 156, 156, 156, 156, 156, 156, 156, 155, 155]),
83
+ symbol(18, 8, 16, 6, 5, 7, [5]),
84
+ symbol(32, 8, 14, 6, 10, 11, [10]),
85
+ symbol(26, 12, 24, 10, 16, 14, [16]),
86
+ symbol(36, 12, 16, 10, 22, 18, [22]),
87
+ symbol(36, 16, 16, 14, 32, 24, [32]),
88
+ symbol(48, 16, 22, 14, 49, 28, [49]),
89
+ ]);
90
+
91
+ /** Compatibility alias. */
92
+ export const SYMBOLS = DATAMATRIX_SYMBOLS;
93
+
94
+ /** Return the smallest permitted symbol that holds `count` data codewords. */
95
+ export function symbolForDataCodewords(count, shape = 'any') {
96
+ for (const s of DATAMATRIX_SYMBOLS) {
97
+ const rectangular = s.width !== s.height;
98
+ if ((shape === 'square' && rectangular) || (shape === 'rectangular' && !rectangular)) continue;
99
+ if (count <= s.dataCodewords) return s;
100
+ }
101
+ throw new RangeError(`Data Matrix: ${count} data codewords do not fit an ECC 200 ${shape} symbol`);
102
+ }
103
+
104
+ /** Check redundant geometry and block identities in the static table. */
105
+ export function validateDataMatrixTables() {
106
+ const issues = [];
107
+ for (const s of DATAMATRIX_SYMBOLS) {
108
+ const regionsX = s.width / s.regionWidth;
109
+ const regionsY = s.height / s.regionHeight;
110
+ if (!Number.isInteger(regionsX) || !Number.isInteger(regionsY)) issues.push(`${s.width}x${s.height}: non-integral regions`);
111
+ const modules = regionsX * regionsY * s.dataRegionWidth * s.dataRegionHeight;
112
+ // Annex F reserves four terminal modules on a few lattice dimensions.
113
+ // They are set to dark after codeword placement and do not carry data.
114
+ const unused = modules - (s.dataCodewords + s.errorCodewords) * 8;
115
+ if (unused !== 0 && unused !== 4) issues.push(`${s.width}x${s.height}: geometry/codeword mismatch`);
116
+ if (s.dataBlockLengths.reduce((a, b) => a + b, 0) !== s.dataCodewords) issues.push(`${s.width}x${s.height}: data block mismatch`);
117
+ if (s.eccPerBlock * s.blockCount !== s.errorCodewords) issues.push(`${s.width}x${s.height}: ecc block mismatch`);
118
+ }
119
+ return issues;
120
+ }
121
+
122
+ /** Compatibility alias. */
123
+ export const validateTables = validateDataMatrixTables;
package/src/index.js CHANGED
@@ -53,6 +53,7 @@ import { ONED_FORMATS } from './oned/index.js';
53
53
  import { decodeOneD } from './oned/reader.js';
54
54
  import * as datamatrix from './datamatrix/index.js';
55
55
  import * as qr from './qr/index.js';
56
+ import * as aztec from './aztec/index.js';
56
57
 
57
58
  export { BitMatrix };
58
59
  export {
@@ -70,6 +71,7 @@ export { encodeQR, decodeQR, detectQR, detectAndDecodeQR } from './qr/index.js';
70
71
  export {
71
72
  encodeDataMatrix, decodeDataMatrix, detectDataMatrix, detectAndDecodeDataMatrix,
72
73
  } from './datamatrix/index.js';
74
+ export { encodeAztec, decodeAztec, detectAztec, detectAndDecodeAztec } from './aztec/index.js';
73
75
 
74
76
  /**
75
77
  * @typedef {object} FormatInfo
@@ -95,6 +97,8 @@ const qrCanDecode = qrPresent &&
95
97
  typeof qr.detectAndDecodeQR === 'function' && qr.QR_CAN_DECODE !== false;
96
98
  const dataMatrixCanEncode = typeof datamatrix.encodeDataMatrix === 'function';
97
99
  const dataMatrixCanDecode = typeof datamatrix.detectAndDecodeDataMatrix === 'function';
100
+ const aztecCanEncode = typeof aztec.encodeAztec === 'function';
101
+ const aztecCanDecode = typeof aztec.detectAndDecodeAztec === 'function';
98
102
 
99
103
  /**
100
104
  * Every format this build supports.
@@ -129,6 +133,13 @@ export function listFormats() {
129
133
  canRead: dataMatrixCanDecode,
130
134
  kind: /** @type {'2D'} */ ('2D'),
131
135
  });
136
+ formats.push({
137
+ id: 'aztec',
138
+ label: 'Aztec Code',
139
+ canWrite: aztecCanEncode,
140
+ canRead: aztecCanDecode,
141
+ kind: /** @type {'2D'} */ ('2D'),
142
+ });
132
143
 
133
144
  return formats;
134
145
  }
@@ -149,6 +160,9 @@ export function listFormats() {
149
160
  * @param {boolean} [options.checkDigit] Append a check digit, where optional.
150
161
  * @param {boolean} [options.fullAscii] Code 39 extended encoding.
151
162
  * @param {boolean} [options.gs1] Emit a leading FNC1.
163
+ * @param {number} [options.layers] Aztec layer count; automatic if omitted.
164
+ * @param {boolean} [options.compact] Force an Aztec Compact or Full symbol.
165
+ * @param {number} [options.eccPercent] Requested Aztec error-correction percentage.
152
166
  * @returns {BitMatrix}
153
167
  */
154
168
  export function encode(text, options = {}) {
@@ -161,10 +175,13 @@ export function encode(text, options = {}) {
161
175
  if (format === 'datamatrix' || format === 'data-matrix') {
162
176
  return datamatrix.encodeDataMatrix(value, options);
163
177
  }
178
+ if (format === 'aztec' || format === 'aztec-code') {
179
+ return aztec.encodeAztec(value, options);
180
+ }
164
181
 
165
182
  const entry = ONED_FORMATS[format];
166
183
  if (!entry) {
167
- const known = [...Object.keys(ONED_FORMATS), 'qr', 'datamatrix'].join(', ');
184
+ const known = [...Object.keys(ONED_FORMATS), 'qr', 'datamatrix', 'aztec'].join(', ');
168
185
  throw new EncodeError(`Unknown format "${format}". Known formats: ${known}`);
169
186
  }
170
187
  return entry.encode(value, options);
@@ -177,6 +194,9 @@ export function encode(text, options = {}) {
177
194
  * @property {Uint8Array} [bytes] Raw payload, before text decoding.
178
195
  * @property {number} [version] QR version.
179
196
  * @property {string} [ecc] QR error-correction level.
197
+ * @property {number} [layers] Aztec layer count.
198
+ * @property {boolean} [compact] Whether an Aztec symbol is Compact.
199
+ * @property {number} [corrections] Reed–Solomon corrections applied by an Aztec decode.
180
200
  */
181
201
 
182
202
  /**
@@ -198,6 +218,7 @@ export function decode(image, options = {}) {
198
218
  const want = formats ? new Set(formats.map((f) => f.toLowerCase())) : null;
199
219
  const wantQR = !want || want.has('qr') || want.has('qrcode');
200
220
  const wantDataMatrix = !want || want.has('datamatrix') || want.has('data-matrix');
221
+ const wantAztec = !want || want.has('aztec') || want.has('aztec-code');
201
222
  const wantOneD = !want || [...want].some((f) => f in ONED_FORMATS);
202
223
 
203
224
  const source = LuminanceSource.fromImageData(image);
@@ -235,6 +256,21 @@ export function decode(image, options = {}) {
235
256
  }
236
257
  }
237
258
 
259
+ if (wantAztec && aztecCanDecode) {
260
+ // The central bull's-eye is a small, high-contrast target. Hybrid
261
+ // thresholding can flatten it on clean rendered symbols, so mirror the
262
+ // Data Matrix global fallback in auto mode.
263
+ const aztecBits = binarizer === 'auto' ? [bits, binarize(pass, 'global')] : [bits];
264
+ for (const candidateBits of aztecBits) {
265
+ try {
266
+ const found = aztec.detectAndDecodeAztec(candidateBits);
267
+ if (found) { results.push({ ...found, format: 'aztec' }); break; }
268
+ } catch {
269
+ /* no Aztec code with this threshold */
270
+ }
271
+ }
272
+ }
273
+
238
274
  if (wantOneD) {
239
275
  const oneDFormats = want ? [...want].filter((f) => f in ONED_FORMATS) : null;
240
276
  for (const found of decodeOneD(bits, { formats: oneDFormats, tryHarder })) {
@@ -269,4 +305,4 @@ export function decodeStrict(image, options) {
269
305
  }
270
306
 
271
307
  /** Library version, matching package.json. */
272
- export const VERSION = '1.0.0';
308
+ export const VERSION = '1.1.0';