@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.
@@ -0,0 +1,42 @@
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
+ /** Data Matrix ECC 200 entry points. @module datamatrix */
32
+
33
+ export { encodeDataMatrix, encodeDataMatrixCodewords } from './encoder.js';
34
+ export { decodeDataMatrix } from './decoder.js';
35
+ export { detectDataMatrix, detectAndDecodeDataMatrix } from './detector.js';
36
+ export {
37
+ DATAMATRIX_SYMBOLS,
38
+ SYMBOLS,
39
+ symbolForDataCodewords,
40
+ validateDataMatrixTables,
41
+ validateTables,
42
+ } from './tables.js';
@@ -0,0 +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;
package/src/index.js CHANGED
@@ -51,7 +51,9 @@ import { LuminanceSource } from './image/luminance.js';
51
51
  import { binarize } from './image/binarizer.js';
52
52
  import { ONED_FORMATS } from './oned/index.js';
53
53
  import { decodeOneD } from './oned/reader.js';
54
+ import * as datamatrix from './datamatrix/index.js';
54
55
  import * as qr from './qr/index.js';
56
+ import * as aztec from './aztec/index.js';
55
57
 
56
58
  export { BitMatrix };
57
59
  export {
@@ -66,6 +68,10 @@ export { toPNG, toPNGDataURI } from './render/png.js';
66
68
  export { renderToCanvasAuto, isWebGL2Available } from './render/index.js';
67
69
  export { renderToCanvasAutoAsync, isWebGPUAvailable } from './render/index.js';
68
70
  export { encodeQR, decodeQR, detectQR, detectAndDecodeQR } from './qr/index.js';
71
+ export {
72
+ encodeDataMatrix, decodeDataMatrix, detectDataMatrix, detectAndDecodeDataMatrix,
73
+ } from './datamatrix/index.js';
74
+ export { encodeAztec, decodeAztec, detectAztec, detectAndDecodeAztec } from './aztec/index.js';
69
75
 
70
76
  /**
71
77
  * @typedef {object} FormatInfo
@@ -89,6 +95,10 @@ const qrCanEncode = qrPresent &&
89
95
  typeof qr.encodeQR === 'function' && qr.QR_CAN_ENCODE !== false;
90
96
  const qrCanDecode = qrPresent &&
91
97
  typeof qr.detectAndDecodeQR === 'function' && qr.QR_CAN_DECODE !== false;
98
+ const dataMatrixCanEncode = typeof datamatrix.encodeDataMatrix === 'function';
99
+ const dataMatrixCanDecode = typeof datamatrix.detectAndDecodeDataMatrix === 'function';
100
+ const aztecCanEncode = typeof aztec.encodeAztec === 'function';
101
+ const aztecCanDecode = typeof aztec.detectAndDecodeAztec === 'function';
92
102
 
93
103
  /**
94
104
  * Every format this build supports.
@@ -116,6 +126,20 @@ export function listFormats() {
116
126
  canRead: qrCanDecode,
117
127
  kind: /** @type {'2D'} */ ('2D'),
118
128
  });
129
+ formats.push({
130
+ id: 'datamatrix',
131
+ label: 'Data Matrix ECC 200',
132
+ canWrite: dataMatrixCanEncode,
133
+ canRead: dataMatrixCanDecode,
134
+ kind: /** @type {'2D'} */ ('2D'),
135
+ });
136
+ formats.push({
137
+ id: 'aztec',
138
+ label: 'Aztec Code',
139
+ canWrite: aztecCanEncode,
140
+ canRead: aztecCanDecode,
141
+ kind: /** @type {'2D'} */ ('2D'),
142
+ });
119
143
 
120
144
  return formats;
121
145
  }
@@ -136,6 +160,9 @@ export function listFormats() {
136
160
  * @param {boolean} [options.checkDigit] Append a check digit, where optional.
137
161
  * @param {boolean} [options.fullAscii] Code 39 extended encoding.
138
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.
139
166
  * @returns {BitMatrix}
140
167
  */
141
168
  export function encode(text, options = {}) {
@@ -145,10 +172,16 @@ export function encode(text, options = {}) {
145
172
  if (format === 'qr' || format === 'qrcode') {
146
173
  return qr.encodeQR(value, options);
147
174
  }
175
+ if (format === 'datamatrix' || format === 'data-matrix') {
176
+ return datamatrix.encodeDataMatrix(value, options);
177
+ }
178
+ if (format === 'aztec' || format === 'aztec-code') {
179
+ return aztec.encodeAztec(value, options);
180
+ }
148
181
 
149
182
  const entry = ONED_FORMATS[format];
150
183
  if (!entry) {
151
- const known = [...Object.keys(ONED_FORMATS), 'qr'].join(', ');
184
+ const known = [...Object.keys(ONED_FORMATS), 'qr', 'datamatrix', 'aztec'].join(', ');
152
185
  throw new EncodeError(`Unknown format "${format}". Known formats: ${known}`);
153
186
  }
154
187
  return entry.encode(value, options);
@@ -161,6 +194,9 @@ export function encode(text, options = {}) {
161
194
  * @property {Uint8Array} [bytes] Raw payload, before text decoding.
162
195
  * @property {number} [version] QR version.
163
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.
164
200
  */
165
201
 
166
202
  /**
@@ -181,6 +217,8 @@ export function decode(image, options = {}) {
181
217
  const { formats = null, tryHarder = true, binarizer = 'auto' } = options;
182
218
  const want = formats ? new Set(formats.map((f) => f.toLowerCase())) : null;
183
219
  const wantQR = !want || want.has('qr') || want.has('qrcode');
220
+ const wantDataMatrix = !want || want.has('datamatrix') || want.has('data-matrix');
221
+ const wantAztec = !want || want.has('aztec') || want.has('aztec-code');
184
222
  const wantOneD = !want || [...want].some((f) => f in ONED_FORMATS);
185
223
 
186
224
  const source = LuminanceSource.fromImageData(image);
@@ -203,6 +241,36 @@ export function decode(image, options = {}) {
203
241
  }
204
242
  }
205
243
 
244
+ if (wantDataMatrix && dataMatrixCanDecode) {
245
+ // Hybrid thresholding can erase the interior of very large, perfectly
246
+ // uniform modules. In auto mode keep the local-threshold attempt, then
247
+ // retry Data Matrix once with the global threshold before giving up.
248
+ const dataMatrixBits = binarizer === 'auto' ? [bits, binarize(pass, 'global')] : [bits];
249
+ for (const candidateBits of dataMatrixBits) {
250
+ try {
251
+ const found = datamatrix.detectAndDecodeDataMatrix(candidateBits);
252
+ if (found) { results.push({ ...found, format: 'datamatrix' }); break; }
253
+ } catch {
254
+ /* no Data Matrix with this threshold */
255
+ }
256
+ }
257
+ }
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
+
206
274
  if (wantOneD) {
207
275
  const oneDFormats = want ? [...want].filter((f) => f in ONED_FORMATS) : null;
208
276
  for (const found of decodeOneD(bits, { formats: oneDFormats, tryHarder })) {
@@ -237,4 +305,4 @@ export function decodeStrict(image, options) {
237
305
  }
238
306
 
239
307
  /** Library version, matching package.json. */
240
- export const VERSION = '0.1.0';
308
+ export const VERSION = '1.1.0';