@sythos/js_barcode_universal 1.4.1 → 1.5.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.
- package/NOTICE.md +2 -0
- package/README.md +42 -29
- package/bundle/sythos-barcode.esm.js +10617 -10163
- package/bundle/sythos-barcode.js +10614 -10163
- package/examples/read.html +5 -0
- package/licenses/README.md +2 -2
- package/licenses/ean-2.license +3 -2
- package/licenses/ean-5.license +3 -2
- package/licenses/frameqr.license +6 -6
- package/licenses/gs1-databar.license +10 -7
- package/package.json +1 -1
- package/src/databar/decoder.js +102 -1
- package/src/databar/index.js +2 -2
- package/src/index.js +32 -7
- package/src/oned/index.js +9 -9
- package/src/oned/reader.js +330 -12
- package/src/oned/writers.js +6 -1
package/src/oned/reader.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* MIT License
|
|
5
5
|
*
|
|
6
6
|
* Copyright (c) 2026 Sythos
|
|
7
|
+
* SPDX-FileCopyrightText: 2026 Sythos (https://www.sythos.net)
|
|
7
8
|
*
|
|
8
9
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
9
10
|
* of this software and associated documentation files (the "Software"), to deal
|
|
@@ -23,7 +24,6 @@
|
|
|
23
24
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
24
25
|
* SOFTWARE.
|
|
25
26
|
*
|
|
26
|
-
* SPDX-FileCopyrightText: 2026 Sythos (https://www.sythos.net)
|
|
27
27
|
* SPDX-License-Identifier: MIT
|
|
28
28
|
*
|
|
29
29
|
* Original work. No code from any other barcode implementation.
|
|
@@ -58,8 +58,14 @@ import {
|
|
|
58
58
|
CODE128_STOP, CODE128_FNC1, CODE128_CODE_A, CODE128_CODE_B, CODE128_CODE_C,
|
|
59
59
|
CODE128_SHIFT,
|
|
60
60
|
ITF, CODABAR, CODABAR_START_STOP,
|
|
61
|
+
CODE11, CODE11_START_STOP, MSI_START, MSI_STOP, MSI_BIT,
|
|
61
62
|
} from './patterns.js';
|
|
62
63
|
import { ean13CheckDigit, upceToUpcaBody } from './writers.js';
|
|
64
|
+
import {
|
|
65
|
+
EAN2_PARITY, EAN5_PARITY, EAN_ADDON_START, EAN_ADDON_SEPARATOR,
|
|
66
|
+
ean5Checksum,
|
|
67
|
+
} from './addons.js';
|
|
68
|
+
import { decodeDataBar14Scanline } from '../databar/decoder.js';
|
|
63
69
|
|
|
64
70
|
/* ------------------------------------------------------------------ *
|
|
65
71
|
* Pattern matching primitives
|
|
@@ -232,6 +238,14 @@ const CODABAR_BITS = Object.fromEntries(
|
|
|
232
238
|
Object.entries(CODABAR).map(([k, v]) => [nwToBits(v), k])
|
|
233
239
|
);
|
|
234
240
|
const ITF_BITS = Object.fromEntries(ITF.map((v, i) => [nwToBits(v), i]));
|
|
241
|
+
const CODE11_BITS = Object.fromEntries(
|
|
242
|
+
Object.entries(CODE11).flatMap(([ch, value]) => {
|
|
243
|
+
const bits = nwToBits(value);
|
|
244
|
+
return [[`${bits}:1`, ch], [`${bits}:2`, ch]];
|
|
245
|
+
})
|
|
246
|
+
);
|
|
247
|
+
const CODE11_START_BITS = nwToBits(CODE11_START_STOP);
|
|
248
|
+
const CODE11_CHARSET = '0123456789-';
|
|
235
249
|
|
|
236
250
|
/**
|
|
237
251
|
* Shortest ITF payload treated as a real read.
|
|
@@ -317,7 +331,8 @@ function decodeEANFamily(row) {
|
|
|
317
331
|
digits.push(d.digit);
|
|
318
332
|
offset = d.end;
|
|
319
333
|
}
|
|
320
|
-
|
|
334
|
+
const trailing = matchAt(row, offset, START_END_PATTERN);
|
|
335
|
+
if (!trailing) return null;
|
|
321
336
|
|
|
322
337
|
const parityStr = [];
|
|
323
338
|
for (let i = 0; i < 6; i++) parityStr.push((parityBits >> (5 - i)) & 1 ? 'G' : 'L');
|
|
@@ -328,9 +343,10 @@ function decodeEANFamily(row) {
|
|
|
328
343
|
if (Number(text[12]) !== ean13CheckDigit(text.slice(0, 12))) return null;
|
|
329
344
|
|
|
330
345
|
// A leading zero means this was printed as UPC-A.
|
|
331
|
-
|
|
346
|
+
const result = first === 0
|
|
332
347
|
? { format: 'upca', text: text.slice(1) }
|
|
333
348
|
: { format: 'ean13', text };
|
|
349
|
+
return attachEANAddon(result, row, trailing.end);
|
|
334
350
|
}
|
|
335
351
|
|
|
336
352
|
return null;
|
|
@@ -365,11 +381,12 @@ function decodeEAN8(row) {
|
|
|
365
381
|
digits.push(d.digit);
|
|
366
382
|
offset = d.end;
|
|
367
383
|
}
|
|
368
|
-
|
|
384
|
+
const trailing = matchAt(row, offset, START_END_PATTERN);
|
|
385
|
+
if (!trailing) return null;
|
|
369
386
|
|
|
370
387
|
const text = digits.join('');
|
|
371
388
|
if (Number(text[7]) !== ean13CheckDigit(text.slice(0, 7))) return null;
|
|
372
|
-
return { format: 'ean8', text };
|
|
389
|
+
return attachEANAddon({ format: 'ean8', text }, row, trailing.end);
|
|
373
390
|
}
|
|
374
391
|
|
|
375
392
|
/**
|
|
@@ -396,7 +413,8 @@ function decodeUPCE(row) {
|
|
|
396
413
|
// Six digits and then the end guard, in that order and nothing between. The
|
|
397
414
|
// EAN readers above match their trailing guard; this one used to stop at the
|
|
398
415
|
// last digit, which let it report a symbol it had never seen the end of.
|
|
399
|
-
|
|
416
|
+
const trailing = matchAt(row, offset, UPCE_END_PATTERN);
|
|
417
|
+
if (!trailing) return null;
|
|
400
418
|
|
|
401
419
|
const parityStr = [];
|
|
402
420
|
for (let i = 0; i < 6; i++) parityStr.push((parityBits >> (5 - i)) & 1 ? 'E' : 'O');
|
|
@@ -413,7 +431,7 @@ function decodeUPCE(row) {
|
|
|
413
431
|
const body = digits.join('');
|
|
414
432
|
if (ean13CheckDigit(upceToUpcaBody(0, body)) !== check) return null;
|
|
415
433
|
|
|
416
|
-
return { format: 'upce', text: '0' + body + String(check) };
|
|
434
|
+
return attachEANAddon({ format: 'upce', text: '0' + body + String(check) }, row, trailing.end);
|
|
417
435
|
}
|
|
418
436
|
|
|
419
437
|
/* ------------------------------------------------------------------ *
|
|
@@ -484,6 +502,256 @@ function matchAt(row, start, pattern) {
|
|
|
484
502
|
return { end: start + width };
|
|
485
503
|
}
|
|
486
504
|
|
|
505
|
+
/* ------------------------------------------------------------------ *
|
|
506
|
+
* EAN supplements
|
|
507
|
+
* ------------------------------------------------------------------ */
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Decode a supplement immediately following a validated EAN/UPC symbol.
|
|
511
|
+
* The caller supplies the end of the parent trailing guard, so a supplement
|
|
512
|
+
* can never be accepted as an unrelated standalone linear symbol.
|
|
513
|
+
*
|
|
514
|
+
* @param {Uint8Array} row
|
|
515
|
+
* @param {number} baseEnd
|
|
516
|
+
* @param {2|5} digitCount
|
|
517
|
+
* @returns {{format:'ean2'|'ean5', text:string, parity:string, checksum?:number, end:number}|null}
|
|
518
|
+
*/
|
|
519
|
+
function decodeEANSupplementRow(row, baseEnd, digitCount) {
|
|
520
|
+
const guard = findGuard(row, baseEnd, [1, 1, 2], false);
|
|
521
|
+
if (!guard || guard.start - baseEnd < 4) return null;
|
|
522
|
+
|
|
523
|
+
let offset = guard.end;
|
|
524
|
+
let text = '';
|
|
525
|
+
let parity = '';
|
|
526
|
+
|
|
527
|
+
for (let i = 0; i < digitCount; i++) {
|
|
528
|
+
const digit = decodeEANDigit(row, offset, false);
|
|
529
|
+
if (!digit) return null;
|
|
530
|
+
text += String(digit.digit);
|
|
531
|
+
parity += digit.even ? 'B' : 'A';
|
|
532
|
+
offset = digit.end;
|
|
533
|
+
|
|
534
|
+
if (i + 1 < digitCount) {
|
|
535
|
+
const separator = matchAt(row, offset, [1, 1]);
|
|
536
|
+
if (!separator) return null;
|
|
537
|
+
offset = separator.end;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
if (digitCount === 2) {
|
|
542
|
+
if (EAN2_PARITY[Number(text) % 4] !== parity) return null;
|
|
543
|
+
return { format: 'ean2', text, parity, end: offset };
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
const checksum = ean5Checksum(text);
|
|
547
|
+
if (EAN5_PARITY[checksum] !== parity) return null;
|
|
548
|
+
return { format: 'ean5', text, parity, checksum, end: offset };
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* Attach the longest valid supplement to a base result. EAN-5 is attempted
|
|
553
|
+
* first so its prefix cannot be reported as a shorter EAN-2 symbol.
|
|
554
|
+
*
|
|
555
|
+
* @param {object} base
|
|
556
|
+
* @param {Uint8Array} row
|
|
557
|
+
* @param {number} baseEnd
|
|
558
|
+
* @returns {object}
|
|
559
|
+
*/
|
|
560
|
+
function attachEANAddon(base, row, baseEnd) {
|
|
561
|
+
const addon = decodeEANSupplementRow(row, baseEnd, 5)
|
|
562
|
+
?? decodeEANSupplementRow(row, baseEnd, 2);
|
|
563
|
+
if (!addon) return base;
|
|
564
|
+
const { end, ...publicAddon } = addon;
|
|
565
|
+
void end;
|
|
566
|
+
return { ...base, addon: publicAddon };
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/* ------------------------------------------------------------------ *
|
|
570
|
+
* Code 11 and MSI/Plessey
|
|
571
|
+
* ------------------------------------------------------------------ */
|
|
572
|
+
|
|
573
|
+
/** @param {number[]} counters @returns {number} */
|
|
574
|
+
function counterTotal(counters) {
|
|
575
|
+
return counters.reduce((sum, value) => sum + value, 0);
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
/**
|
|
579
|
+
* Apply the Code 11 C/K checksum grammar used by the writer.
|
|
580
|
+
*
|
|
581
|
+
* @param {string} encoded
|
|
582
|
+
* @param {boolean|undefined} requested
|
|
583
|
+
* @returns {string|null}
|
|
584
|
+
*/
|
|
585
|
+
function finalizeCode11(encoded, requested) {
|
|
586
|
+
const weighted = (text, maxWeight) => {
|
|
587
|
+
let sum = 0;
|
|
588
|
+
for (let i = 0; i < text.length; i++) {
|
|
589
|
+
const weight = ((text.length - 1 - i) % maxWeight) + 1;
|
|
590
|
+
sum += weight * CODE11_CHARSET.indexOf(text[i]);
|
|
591
|
+
}
|
|
592
|
+
return sum;
|
|
593
|
+
};
|
|
594
|
+
const validC = (text) => {
|
|
595
|
+
if (text.length < 2) return null;
|
|
596
|
+
const body = text.slice(0, -1);
|
|
597
|
+
const expected = CODE11_CHARSET[weighted(body, 10) % 11];
|
|
598
|
+
return text[text.length - 1] === expected ? body : null;
|
|
599
|
+
};
|
|
600
|
+
const validCK = (text) => {
|
|
601
|
+
if (text.length < 12) return null;
|
|
602
|
+
const body = text.slice(0, -2);
|
|
603
|
+
const c = text[text.length - 2];
|
|
604
|
+
const k = text[text.length - 1];
|
|
605
|
+
const expectedC = CODE11_CHARSET[weighted(body, 10) % 11];
|
|
606
|
+
if (c !== expectedC) return null;
|
|
607
|
+
const expectedK = CODE11_CHARSET[weighted(body + c, 9) % 11];
|
|
608
|
+
return k === expectedK ? body : null;
|
|
609
|
+
};
|
|
610
|
+
|
|
611
|
+
if (requested === false) return encoded;
|
|
612
|
+
if (requested === true) {
|
|
613
|
+
const checked = encoded.length >= 12 ? validCK(encoded) : validC(encoded);
|
|
614
|
+
return checked;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
// With no explicit option, strip checks only when the complete grammar is
|
|
618
|
+
// unambiguous; otherwise preserve the literal payload.
|
|
619
|
+
return validCK(encoded) ?? validC(encoded) ?? encoded;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
/**
|
|
623
|
+
* Decode Code 11 from one binarized scanline.
|
|
624
|
+
*
|
|
625
|
+
* @param {Uint8Array} row
|
|
626
|
+
* @param {object} [options]
|
|
627
|
+
* @param {boolean} [options.checkDigit]
|
|
628
|
+
* @returns {{format:'code11', text:string}|null}
|
|
629
|
+
*/
|
|
630
|
+
export function decodeCode11(row, options = {}) {
|
|
631
|
+
const counters = new Array(5).fill(0);
|
|
632
|
+
let start = null;
|
|
633
|
+
for (let i = 0; i < row.length; i++) {
|
|
634
|
+
if (row[i] !== 1 || (i > 0 && row[i - 1] === 1)) continue;
|
|
635
|
+
if (!recordPattern(row, i, counters)) continue;
|
|
636
|
+
if (toNarrowWidePattern(counters, 2) === CODE11_START_BITS) {
|
|
637
|
+
start = { position: i, end: i + counterTotal(counters), scale: counterTotal(counters) / 9 };
|
|
638
|
+
break;
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
if (!start) return null;
|
|
642
|
+
|
|
643
|
+
let offset = start.end;
|
|
644
|
+
while (offset < row.length && row[offset] === 0) offset++;
|
|
645
|
+
let encoded = '';
|
|
646
|
+
|
|
647
|
+
for (let count = 0; count < 160 && offset < row.length; count++) {
|
|
648
|
+
if (!recordPattern(row, offset, counters)) return null;
|
|
649
|
+
const width = counterTotal(counters);
|
|
650
|
+
const stop = toNarrowWidePattern(counters, 2);
|
|
651
|
+
if (stop === CODE11_START_BITS) {
|
|
652
|
+
if (encoded.length === 0) return null;
|
|
653
|
+
const stopEnd = offset + width;
|
|
654
|
+
let nextDark = stopEnd;
|
|
655
|
+
while (nextDark < row.length && row[nextDark] === 0) nextDark++;
|
|
656
|
+
if (nextDark !== row.length && nextDark - stopEnd < Math.max(3, Math.ceil(start.scale * 3))) {
|
|
657
|
+
return null;
|
|
658
|
+
}
|
|
659
|
+
const text = finalizeCode11(encoded, options.checkDigit);
|
|
660
|
+
return text == null ? null : { format: 'code11', text };
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
let character = null;
|
|
664
|
+
for (const expectedWide of [1, 2]) {
|
|
665
|
+
const bits = toNarrowWidePattern(counters, expectedWide);
|
|
666
|
+
if (bits < 0) continue;
|
|
667
|
+
const candidate = CODE11_BITS[`${bits}:${expectedWide}`];
|
|
668
|
+
if (candidate !== undefined) {
|
|
669
|
+
character = candidate;
|
|
670
|
+
break;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
if (character == null) return null;
|
|
674
|
+
encoded += character;
|
|
675
|
+
if (encoded.length > 128) return null;
|
|
676
|
+
offset += width;
|
|
677
|
+
while (offset < row.length && row[offset] === 0) offset++;
|
|
678
|
+
}
|
|
679
|
+
return null;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
/** @param {string} encoded @param {boolean|undefined} requested */
|
|
683
|
+
function finalizeMSI(encoded, requested) {
|
|
684
|
+
if (requested !== true) return encoded;
|
|
685
|
+
if (encoded.length < 2) return null;
|
|
686
|
+
const body = encoded.slice(0, -1);
|
|
687
|
+
let odd = '';
|
|
688
|
+
for (let i = body.length - 1; i >= 0; i -= 2) odd = body[i] + odd;
|
|
689
|
+
const doubled = String(Number(odd) * 2);
|
|
690
|
+
let sum = 0;
|
|
691
|
+
for (const ch of doubled) sum += Number(ch);
|
|
692
|
+
for (let i = body.length - 2; i >= 0; i -= 2) sum += Number(body[i]);
|
|
693
|
+
const expected = String((10 - (sum % 10)) % 10);
|
|
694
|
+
return encoded.endsWith(expected) ? body : null;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
/**
|
|
698
|
+
* Decode MSI/Plessey from one binarized scanline.
|
|
699
|
+
*
|
|
700
|
+
* @param {Uint8Array} row
|
|
701
|
+
* @param {object} [options]
|
|
702
|
+
* @param {boolean} [options.checkDigit]
|
|
703
|
+
* @returns {{format:'msi', text:string}|null}
|
|
704
|
+
*/
|
|
705
|
+
export function decodeMSI(row, options = {}) {
|
|
706
|
+
const start = findGuard(row, 0, [2, 1], false);
|
|
707
|
+
if (!start) return null;
|
|
708
|
+
const scale = (start.end - start.start) / 3;
|
|
709
|
+
if (!(scale >= 1)) return null;
|
|
710
|
+
|
|
711
|
+
let offset = start.end;
|
|
712
|
+
let encoded = '';
|
|
713
|
+
const bitPatterns = [
|
|
714
|
+
{ pattern: [1, 2], bit: '0' },
|
|
715
|
+
{ pattern: [2, 1], bit: '1' },
|
|
716
|
+
];
|
|
717
|
+
|
|
718
|
+
for (let digitIndex = 0; digitIndex < 80; digitIndex++) {
|
|
719
|
+
const stop = matchAt(row, offset, [1, 2, 1]);
|
|
720
|
+
if (stop && encoded.length > 0) {
|
|
721
|
+
let nextDark = stop.end;
|
|
722
|
+
while (nextDark < row.length && row[nextDark] === 0) nextDark++;
|
|
723
|
+
if (nextDark === row.length ||
|
|
724
|
+
nextDark - stop.end >= Math.max(3, Math.ceil(scale * 3))) {
|
|
725
|
+
if (encoded.length < 6 && !(options.formats && options.formats.includes('msi'))) {
|
|
726
|
+
return null;
|
|
727
|
+
}
|
|
728
|
+
const text = finalizeMSI(encoded, options.checkDigit);
|
|
729
|
+
return text == null ? null : { format: 'msi', text };
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
let nibble = '';
|
|
734
|
+
for (let bitIndex = 0; bitIndex < 4; bitIndex++) {
|
|
735
|
+
let matched = null;
|
|
736
|
+
for (const candidate of bitPatterns) {
|
|
737
|
+
const found = matchAt(row, offset, candidate.pattern);
|
|
738
|
+
if (found) {
|
|
739
|
+
matched = { ...candidate, end: found.end };
|
|
740
|
+
break;
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
if (!matched) return null;
|
|
744
|
+
nibble += matched.bit;
|
|
745
|
+
offset = matched.end;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
const digit = Number.parseInt(nibble, 2);
|
|
749
|
+
if (digit > 9) return null;
|
|
750
|
+
encoded += String(digit);
|
|
751
|
+
}
|
|
752
|
+
return null;
|
|
753
|
+
}
|
|
754
|
+
|
|
487
755
|
/* ------------------------------------------------------------------ *
|
|
488
756
|
* Code 128
|
|
489
757
|
* ------------------------------------------------------------------ */
|
|
@@ -556,8 +824,11 @@ function decodeCode128(row) {
|
|
|
556
824
|
: start.code === CODE128_START_B ? 'B' : 'C';
|
|
557
825
|
let shifted = null;
|
|
558
826
|
let text = '';
|
|
827
|
+
const fnc1AtStart = dataValues[0] === CODE128_FNC1;
|
|
828
|
+
const fnc1Positions = [];
|
|
559
829
|
|
|
560
|
-
for (
|
|
830
|
+
for (let dataIndex = 0; dataIndex < dataValues.length; dataIndex++) {
|
|
831
|
+
const value = dataValues[dataIndex];
|
|
561
832
|
const active = shifted || mode;
|
|
562
833
|
shifted = null;
|
|
563
834
|
|
|
@@ -565,7 +836,13 @@ function decodeCode128(row) {
|
|
|
565
836
|
if (value === CODE128_CODE_B && mode !== 'B') { mode = 'B'; continue; }
|
|
566
837
|
if (value === CODE128_CODE_C) { mode = 'C'; continue; }
|
|
567
838
|
if (value === CODE128_SHIFT) { shifted = mode === 'A' ? 'B' : 'A'; continue; }
|
|
568
|
-
if (value === CODE128_FNC1) {
|
|
839
|
+
if (value === CODE128_FNC1) {
|
|
840
|
+
if (dataIndex > 0) {
|
|
841
|
+
fnc1Positions.push(text.length);
|
|
842
|
+
text += '\x1d';
|
|
843
|
+
}
|
|
844
|
+
continue;
|
|
845
|
+
}
|
|
569
846
|
if (value >= 96 && value <= 102) { continue; } // other function characters
|
|
570
847
|
|
|
571
848
|
if (active === 'C') {
|
|
@@ -578,6 +855,16 @@ function decodeCode128(row) {
|
|
|
578
855
|
}
|
|
579
856
|
|
|
580
857
|
if (text.length === 0) return null;
|
|
858
|
+
if (fnc1AtStart) {
|
|
859
|
+
return {
|
|
860
|
+
format: 'gs1128',
|
|
861
|
+
text,
|
|
862
|
+
gs1: true,
|
|
863
|
+
symbologyIdentifier: ']C1',
|
|
864
|
+
fnc1AtStart: true,
|
|
865
|
+
fnc1Positions,
|
|
866
|
+
};
|
|
867
|
+
}
|
|
581
868
|
return { format: 'code128', text };
|
|
582
869
|
}
|
|
583
870
|
|
|
@@ -722,6 +1009,7 @@ function decodeCode93(row) {
|
|
|
722
1009
|
function decodeITF(row) {
|
|
723
1010
|
const start = findGuard(row, 0, [1, 1, 1, 1], false);
|
|
724
1011
|
if (!start) return null;
|
|
1012
|
+
const startScale = (start.end - start.start) / 4;
|
|
725
1013
|
|
|
726
1014
|
let offset = start.end;
|
|
727
1015
|
const digits = [];
|
|
@@ -767,6 +1055,10 @@ function decodeITF(row) {
|
|
|
767
1055
|
const stop = new Array(3).fill(0);
|
|
768
1056
|
if (!recordPattern(row, offset, stop)) return null;
|
|
769
1057
|
if (toNarrowWidePattern(stop, 1) !== 0b100) return null;
|
|
1058
|
+
const stopEnd = offset + counterTotal(stop);
|
|
1059
|
+
let nextDark = stopEnd;
|
|
1060
|
+
while (nextDark < row.length && row[nextDark] === 0) nextDark++;
|
|
1061
|
+
if (nextDark !== row.length && nextDark - stopEnd < Math.max(3, Math.ceil(startScale * 3))) return null;
|
|
770
1062
|
|
|
771
1063
|
return { format: 'itf', text: digits.join('') };
|
|
772
1064
|
}
|
|
@@ -841,6 +1133,9 @@ const DECODERS = [
|
|
|
841
1133
|
['ean8', decodeEAN8],
|
|
842
1134
|
['upce', decodeUPCE],
|
|
843
1135
|
['code128', decodeCode128],
|
|
1136
|
+
['code11', decodeCode11],
|
|
1137
|
+
['msi', decodeMSI],
|
|
1138
|
+
['gs1databar14', decodeDataBar14Scanline],
|
|
844
1139
|
['code39', decodeCode39],
|
|
845
1140
|
['code93', decodeCode93],
|
|
846
1141
|
['itf', decodeITF],
|
|
@@ -860,7 +1155,16 @@ const DECODERS = [
|
|
|
860
1155
|
export function decodeOneD(image, options = {}) {
|
|
861
1156
|
const { formats = null, rows = 15, tryHarder = true } = options;
|
|
862
1157
|
const enabled = formats ? new Set(formats) : null;
|
|
863
|
-
const active = DECODERS.filter(([id]) =>
|
|
1158
|
+
const active = DECODERS.filter(([id]) => {
|
|
1159
|
+
if (!enabled) return true;
|
|
1160
|
+
if (enabled.has(id)) return true;
|
|
1161
|
+
if (id === 'ean13' || id === 'ean8' || id === 'upca' || id === 'upce') {
|
|
1162
|
+
return enabled.has('ean2') || enabled.has('ean5');
|
|
1163
|
+
}
|
|
1164
|
+
if (id === 'code128') return enabled.has('gs1128');
|
|
1165
|
+
if (id === 'gs1databar14') return enabled.has('databar') || enabled.has('gs1-databar14');
|
|
1166
|
+
return false;
|
|
1167
|
+
});
|
|
864
1168
|
if (active.length === 0) return [];
|
|
865
1169
|
|
|
866
1170
|
const results = [];
|
|
@@ -891,9 +1195,23 @@ export function decodeOneD(image, options = {}) {
|
|
|
891
1195
|
result = null; // a malformed candidate is not an error
|
|
892
1196
|
}
|
|
893
1197
|
if (!result) continue;
|
|
894
|
-
if (enabled
|
|
1198
|
+
if (enabled) {
|
|
1199
|
+
const addonRequested = enabled.has('ean2') || enabled.has('ean5');
|
|
1200
|
+
const isEANBase = result.format === 'ean13' || result.format === 'ean8' ||
|
|
1201
|
+
result.format === 'upca' || result.format === 'upce';
|
|
1202
|
+
if (isEANBase && addonRequested) {
|
|
1203
|
+
if (!result.addon || !enabled.has(result.addon.format)) continue;
|
|
1204
|
+
} else if (result.format === 'gs1128') {
|
|
1205
|
+
if (!enabled.has('gs1128') && !enabled.has('code128')) continue;
|
|
1206
|
+
} else if (result.format === 'gs1databar14') {
|
|
1207
|
+
if (!enabled.has('gs1databar14') && !enabled.has('databar') && !enabled.has('gs1-databar14')) continue;
|
|
1208
|
+
} else if (!enabled.has(result.format)) {
|
|
1209
|
+
continue;
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
895
1212
|
|
|
896
|
-
const
|
|
1213
|
+
const addonKey = result.addon ? `:${result.addon.format}:${result.addon.text}` : '';
|
|
1214
|
+
const key = `${result.format}:${result.text}${addonKey}`;
|
|
897
1215
|
if (seen.has(key)) continue;
|
|
898
1216
|
seen.add(key);
|
|
899
1217
|
results.push({ ...result, row: y });
|
package/src/oned/writers.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* MIT License
|
|
5
5
|
*
|
|
6
6
|
* Copyright (c) 2026 Sythos
|
|
7
|
+
* SPDX-FileCopyrightText: 2026 Sythos (https://www.sythos.net)
|
|
7
8
|
*
|
|
8
9
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
9
10
|
* of this software and associated documentation files (the "Software"), to deal
|
|
@@ -23,7 +24,6 @@
|
|
|
23
24
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
24
25
|
* SOFTWARE.
|
|
25
26
|
*
|
|
26
|
-
* SPDX-FileCopyrightText: 2026 Sythos (https://www.sythos.net)
|
|
27
27
|
* SPDX-License-Identifier: MIT
|
|
28
28
|
*
|
|
29
29
|
* Original work. No code from any other barcode implementation.
|
|
@@ -464,6 +464,11 @@ export function encodeCode128(value, options = {}) {
|
|
|
464
464
|
if (gs1) codes.push(CODE128_FNC1);
|
|
465
465
|
|
|
466
466
|
while (i < value.length) {
|
|
467
|
+
if (gs1 && value[i] === '\x1d') {
|
|
468
|
+
codes.push(CODE128_FNC1);
|
|
469
|
+
i++;
|
|
470
|
+
continue;
|
|
471
|
+
}
|
|
467
472
|
const run = digitRun(i);
|
|
468
473
|
const atEnd = i + run === value.length;
|
|
469
474
|
const worthC = run >= 6 || (i === 0 && run >= 4) || (atEnd && run >= 4);
|