@sythos/js_barcode_universal 1.5.7 → 1.5.9
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/LICENSE +5 -4
- package/NOTICE.md +2 -2
- package/README.md +50 -37
- package/bundle/sythos-barcode.esm.js +12051 -11484
- package/bundle/sythos-barcode.js +12051 -11484
- package/package.json +52 -45
- package/src/index.d.ts +363 -0
- package/src/index.js +573 -428
- package/src/js/aztec/decoder.js +326 -0
- package/src/js/aztec/detector.js +251 -0
- package/src/js/aztec/encoder.js +276 -0
- package/src/js/aztec/high-level.js +216 -0
- package/src/js/aztec/index.js +34 -0
- package/src/js/aztec/tables.js +196 -0
- package/src/js/aztecrune/decoder.js +146 -0
- package/src/js/aztecrune/detector.js +180 -0
- package/src/js/aztecrune/encoder.js +103 -0
- package/src/js/aztecrune/index.js +34 -0
- package/src/js/aztecrune/tables.js +142 -0
- package/src/js/compactpdf417/decoder.js +123 -0
- package/src/js/compactpdf417/detector.js +138 -0
- package/src/js/compactpdf417/encoder.js +127 -0
- package/src/js/compactpdf417/index.js +5 -0
- package/src/js/compactpdf417/tables.js +164 -0
- package/src/js/core/bit-buffer.js +158 -0
- package/src/js/core/bit-matrix.js +243 -0
- package/src/js/core/errors.js +59 -0
- package/src/js/core/galois-field.js +193 -0
- package/src/js/core/index.js +39 -0
- package/src/js/core/reed-solomon.js +318 -0
- package/src/js/databar/codec.js +166 -0
- package/src/js/databar/decoder.js +224 -0
- package/src/js/databar/encoder.js +88 -0
- package/src/js/databar/gs1.js +176 -0
- package/src/js/databar/index.js +36 -0
- package/src/js/databar/patterns.js +139 -0
- package/src/js/databar/tables.js +123 -0
- package/src/js/datamatrix/decoder.js +296 -0
- package/src/js/datamatrix/detector.js +254 -0
- package/src/js/datamatrix/encoder.js +250 -0
- package/src/js/datamatrix/index.js +34 -0
- package/src/js/datamatrix/tables.js +122 -0
- package/src/js/frameqr/decoder.js +216 -0
- package/src/js/frameqr/detector.js +199 -0
- package/src/js/frameqr/encoder.js +143 -0
- package/src/js/frameqr/index.js +33 -0
- package/src/js/frameqr/tables.js +252 -0
- package/src/js/image/binarizer.js +254 -0
- package/src/js/image/grid-sampler.js +148 -0
- package/src/js/image/index.js +38 -0
- package/src/js/image/luminance.js +185 -0
- package/src/js/image/perspective.js +158 -0
- package/src/js/micropdf417/compaction.js +115 -0
- package/src/js/micropdf417/decoder.js +180 -0
- package/src/js/micropdf417/detector.js +152 -0
- package/src/js/micropdf417/encoder.js +184 -0
- package/src/js/micropdf417/error-correction.js +50 -0
- package/src/js/micropdf417/index.js +35 -0
- package/src/js/micropdf417/tables.js +197 -0
- package/src/js/microqr/decoder.js +240 -0
- package/src/js/microqr/detector.js +339 -0
- package/src/js/microqr/encoder.js +314 -0
- package/src/js/microqr/index.js +34 -0
- package/src/js/microqr/tables.js +324 -0
- package/src/js/oned/addons.js +396 -0
- package/src/js/oned/index.js +75 -0
- package/src/js/oned/patterns.js +365 -0
- package/src/js/oned/reader.js +1383 -0
- package/src/js/oned/writers.js +691 -0
- package/src/js/pdf417/compaction.js +391 -0
- package/src/js/pdf417/decoder.js +110 -0
- package/src/js/pdf417/detector.js +484 -0
- package/src/js/pdf417/encoder.js +102 -0
- package/src/js/pdf417/error-correction.js +44 -0
- package/src/{pdf417 → js/pdf417}/index.js +0 -5
- package/src/js/pdf417/tables.js +315 -0
- package/src/js/qr/decoder.js +539 -0
- package/src/js/qr/detector.js +613 -0
- package/src/js/qr/encoder.js +873 -0
- package/src/js/qr/index.js +42 -0
- package/src/js/qr/tables.js +690 -0
- package/src/js/render/image-data.js +120 -0
- package/src/js/render/index.js +128 -0
- package/src/js/render/options.js +152 -0
- package/src/js/render/png.js +270 -0
- package/src/js/render/svg.js +117 -0
- package/src/js/render/webgl.js +200 -0
- package/src/js/render/webgpu.js +357 -0
- package/src/js/rmqr/decoder.js +209 -0
- package/src/js/rmqr/detector.js +122 -0
- package/src/js/rmqr/encoder.js +321 -0
- package/src/js/rmqr/index.js +33 -0
- package/src/js/rmqr/tables.js +177 -0
- package/src/{aztec/decoder.js → ts/aztec/decoder.ts} +0 -1
- package/{typings → src/ts}/aztec/detector.d.ts +2 -2
- package/src/{aztec/detector.js → ts/aztec/detector.ts} +0 -1
- package/src/{aztec/encoder.js → ts/aztec/encoder.ts} +0 -1
- package/src/{aztec/high-level.js → ts/aztec/high-level.ts} +0 -1
- package/src/{aztec/index.js → ts/aztec/index.ts} +0 -1
- package/src/{aztec/tables.js → ts/aztec/tables.ts} +0 -1
- package/src/{aztecrune/decoder.js → ts/aztecrune/decoder.ts} +0 -1
- package/src/{aztecrune/detector.js → ts/aztecrune/detector.ts} +0 -1
- package/src/{aztecrune/encoder.js → ts/aztecrune/encoder.ts} +0 -1
- package/src/{aztecrune/index.js → ts/aztecrune/index.ts} +0 -1
- package/src/{aztecrune/tables.js → ts/aztecrune/tables.ts} +0 -1
- package/src/{compactpdf417/decoder.js → ts/compactpdf417/decoder.ts} +0 -1
- package/src/{compactpdf417/detector.js → ts/compactpdf417/detector.ts} +0 -1
- package/src/{compactpdf417/encoder.js → ts/compactpdf417/encoder.ts} +0 -1
- package/src/{compactpdf417/index.js → ts/compactpdf417/index.ts} +0 -5
- package/src/{compactpdf417/tables.js → ts/compactpdf417/tables.ts} +0 -1
- package/src/{core/bit-buffer.js → ts/core/bit-buffer.ts} +0 -1
- package/src/{core/bit-matrix.js → ts/core/bit-matrix.ts} +0 -1
- package/src/{core/errors.js → ts/core/errors.ts} +0 -1
- package/src/{core/galois-field.js → ts/core/galois-field.ts} +0 -1
- package/src/{core/index.js → ts/core/index.ts} +0 -1
- package/src/{core/reed-solomon.js → ts/core/reed-solomon.ts} +0 -1
- package/src/{databar/codec.js → ts/databar/codec.ts} +0 -1
- package/src/{databar/decoder.js → ts/databar/decoder.ts} +0 -1
- package/src/{databar/encoder.js → ts/databar/encoder.ts} +0 -1
- package/src/{databar/gs1.js → ts/databar/gs1.ts} +0 -1
- package/src/{databar/index.js → ts/databar/index.ts} +0 -1
- package/src/{databar/patterns.js → ts/databar/patterns.ts} +0 -1
- package/src/{databar/tables.js → ts/databar/tables.ts} +0 -1
- package/src/{datamatrix/decoder.js → ts/datamatrix/decoder.ts} +0 -1
- package/{typings → src/ts}/datamatrix/detector.d.ts +2 -2
- package/src/{datamatrix/detector.js → ts/datamatrix/detector.ts} +0 -1
- package/src/{datamatrix/encoder.js → ts/datamatrix/encoder.ts} +0 -1
- package/src/{datamatrix/index.js → ts/datamatrix/index.ts} +0 -1
- package/src/{datamatrix/tables.js → ts/datamatrix/tables.ts} +0 -1
- package/src/{frameqr/decoder.js → ts/frameqr/decoder.ts} +15 -16
- package/src/{frameqr/detector.js → ts/frameqr/detector.ts} +2 -3
- package/src/{frameqr/encoder.js → ts/frameqr/encoder.ts} +7 -8
- package/src/{frameqr/index.js → ts/frameqr/index.ts} +0 -1
- package/src/{frameqr/tables.js → ts/frameqr/tables.ts} +10 -11
- package/src/{image/binarizer.js → ts/image/binarizer.ts} +0 -1
- package/src/{image/grid-sampler.js → ts/image/grid-sampler.ts} +0 -1
- package/src/{image/index.js → ts/image/index.ts} +0 -1
- package/src/{image/luminance.js → ts/image/luminance.ts} +0 -1
- package/src/{image/perspective.js → ts/image/perspective.ts} +0 -1
- package/{typings → src/ts}/index.d.ts +3 -3
- package/src/ts/index.ts +790 -0
- package/src/{micropdf417/compaction.js → ts/micropdf417/compaction.ts} +0 -1
- package/src/{micropdf417/decoder.js → ts/micropdf417/decoder.ts} +0 -1
- package/src/{micropdf417/detector.js → ts/micropdf417/detector.ts} +0 -1
- package/src/{micropdf417/encoder.js → ts/micropdf417/encoder.ts} +0 -1
- package/src/{micropdf417/error-correction.js → ts/micropdf417/error-correction.ts} +0 -1
- package/src/{micropdf417/index.js → ts/micropdf417/index.ts} +0 -1
- package/src/{micropdf417/tables.js → ts/micropdf417/tables.ts} +0 -1
- package/src/{microqr/decoder.js → ts/microqr/decoder.ts} +0 -1
- package/src/{microqr/detector.js → ts/microqr/detector.ts} +0 -1
- package/src/{microqr/encoder.js → ts/microqr/encoder.ts} +0 -1
- package/src/{microqr/index.js → ts/microqr/index.ts} +0 -1
- package/src/{microqr/tables.js → ts/microqr/tables.ts} +0 -1
- package/src/{oned/addons.js → ts/oned/addons.ts} +0 -1
- package/src/{oned/index.js → ts/oned/index.ts} +0 -1
- package/src/{oned/patterns.js → ts/oned/patterns.ts} +0 -1
- package/src/{oned/reader.js → ts/oned/reader.ts} +0 -1
- package/src/{oned/writers.js → ts/oned/writers.ts} +0 -1
- package/src/{pdf417/compaction.js → ts/pdf417/compaction.ts} +0 -1
- package/src/{pdf417/decoder.js → ts/pdf417/decoder.ts} +0 -1
- package/src/{pdf417/detector.js → ts/pdf417/detector.ts} +0 -1
- package/src/{pdf417/encoder.js → ts/pdf417/encoder.ts} +0 -1
- package/src/{pdf417/error-correction.js → ts/pdf417/error-correction.ts} +0 -1
- package/src/ts/pdf417/index.ts +6 -0
- package/src/{pdf417/tables.js → ts/pdf417/tables.ts} +0 -1
- package/src/{qr/decoder.js → ts/qr/decoder.ts} +0 -1
- package/src/{qr/detector.js → ts/qr/detector.ts} +0 -1
- package/src/{qr/encoder.js → ts/qr/encoder.ts} +0 -1
- package/src/{qr/index.js → ts/qr/index.ts} +0 -1
- package/src/{qr/tables.js → ts/qr/tables.ts} +0 -1
- package/src/{render/image-data.js → ts/render/image-data.ts} +0 -1
- package/src/{render/index.js → ts/render/index.ts} +0 -1
- package/src/{render/options.js → ts/render/options.ts} +0 -1
- package/src/{render/png.js → ts/render/png.ts} +0 -1
- package/src/{render/svg.js → ts/render/svg.ts} +0 -1
- package/src/{render/webgl.js → ts/render/webgl.ts} +0 -1
- package/src/{render/webgpu.js → ts/render/webgpu.ts} +0 -1
- package/src/{rmqr/decoder.js → ts/rmqr/decoder.ts} +0 -1
- package/src/{rmqr/detector.js → ts/rmqr/detector.ts} +0 -1
- package/src/{rmqr/encoder.js → ts/rmqr/encoder.ts} +0 -1
- package/src/{rmqr/index.js → ts/rmqr/index.ts} +0 -1
- package/src/{rmqr/tables.js → ts/rmqr/tables.ts} +0 -1
- /package/{typings → src/ts}/aztec/decoder.d.ts +0 -0
- /package/{typings → src/ts}/aztec/encoder.d.ts +0 -0
- /package/{typings → src/ts}/aztec/high-level.d.ts +0 -0
- /package/{typings → src/ts}/aztec/index.d.ts +0 -0
- /package/{typings → src/ts}/aztec/tables.d.ts +0 -0
- /package/{typings → src/ts}/aztecrune/decoder.d.ts +0 -0
- /package/{typings → src/ts}/aztecrune/detector.d.ts +0 -0
- /package/{typings → src/ts}/aztecrune/encoder.d.ts +0 -0
- /package/{typings → src/ts}/aztecrune/index.d.ts +0 -0
- /package/{typings → src/ts}/aztecrune/tables.d.ts +0 -0
- /package/{typings → src/ts}/bundle/sythos-barcode.d.ts +0 -0
- /package/{typings → src/ts}/bundle/sythos-barcode.esm.d.ts +0 -0
- /package/{typings → src/ts}/compactpdf417/decoder.d.ts +0 -0
- /package/{typings → src/ts}/compactpdf417/detector.d.ts +0 -0
- /package/{typings → src/ts}/compactpdf417/encoder.d.ts +0 -0
- /package/{typings → src/ts}/compactpdf417/index.d.ts +0 -0
- /package/{typings → src/ts}/compactpdf417/tables.d.ts +0 -0
- /package/{typings → src/ts}/core/bit-buffer.d.ts +0 -0
- /package/{typings → src/ts}/core/bit-matrix.d.ts +0 -0
- /package/{typings → src/ts}/core/errors.d.ts +0 -0
- /package/{typings → src/ts}/core/galois-field.d.ts +0 -0
- /package/{typings → src/ts}/core/index.d.ts +0 -0
- /package/{typings → src/ts}/core/reed-solomon.d.ts +0 -0
- /package/{typings → src/ts}/databar/codec.d.ts +0 -0
- /package/{typings → src/ts}/databar/decoder.d.ts +0 -0
- /package/{typings → src/ts}/databar/encoder.d.ts +0 -0
- /package/{typings → src/ts}/databar/gs1.d.ts +0 -0
- /package/{typings → src/ts}/databar/index.d.ts +0 -0
- /package/{typings → src/ts}/databar/patterns.d.ts +0 -0
- /package/{typings → src/ts}/databar/tables.d.ts +0 -0
- /package/{typings → src/ts}/datamatrix/decoder.d.ts +0 -0
- /package/{typings → src/ts}/datamatrix/encoder.d.ts +0 -0
- /package/{typings → src/ts}/datamatrix/index.d.ts +0 -0
- /package/{typings → src/ts}/datamatrix/tables.d.ts +0 -0
- /package/{typings → src/ts}/frameqr/decoder.d.ts +0 -0
- /package/{typings → src/ts}/frameqr/detector.d.ts +0 -0
- /package/{typings → src/ts}/frameqr/encoder.d.ts +0 -0
- /package/{typings → src/ts}/frameqr/index.d.ts +0 -0
- /package/{typings → src/ts}/frameqr/tables.d.ts +0 -0
- /package/{typings → src/ts}/image/binarizer.d.ts +0 -0
- /package/{typings → src/ts}/image/grid-sampler.d.ts +0 -0
- /package/{typings → src/ts}/image/index.d.ts +0 -0
- /package/{typings → src/ts}/image/luminance.d.ts +0 -0
- /package/{typings → src/ts}/image/perspective.d.ts +0 -0
- /package/{typings → src/ts}/micropdf417/compaction.d.ts +0 -0
- /package/{typings → src/ts}/micropdf417/decoder.d.ts +0 -0
- /package/{typings → src/ts}/micropdf417/detector.d.ts +0 -0
- /package/{typings → src/ts}/micropdf417/encoder.d.ts +0 -0
- /package/{typings → src/ts}/micropdf417/error-correction.d.ts +0 -0
- /package/{typings → src/ts}/micropdf417/index.d.ts +0 -0
- /package/{typings → src/ts}/micropdf417/tables.d.ts +0 -0
- /package/{typings → src/ts}/microqr/decoder.d.ts +0 -0
- /package/{typings → src/ts}/microqr/detector.d.ts +0 -0
- /package/{typings → src/ts}/microqr/encoder.d.ts +0 -0
- /package/{typings → src/ts}/microqr/index.d.ts +0 -0
- /package/{typings → src/ts}/microqr/tables.d.ts +0 -0
- /package/{typings → src/ts}/oned/addons.d.ts +0 -0
- /package/{typings → src/ts}/oned/index.d.ts +0 -0
- /package/{typings → src/ts}/oned/patterns.d.ts +0 -0
- /package/{typings → src/ts}/oned/reader.d.ts +0 -0
- /package/{typings → src/ts}/oned/writers.d.ts +0 -0
- /package/{typings → src/ts}/pdf417/compaction.d.ts +0 -0
- /package/{typings → src/ts}/pdf417/decoder.d.ts +0 -0
- /package/{typings → src/ts}/pdf417/detector.d.ts +0 -0
- /package/{typings → src/ts}/pdf417/encoder.d.ts +0 -0
- /package/{typings → src/ts}/pdf417/error-correction.d.ts +0 -0
- /package/{typings → src/ts}/pdf417/index.d.ts +0 -0
- /package/{typings → src/ts}/pdf417/tables.d.ts +0 -0
- /package/{typings → src/ts}/qr/decoder.d.ts +0 -0
- /package/{typings → src/ts}/qr/detector.d.ts +0 -0
- /package/{typings → src/ts}/qr/encoder.d.ts +0 -0
- /package/{typings → src/ts}/qr/index.d.ts +0 -0
- /package/{typings → src/ts}/qr/tables.d.ts +0 -0
- /package/{typings → src/ts}/render/image-data.d.ts +0 -0
- /package/{typings → src/ts}/render/index.d.ts +0 -0
- /package/{typings → src/ts}/render/options.d.ts +0 -0
- /package/{typings → src/ts}/render/png.d.ts +0 -0
- /package/{typings → src/ts}/render/svg.d.ts +0 -0
- /package/{typings → src/ts}/render/webgl.d.ts +0 -0
- /package/{typings → src/ts}/render/webgpu.d.ts +0 -0
- /package/{typings → src/ts}/rmqr/decoder.d.ts +0 -0
- /package/{typings → src/ts}/rmqr/detector.d.ts +0 -0
- /package/{typings → src/ts}/rmqr/encoder.d.ts +0 -0
- /package/{typings → src/ts}/rmqr/index.d.ts +0 -0
- /package/{typings → src/ts}/rmqr/tables.d.ts +0 -0
|
@@ -0,0 +1,166 @@
|
|
|
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
|
+
* GTIN validation and numeric compaction for GS1 DataBar GTIN-only symbols.
|
|
32
|
+
*
|
|
33
|
+
* This is intentionally not a bar-pattern encoder. It emits the exact symbol
|
|
34
|
+
* character values required by ISO/IEC 24724:2011; a later encoder can turn
|
|
35
|
+
* those values into widths without reinterpreting a GTIN or linkage flag.
|
|
36
|
+
*
|
|
37
|
+
* @module databar/codec
|
|
38
|
+
*/
|
|
39
|
+
import { DATABAR14_INSIDE_RADIX, DATABAR14_PAIR_RADIX, DATABAR14_SYMBOL_LIMIT, DATABAR_LIMITED_DATA_LIMIT, DATABAR_LIMITED_LINKAGE_OFFSET, DATABAR_LIMITED_PAIR_RADIX, } from './tables.js';
|
|
40
|
+
const GTIN_LENGTHS = new Set([8, 12, 13, 14]);
|
|
41
|
+
function digits(value, label) {
|
|
42
|
+
const text = String(value);
|
|
43
|
+
if (!/^\d+$/.test(text))
|
|
44
|
+
throw new TypeError(`${label} must contain only decimal digits`);
|
|
45
|
+
return text;
|
|
46
|
+
}
|
|
47
|
+
function asCharacterValue(value, label, maximum) {
|
|
48
|
+
if (!Number.isInteger(value) || value < 0 || value > maximum) {
|
|
49
|
+
throw new RangeError(`${label} must be an integer from 0 to ${maximum}`);
|
|
50
|
+
}
|
|
51
|
+
return value;
|
|
52
|
+
}
|
|
53
|
+
/** Calculate the GS1 modulo-10 check digit for a GTIN body. */
|
|
54
|
+
export function gtinCheckDigit(body) {
|
|
55
|
+
const text = digits(body, 'GTIN body');
|
|
56
|
+
if (text.length < 1 || text.length > 13)
|
|
57
|
+
throw new RangeError('GTIN body must contain 1 to 13 digits');
|
|
58
|
+
let sum = 0;
|
|
59
|
+
for (let i = text.length - 1, weight = 3; i >= 0; i--, weight = weight === 3 ? 1 : 3) {
|
|
60
|
+
sum += Number(text[i]) * weight;
|
|
61
|
+
}
|
|
62
|
+
return String((10 - (sum % 10)) % 10);
|
|
63
|
+
}
|
|
64
|
+
/** Return a checked 14-digit GTIN, preserving only its standardized digits. */
|
|
65
|
+
export function normalizeGTIN(value) {
|
|
66
|
+
const input = digits(value, 'GTIN');
|
|
67
|
+
if (!GTIN_LENGTHS.has(input.length)) {
|
|
68
|
+
throw new RangeError('GTIN must contain 8, 12, 13, or 14 digits including its check digit');
|
|
69
|
+
}
|
|
70
|
+
if (gtinCheckDigit(input.slice(0, -1)) !== input.at(-1)) {
|
|
71
|
+
throw new RangeError(`GTIN check digit is invalid for ${input}`);
|
|
72
|
+
}
|
|
73
|
+
return input.padStart(14, '0');
|
|
74
|
+
}
|
|
75
|
+
/** Build a checked GTIN-14 from its thirteen-digit data body. */
|
|
76
|
+
export function makeGTIN14(body) {
|
|
77
|
+
const input = digits(body, 'GTIN-14 body');
|
|
78
|
+
if (input.length > 13)
|
|
79
|
+
throw new RangeError('GTIN-14 body must contain at most 13 digits');
|
|
80
|
+
const padded = input.padStart(13, '0');
|
|
81
|
+
return padded + gtinCheckDigit(padded);
|
|
82
|
+
}
|
|
83
|
+
function gtinFromSymbolValue(symbolValue) {
|
|
84
|
+
const value = BigInt(symbolValue);
|
|
85
|
+
if (value < 0n || value >= DATABAR14_SYMBOL_LIMIT)
|
|
86
|
+
throw new RangeError('GS1 DataBar-14 symbol value is out of range');
|
|
87
|
+
const linkage = value >= 10000000000000n;
|
|
88
|
+
const body = (linkage ? value - 10000000000000n : value).toString().padStart(13, '0');
|
|
89
|
+
return { linkage, gtin: body + gtinCheckDigit(body) };
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Compact a GTIN into the four values shared by Omnidirectional, Truncated,
|
|
93
|
+
* Stacked and Stacked Omnidirectional. The `physicalCharacters` sequence is
|
|
94
|
+
* left-to-right on a linear DataBar-14 row: 1, 2, 4, 3.
|
|
95
|
+
*/
|
|
96
|
+
export function encodeDataBar14GTIN(value, options = {}) {
|
|
97
|
+
const gtin = normalizeGTIN(value);
|
|
98
|
+
const linkage = options.linkage === true;
|
|
99
|
+
if (options.linkage !== undefined && typeof options.linkage !== 'boolean') {
|
|
100
|
+
throw new TypeError('GS1 DataBar linkage must be a boolean');
|
|
101
|
+
}
|
|
102
|
+
const dataBody = gtin.slice(0, -1);
|
|
103
|
+
const symbolValue = BigInt(dataBody) + (linkage ? 10000000000000n : 0n);
|
|
104
|
+
const leftPair = symbolValue / BigInt(DATABAR14_PAIR_RADIX);
|
|
105
|
+
const rightPair = symbolValue % BigInt(DATABAR14_PAIR_RADIX);
|
|
106
|
+
const outerLeft = Number(leftPair / BigInt(DATABAR14_INSIDE_RADIX));
|
|
107
|
+
const innerLeft = Number(leftPair % BigInt(DATABAR14_INSIDE_RADIX));
|
|
108
|
+
const outerRight = Number(rightPair / BigInt(DATABAR14_INSIDE_RADIX));
|
|
109
|
+
const innerRight = Number(rightPair % BigInt(DATABAR14_INSIDE_RADIX));
|
|
110
|
+
return Object.freeze({
|
|
111
|
+
gtin, linkage, symbolValue, leftPair, rightPair,
|
|
112
|
+
logicalCharacters: Object.freeze({ outerLeft, innerLeft, outerRight, innerRight }),
|
|
113
|
+
physicalCharacters: Object.freeze([outerLeft, innerLeft, innerRight, outerRight]),
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
/** Reconstruct and validate a GTIN from the four DataBar-14 character values. */
|
|
117
|
+
export function decodeDataBar14GTIN(values) {
|
|
118
|
+
if (values === null || typeof values !== 'object')
|
|
119
|
+
throw new TypeError('GS1 DataBar-14 characters must be an object');
|
|
120
|
+
const outerLeft = asCharacterValue(values.outerLeft, 'outerLeft', 2840);
|
|
121
|
+
const innerLeft = asCharacterValue(values.innerLeft, 'innerLeft', 1596);
|
|
122
|
+
const outerRight = asCharacterValue(values.outerRight, 'outerRight', 2840);
|
|
123
|
+
const innerRight = asCharacterValue(values.innerRight, 'innerRight', 1596);
|
|
124
|
+
const leftPair = BigInt(outerLeft) * BigInt(DATABAR14_INSIDE_RADIX) + BigInt(innerLeft);
|
|
125
|
+
const rightPair = BigInt(outerRight) * BigInt(DATABAR14_INSIDE_RADIX) + BigInt(innerRight);
|
|
126
|
+
return Object.freeze({
|
|
127
|
+
...gtinFromSymbolValue(leftPair * BigInt(DATABAR14_PAIR_RADIX) + rightPair),
|
|
128
|
+
leftPair, rightPair,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
/** Compact a DataBar Limited-eligible GTIN into its two data character values. */
|
|
132
|
+
export function encodeDataBarLimitedGTIN(value, options = {}) {
|
|
133
|
+
const gtin = normalizeGTIN(value);
|
|
134
|
+
const linkage = options.linkage === true;
|
|
135
|
+
if (options.linkage !== undefined && typeof options.linkage !== 'boolean') {
|
|
136
|
+
throw new TypeError('GS1 DataBar linkage must be a boolean');
|
|
137
|
+
}
|
|
138
|
+
const dataBody = BigInt(gtin.slice(0, -1));
|
|
139
|
+
if (dataBody >= DATABAR_LIMITED_DATA_LIMIT) {
|
|
140
|
+
throw new RangeError('GS1 DataBar Limited requires a GTIN whose indicator digit is 0 or 1');
|
|
141
|
+
}
|
|
142
|
+
const symbolValue = dataBody + (linkage ? DATABAR_LIMITED_LINKAGE_OFFSET : 0n);
|
|
143
|
+
const left = Number(symbolValue / BigInt(DATABAR_LIMITED_PAIR_RADIX));
|
|
144
|
+
const right = Number(symbolValue % BigInt(DATABAR_LIMITED_PAIR_RADIX));
|
|
145
|
+
return Object.freeze({ gtin, linkage, symbolValue, left, right });
|
|
146
|
+
}
|
|
147
|
+
/** Reconstruct and validate a GTIN from DataBar Limited data character values. */
|
|
148
|
+
export function decodeDataBarLimitedGTIN(values) {
|
|
149
|
+
if (values === null || typeof values !== 'object')
|
|
150
|
+
throw new TypeError('GS1 DataBar Limited characters must be an object');
|
|
151
|
+
const left = asCharacterValue(values.left, 'left', 1994036);
|
|
152
|
+
const right = asCharacterValue(values.right, 'right', DATABAR_LIMITED_PAIR_RADIX - 1);
|
|
153
|
+
let symbolValue = BigInt(left) * BigInt(DATABAR_LIMITED_PAIR_RADIX) + BigInt(right);
|
|
154
|
+
const linkage = symbolValue >= DATABAR_LIMITED_LINKAGE_OFFSET;
|
|
155
|
+
if (linkage)
|
|
156
|
+
symbolValue -= DATABAR_LIMITED_LINKAGE_OFFSET;
|
|
157
|
+
if (symbolValue < 0n || symbolValue >= DATABAR_LIMITED_DATA_LIMIT) {
|
|
158
|
+
throw new RangeError('GS1 DataBar Limited character values do not encode a permitted GTIN');
|
|
159
|
+
}
|
|
160
|
+
const body = symbolValue.toString().padStart(13, '0');
|
|
161
|
+
return Object.freeze({ linkage, gtin: body + gtinCheckDigit(body), symbolValue: BigInt(left) * BigInt(DATABAR_LIMITED_PAIR_RADIX) + BigInt(right) });
|
|
162
|
+
}
|
|
163
|
+
/** GS1 transmitted form for the GTIN-only DataBar variants. */
|
|
164
|
+
export function dataBarGtinTransmission(value) {
|
|
165
|
+
return `]e001${normalizeGTIN(value)}`;
|
|
166
|
+
}
|
|
@@ -0,0 +1,224 @@
|
|
|
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
|
+
/** Clean-matrix decoder for GS1 DataBar Omnidirectional and Truncated. @module databar/decoder */
|
|
31
|
+
import { ChecksumError, FormatError } from '../core/errors.js';
|
|
32
|
+
import { BitMatrix } from '../core/bit-matrix.js';
|
|
33
|
+
import { decodeDataBar14GTIN } from './codec.js';
|
|
34
|
+
import { DATABAR14_CHECKSUM_WEIGHTS, DATABAR14_FINDERS, dataBar14ValueForWidths, } from './patterns.js';
|
|
35
|
+
function sampledRuns(matrix) {
|
|
36
|
+
if (!matrix || !Number.isInteger(matrix.width) || !Number.isInteger(matrix.height)) {
|
|
37
|
+
throw new TypeError('GS1 DataBar decoder expects a BitMatrix-like value');
|
|
38
|
+
}
|
|
39
|
+
if (matrix.width % 96 !== 0)
|
|
40
|
+
throw new FormatError('GS1 DataBar-14 matrix width must be an integer multiple of 96 modules');
|
|
41
|
+
const scale = matrix.width / 96;
|
|
42
|
+
const y = Math.floor(matrix.height / 2);
|
|
43
|
+
const bits = Array.from({ length: 96 }, (_, x) => matrix.get(x * scale + Math.floor(scale / 2), y));
|
|
44
|
+
if (bits[0] || !bits[95])
|
|
45
|
+
throw new FormatError('GS1 DataBar-14 guard pattern is invalid');
|
|
46
|
+
const runs = [];
|
|
47
|
+
let current = bits[0];
|
|
48
|
+
let length = 0;
|
|
49
|
+
for (const bit of bits) {
|
|
50
|
+
if (bit === current)
|
|
51
|
+
length++;
|
|
52
|
+
else {
|
|
53
|
+
runs.push(length);
|
|
54
|
+
current = bit;
|
|
55
|
+
length = 1;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
runs.push(length);
|
|
59
|
+
if (runs.length !== 46 || runs[0] !== 1 || runs[1] !== 1 || runs[44] !== 1 || runs[45] !== 1) {
|
|
60
|
+
throw new FormatError('GS1 DataBar-14 element count or guard widths are invalid');
|
|
61
|
+
}
|
|
62
|
+
return runs;
|
|
63
|
+
}
|
|
64
|
+
function finderIndex(widths) {
|
|
65
|
+
const key = widths.join(',');
|
|
66
|
+
return DATABAR14_FINDERS.findIndex((candidate) => candidate.join(',') === key);
|
|
67
|
+
}
|
|
68
|
+
function expectedChecksum(widths) {
|
|
69
|
+
const offsets = [0, 8, 24, 16];
|
|
70
|
+
let checksum = 0;
|
|
71
|
+
for (let character = 0; character < 4; character++) {
|
|
72
|
+
for (let element = 0; element < 8; element++) {
|
|
73
|
+
checksum += widths[character][element] * DATABAR14_CHECKSUM_WEIGHTS[offsets[character] + element];
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
checksum %= 79;
|
|
77
|
+
if (checksum >= 8)
|
|
78
|
+
checksum++;
|
|
79
|
+
if (checksum >= 72)
|
|
80
|
+
checksum++;
|
|
81
|
+
return checksum;
|
|
82
|
+
}
|
|
83
|
+
/** Decode a clean or integer-scaled 96-module DataBar-14 matrix. */
|
|
84
|
+
export function decodeDataBar14(matrix) {
|
|
85
|
+
const runs = sampledRuns(matrix);
|
|
86
|
+
const widths = [
|
|
87
|
+
runs.slice(2, 10),
|
|
88
|
+
runs.slice(15, 23).slice().reverse(),
|
|
89
|
+
runs.slice(23, 31),
|
|
90
|
+
runs.slice(36, 44).slice().reverse(),
|
|
91
|
+
];
|
|
92
|
+
const leftFinder = finderIndex(runs.slice(10, 15));
|
|
93
|
+
const rightFinder = finderIndex(runs.slice(31, 36).slice().reverse());
|
|
94
|
+
if (leftFinder < 0 || rightFinder < 0)
|
|
95
|
+
throw new FormatError('GS1 DataBar-14 finder pattern is invalid');
|
|
96
|
+
const encodedChecksum = leftFinder * 9 + rightFinder;
|
|
97
|
+
if (encodedChecksum !== expectedChecksum(widths))
|
|
98
|
+
throw new ChecksumError('GS1 DataBar-14 checksum mismatch');
|
|
99
|
+
const outerLeft = dataBar14ValueForWidths(widths[0], 'outside');
|
|
100
|
+
const innerLeft = dataBar14ValueForWidths(widths[1], 'inside');
|
|
101
|
+
const innerRight = dataBar14ValueForWidths(widths[2], 'inside');
|
|
102
|
+
const outerRight = dataBar14ValueForWidths(widths[3], 'outside');
|
|
103
|
+
const decoded = decodeDataBar14GTIN({ outerLeft, innerLeft, outerRight, innerRight });
|
|
104
|
+
return Object.freeze({
|
|
105
|
+
format: 'databar-omnidirectional',
|
|
106
|
+
text: decoded.gtin,
|
|
107
|
+
gtin: decoded.gtin,
|
|
108
|
+
linkage: decoded.linkage,
|
|
109
|
+
symbologyIdentifier: ']e0',
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Extract alternating runs from a binarized scanline.
|
|
114
|
+
* @param {Uint8Array} row
|
|
115
|
+
* @returns {{widths:number[], dark:boolean}[]}
|
|
116
|
+
*/
|
|
117
|
+
function scanlineRuns(row) {
|
|
118
|
+
if (!row || row.length < 96)
|
|
119
|
+
return [];
|
|
120
|
+
const runs = [];
|
|
121
|
+
let dark = row[0] === 1;
|
|
122
|
+
let start = 0;
|
|
123
|
+
for (let x = 1; x < row.length; x++) {
|
|
124
|
+
const nextDark = row[x] === 1;
|
|
125
|
+
if (nextDark === dark)
|
|
126
|
+
continue;
|
|
127
|
+
runs.push({ width: x - start, dark });
|
|
128
|
+
start = x;
|
|
129
|
+
dark = nextDark;
|
|
130
|
+
}
|
|
131
|
+
runs.push({ width: row.length - start, dark });
|
|
132
|
+
return runs;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Normalize a candidate's pixel runs to the 96 logical DataBar modules.
|
|
136
|
+
* Rounding is followed by a small conservation pass so mild non-integer
|
|
137
|
+
* scaling does not change the total symbol width.
|
|
138
|
+
* @param {number[]} raw
|
|
139
|
+
* @returns {number[]|null}
|
|
140
|
+
*/
|
|
141
|
+
function normalizeScanlineWidths(raw) {
|
|
142
|
+
const total = raw.reduce((sum, width) => sum + width, 0);
|
|
143
|
+
if (total <= 0)
|
|
144
|
+
return null;
|
|
145
|
+
const scale = total / 96;
|
|
146
|
+
if (scale < 0.5)
|
|
147
|
+
return null;
|
|
148
|
+
const widths = raw.map((width) => Math.max(1, Math.round(width / scale)));
|
|
149
|
+
let delta = 96 - widths.reduce((sum, width) => sum + width, 0);
|
|
150
|
+
while (delta !== 0) {
|
|
151
|
+
if (delta > 0) {
|
|
152
|
+
let index = 0;
|
|
153
|
+
for (let i = 1; i < widths.length; i++)
|
|
154
|
+
if (raw[i] > raw[index])
|
|
155
|
+
index = i;
|
|
156
|
+
widths[index]++;
|
|
157
|
+
delta--;
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
let index = -1;
|
|
161
|
+
for (let i = 0; i < widths.length; i++) {
|
|
162
|
+
if (widths[i] <= 1)
|
|
163
|
+
continue;
|
|
164
|
+
if (index < 0 || raw[i] / widths[i] > raw[index] / widths[index])
|
|
165
|
+
index = i;
|
|
166
|
+
}
|
|
167
|
+
if (index < 0)
|
|
168
|
+
return null;
|
|
169
|
+
widths[index]--;
|
|
170
|
+
delta++;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
if (widths[0] !== 1 || widths[widths.length - 1] !== 1)
|
|
174
|
+
return null;
|
|
175
|
+
return widths;
|
|
176
|
+
}
|
|
177
|
+
/** Build a 96-module row from normalized alternating runs. */
|
|
178
|
+
function matrixFromRuns(widths, darkFirst) {
|
|
179
|
+
const matrix = new BitMatrix(96, 1);
|
|
180
|
+
let x = 0;
|
|
181
|
+
let dark = darkFirst;
|
|
182
|
+
for (const width of widths) {
|
|
183
|
+
if (dark)
|
|
184
|
+
for (let i = 0; i < width; i++)
|
|
185
|
+
matrix.set(x + i, 0);
|
|
186
|
+
x += width;
|
|
187
|
+
dark = !dark;
|
|
188
|
+
}
|
|
189
|
+
return matrix;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Decode GS1 DataBar-14 from one raster scanline. This is the image layer over
|
|
193
|
+
* the existing clean 96-module decoder; it recognizes both Omnidirectional
|
|
194
|
+
* and Truncated symbols because their horizontal pattern is identical.
|
|
195
|
+
*
|
|
196
|
+
* @param {Uint8Array} row
|
|
197
|
+
* @returns {{format:'gs1databar14', text:string, gtin:string, gs1:boolean, linkage:boolean, symbologyIdentifier:string, elements:Array}|null}
|
|
198
|
+
*/
|
|
199
|
+
export function decodeDataBar14Scanline(row) {
|
|
200
|
+
const runs = scanlineRuns(row);
|
|
201
|
+
for (let start = 0; start + 46 <= runs.length; start++) {
|
|
202
|
+
if (runs[start].dark)
|
|
203
|
+
continue;
|
|
204
|
+
const candidate = runs.slice(start, start + 46);
|
|
205
|
+
if (start + 46 < runs.length && runs[start + 46].dark)
|
|
206
|
+
continue;
|
|
207
|
+
const widths = normalizeScanlineWidths(candidate.map((run) => run.width));
|
|
208
|
+
if (!widths)
|
|
209
|
+
continue;
|
|
210
|
+
try {
|
|
211
|
+
const decoded = decodeDataBar14(matrixFromRuns(widths, false));
|
|
212
|
+
return {
|
|
213
|
+
...decoded,
|
|
214
|
+
format: 'gs1databar14',
|
|
215
|
+
gs1: true,
|
|
216
|
+
elements: [{ ai: '01', value: decoded.gtin, fixed: true }],
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
// Try the next light-run candidate in the scanline.
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
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
|
+
/** GS1 DataBar Omnidirectional and Truncated encoder. @module databar/encoder */
|
|
31
|
+
import { BitMatrix } from '../core/bit-matrix.js';
|
|
32
|
+
import { EncodeError } from '../core/errors.js';
|
|
33
|
+
import { encodeDataBar14GTIN } from './codec.js';
|
|
34
|
+
import { DATABAR14_CHECKSUM_WEIGHTS, DATABAR14_FINDERS, dataBar14CharacterWidths, } from './patterns.js';
|
|
35
|
+
function finderIndexes(characters) {
|
|
36
|
+
const widths = characters.map((value, index) => dataBar14CharacterWidths(value, index === 0 || index === 3 ? 'outside' : 'inside'));
|
|
37
|
+
let checksum = 0;
|
|
38
|
+
const weightOffsets = [0, 8, 24, 16];
|
|
39
|
+
for (let character = 0; character < 4; character++) {
|
|
40
|
+
for (let element = 0; element < 8; element++) {
|
|
41
|
+
checksum += widths[character][element] * DATABAR14_CHECKSUM_WEIGHTS[weightOffsets[character] + element];
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
checksum %= 79;
|
|
45
|
+
if (checksum >= 8)
|
|
46
|
+
checksum++;
|
|
47
|
+
if (checksum >= 72)
|
|
48
|
+
checksum++;
|
|
49
|
+
return { widths, checksum, left: Math.floor(checksum / 9), right: checksum % 9 };
|
|
50
|
+
}
|
|
51
|
+
function widthsToMatrix(widths, height) {
|
|
52
|
+
const width = widths.reduce((sum, value) => sum + value, 0);
|
|
53
|
+
const matrix = new BitMatrix(width, height);
|
|
54
|
+
let dark = false;
|
|
55
|
+
let x = 0;
|
|
56
|
+
for (const run of widths) {
|
|
57
|
+
if (dark)
|
|
58
|
+
matrix.setRegion(x, 0, run, height);
|
|
59
|
+
x += run;
|
|
60
|
+
dark = !dark;
|
|
61
|
+
}
|
|
62
|
+
return matrix;
|
|
63
|
+
}
|
|
64
|
+
/** Encode a checked GTIN as DataBar Omnidirectional or Truncated. */
|
|
65
|
+
export function encodeDataBar14(value, options = {}) {
|
|
66
|
+
const variant = options.variant ?? 'omnidirectional';
|
|
67
|
+
if (variant !== 'omnidirectional' && variant !== 'truncated') {
|
|
68
|
+
throw new EncodeError('GS1 DataBar-14 physical encoder currently supports omnidirectional and truncated');
|
|
69
|
+
}
|
|
70
|
+
const height = options.height ?? (variant === 'omnidirectional' ? 33 : 13);
|
|
71
|
+
if (!Number.isInteger(height) || height < (variant === 'omnidirectional' ? 33 : 13)) {
|
|
72
|
+
throw new EncodeError(`GS1 DataBar ${variant} height is below its normative minimum`);
|
|
73
|
+
}
|
|
74
|
+
const compacted = encodeDataBar14GTIN(value, { linkage: options.linkage });
|
|
75
|
+
const characters = compacted.physicalCharacters;
|
|
76
|
+
const check = finderIndexes(characters);
|
|
77
|
+
const widths = [1, 1];
|
|
78
|
+
widths.push(...check.widths[0]);
|
|
79
|
+
widths.push(...DATABAR14_FINDERS[check.left]);
|
|
80
|
+
widths.push(...check.widths[1].slice().reverse());
|
|
81
|
+
widths.push(...check.widths[2]);
|
|
82
|
+
widths.push(...DATABAR14_FINDERS[check.right].slice().reverse());
|
|
83
|
+
widths.push(...check.widths[3].slice().reverse());
|
|
84
|
+
widths.push(1, 1);
|
|
85
|
+
const matrix = widthsToMatrix(widths, height);
|
|
86
|
+
matrix.databar = Object.freeze({ variant, gtin: compacted.gtin, linkage: compacted.linkage, checksum: check.checksum });
|
|
87
|
+
return matrix;
|
|
88
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
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
|
+
/** GS1 element-string codec used by GS1 DataBar Expanded. @module databar/gs1 */
|
|
31
|
+
import { EncodeError, FormatError } from '../core/errors.js';
|
|
32
|
+
export const GS1_SEPARATOR = '\x1d';
|
|
33
|
+
const EXACT = new Map([
|
|
34
|
+
['00', [18, true]], ['01', [14, true]], ['02', [14, true]],
|
|
35
|
+
['10', [20, false]], ['11', [6, true]], ['12', [6, true]],
|
|
36
|
+
['13', [6, true]], ['15', [6, true]], ['16', [6, true]],
|
|
37
|
+
['17', [6, true]], ['20', [2, true]], ['21', [20, false]],
|
|
38
|
+
['22', [29, false]], ['30', [8, false]], ['37', [8, false]],
|
|
39
|
+
['240', [30, false]], ['241', [30, false]], ['242', [6, false]],
|
|
40
|
+
['243', [20, false]], ['250', [30, false]], ['251', [30, false]],
|
|
41
|
+
['253', [30, false]], ['254', [20, false]], ['255', [13, true]],
|
|
42
|
+
['400', [30, false]], ['401', [30, false]], ['402', [17, true]],
|
|
43
|
+
['403', [30, false]], ['410', [13, true]], ['411', [13, true]],
|
|
44
|
+
['412', [13, true]], ['413', [13, true]], ['414', [13, true]],
|
|
45
|
+
['415', [13, true]], ['416', [13, true]], ['417', [13, true]],
|
|
46
|
+
['420', [20, false]], ['421', [15, false]], ['422', [3, true]],
|
|
47
|
+
['423', [15, false]], ['424', [3, true]], ['425', [3, true]],
|
|
48
|
+
['426', [3, true]], ['427', [3, false]],
|
|
49
|
+
['7001', [13, true]], ['7002', [30, false]], ['7003', [10, true]],
|
|
50
|
+
['7004', [4, false]], ['7005', [12, false]], ['7006', [6, true]],
|
|
51
|
+
['7007', [12, false]], ['7008', [3, false]], ['7009', [10, false]],
|
|
52
|
+
['7010', [2, false]], ['7020', [20, false]], ['7021', [20, false]],
|
|
53
|
+
['7022', [20, false]], ['710', [20, false]], ['711', [20, false]],
|
|
54
|
+
['712', [20, false]], ['713', [20, false]], ['714', [20, false]],
|
|
55
|
+
['715', [20, false]],
|
|
56
|
+
['8001', [14, true]], ['8002', [20, false]], ['8003', [30, false]],
|
|
57
|
+
['8004', [30, false]], ['8005', [6, true]], ['8006', [18, true]],
|
|
58
|
+
['8007', [34, false]], ['8008', [12, false]], ['8009', [50, false]],
|
|
59
|
+
['8010', [30, false]], ['8011', [12, false]], ['8012', [20, false]],
|
|
60
|
+
['8013', [30, false]], ['8017', [18, true]], ['8018', [18, true]],
|
|
61
|
+
['8019', [10, false]], ['8020', [25, false]], ['8026', [18, true]],
|
|
62
|
+
['8110', [70, false]], ['8111', [4, true]], ['8112', [70, false]],
|
|
63
|
+
['8200', [70, false]],
|
|
64
|
+
]);
|
|
65
|
+
function rangedAI(ai) {
|
|
66
|
+
if (/^31[0-6]\d$/.test(ai) || /^32[0-7]\d$/.test(ai) ||
|
|
67
|
+
/^33[0-7]\d$/.test(ai) || /^34[0-9]\d$/.test(ai) ||
|
|
68
|
+
/^35[0-7]\d$/.test(ai) || /^36[0-9]\d$/.test(ai))
|
|
69
|
+
return [6, true];
|
|
70
|
+
if (/^390\d$/.test(ai) || /^392\d$/.test(ai))
|
|
71
|
+
return [15, false];
|
|
72
|
+
if (/^391\d$/.test(ai) || /^393\d$/.test(ai))
|
|
73
|
+
return [18, false];
|
|
74
|
+
if (/^394\d$/.test(ai))
|
|
75
|
+
return [4, true];
|
|
76
|
+
if (/^395\d$/.test(ai))
|
|
77
|
+
return [6, true];
|
|
78
|
+
if (/^703\d$/.test(ai) || /^723\d$/.test(ai))
|
|
79
|
+
return [30, false];
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
export function gs1AIInfo(ai) {
|
|
83
|
+
if (typeof ai !== 'string' || !/^\d{2,4}$/.test(ai))
|
|
84
|
+
return undefined;
|
|
85
|
+
const spec = EXACT.get(ai) || rangedAI(ai);
|
|
86
|
+
if (!spec)
|
|
87
|
+
return undefined;
|
|
88
|
+
return { ai, length: spec[0], fixed: spec[1] };
|
|
89
|
+
}
|
|
90
|
+
function assertCharacters(value, label, ErrorType) {
|
|
91
|
+
if (/[^\x20-\x7e]/.test(value)) {
|
|
92
|
+
throw new ErrorType(`GS1: ${label} contains a character outside the GS1 element-string character set`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function validateElement(ai, value, ErrorType) {
|
|
96
|
+
const info = gs1AIInfo(ai);
|
|
97
|
+
if (!info)
|
|
98
|
+
throw new ErrorType(`GS1: unsupported or invalid Application Identifier ${ai}`);
|
|
99
|
+
if (value.length === 0)
|
|
100
|
+
throw new ErrorType(`GS1: AI ${ai} must not be empty`);
|
|
101
|
+
if (info.fixed ? value.length !== info.length : value.length > info.length) {
|
|
102
|
+
const expectation = info.fixed ? `exactly ${info.length}` : `at most ${info.length}`;
|
|
103
|
+
throw new ErrorType(`GS1: AI ${ai} requires ${expectation} characters`);
|
|
104
|
+
}
|
|
105
|
+
assertCharacters(value, `AI ${ai}`, ErrorType);
|
|
106
|
+
return { ai, value, fixed: info.fixed };
|
|
107
|
+
}
|
|
108
|
+
export function parseGS1ElementString(input) {
|
|
109
|
+
if (typeof input !== 'string' || input.length === 0) {
|
|
110
|
+
throw new EncodeError('GS1: element string must be a non-empty string');
|
|
111
|
+
}
|
|
112
|
+
const elements = [];
|
|
113
|
+
let offset = 0;
|
|
114
|
+
while (offset < input.length) {
|
|
115
|
+
const match = /^\((\d{2,4})\)/.exec(input.slice(offset));
|
|
116
|
+
if (!match)
|
|
117
|
+
throw new EncodeError(`GS1: expected an Application Identifier at offset ${offset}`);
|
|
118
|
+
const ai = match[1];
|
|
119
|
+
offset += match[0].length;
|
|
120
|
+
const next = input.indexOf('(', offset);
|
|
121
|
+
const end = next < 0 ? input.length : next;
|
|
122
|
+
elements.push(validateElement(ai, input.slice(offset, end), EncodeError));
|
|
123
|
+
offset = end;
|
|
124
|
+
}
|
|
125
|
+
return elements;
|
|
126
|
+
}
|
|
127
|
+
export function encodeGS1ElementString(input) {
|
|
128
|
+
const elements = Array.isArray(input)
|
|
129
|
+
? input.map(({ ai, value }) => validateElement(String(ai), String(value), EncodeError))
|
|
130
|
+
: parseGS1ElementString(input);
|
|
131
|
+
return elements.map((element, index) => `${element.ai}${element.value}${!element.fixed && index + 1 < elements.length ? GS1_SEPARATOR : ''}`).join('');
|
|
132
|
+
}
|
|
133
|
+
function matchAIAt(data, offset) {
|
|
134
|
+
for (const length of [4, 3, 2]) {
|
|
135
|
+
const ai = data.slice(offset, offset + length);
|
|
136
|
+
if (gs1AIInfo(ai))
|
|
137
|
+
return ai;
|
|
138
|
+
}
|
|
139
|
+
return undefined;
|
|
140
|
+
}
|
|
141
|
+
export function decodeGS1ElementString(data) {
|
|
142
|
+
if (typeof data !== 'string' || data.length === 0) {
|
|
143
|
+
throw new FormatError('GS1: encoded data must be a non-empty string');
|
|
144
|
+
}
|
|
145
|
+
const elements = [];
|
|
146
|
+
let offset = 0;
|
|
147
|
+
while (offset < data.length) {
|
|
148
|
+
if (data[offset] === GS1_SEPARATOR) {
|
|
149
|
+
throw new FormatError(`GS1: unexpected separator at offset ${offset}`);
|
|
150
|
+
}
|
|
151
|
+
const ai = matchAIAt(data, offset);
|
|
152
|
+
if (!ai)
|
|
153
|
+
throw new FormatError(`GS1: unknown Application Identifier at offset ${offset}`);
|
|
154
|
+
offset += ai.length;
|
|
155
|
+
const info = gs1AIInfo(ai);
|
|
156
|
+
let value;
|
|
157
|
+
if (info.fixed) {
|
|
158
|
+
value = data.slice(offset, offset + info.length);
|
|
159
|
+
if (value.length !== info.length || value.includes(GS1_SEPARATOR)) {
|
|
160
|
+
throw new FormatError(`GS1: truncated fixed-length AI ${ai}`);
|
|
161
|
+
}
|
|
162
|
+
offset += info.length;
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
const separator = data.indexOf(GS1_SEPARATOR, offset);
|
|
166
|
+
const end = separator < 0 ? data.length : separator;
|
|
167
|
+
value = data.slice(offset, end);
|
|
168
|
+
offset = separator < 0 ? data.length : separator + 1;
|
|
169
|
+
}
|
|
170
|
+
elements.push(validateElement(ai, value, FormatError));
|
|
171
|
+
}
|
|
172
|
+
return elements;
|
|
173
|
+
}
|
|
174
|
+
export function formatGS1Elements(elements) {
|
|
175
|
+
return elements.map(({ ai, value }) => `(${ai})${value}`).join('');
|
|
176
|
+
}
|