@sythos/js_barcode_universal 1.2.5 → 1.4.1

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.
Files changed (124) hide show
  1. package/AI_USAGE.md +33 -0
  2. package/LICENSE +11 -2
  3. package/NOTICE.md +10 -4
  4. package/README.md +601 -465
  5. package/bundle/sythos-barcode.esm.js +4669 -74
  6. package/bundle/sythos-barcode.js +4575 -72
  7. package/examples/create.html +1010 -730
  8. package/examples/read.html +1 -0
  9. package/licenses/README.md +40 -58
  10. package/licenses/aztec-code.license +15 -13
  11. package/licenses/aztec-rune.license +76 -0
  12. package/licenses/codabar.license +18 -15
  13. package/licenses/code-11.license +11 -9
  14. package/licenses/code-128.license +10 -8
  15. package/licenses/code-39.license +10 -8
  16. package/licenses/code-93.license +16 -14
  17. package/licenses/compact-pdf417.license +76 -0
  18. package/licenses/data-matrix.license +15 -13
  19. package/licenses/ean-13.license +10 -8
  20. package/licenses/ean-2.license +74 -0
  21. package/licenses/ean-5.license +74 -0
  22. package/licenses/ean-8.license +10 -8
  23. package/licenses/frameqr.license +84 -0
  24. package/licenses/gs1-128.license +10 -8
  25. package/licenses/gs1-databar.license +81 -0
  26. package/licenses/isbn.license +10 -8
  27. package/licenses/itf-14.license +10 -8
  28. package/licenses/itf.license +9 -7
  29. package/licenses/micro-qr.license +79 -0
  30. package/licenses/micropdf417.license +52 -67
  31. package/licenses/msi-plessey.license +13 -11
  32. package/licenses/pdf417.license +78 -37
  33. package/licenses/pharmacode.license +14 -12
  34. package/licenses/qr-code.license +9 -7
  35. package/licenses/rmqr.license +79 -0
  36. package/licenses/upc-a.license +12 -10
  37. package/licenses/upc-e.license +10 -8
  38. package/package.json +109 -88
  39. package/src/aztec/decoder.js +1 -0
  40. package/src/aztec/detector.js +1 -0
  41. package/src/aztec/encoder.js +1 -0
  42. package/src/aztec/high-level.js +1 -0
  43. package/src/aztec/index.js +1 -0
  44. package/src/aztec/tables.js +1 -0
  45. package/src/aztecrune/decoder.js +156 -0
  46. package/src/aztecrune/detector.js +167 -0
  47. package/src/aztecrune/encoder.js +122 -0
  48. package/src/aztecrune/index.js +50 -0
  49. package/src/aztecrune/tables.js +138 -0
  50. package/src/compactpdf417/decoder.js +129 -0
  51. package/src/compactpdf417/detector.js +140 -0
  52. package/src/compactpdf417/encoder.js +141 -0
  53. package/src/compactpdf417/index.js +22 -0
  54. package/src/compactpdf417/tables.js +174 -0
  55. package/src/core/bit-buffer.js +1 -0
  56. package/src/core/bit-matrix.js +1 -0
  57. package/src/core/errors.js +1 -0
  58. package/src/core/galois-field.js +1 -0
  59. package/src/core/index.js +1 -0
  60. package/src/core/reed-solomon.js +1 -0
  61. package/src/databar/codec.js +182 -0
  62. package/src/databar/decoder.js +115 -0
  63. package/src/databar/encoder.js +97 -0
  64. package/src/databar/gs1.js +178 -0
  65. package/src/databar/index.js +69 -0
  66. package/src/databar/patterns.js +144 -0
  67. package/src/databar/tables.js +125 -0
  68. package/src/datamatrix/decoder.js +263 -262
  69. package/src/datamatrix/detector.js +226 -225
  70. package/src/datamatrix/encoder.js +192 -191
  71. package/src/datamatrix/index.js +43 -42
  72. package/src/datamatrix/tables.js +124 -123
  73. package/src/frameqr/decoder.js +240 -0
  74. package/src/frameqr/detector.js +193 -0
  75. package/src/frameqr/encoder.js +157 -0
  76. package/src/frameqr/index.js +43 -0
  77. package/src/frameqr/tables.js +271 -0
  78. package/src/image/binarizer.js +1 -0
  79. package/src/image/grid-sampler.js +1 -0
  80. package/src/image/index.js +1 -0
  81. package/src/image/luminance.js +1 -0
  82. package/src/image/perspective.js +1 -0
  83. package/src/index.js +155 -2
  84. package/src/micropdf417/compaction.js +1 -0
  85. package/src/micropdf417/decoder.js +1 -0
  86. package/src/micropdf417/detector.js +1 -0
  87. package/src/micropdf417/encoder.js +1 -0
  88. package/src/micropdf417/error-correction.js +1 -0
  89. package/src/micropdf417/index.js +1 -0
  90. package/src/micropdf417/tables.js +1 -0
  91. package/src/microqr/decoder.js +246 -0
  92. package/src/microqr/detector.js +356 -0
  93. package/src/microqr/encoder.js +270 -0
  94. package/src/microqr/index.js +37 -0
  95. package/src/microqr/tables.js +317 -0
  96. package/src/oned/addons.js +421 -0
  97. package/src/oned/index.js +77 -59
  98. package/src/oned/patterns.js +1 -0
  99. package/src/oned/reader.js +1 -0
  100. package/src/oned/writers.js +1 -0
  101. package/src/pdf417/compaction.js +1 -0
  102. package/src/pdf417/decoder.js +1 -0
  103. package/src/pdf417/detector.js +1 -0
  104. package/src/pdf417/encoder.js +1 -0
  105. package/src/pdf417/error-correction.js +1 -0
  106. package/src/pdf417/index.js +5 -0
  107. package/src/pdf417/tables.js +1 -0
  108. package/src/qr/decoder.js +1 -0
  109. package/src/qr/detector.js +1 -0
  110. package/src/qr/encoder.js +1 -0
  111. package/src/qr/index.js +1 -0
  112. package/src/qr/tables.js +1 -0
  113. package/src/render/image-data.js +1 -0
  114. package/src/render/index.js +1 -0
  115. package/src/render/options.js +1 -0
  116. package/src/render/png.js +1 -0
  117. package/src/render/svg.js +1 -0
  118. package/src/render/webgl.js +1 -0
  119. package/src/render/webgpu.js +1 -0
  120. package/src/rmqr/decoder.js +102 -0
  121. package/src/rmqr/detector.js +91 -0
  122. package/src/rmqr/encoder.js +173 -0
  123. package/src/rmqr/index.js +38 -0
  124. package/src/rmqr/tables.js +155 -0
@@ -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-FileCopyrightText: 2026 Sythos (https://www.sythos.net)
27
+ * SPDX-License-Identifier: MIT
28
+ *
29
+ * Original work. No code from any other barcode implementation.
30
+ */
31
+
32
+ /**
33
+ * Micro QR Code structural facts and geometry.
34
+ *
35
+ * Only the irreducible public symbology values are tabulated. Grid capacity,
36
+ * reserved areas and placement order are derived independently and checked by
37
+ * {@link validateMicroQrTables}. M1 and M3 have a four-bit final data symbol
38
+ * character; `dataBits` therefore is authoritative and must not be inferred as
39
+ * `dataCodewords * 8` for those versions.
40
+ *
41
+ * @module microqr/tables
42
+ */
43
+
44
+ import { BitMatrix } from '../core/bit-matrix.js';
45
+
46
+ /** Numeric version identifiers used by the encoder selection loop. */
47
+ export const MICROQR_VERSIONS = Object.freeze([1, 2, 3, 4]);
48
+ export const MICROQR_VERSION_NAMES = Object.freeze(['M1', 'M2', 'M3', 'M4']);
49
+ export const MICROQR_ECC_LEVELS = Object.freeze(['DETECT', 'L', 'M', 'Q']);
50
+ export const MICROQR_FORMAT_MASK = 0x4445;
51
+ export const MICROQR_FORMAT_GENERATOR = 0x537;
52
+
53
+ const symbol = (version, ecc, symbolNumber, totalCodewords, dataCodewords, dataBits, eccCodewords) => Object.freeze({
54
+ version,
55
+ ecc,
56
+ symbolNumber,
57
+ size: 9 + Number(version.slice(1)) * 2,
58
+ totalCodewords,
59
+ dataCodewords,
60
+ dataBits,
61
+ eccCodewords,
62
+ blockCount: 1,
63
+ shortDataCodewordBits: dataBits % 8 || 8,
64
+ remainderBits: 0,
65
+ });
66
+
67
+ /** The eight legal Micro QR version/error-correction combinations. */
68
+ export const MICROQR_SYMBOLS = Object.freeze([
69
+ symbol('M1', 'DETECT', 0, 5, 3, 20, 2),
70
+ symbol('M2', 'L', 1, 10, 5, 40, 5),
71
+ symbol('M2', 'M', 2, 10, 4, 32, 6),
72
+ symbol('M3', 'L', 3, 17, 11, 84, 6),
73
+ symbol('M3', 'M', 4, 17, 9, 68, 8),
74
+ symbol('M4', 'L', 5, 24, 16, 128, 8),
75
+ symbol('M4', 'M', 6, 24, 14, 112, 10),
76
+ symbol('M4', 'Q', 7, 24, 10, 80, 14),
77
+ ]);
78
+
79
+ const symbolByKey = new Map(MICROQR_SYMBOLS.map((entry) => [`${entry.version}:${entry.ecc}`, entry]));
80
+ const symbolByNumber = new Map(MICROQR_SYMBOLS.map((entry) => [entry.symbolNumber, entry]));
81
+
82
+ function canonicalVersion(version) {
83
+ const result = typeof version === 'number' ? `M${version}` : String(version).toUpperCase();
84
+ if (!MICROQR_VERSION_NAMES.includes(result)) throw new RangeError(`Micro QR: version must be M1-M4, got ${version}`);
85
+ return result;
86
+ }
87
+
88
+ /** @param {string|number} version @returns {number} */
89
+ export function microQrVersionSize(version) {
90
+ return 9 + Number(canonicalVersion(version).slice(1)) * 2;
91
+ }
92
+
93
+ /** Resolve the format symbol number for a legal version/ECC pair. */
94
+ export function microQrSymbolNumber(version, ecc) {
95
+ return microQrBlockLayout(version, ecc).symbolNumber;
96
+ }
97
+
98
+ /** Resolve the immutable single-block layout for a legal version/ECC pair. */
99
+ export function microQrBlockLayout(version, ecc) {
100
+ const canonical = canonicalVersion(version);
101
+ const level = ecc == null && canonical === 'M1' ? 'DETECT' : String(ecc).toUpperCase();
102
+ const entry = symbolByKey.get(`${canonical}:${level}`);
103
+ if (!entry) throw new RangeError(`Micro QR: error correction level ${ecc} is not valid for ${canonical}`);
104
+ return entry;
105
+ }
106
+
107
+ /** @returns {number} Usable message bits, including mode/count overhead. */
108
+ export function microQrDataCapacityBits(version, ecc) {
109
+ return microQrBlockLayout(version, ecc).dataBits;
110
+ }
111
+
112
+ /**
113
+ * Encode the five format data bits with BCH(15,5), then apply the Micro QR
114
+ * format mask. `symbolNumber` occupies the high three data bits and `mask`
115
+ * the low two.
116
+ */
117
+ export function microQrFormatInfo(symbolNumber, mask, maybeMask) {
118
+ // Public convenience overload: (version, ecc, mask).
119
+ if (arguments.length === 3) {
120
+ symbolNumber = microQrSymbolNumber(symbolNumber, mask);
121
+ mask = maybeMask;
122
+ }
123
+ if (!Number.isInteger(symbolNumber) || symbolNumber < 0 || symbolNumber > 7) {
124
+ throw new RangeError('Micro QR: symbol number must be an integer in 0..7');
125
+ }
126
+ if (!Number.isInteger(mask) || mask < 0 || mask > 3) {
127
+ throw new RangeError('Micro QR: mask must be an integer in 0..3');
128
+ }
129
+ const data = (symbolNumber << 2) | mask;
130
+ let remainder = data << 10;
131
+ for (let bit = 14; bit >= 10; bit--) {
132
+ if ((remainder & (1 << bit)) !== 0) remainder ^= MICROQR_FORMAT_GENERATOR << (bit - 10);
133
+ }
134
+ return ((data << 10) | remainder) ^ MICROQR_FORMAT_MASK;
135
+ }
136
+
137
+ function hammingDistance(a, b) {
138
+ let bits = (a ^ b) & 0x7fff;
139
+ let count = 0;
140
+ while (bits) { bits &= bits - 1; count++; }
141
+ return count;
142
+ }
143
+
144
+ /** Decode/correct a 15-bit format word. Returns null beyond three errors. */
145
+ export function microQrDecodeFormatInfo(bits) {
146
+ if (!Number.isInteger(bits) || bits < 0 || bits > 0x7fff) {
147
+ throw new RangeError('Micro QR: format information must be a 15-bit integer');
148
+ }
149
+ let best = null;
150
+ let bestDistance = 16;
151
+ for (const entry of MICROQR_SYMBOLS) for (let mask = 0; mask < 4; mask++) {
152
+ const expected = microQrFormatInfo(entry.symbolNumber, mask);
153
+ const distance = hammingDistance(bits, expected);
154
+ if (distance < bestDistance) {
155
+ bestDistance = distance;
156
+ best = { version: entry.version, ecc: entry.ecc, symbolNumber: entry.symbolNumber, mask, correctedBits: distance, bits: expected };
157
+ }
158
+ }
159
+ return bestDistance <= 3 ? best : null;
160
+ }
161
+
162
+ /**
163
+ * Format modules in bit-number order, least significant bit first. Bits 0..7
164
+ * run down column 8; bits 8..14 continue right-to-left along row 8.
165
+ * Position (8,8) is shared by the two arms and appears once.
166
+ */
167
+ export function microQrFormatInfoPositions(sizeOrVersion) {
168
+ const size = typeof sizeOrVersion === 'number' && sizeOrVersion >= 11
169
+ ? sizeOrVersion
170
+ : microQrVersionSize(sizeOrVersion);
171
+ if (![11, 13, 15, 17].includes(size)) throw new RangeError(`Micro QR: invalid symbol size ${size}`);
172
+ const positions = [];
173
+ for (let y = 1; y <= 8; y++) positions.push([8, y]);
174
+ for (let x = 7; x >= 1; x--) positions.push([x, 8]);
175
+ return positions;
176
+ }
177
+
178
+ const reservedCache = new Map();
179
+ const functionCache = new Map();
180
+
181
+ /**
182
+ * Fixed dark function modules before format information is written. Light
183
+ * separator and light timing modules remain unset; use
184
+ * {@link microQrReservedModules} to distinguish them from payload modules.
185
+ */
186
+ export function microQrFunctionModules(version) {
187
+ const canonical = canonicalVersion(version);
188
+ const cached = functionCache.get(canonical);
189
+ if (cached) return cached;
190
+ const size = microQrVersionSize(canonical);
191
+ const matrix = new BitMatrix(size, size);
192
+ for (let y = 0; y < 7; y++) for (let x = 0; x < 7; x++) {
193
+ const outer = x === 0 || x === 6 || y === 0 || y === 6;
194
+ const centre = x >= 2 && x <= 4 && y >= 2 && y <= 4;
195
+ if (outer || centre) matrix.set(x, y);
196
+ }
197
+ for (let coordinate = 8; coordinate < size; coordinate += 2) {
198
+ matrix.set(coordinate, 0);
199
+ matrix.set(0, coordinate);
200
+ }
201
+ functionCache.set(canonical, matrix);
202
+ return matrix;
203
+ }
204
+
205
+ /** Shared immutable-in-use map of finder, separator, timing and format modules. */
206
+ export function microQrReservedModules(version) {
207
+ const canonical = canonicalVersion(version);
208
+ const cached = reservedCache.get(canonical);
209
+ if (cached) return cached;
210
+ const size = microQrVersionSize(canonical);
211
+ const matrix = new BitMatrix(size, size);
212
+ matrix.setRegion(0, 0, 8, 8); // 7x7 finder plus inner separator
213
+ for (let coordinate = 8; coordinate < size; coordinate++) {
214
+ matrix.set(coordinate, 0); // horizontal timing
215
+ matrix.set(0, coordinate); // vertical timing
216
+ }
217
+ for (const [x, y] of microQrFormatInfoPositions(size)) matrix.set(x, y);
218
+ reservedCache.set(canonical, matrix);
219
+ return matrix;
220
+ }
221
+
222
+ /** Number of modules carrying data or error-correction bits. */
223
+ export function microQrFreeModuleCount(version) {
224
+ const size = microQrVersionSize(version);
225
+ const reserved = microQrReservedModules(version);
226
+ let count = 0;
227
+ for (let y = 0; y < size; y++) for (let x = 0; x < size; x++) {
228
+ if (!reserved.get(x, y)) count++;
229
+ }
230
+ return count;
231
+ }
232
+
233
+ const orderCache = new Map();
234
+
235
+ /** Payload module coordinates as interleaved x,y pairs, MSB-first stream order. */
236
+ export function microQrDataModuleOrder(version) {
237
+ const canonical = canonicalVersion(version);
238
+ const cached = orderCache.get(canonical);
239
+ if (cached) return cached;
240
+ const size = microQrVersionSize(canonical);
241
+ const reserved = microQrReservedModules(canonical);
242
+ const order = new Int32Array(microQrFreeModuleCount(canonical) * 2);
243
+ let offset = 0;
244
+ let upward = true;
245
+ for (let column = size - 1; column > 0; column -= 2) {
246
+ for (let rowOffset = 0; rowOffset < size; rowOffset++) {
247
+ const y = upward ? size - 1 - rowOffset : rowOffset;
248
+ for (let side = 0; side < 2; side++) {
249
+ const x = column - side;
250
+ if (reserved.get(x, y)) continue;
251
+ order[offset++] = x;
252
+ order[offset++] = y;
253
+ }
254
+ }
255
+ upward = !upward;
256
+ }
257
+ orderCache.set(canonical, order);
258
+ return order;
259
+ }
260
+
261
+ /** The four Micro QR data-mask predicates. */
262
+ export function microQrMaskBit(mask, x, y) {
263
+ switch (mask) {
264
+ case 0: return (y & 1) === 0;
265
+ case 1: return (((y >> 1) + Math.floor(x / 3)) & 1) === 0;
266
+ case 2: return ((((y * x) & 1) + ((y * x) % 3)) & 1) === 0;
267
+ case 3: return ((((y + x) & 1) + ((y * x) % 3)) & 1) === 0;
268
+ default: throw new RangeError(`Micro QR: mask must be an integer in 0..3, got ${mask}`);
269
+ }
270
+ }
271
+
272
+ /** Return all internal table/geometry invariant failures. */
273
+ export function validateMicroQrTables() {
274
+ const issues = [];
275
+ if (MICROQR_SYMBOLS.length !== 8) issues.push('expected eight symbol/ECC combinations');
276
+ const numbers = new Set();
277
+ for (const entry of MICROQR_SYMBOLS) {
278
+ const tag = `${entry.version}-${entry.ecc}`;
279
+ if (numbers.has(entry.symbolNumber)) issues.push(`${tag}: duplicate symbol number`);
280
+ numbers.add(entry.symbolNumber);
281
+ if (entry.blockCount !== 1) issues.push(`${tag}: Micro QR must use one block`);
282
+ if (entry.dataBits + entry.eccCodewords * 8 !== microQrFreeModuleCount(entry.version)) {
283
+ issues.push(`${tag}: data/ECC bits do not fill the encoding region`);
284
+ }
285
+ if (entry.totalCodewords !== entry.dataCodewords + entry.eccCodewords) issues.push(`${tag}: codeword count mismatch`);
286
+ if (entry.dataBits !== (entry.dataCodewords - 1) * 8 + entry.shortDataCodewordBits) issues.push(`${tag}: final data codeword mismatch`);
287
+ if (entry.shortDataCodewordBits !== (entry.version === 'M1' || entry.version === 'M3' ? 4 : 8)) issues.push(`${tag}: wrong final data codeword width`);
288
+ }
289
+ for (const version of MICROQR_VERSIONS) {
290
+ const size = microQrVersionSize(version);
291
+ const positions = microQrFormatInfoPositions(size);
292
+ const unique = new Set(positions.map(([x, y]) => `${x},${y}`));
293
+ if (positions.length !== 15 || unique.size !== 15) issues.push(`${version}: format positions must be 15 unique modules`);
294
+ const order = microQrDataModuleOrder(version);
295
+ const orderUnique = new Set();
296
+ for (let i = 0; i < order.length; i += 2) {
297
+ const x = order[i], y = order[i + 1];
298
+ if (microQrReservedModules(version).get(x, y)) issues.push(`${version}: placement enters reserved module ${x},${y}`);
299
+ orderUnique.add(`${x},${y}`);
300
+ }
301
+ if (orderUnique.size * 2 !== order.length) issues.push(`${version}: placement repeats a module`);
302
+ if (order.length !== microQrFreeModuleCount(version) * 2) issues.push(`${version}: placement does not cover encoding region`);
303
+ const functions = microQrFunctionModules(version);
304
+ for (let y = 0; y < size; y++) for (let x = 0; x < size; x++) {
305
+ if (functions.get(x, y) && !microQrReservedModules(version).get(x, y)) {
306
+ issues.push(`${version}: dark function module ${x},${y} is not reserved`);
307
+ }
308
+ }
309
+ }
310
+ for (const entry of MICROQR_SYMBOLS) for (let mask = 0; mask < 4; mask++) {
311
+ const decoded = microQrDecodeFormatInfo(microQrFormatInfo(entry.symbolNumber, mask));
312
+ if (!decoded || decoded.symbolNumber !== entry.symbolNumber || decoded.mask !== mask || decoded.correctedBits !== 0) {
313
+ issues.push(`${entry.version}-${entry.ecc}: format round-trip failed for mask ${mask}`);
314
+ }
315
+ }
316
+ return issues;
317
+ }