@sythos/js_barcode_universal 0.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.
Files changed (53) hide show
  1. package/LICENSE +215 -0
  2. package/NOTICE.md +106 -0
  3. package/README.md +433 -0
  4. package/bundle/sythos-barcode.esm.js +7998 -0
  5. package/bundle/sythos-barcode.js +7948 -0
  6. package/examples/create.html +731 -0
  7. package/examples/read.html +341 -0
  8. package/licenses/README.md +42 -0
  9. package/licenses/codabar.license +74 -0
  10. package/licenses/code-11.license +69 -0
  11. package/licenses/code-128.license +69 -0
  12. package/licenses/code-39.license +70 -0
  13. package/licenses/code-93.license +71 -0
  14. package/licenses/ean-13.license +70 -0
  15. package/licenses/ean-8.license +70 -0
  16. package/licenses/gs1-128.license +71 -0
  17. package/licenses/isbn.license +76 -0
  18. package/licenses/itf-14.license +69 -0
  19. package/licenses/itf.license +70 -0
  20. package/licenses/msi-plessey.license +72 -0
  21. package/licenses/pharmacode.license +71 -0
  22. package/licenses/qr-code.license +75 -0
  23. package/licenses/upc-a.license +72 -0
  24. package/licenses/upc-e.license +69 -0
  25. package/package.json +89 -0
  26. package/src/core/bit-buffer.js +174 -0
  27. package/src/core/bit-matrix.js +241 -0
  28. package/src/core/errors.js +61 -0
  29. package/src/core/galois-field.js +204 -0
  30. package/src/core/index.js +56 -0
  31. package/src/core/reed-solomon.js +313 -0
  32. package/src/image/binarizer.js +270 -0
  33. package/src/image/grid-sampler.js +164 -0
  34. package/src/image/index.js +40 -0
  35. package/src/image/luminance.js +196 -0
  36. package/src/image/perspective.js +195 -0
  37. package/src/index.js +240 -0
  38. package/src/oned/index.js +89 -0
  39. package/src/oned/patterns.js +384 -0
  40. package/src/oned/reader.js +918 -0
  41. package/src/oned/writers.js +741 -0
  42. package/src/qr/decoder.js +575 -0
  43. package/src/qr/detector.js +630 -0
  44. package/src/qr/encoder.js +958 -0
  45. package/src/qr/index.js +44 -0
  46. package/src/qr/tables.js +737 -0
  47. package/src/render/image-data.js +125 -0
  48. package/src/render/index.js +130 -0
  49. package/src/render/options.js +160 -0
  50. package/src/render/png.js +295 -0
  51. package/src/render/svg.js +120 -0
  52. package/src/render/webgl.js +206 -0
  53. package/src/render/webgpu.js +369 -0
@@ -0,0 +1,384 @@
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
+ * Pattern tables for the linear symbologies.
33
+ *
34
+ * Every table here is a published fact about a symbology, expressed in this
35
+ * project's own notation and validated by structural invariants rather than
36
+ * trusted. `validateTables()` at the bottom asserts the properties each
37
+ * symbology guarantees — module counts, wide-element counts, uniqueness — and
38
+ * the test suite runs it. A transcription slip that breaks an invariant fails
39
+ * the build; the invariants are chosen so that most single-character slips do.
40
+ *
41
+ * Two notations appear:
42
+ *
43
+ * width strings "212222" — element widths in modules, bar first, then
44
+ * alternating space/bar. Used by Code 128, 93,
45
+ * Codabar, ITF, Code 39, Code 11.
46
+ * module strings "0001101" — one character per module, 1 = dark. Used by
47
+ * EAN/UPC, where every element is a whole number
48
+ * of modules out of a fixed 7.
49
+ *
50
+ * @module oned/patterns
51
+ */
52
+
53
+ /* ------------------------------------------------------------------ *
54
+ * EAN / UPC
55
+ * ------------------------------------------------------------------ */
56
+
57
+ /** Odd-parity ("L") digit patterns, 7 modules each. */
58
+ export const EAN_L = [
59
+ '0001101', '0011001', '0010011', '0111101', '0100011',
60
+ '0110001', '0101111', '0111011', '0110111', '0001011',
61
+ ];
62
+
63
+ /** Even-parity ("G") patterns: the R pattern reversed. */
64
+ export const EAN_G = EAN_L.map((p) => [...p].reverse().map((b) => (b === '1' ? '0' : '1')).join(''));
65
+
66
+ /** Right-hand ("R") patterns: the L pattern complemented. */
67
+ export const EAN_R = EAN_L.map((p) => [...p].map((b) => (b === '1' ? '0' : '1')).join(''));
68
+
69
+ /**
70
+ * Which parity set each of the six left-hand EAN-13 digits uses, indexed by
71
+ * the first digit. This is how the thirteenth digit is carried without a
72
+ * thirteenth symbol position.
73
+ */
74
+ export const EAN13_PARITY = [
75
+ 'LLLLLL', 'LLGLGG', 'LLGGLG', 'LLGGGL', 'LGLLGG',
76
+ 'LGGLLG', 'LGGGLL', 'LGLGLG', 'LGLGGL', 'LGGLGL',
77
+ ];
78
+
79
+ /** UPC-E parity patterns, indexed by check digit. Used when the number system is 0. */
80
+ export const UPCE_PARITY = [
81
+ 'EEEOOO', 'EEOEOO', 'EEOOEO', 'EEOOOE', 'EOEEOO',
82
+ 'EOOEEO', 'EOOOEE', 'EOEOEO', 'EOEOOE', 'EOOEOE',
83
+ ];
84
+
85
+ export const EAN_START_END = '101';
86
+ export const EAN_MIDDLE = '01010';
87
+ export const UPCE_END = '010101';
88
+
89
+ /* ------------------------------------------------------------------ *
90
+ * Code 39
91
+ * ------------------------------------------------------------------ */
92
+
93
+ /**
94
+ * Code 39 is "three of nine": nine elements per character, of which exactly
95
+ * three are wide. `n` and `w` below are narrow and wide; elements alternate
96
+ * bar, space, bar, ... starting and ending with a bar.
97
+ */
98
+ export const CODE39 = {
99
+ '0': 'nnnwwnwnn', '1': 'wnnwnnnnw', '2': 'nnwwnnnnw', '3': 'wnwwnnnnn',
100
+ '4': 'nnnwwnnnw', '5': 'wnnwwnnnn', '6': 'nnwwwnnnn', '7': 'nnnwnnwnw',
101
+ '8': 'wnnwnnwnn', '9': 'nnwwnnwnn', 'A': 'wnnnnwnnw', 'B': 'nnwnnwnnw',
102
+ 'C': 'wnwnnwnnn', 'D': 'nnnnwwnnw', 'E': 'wnnnwwnnn', 'F': 'nnwnwwnnn',
103
+ 'G': 'nnnnnwwnw', 'H': 'wnnnnwwnn', 'I': 'nnwnnwwnn', 'J': 'nnnnwwwnn',
104
+ 'K': 'wnnnnnnww', 'L': 'nnwnnnnww', 'M': 'wnwnnnnwn', 'N': 'nnnnwnnww',
105
+ 'O': 'wnnnwnnwn', 'P': 'nnwnwnnwn', 'Q': 'nnnnnnwww', 'R': 'wnnnnnwwn',
106
+ 'S': 'nnwnnnwwn', 'T': 'nnnnwnwwn', 'U': 'wwnnnnnnw', 'V': 'nwwnnnnnw',
107
+ 'W': 'wwwnnnnnn', 'X': 'nwnnwnnnw', 'Y': 'wwnnwnnnn', 'Z': 'nwwnwnnnn',
108
+ '-': 'nwnnnnwnw', '.': 'wwnnnnwnn', ' ': 'nwwnnnwnn', '$': 'nwnwnwnnn',
109
+ '/': 'nwnwnnnwn', '+': 'nwnnnwnwn', '%': 'nnnwnwnwn', '*': 'nwnnwnwnn',
110
+ };
111
+
112
+ /** Character set for the optional modulo-43 check digit; '*' is excluded. */
113
+ export const CODE39_CHECK_SET = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%';
114
+
115
+ /** Two-character escapes giving Code 39 the full ASCII range. */
116
+ export const CODE39_EXTENDED = (() => {
117
+ const map = new Array(128);
118
+ const upper = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
119
+ for (let i = 0; i < 26; i++) {
120
+ map[i + 1] = '$' + upper[i]; // SOH..SUB
121
+ map[i + 65] = upper[i]; // A-Z
122
+ map[i + 97] = '+' + upper[i]; // a-z
123
+ }
124
+ for (let i = 0; i < 10; i++) map[i + 48] = String(i);
125
+ map[0] = '%U';
126
+ for (let i = 27; i <= 31; i++) map[i] = '%' + upper[i - 27 + 0]; // ESC..US -> %A..%E
127
+ const symbols = {
128
+ 32: ' ', 33: '/A', 34: '/B', 35: '/C', 36: '/D', 37: '/E', 38: '/F',
129
+ 39: '/G', 40: '/H', 41: '/I', 42: '/J', 43: '/K', 44: '/L', 45: '-',
130
+ 46: '.', 47: '/O', 58: '/Z', 59: '%F', 60: '%G', 61: '%H', 62: '%I',
131
+ 63: '%J', 64: '%V', 91: '%K', 92: '%L', 93: '%M', 94: '%N', 95: '%O',
132
+ 96: '%W', 123: '%P', 124: '%Q', 125: '%R', 126: '%S', 127: '%T',
133
+ };
134
+ for (const [code, seq] of Object.entries(symbols)) map[Number(code)] = seq;
135
+ return map;
136
+ })();
137
+
138
+ /* ------------------------------------------------------------------ *
139
+ * Code 93
140
+ * ------------------------------------------------------------------ */
141
+
142
+ /** Nine modules per character across six elements. */
143
+ export const CODE93 = {
144
+ '0': '131112', '1': '111213', '2': '111312', '3': '111411', '4': '121113',
145
+ '5': '121212', '6': '121311', '7': '111114', '8': '131211', '9': '141111',
146
+ 'A': '211113', 'B': '211212', 'C': '211311', 'D': '221112', 'E': '221211',
147
+ 'F': '231111', 'G': '112113', 'H': '112212', 'I': '112311', 'J': '122112',
148
+ 'K': '132111', 'L': '111123', 'M': '111222', 'N': '111321', 'O': '121122',
149
+ 'P': '131121', 'Q': '212112', 'R': '212211', 'S': '211122', 'T': '211221',
150
+ 'U': '221121', 'V': '222111', 'W': '112122', 'X': '112221', 'Y': '122121',
151
+ 'Z': '123111', '-': '121131', '.': '311112', ' ': '311211', '$': '321111',
152
+ '/': '112131', '+': '113121', '%': '211131',
153
+ // The four shift characters. Their conventional names -- ($) (%) (/) (+) --
154
+ // collide with the literal single-character entries above, so they take
155
+ // distinct multi-character keys. Writing the bare symbols here would create
156
+ // duplicate object keys, which JavaScript collapses silently: the table
157
+ // would end up three entries short, with no error raised anywhere.
158
+ 'S$': '121221',
159
+ 'S%': '312111',
160
+ 'S/': '311121',
161
+ 'S+': '122211',
162
+ };
163
+
164
+ /**
165
+ * Symbol values 0..46 in order, as keys into {@link CODE93}. An array rather
166
+ * than a string, because the four shift characters have multi-character keys.
167
+ */
168
+ export const CODE93_VALUES = [
169
+ ...'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%',
170
+ 'S$', 'S%', 'S/', 'S+',
171
+ ];
172
+
173
+ export const CODE93_START_STOP = '111141';
174
+
175
+ /* ------------------------------------------------------------------ *
176
+ * Code 128
177
+ * ------------------------------------------------------------------ */
178
+
179
+ /**
180
+ * All 107 symbol patterns. Eleven modules each across six elements; the stop
181
+ * pattern is the sole exception at thirteen modules across seven.
182
+ *
183
+ * The eleven-module invariant is checked below and catches the overwhelming
184
+ * majority of transcription slips, since almost any single-digit change to a
185
+ * pattern breaks the sum.
186
+ */
187
+ export const CODE128 = [
188
+ '212222', '222122', '222221', '121223', '121322', '131222', '122213',
189
+ '122312', '132212', '221213', '221312', '231212', '112232', '122132',
190
+ '122231', '113222', '123122', '123221', '223211', '221132', '221231',
191
+ '213212', '223112', '312131', '311222', '321122', '321221', '312212',
192
+ '322112', '322211', '212123', '212321', '232121', '111323', '131123',
193
+ '131321', '112313', '132113', '132311', '211313', '231113', '231311',
194
+ '112133', '112331', '132131', '113123', '113321', '133121', '313121',
195
+ '211331', '231131', '213113', '213311', '213131', '311123', '311321',
196
+ '331121', '312113', '312311', '332111', '314111', '221411', '431111',
197
+ '111224', '111422', '121124', '121421', '141122', '141221', '112214',
198
+ '112412', '122114', '122411', '142112', '142211', '241211', '221114',
199
+ '413111', '241112', '134111', '111242', '121142', '121241', '114212',
200
+ '124112', '124211', '411212', '421112', '421211', '212141', '214121',
201
+ '412121', '111143', '111341', '131141', '114113', '114311', '411113',
202
+ '411311', '113141', '114131', '311141', '411131', '211412', '211214',
203
+ '211232', '2331112',
204
+ ];
205
+
206
+ export const CODE128_START_A = 103;
207
+ export const CODE128_START_B = 104;
208
+ export const CODE128_START_C = 105;
209
+ export const CODE128_STOP = 106;
210
+ export const CODE128_FNC1 = 102;
211
+ export const CODE128_FNC2 = 97;
212
+ export const CODE128_FNC3 = 96;
213
+ export const CODE128_FNC4_A = 101;
214
+ export const CODE128_FNC4_B = 100;
215
+ export const CODE128_SHIFT = 98;
216
+ export const CODE128_CODE_A = 101;
217
+ export const CODE128_CODE_B = 100;
218
+ export const CODE128_CODE_C = 99;
219
+
220
+ /* ------------------------------------------------------------------ *
221
+ * Interleaved 2 of 5
222
+ * ------------------------------------------------------------------ */
223
+
224
+ /** Five elements per digit, exactly two of them wide. */
225
+ export const ITF = [
226
+ 'nnwwn', 'wnnnw', 'nwnnw', 'wwnnn', 'nnwnw',
227
+ 'wnwnn', 'nwwnn', 'nnnww', 'wnnwn', 'nwnwn',
228
+ ];
229
+
230
+ /* ------------------------------------------------------------------ *
231
+ * Codabar
232
+ * ------------------------------------------------------------------ */
233
+
234
+ /** Seven elements per character. */
235
+ export const CODABAR = {
236
+ '0': 'nnnnnww', '1': 'nnnnwwn', '2': 'nnnwnnw', '3': 'wwnnnnn',
237
+ '4': 'nnwnnwn', '5': 'wnnnnwn', '6': 'nwnnnnw', '7': 'nwnnwnn',
238
+ '8': 'nwwnnnn', '9': 'wnnwnnn', '-': 'nnnwwnn', '$': 'nnwwnnn',
239
+ ':': 'wnnnwnw', '/': 'wnwnnnw', '.': 'wnwnwnn', '+': 'nnwnwnw',
240
+ 'A': 'nnwwnwn', 'B': 'nwnwnnw', 'C': 'nnnwnww', 'D': 'nnnwwwn',
241
+ };
242
+
243
+ export const CODABAR_START_STOP = 'ABCD';
244
+
245
+ /* ------------------------------------------------------------------ *
246
+ * Code 11
247
+ * ------------------------------------------------------------------ */
248
+
249
+ /** Five elements per character, one or two of them wide. */
250
+ export const CODE11 = {
251
+ '0': 'nnnnw', '1': 'wnnnw', '2': 'nwnnw', '3': 'wwnnn', '4': 'nnwnw',
252
+ '5': 'wnwnn', '6': 'nwwnn', '7': 'nnnww', '8': 'wnnwn', '9': 'wnnnn',
253
+ '-': 'nnwnn',
254
+ };
255
+
256
+ export const CODE11_START_STOP = 'nnwwn';
257
+
258
+ /* ------------------------------------------------------------------ *
259
+ * MSI / Plessey
260
+ * ------------------------------------------------------------------ */
261
+
262
+ /** Each bit of a digit becomes a bar pair: 1 is wide-then-narrow, 0 the reverse. */
263
+ export const MSI_BIT = { 0: '100', 1: '110' };
264
+ export const MSI_START = '110';
265
+ export const MSI_STOP = '1001';
266
+
267
+ /* ------------------------------------------------------------------ *
268
+ * Structural validation
269
+ * ------------------------------------------------------------------ */
270
+
271
+ /**
272
+ * Assert every invariant these tables are supposed to satisfy.
273
+ *
274
+ * This is the first of the correctness mechanisms described in NOTICE.md:
275
+ * the tables are redundant with the symbology rules, so the rules can check
276
+ * the tables. Called from the test suite; cheap enough to call anywhere.
277
+ *
278
+ * @returns {string[]} Problems found; empty means all invariants hold.
279
+ */
280
+ export function validateTables() {
281
+ const problems = [];
282
+ const sum = (s) => [...s].reduce((a, c) => a + Number(c), 0);
283
+ const countWide = (s) => [...s].filter((c) => c === 'w').length;
284
+
285
+ // --- EAN/UPC: 7 modules per digit; L odd parity; R the complement of L;
286
+ // G the reverse of R. All 30 patterns distinct.
287
+ for (let d = 0; d < 10; d++) {
288
+ for (const [name, table] of [['L', EAN_L], ['G', EAN_G], ['R', EAN_R]]) {
289
+ if (table[d].length !== 7) problems.push(`EAN ${name}${d}: ${table[d].length} modules, expected 7`);
290
+ }
291
+ const darkL = [...EAN_L[d]].filter((c) => c === '1').length;
292
+ if (darkL % 2 === 0) problems.push(`EAN L${d}: even parity, expected odd`);
293
+ const darkG = [...EAN_G[d]].filter((c) => c === '1').length;
294
+ if (darkG % 2 !== 0) problems.push(`EAN G${d}: odd parity, expected even`);
295
+ if (EAN_L[d][0] !== '0' || EAN_L[d][6] !== '1') {
296
+ problems.push(`EAN L${d}: must start with a space and end with a bar`);
297
+ }
298
+ }
299
+ const eanAll = new Set([...EAN_L, ...EAN_G, ...EAN_R]);
300
+ if (eanAll.size !== 30) problems.push(`EAN: ${eanAll.size} distinct patterns, expected 30`);
301
+
302
+ // --- EAN-13 parity: first row all-L, every row six characters, all distinct.
303
+ if (EAN13_PARITY[0] !== 'LLLLLL') problems.push('EAN-13 parity[0] must be LLLLLL');
304
+ if (new Set(EAN13_PARITY).size !== 10) problems.push('EAN-13 parity rows are not distinct');
305
+ for (const [i, row] of EAN13_PARITY.entries()) {
306
+ if (row.length !== 6) problems.push(`EAN-13 parity[${i}]: ${row.length} entries, expected 6`);
307
+ }
308
+
309
+ // --- Code 39: nine elements, exactly three wide, all patterns distinct.
310
+ for (const [ch, p] of Object.entries(CODE39)) {
311
+ if (p.length !== 9) problems.push(`Code39 '${ch}': ${p.length} elements, expected 9`);
312
+ if (countWide(p) !== 3) problems.push(`Code39 '${ch}': ${countWide(p)} wide, expected 3`);
313
+ }
314
+ if (new Set(Object.values(CODE39)).size !== Object.keys(CODE39).length) {
315
+ problems.push('Code39: duplicate patterns');
316
+ }
317
+ if (CODE39_CHECK_SET.length !== 43) {
318
+ problems.push(`Code39 check set: ${CODE39_CHECK_SET.length} characters, expected 43`);
319
+ }
320
+
321
+ // --- Code 93: nine modules across six elements, all patterns distinct.
322
+ for (const [ch, p] of Object.entries(CODE93)) {
323
+ if (p.length !== 6) problems.push(`Code93 '${ch}': ${p.length} elements, expected 6`);
324
+ if (sum(p) !== 9) problems.push(`Code93 '${ch}': ${sum(p)} modules, expected 9`);
325
+ }
326
+ if (new Set(Object.values(CODE93)).size !== Object.keys(CODE93).length) {
327
+ problems.push('Code93: duplicate patterns');
328
+ }
329
+ if (CODE93_VALUES.length !== 47) {
330
+ problems.push(`Code93 values: ${CODE93_VALUES.length}, expected 47`);
331
+ }
332
+ if (Object.keys(CODE93).length !== 47) {
333
+ problems.push(
334
+ `Code93 table: ${Object.keys(CODE93).length} entries, expected 47 ` +
335
+ '(duplicate object keys collapse silently — check the shift characters)'
336
+ );
337
+ }
338
+ if (new Set(CODE93_VALUES).size !== CODE93_VALUES.length) {
339
+ problems.push('Code93: duplicate entries in the value order');
340
+ }
341
+ for (const ch of CODE93_VALUES) {
342
+ if (!CODE93[ch]) problems.push(`Code93: value character '${ch}' has no pattern`);
343
+ }
344
+
345
+ // --- Code 128: 107 patterns, 11 modules each, stop 13. All distinct.
346
+ if (CODE128.length !== 107) {
347
+ problems.push(`Code128: ${CODE128.length} patterns, expected 107`);
348
+ }
349
+ for (const [i, p] of CODE128.entries()) {
350
+ const expected = i === CODE128_STOP ? 13 : 11;
351
+ const elements = i === CODE128_STOP ? 7 : 6;
352
+ if (sum(p) !== expected) problems.push(`Code128 [${i}] "${p}": ${sum(p)} modules, expected ${expected}`);
353
+ if (p.length !== elements) problems.push(`Code128 [${i}] "${p}": ${p.length} elements, expected ${elements}`);
354
+ if ([...p].some((c) => c === '0' || Number(c) > 4)) {
355
+ problems.push(`Code128 [${i}] "${p}": element width out of range 1-4`);
356
+ }
357
+ }
358
+ if (new Set(CODE128).size !== CODE128.length) problems.push('Code128: duplicate patterns');
359
+
360
+ // --- ITF: five elements, exactly two wide, ten distinct patterns.
361
+ for (const [d, p] of ITF.entries()) {
362
+ if (p.length !== 5) problems.push(`ITF ${d}: ${p.length} elements, expected 5`);
363
+ if (countWide(p) !== 2) problems.push(`ITF ${d}: ${countWide(p)} wide, expected 2`);
364
+ }
365
+ if (new Set(ITF).size !== 10) problems.push('ITF: duplicate patterns');
366
+
367
+ // --- Codabar: seven elements, all distinct.
368
+ for (const [ch, p] of Object.entries(CODABAR)) {
369
+ if (p.length !== 7) problems.push(`Codabar '${ch}': ${p.length} elements, expected 7`);
370
+ }
371
+ if (new Set(Object.values(CODABAR)).size !== Object.keys(CODABAR).length) {
372
+ problems.push('Codabar: duplicate patterns');
373
+ }
374
+
375
+ // --- Code 11: five elements, all distinct.
376
+ for (const [ch, p] of Object.entries(CODE11)) {
377
+ if (p.length !== 5) problems.push(`Code11 '${ch}': ${p.length} elements, expected 5`);
378
+ }
379
+ if (new Set(Object.values(CODE11)).size !== Object.keys(CODE11).length) {
380
+ problems.push('Code11: duplicate patterns');
381
+ }
382
+
383
+ return problems;
384
+ }