@sythos/js_barcode_universal 1.5.8 → 1.5.10

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 (179) hide show
  1. package/LICENSE +5 -4
  2. package/NOTICE.md +2 -2
  3. package/README.md +61 -13
  4. package/bundle/sythos-barcode.esm.js +11658 -11244
  5. package/bundle/sythos-barcode.js +11658 -11244
  6. package/package.json +10 -3
  7. package/src/index.d.ts +1 -1
  8. package/src/index.js +508 -516
  9. package/src/js/aztec/decoder.js +208 -199
  10. package/src/js/aztec/detector.js +153 -126
  11. package/src/js/aztec/encoder.js +172 -153
  12. package/src/js/aztec/high-level.js +106 -101
  13. package/src/js/aztec/index.js +1 -12
  14. package/src/js/aztec/tables.js +106 -120
  15. package/src/js/aztecrune/decoder.js +76 -85
  16. package/src/js/aztecrune/detector.js +107 -93
  17. package/src/js/aztecrune/encoder.js +50 -68
  18. package/src/js/aztecrune/index.js +1 -16
  19. package/src/js/aztecrune/tables.js +59 -54
  20. package/src/js/compactpdf417/decoder.js +69 -74
  21. package/src/js/compactpdf417/detector.js +74 -75
  22. package/src/js/compactpdf417/encoder.js +68 -81
  23. package/src/js/compactpdf417/index.js +1 -13
  24. package/src/js/compactpdf417/tables.js +71 -80
  25. package/src/js/core/bit-buffer.js +106 -122
  26. package/src/js/core/bit-matrix.js +196 -194
  27. package/src/js/core/errors.js +13 -15
  28. package/src/js/core/galois-field.js +119 -133
  29. package/src/js/core/index.js +2 -19
  30. package/src/js/core/reed-solomon.js +195 -204
  31. package/src/js/databar/codec.js +93 -108
  32. package/src/js/databar/decoder.js +149 -140
  33. package/src/js/databar/encoder.js +47 -55
  34. package/src/js/databar/gs1.js +122 -123
  35. package/src/js/databar/index.js +4 -36
  36. package/src/js/databar/patterns.js +81 -85
  37. package/src/js/databar/tables.js +51 -52
  38. package/src/js/datamatrix/decoder.js +216 -182
  39. package/src/js/datamatrix/detector.js +161 -132
  40. package/src/js/datamatrix/encoder.js +192 -133
  41. package/src/js/datamatrix/index.js +1 -9
  42. package/src/js/datamatrix/tables.js +68 -69
  43. package/src/js/frameqr/decoder.js +102 -125
  44. package/src/js/frameqr/detector.js +101 -94
  45. package/src/js/frameqr/encoder.js +67 -80
  46. package/src/js/frameqr/index.js +1 -10
  47. package/src/js/frameqr/tables.js +145 -163
  48. package/src/js/image/binarizer.js +146 -162
  49. package/src/js/image/grid-sampler.js +63 -79
  50. package/src/js/image/index.js +0 -2
  51. package/src/js/image/luminance.js +129 -140
  52. package/src/js/image/perspective.js +112 -149
  53. package/src/js/micropdf417/compaction.js +64 -65
  54. package/src/js/micropdf417/decoder.js +106 -109
  55. package/src/js/micropdf417/detector.js +73 -70
  56. package/src/js/micropdf417/encoder.js +129 -154
  57. package/src/js/micropdf417/error-correction.js +6 -11
  58. package/src/js/micropdf417/index.js +2 -16
  59. package/src/js/micropdf417/tables.js +112 -99
  60. package/src/js/microqr/decoder.js +177 -182
  61. package/src/js/microqr/detector.js +240 -256
  62. package/src/js/microqr/encoder.js +239 -194
  63. package/src/js/microqr/index.js +0 -2
  64. package/src/js/microqr/tables.js +208 -200
  65. package/src/js/oned/addons.js +167 -191
  66. package/src/js/oned/index.js +27 -58
  67. package/src/js/oned/patterns.js +194 -213
  68. package/src/js/oned/reader.js +1003 -984
  69. package/src/js/oned/writers.js +411 -466
  70. package/src/js/pdf417/compaction.js +315 -222
  71. package/src/js/pdf417/decoder.js +71 -36
  72. package/src/js/pdf417/detector.js +377 -361
  73. package/src/js/pdf417/encoder.js +57 -46
  74. package/src/js/pdf417/error-correction.js +5 -8
  75. package/src/js/pdf417/tables.js +248 -250
  76. package/src/js/qr/decoder.js +311 -347
  77. package/src/js/qr/detector.js +378 -395
  78. package/src/js/qr/encoder.js +514 -599
  79. package/src/js/qr/index.js +0 -2
  80. package/src/js/qr/tables.js +375 -422
  81. package/src/js/render/image-data.js +57 -62
  82. package/src/js/render/index.js +21 -23
  83. package/src/js/render/options.js +82 -90
  84. package/src/js/render/png.js +144 -169
  85. package/src/js/render/svg.js +41 -44
  86. package/src/js/render/webgl.js +103 -109
  87. package/src/js/render/webgpu.js +205 -217
  88. package/src/js/rmqr/decoder.js +161 -53
  89. package/src/js/rmqr/detector.js +73 -41
  90. package/src/js/rmqr/encoder.js +253 -104
  91. package/src/js/rmqr/index.js +1 -5
  92. package/src/js/rmqr/tables.js +108 -85
  93. package/src/ts/aztec/decoder.ts +317 -0
  94. package/src/ts/aztec/detector.ts +224 -0
  95. package/src/ts/aztec/encoder.ts +257 -0
  96. package/src/ts/aztec/high-level.ts +211 -0
  97. package/src/ts/aztec/index.ts +45 -0
  98. package/src/ts/aztec/tables.ts +210 -0
  99. package/src/ts/aztecrune/decoder.ts +155 -0
  100. package/src/ts/aztecrune/detector.ts +166 -0
  101. package/src/ts/aztecrune/encoder.ts +121 -0
  102. package/src/ts/aztecrune/index.ts +49 -0
  103. package/src/ts/aztecrune/tables.ts +137 -0
  104. package/src/ts/compactpdf417/decoder.ts +128 -0
  105. package/src/ts/compactpdf417/detector.ts +139 -0
  106. package/src/ts/compactpdf417/encoder.ts +140 -0
  107. package/src/ts/compactpdf417/index.ts +17 -0
  108. package/src/ts/compactpdf417/tables.ts +173 -0
  109. package/src/ts/core/bit-buffer.ts +174 -0
  110. package/src/ts/core/bit-matrix.ts +241 -0
  111. package/src/ts/core/errors.ts +61 -0
  112. package/src/ts/core/galois-field.ts +207 -0
  113. package/src/ts/core/index.ts +56 -0
  114. package/src/ts/core/reed-solomon.ts +327 -0
  115. package/src/ts/databar/codec.ts +181 -0
  116. package/src/ts/databar/decoder.ts +215 -0
  117. package/src/ts/databar/encoder.ts +96 -0
  118. package/src/ts/databar/gs1.ts +177 -0
  119. package/src/ts/databar/index.ts +68 -0
  120. package/src/ts/databar/patterns.ts +143 -0
  121. package/src/ts/databar/tables.ts +124 -0
  122. package/src/ts/datamatrix/decoder.ts +262 -0
  123. package/src/ts/datamatrix/detector.ts +225 -0
  124. package/src/ts/datamatrix/encoder.ts +191 -0
  125. package/src/ts/datamatrix/index.ts +42 -0
  126. package/src/ts/datamatrix/tables.ts +123 -0
  127. package/src/ts/frameqr/decoder.ts +239 -0
  128. package/src/ts/frameqr/detector.ts +192 -0
  129. package/src/ts/frameqr/encoder.ts +156 -0
  130. package/src/ts/frameqr/index.ts +42 -0
  131. package/src/ts/frameqr/tables.ts +270 -0
  132. package/src/ts/image/binarizer.ts +270 -0
  133. package/src/ts/image/grid-sampler.ts +164 -0
  134. package/src/ts/image/index.ts +40 -0
  135. package/src/ts/image/luminance.ts +196 -0
  136. package/src/ts/image/perspective.ts +195 -0
  137. package/src/ts/index.d.ts +1 -1
  138. package/src/ts/index.ts +790 -0
  139. package/src/ts/micropdf417/compaction.ts +116 -0
  140. package/src/ts/micropdf417/decoder.ts +183 -0
  141. package/src/ts/micropdf417/detector.ts +149 -0
  142. package/src/ts/micropdf417/encoder.ts +209 -0
  143. package/src/ts/micropdf417/error-correction.ts +55 -0
  144. package/src/ts/micropdf417/index.ts +49 -0
  145. package/src/ts/micropdf417/tables.ts +184 -0
  146. package/src/ts/microqr/decoder.ts +245 -0
  147. package/src/ts/microqr/detector.ts +355 -0
  148. package/src/ts/microqr/encoder.ts +269 -0
  149. package/src/ts/microqr/index.ts +36 -0
  150. package/src/ts/microqr/tables.ts +316 -0
  151. package/src/ts/oned/addons.ts +420 -0
  152. package/src/ts/oned/index.ts +106 -0
  153. package/src/ts/oned/patterns.ts +384 -0
  154. package/src/ts/oned/reader.ts +1364 -0
  155. package/src/ts/oned/writers.ts +746 -0
  156. package/src/ts/pdf417/compaction.ts +298 -0
  157. package/src/ts/pdf417/decoder.ts +75 -0
  158. package/src/ts/pdf417/detector.ts +468 -0
  159. package/src/ts/pdf417/encoder.ts +91 -0
  160. package/src/ts/pdf417/error-correction.ts +47 -0
  161. package/src/ts/pdf417/index.ts +6 -0
  162. package/src/ts/pdf417/tables.ts +317 -0
  163. package/src/ts/qr/decoder.ts +575 -0
  164. package/src/ts/qr/detector.ts +630 -0
  165. package/src/ts/qr/encoder.ts +958 -0
  166. package/src/ts/qr/index.ts +44 -0
  167. package/src/ts/qr/tables.ts +737 -0
  168. package/src/ts/render/image-data.ts +125 -0
  169. package/src/ts/render/index.ts +130 -0
  170. package/src/ts/render/options.ts +160 -0
  171. package/src/ts/render/png.ts +295 -0
  172. package/src/ts/render/svg.ts +120 -0
  173. package/src/ts/render/webgl.ts +206 -0
  174. package/src/ts/render/webgpu.ts +369 -0
  175. package/src/ts/rmqr/decoder.ts +101 -0
  176. package/src/ts/rmqr/detector.ts +90 -0
  177. package/src/ts/rmqr/encoder.ts +172 -0
  178. package/src/ts/rmqr/index.ts +37 -0
  179. package/src/ts/rmqr/tables.ts +154 -0
@@ -0,0 +1,420 @@
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
+ * UPC/EAN two- and five-digit supplements.
33
+ *
34
+ * Supplements are small symbols printed to the right of an EAN/UPC symbol.
35
+ * They use the ordinary EAN odd/even digit patterns, a 1011 start guard and
36
+ * 01 separators between digits. EAN-2 selects its parity from the numeric
37
+ * value modulo four. EAN-5 derives a checksum from the five digits and uses
38
+ * that value to select one of ten parity rows.
39
+ *
40
+ * This module intentionally keeps supplements separate from the base EAN/UPC
41
+ * writers. `composeEANAddon` and the `*WithAddon` helpers are opt-in; existing
42
+ * EAN/UPC APIs continue to return their original one-row matrices unchanged.
43
+ *
44
+ * @module oned/addons
45
+ */
46
+
47
+ import { BitMatrix } from '../core/bit-matrix.js';
48
+ import { EncodeError, FormatError } from '../core/errors.js';
49
+ import {
50
+ EAN_L,
51
+ EAN_G,
52
+ } from './patterns.js';
53
+ import {
54
+ encodeEAN13,
55
+ encodeEAN8,
56
+ encodeUPCA,
57
+ encodeUPCE,
58
+ } from './writers.js';
59
+
60
+ /** EAN-2 parity rows, indexed by the two-digit value modulo four. */
61
+ export const EAN2_PARITY = Object.freeze([
62
+ 'AA', 'AB', 'BA', 'BB',
63
+ ]);
64
+
65
+ /** EAN-5 parity rows, indexed by the supplemental checksum. */
66
+ export const EAN5_PARITY = Object.freeze([
67
+ 'BBAAA', 'BABAA', 'BAABA', 'BAAAB', 'ABBAA',
68
+ 'AABBA', 'AAABB', 'ABABA', 'ABAAB', 'AABAB',
69
+ ]);
70
+
71
+ /** Start guard used by both UPC/EAN supplemental symbols. */
72
+ export const EAN_ADDON_START = '1011';
73
+
74
+ /** Separator inserted between adjacent supplemental digits. */
75
+ export const EAN_ADDON_SEPARATOR = '01';
76
+
77
+ export const EAN2_WIDTH = EAN_ADDON_START.length + 2 * 7 + EAN_ADDON_SEPARATOR.length;
78
+ export const EAN5_WIDTH = EAN_ADDON_START.length + 5 * 7 + EAN_ADDON_SEPARATOR.length * 4;
79
+
80
+ /**
81
+ * @param {unknown} value
82
+ * @param {number} length
83
+ * @param {string} format
84
+ * @returns {string}
85
+ */
86
+ function normalizeDigits(value, length, format) {
87
+ const digits = String(value);
88
+ if (!new RegExp(`^[0-9]{${length}}$`).test(digits)) {
89
+ throw new EncodeError(`${format}: payload must contain exactly ${length} digits`);
90
+ }
91
+ return digits;
92
+ }
93
+
94
+ /**
95
+ * EAN-5 supplemental checksum.
96
+ *
97
+ * The first, third and fifth digits carry weight three; the second and fourth
98
+ * carry weight nine. The checksum is not printed as a sixth digit: it selects
99
+ * the odd/even parity row for the five encoded digits.
100
+ *
101
+ * @param {string|number} value Five digits.
102
+ * @returns {number} Checksum value, 0-9.
103
+ */
104
+ export function ean5Checksum(value) {
105
+ const digits = normalizeDigits(value, 5, 'EAN-5');
106
+ const weighted =
107
+ 3 * (Number(digits[0]) + Number(digits[2]) + Number(digits[4])) +
108
+ 9 * (Number(digits[1]) + Number(digits[3]));
109
+ return weighted % 10;
110
+ }
111
+
112
+ /** Alias for callers that use the EAN family check-digit terminology. */
113
+ export const ean5CheckDigit = ean5Checksum;
114
+
115
+ /**
116
+ * Resolve the EAN-2 parity row for a payload.
117
+ *
118
+ * @param {string|number} value Two digits.
119
+ * @returns {string} Two-character A/B parity row.
120
+ */
121
+ export function ean2Parity(value) {
122
+ const digits = normalizeDigits(value, 2, 'EAN-2');
123
+ return EAN2_PARITY[Number(digits) % 4];
124
+ }
125
+
126
+ /**
127
+ * Resolve the EAN-5 parity row for a payload.
128
+ *
129
+ * @param {string|number} value Five digits.
130
+ * @returns {string} Five-character A/B parity row.
131
+ */
132
+ export function ean5Parity(value) {
133
+ return EAN5_PARITY[ean5Checksum(value)];
134
+ }
135
+
136
+ /**
137
+ * Expand an A/B parity row into EAN digit patterns.
138
+ *
139
+ * @param {string} digits
140
+ * @param {string} parity
141
+ * @returns {string}
142
+ */
143
+ function encodeDigitPatterns(digits, parity) {
144
+ let modules = EAN_ADDON_START;
145
+ for (let i = 0; i < digits.length; i++) {
146
+ if (i > 0) modules += EAN_ADDON_SEPARATOR;
147
+ const table = parity[i] === 'A' ? EAN_L : EAN_G;
148
+ modules += table[Number(digits[i])];
149
+ }
150
+ return modules;
151
+ }
152
+
153
+ /**
154
+ * Turn a module string into a one-row matrix and attach immutable source
155
+ * metadata for composition/rendering callers.
156
+ *
157
+ * @param {string} modules
158
+ * @param {{format: string, text: string, parity: string, checksum?: number}} metadata
159
+ * @returns {BitMatrix}
160
+ */
161
+ function toAddonMatrix(modules, metadata) {
162
+ const matrix = new BitMatrix(modules.length, 1);
163
+ for (let x = 0; x < modules.length; x++) {
164
+ if (modules[x] === '1') matrix.set(x, 0);
165
+ }
166
+ matrix.eanAddon = Object.freeze({ ...metadata });
167
+ return matrix;
168
+ }
169
+
170
+ /**
171
+ * Encode an EAN-2 supplement.
172
+ *
173
+ * @param {string|number} value Exactly two decimal digits.
174
+ * @returns {BitMatrix} A 20-module, one-row supplement.
175
+ */
176
+ export function encodeEAN2(value) {
177
+ const digits = normalizeDigits(value, 2, 'EAN-2');
178
+ const parity = ean2Parity(digits);
179
+ const matrix = toAddonMatrix(encodeDigitPatterns(digits, parity), {
180
+ format: 'ean2',
181
+ text: digits,
182
+ parity,
183
+ });
184
+ if (matrix.width !== EAN2_WIDTH) {
185
+ throw new EncodeError(`EAN-2: internal width is ${matrix.width}, expected ${EAN2_WIDTH}`);
186
+ }
187
+ return matrix;
188
+ }
189
+
190
+ /**
191
+ * Encode an EAN-5 supplement.
192
+ *
193
+ * @param {string|number} value Exactly five decimal digits.
194
+ * @returns {BitMatrix} A 47-module, one-row supplement.
195
+ */
196
+ export function encodeEAN5(value) {
197
+ const digits = normalizeDigits(value, 5, 'EAN-5');
198
+ const checksum = ean5Checksum(digits);
199
+ const parity = EAN5_PARITY[checksum];
200
+ const matrix = toAddonMatrix(encodeDigitPatterns(digits, parity), {
201
+ format: 'ean5',
202
+ text: digits,
203
+ parity,
204
+ checksum,
205
+ });
206
+ if (matrix.width !== EAN5_WIDTH) {
207
+ throw new EncodeError(`EAN-5: internal width is ${matrix.width}, expected ${EAN5_WIDTH}`);
208
+ }
209
+ return matrix;
210
+ }
211
+
212
+ /**
213
+ * Encode either supported supplement length.
214
+ *
215
+ * @param {string|number} value Two or five decimal digits.
216
+ * @returns {BitMatrix}
217
+ */
218
+ export function encodeEANAddon(value) {
219
+ const digits = String(value);
220
+ if (digits.length === 2) return encodeEAN2(digits);
221
+ if (digits.length === 5) return encodeEAN5(digits);
222
+ throw new EncodeError('EAN add-on: payload must contain exactly two or five digits');
223
+ }
224
+
225
+ /** Alias using the spelling used by some EAN documentation. */
226
+ export const encodeEANAddOn = encodeEANAddon;
227
+
228
+ /**
229
+ * @param {BitMatrix} matrix
230
+ * @param {number} offset
231
+ * @param {string} pattern
232
+ * @returns {boolean}
233
+ */
234
+ function matches(matrix, offset, pattern) {
235
+ if (offset < 0 || offset + pattern.length > matrix.width) return false;
236
+ for (let i = 0; i < pattern.length; i++) {
237
+ if ((matrix.get(offset + i, 0) ? '1' : '0') !== pattern[i]) return false;
238
+ }
239
+ return true;
240
+ }
241
+
242
+ /**
243
+ * @param {BitMatrix} matrix
244
+ * @param {number} start
245
+ * @param {2|5} digitCount
246
+ * @returns {{format:string,text:string,parity:string,checksum?:number,end:number}|null}
247
+ */
248
+ function decodeAt(matrix, start, digitCount) {
249
+ const width = digitCount === 2 ? EAN2_WIDTH : EAN5_WIDTH;
250
+ if (!matches(matrix, start, EAN_ADDON_START)) return null;
251
+ if (start > 0 && matrix.get(start - 1, 0)) return null;
252
+
253
+ let offset = start + EAN_ADDON_START.length;
254
+ let text = '';
255
+ let parity = '';
256
+
257
+ for (let i = 0; i < digitCount; i++) {
258
+ let found = null;
259
+ for (let digit = 0; digit < 10; digit++) {
260
+ for (const [letter, table] of [['A', EAN_L], ['B', EAN_G]]) {
261
+ if (matches(matrix, offset, table[digit]) &&
262
+ (!found || found.letter !== letter || found.digit !== digit)) {
263
+ if (found) return null;
264
+ found = { digit, letter };
265
+ }
266
+ }
267
+ }
268
+ if (!found) return null;
269
+ text += String(found.digit);
270
+ parity += found.letter;
271
+ offset += 7;
272
+ if (i < digitCount - 1) {
273
+ if (!matches(matrix, offset, EAN_ADDON_SEPARATOR)) return null;
274
+ offset += EAN_ADDON_SEPARATOR.length;
275
+ }
276
+ }
277
+
278
+ if (offset - start !== width) return null;
279
+ if (digitCount === 2) {
280
+ if (EAN2_PARITY[Number(text) % 4] !== parity) return null;
281
+ return { format: 'ean2', text, parity, end: offset };
282
+ }
283
+
284
+ const checksum = ean5Checksum(text);
285
+ if (EAN5_PARITY[checksum] !== parity) return null;
286
+ return { format: 'ean5', text, parity, checksum, end: offset };
287
+ }
288
+
289
+ /**
290
+ * Validate and decode an EAN-2 supplement. A leading quiet zone is accepted;
291
+ * the decoder searches the row for a valid start guard so a composed base
292
+ * EAN/UPC matrix can be passed directly as well.
293
+ *
294
+ * @param {BitMatrix} matrix One-row, module-aligned supplement or composition.
295
+ * @returns {{format:'ean2',text:string,parity:string}}
296
+ * @throws {FormatError} When no valid supplement is found.
297
+ */
298
+ export function decodeEAN2(matrix) {
299
+ if (!matrix || !Number.isInteger(matrix.width) || matrix.height !== 1) {
300
+ throw new FormatError('EAN-2: expected a one-row module matrix');
301
+ }
302
+ for (let start = 0; start + EAN2_WIDTH <= matrix.width; start++) {
303
+ const result = decodeAt(matrix, start, 2);
304
+ if (result) {
305
+ const { end, ...publicResult } = result;
306
+ void end;
307
+ return publicResult;
308
+ }
309
+ }
310
+ throw new FormatError('EAN-2: start, parity or digit pattern is invalid');
311
+ }
312
+
313
+ /**
314
+ * Validate and decode an EAN-5 supplement.
315
+ *
316
+ * @param {BitMatrix} matrix One-row, module-aligned supplement or composition.
317
+ * @returns {{format:'ean5',text:string,parity:string,checksum:number}}
318
+ * @throws {FormatError} When no valid supplement is found.
319
+ */
320
+ export function decodeEAN5(matrix) {
321
+ if (!matrix || !Number.isInteger(matrix.width) || matrix.height !== 1) {
322
+ throw new FormatError('EAN-5: expected a one-row module matrix');
323
+ }
324
+ for (let start = 0; start + EAN5_WIDTH <= matrix.width; start++) {
325
+ const result = decodeAt(matrix, start, 5);
326
+ if (result) {
327
+ const { end, ...publicResult } = result;
328
+ void end;
329
+ return publicResult;
330
+ }
331
+ }
332
+ throw new FormatError('EAN-5: start, parity, checksum or digit pattern is invalid');
333
+ }
334
+
335
+ /**
336
+ * Decode either supported supplement length.
337
+ *
338
+ * @param {BitMatrix} matrix One-row, module-aligned supplement or composition.
339
+ * @returns {{format:'ean2'|'ean5',text:string,parity:string,checksum?:number}}
340
+ * @throws {FormatError} When neither supplement grammar matches.
341
+ */
342
+ export function decodeEANAddon(matrix) {
343
+ try {
344
+ return decodeEAN5(matrix);
345
+ } catch (fiveError) {
346
+ try {
347
+ return decodeEAN2(matrix);
348
+ } catch (twoError) {
349
+ throw new FormatError(`EAN add-on: ${fiveError.message}; ${twoError.message}`);
350
+ }
351
+ }
352
+ }
353
+
354
+ /** Alias using the spelling used by some EAN documentation. */
355
+ export const decodeEANAddOn = decodeEANAddon;
356
+
357
+ /**
358
+ * Join a base EAN/UPC matrix and a supplement with the standard quiet gap.
359
+ *
360
+ * The helper does not alter either input. By default it inserts nine light
361
+ * modules, the conventional separation between the base symbol and an add-on.
362
+ * The result carries the supplement metadata under `eanAddon` for renderers or
363
+ * callers that need to label the composed symbol.
364
+ *
365
+ * @param {BitMatrix} base Base EAN/UPC matrix.
366
+ * @param {BitMatrix|string|number} addon Add-on matrix or two/five digits.
367
+ * @param {{gap?:number}} [options]
368
+ * @returns {BitMatrix}
369
+ */
370
+ export function composeEANAddon(base, addon, options = {}) {
371
+ if (!base || !Number.isInteger(base.width) || base.height !== 1) {
372
+ throw new TypeError('EAN add-on: base must be a one-row module matrix');
373
+ }
374
+ const addonMatrix = typeof addon === 'string' || typeof addon === 'number'
375
+ ? encodeEANAddon(addon)
376
+ : addon;
377
+ if (!addonMatrix || !Number.isInteger(addonMatrix.width) || addonMatrix.height !== 1) {
378
+ throw new TypeError('EAN add-on: supplement must be a one-row module matrix or digits');
379
+ }
380
+
381
+ const gap = options.gap === undefined ? 9 : Number(options.gap);
382
+ if (!Number.isInteger(gap) || gap < 1) {
383
+ throw new EncodeError(`EAN add-on: gap must be a positive integer, got ${options.gap}`);
384
+ }
385
+
386
+ const result = new BitMatrix(base.width + gap + addonMatrix.width, 1);
387
+ for (let x = 0; x < base.width; x++) if (base.get(x, 0)) result.set(x, 0);
388
+ for (let x = 0; x < addonMatrix.width; x++) {
389
+ if (addonMatrix.get(x, 0)) result.set(base.width + gap + x, 0);
390
+ }
391
+ if (addonMatrix.eanAddon) result.eanAddon = { ...addonMatrix.eanAddon, gap };
392
+ return result;
393
+ }
394
+
395
+ /**
396
+ * Compose an EAN-13 symbol with an EAN-2 or EAN-5 supplement.
397
+ *
398
+ * @param {string} value Base EAN-13 payload.
399
+ * @param {string|number} addon Two or five supplemental digits.
400
+ * @param {{gap?:number}} [options]
401
+ * @returns {BitMatrix}
402
+ */
403
+ export function encodeEAN13WithAddon(value, addon, options = {}) {
404
+ return composeEANAddon(encodeEAN13(value), addon, options);
405
+ }
406
+
407
+ /** @see encodeEAN13WithAddon */
408
+ export function encodeEAN8WithAddon(value, addon, options = {}) {
409
+ return composeEANAddon(encodeEAN8(value), addon, options);
410
+ }
411
+
412
+ /** @see encodeEAN13WithAddon */
413
+ export function encodeUPCAWithAddon(value, addon, options = {}) {
414
+ return composeEANAddon(encodeUPCA(value), addon, options);
415
+ }
416
+
417
+ /** @see encodeEAN13WithAddon */
418
+ export function encodeUPCEWithAddon(value, addon, options = {}) {
419
+ return composeEANAddon(encodeUPCE(value), addon, options);
420
+ }
@@ -0,0 +1,106 @@
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
+ * Linear symbologies.
33
+ *
34
+ * @module oned
35
+ */
36
+
37
+ export {
38
+ encodeEAN13, encodeEAN8, encodeUPCA, encodeUPCE, encodeISBN,
39
+ encodeCode39, encodeCode93, encodeCode128,
40
+ encodeITF, encodeITF14, encodeCodabar, encodeCode11,
41
+ encodeMSI, encodePharmacode,
42
+ ean13CheckDigit,
43
+ } from './writers.js';
44
+
45
+ export {
46
+ EAN2_PARITY, EAN5_PARITY, EAN2_WIDTH, EAN5_WIDTH,
47
+ EAN_ADDON_START, EAN_ADDON_SEPARATOR,
48
+ ean2Parity, ean5Checksum, ean5CheckDigit, ean5Parity,
49
+ encodeEAN2, encodeEAN5, encodeEANAddon, encodeEANAddOn,
50
+ decodeEAN2, decodeEAN5, decodeEANAddon, decodeEANAddOn,
51
+ composeEANAddon,
52
+ encodeEAN13WithAddon, encodeEAN8WithAddon,
53
+ encodeUPCAWithAddon, encodeUPCEWithAddon,
54
+ } from './addons.js';
55
+
56
+ export {
57
+ decodeOneD, decodeOneDStrict,
58
+ decodeCode11, decodeMSI,
59
+ patternVariance, recordPattern, toNarrowWidePattern,
60
+ } from './reader.js';
61
+
62
+ export { validateTables } from './patterns.js';
63
+
64
+ import {
65
+ encodeEAN13, encodeEAN8, encodeUPCA, encodeUPCE, encodeISBN,
66
+ encodeCode39, encodeCode93, encodeCode128,
67
+ encodeITF, encodeITF14, encodeCodabar, encodeCode11,
68
+ encodeMSI, encodePharmacode,
69
+ } from './writers.js';
70
+ import { encodeEAN2, encodeEAN5 } from './addons.js';
71
+
72
+ /**
73
+ * Writers by format id, for the top-level `encode()` dispatcher.
74
+ *
75
+ * `readable` marks the formats this suite can also decode. Writing is a table
76
+ * lookup and easy to support broadly; reading needs a detector per symbology,
77
+ * so the two lists legitimately differ and the API says so rather than
78
+ * failing at runtime.
79
+ *
80
+ * @type {Record<string, {encode: Function, readable: boolean, label: string, role?: string}>}
81
+ */
82
+ export const ONED_FORMATS = {
83
+ ean13: { encode: encodeEAN13, readable: true, label: 'EAN-13' },
84
+ ean8: { encode: encodeEAN8, readable: true, label: 'EAN-8' },
85
+ upca: { encode: encodeUPCA, readable: true, label: 'UPC-A' },
86
+ isbn: { encode: encodeISBN, readable: true, label: 'ISBN (Bookland EAN-13)' },
87
+ upce: { encode: encodeUPCE, readable: true, label: 'UPC-E' },
88
+ code128: { encode: encodeCode128, readable: true, label: 'Code 128' },
89
+ gs1128: {
90
+ encode: (v, o) => encodeCode128(v, { ...o, gs1: true }),
91
+ readable: true,
92
+ label: 'GS1-128',
93
+ },
94
+ code39: { encode: encodeCode39, readable: true, label: 'Code 39' },
95
+ code93: { encode: encodeCode93, readable: true, label: 'Code 93' },
96
+ itf: { encode: encodeITF, readable: true, label: 'ITF (Interleaved 2 of 5)' },
97
+ itf14: { encode: encodeITF14, readable: true, label: 'ITF-14' },
98
+ codabar: { encode: encodeCodabar, readable: true, label: 'Codabar' },
99
+ code11: { encode: encodeCode11, readable: true, label: 'Code 11' },
100
+ msi: { encode: encodeMSI, readable: true, label: 'MSI Plessey' },
101
+ pharmacode: { encode: encodePharmacode, readable: false, label: 'Pharmacode' },
102
+ // Supplements are reported as readable capabilities, but the image reader
103
+ // only accepts them when attached to a validated EAN/UPC parent symbol.
104
+ ean2: { encode: encodeEAN2, readable: true, role: 'supplement', label: 'EAN-2 supplement' },
105
+ ean5: { encode: encodeEAN5, readable: true, role: 'supplement', label: 'EAN-5 supplement' },
106
+ };