@sythos/js_barcode_universal 1.5.13 → 1.5.15
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 +22 -4
- package/NOTICE.md +10 -1
- package/README.md +416 -22
- package/bundle/sythos-barcode.esm.js +22176 -11991
- package/bundle/sythos-barcode.js +22059 -11991
- package/licenses/README.md +12 -1
- package/licenses/codablockf.license +51 -0
- package/licenses/code16k.license +52 -0
- package/licenses/code25.license +58 -0
- package/licenses/code32.license +51 -0
- package/licenses/dotcode.license +66 -0
- package/licenses/gs1-composite.license +106 -0
- package/licenses/gs1-databar.license +21 -14
- package/licenses/hanxin.license +95 -0
- package/licenses/maxicode.license +62 -0
- package/licenses/postal.license +61 -0
- package/licenses/pzn.license +53 -0
- package/licenses/telepen.license +73 -0
- package/llms.txt +62 -6
- package/package.json +47 -3
- package/src/index.d.ts +111 -6
- package/src/index.js +362 -13
- package/src/js/codablockf/decoder.js +198 -0
- package/src/js/codablockf/encoder.js +97 -0
- package/src/js/codablockf/index.js +3 -0
- package/src/js/code16k/decoder.js +303 -0
- package/src/js/code16k/detector.js +187 -0
- package/src/js/code16k/encoder.js +153 -0
- package/src/js/code16k/index.js +14 -0
- package/src/js/code16k/tables.js +152 -0
- package/src/js/composite/index.js +570 -0
- package/src/js/core/detection-contract.js +185 -0
- package/src/js/core/symbol-layout.js +155 -0
- package/src/js/databar/expanded.js +968 -0
- package/src/js/databar/index.js +4 -0
- package/src/js/databar/layout.js +192 -0
- package/src/js/databar/limited.js +533 -0
- package/src/js/databar/stacked-omnidirectional.js +548 -0
- package/src/js/databar/stacked.js +528 -0
- package/src/js/dotcode/decoder.js +548 -0
- package/src/js/dotcode/detector.js +251 -0
- package/src/js/dotcode/encoder.js +453 -0
- package/src/js/dotcode/index.js +34 -0
- package/src/js/dotcode/tables.js +168 -0
- package/src/js/hanxin/decoder.js +299 -0
- package/src/js/hanxin/detector.js +122 -0
- package/src/js/hanxin/encoder.js +259 -0
- package/src/js/hanxin/index.js +16 -0
- package/src/js/hanxin/tables.js +316 -0
- package/src/js/image/height-coded.js +164 -0
- package/src/js/maxicode/decoder.js +275 -0
- package/src/js/maxicode/detector.js +118 -0
- package/src/js/maxicode/encoder.js +438 -0
- package/src/js/maxicode/index.js +34 -0
- package/src/js/maxicode/tables.js +132 -0
- package/src/js/oned/code25.js +148 -0
- package/src/js/oned/index.js +21 -3
- package/src/js/oned/postal.js +889 -0
- package/src/js/oned/reader.js +272 -7
- package/src/js/oned/telepen.js +361 -0
- package/src/js/oned/writers.js +186 -12
- package/src/js/stacked128/common.js +208 -0
- package/src/ts/codablockf/decoder.ts +212 -0
- package/src/ts/codablockf/encoder.ts +128 -0
- package/src/ts/codablockf/index.d.ts +31 -0
- package/src/ts/codablockf/index.ts +6 -0
- package/src/ts/code16k/decoder.d.ts +25 -0
- package/src/ts/code16k/decoder.ts +313 -0
- package/src/ts/code16k/detector.d.ts +21 -0
- package/src/ts/code16k/detector.ts +203 -0
- package/src/ts/code16k/encoder.d.ts +26 -0
- package/src/ts/code16k/encoder.ts +198 -0
- package/src/ts/code16k/index.d.ts +10 -0
- package/src/ts/code16k/index.ts +47 -0
- package/src/ts/code16k/tables.d.ts +54 -0
- package/src/ts/code16k/tables.ts +195 -0
- package/src/ts/composite/index.d.ts +74 -0
- package/src/ts/composite/index.ts +547 -0
- package/src/ts/core/detection-contract.d.ts +119 -0
- package/src/ts/core/detection-contract.ts +267 -0
- package/src/ts/core/symbol-layout.d.ts +108 -0
- package/src/ts/core/symbol-layout.ts +236 -0
- package/src/ts/databar/expanded.d.ts +40 -0
- package/src/ts/databar/expanded.ts +948 -0
- package/src/ts/databar/index.d.ts +48 -0
- package/src/ts/databar/index.ts +48 -0
- package/src/ts/databar/layout.d.ts +122 -0
- package/src/ts/databar/layout.ts +275 -0
- package/src/ts/databar/limited.d.ts +85 -0
- package/src/ts/databar/limited.ts +553 -0
- package/src/ts/databar/stacked-omnidirectional.d.ts +128 -0
- package/src/ts/databar/stacked-omnidirectional.ts +559 -0
- package/src/ts/databar/stacked.d.ts +96 -0
- package/src/ts/databar/stacked.ts +549 -0
- package/src/ts/dotcode/decoder.d.ts +58 -0
- package/src/ts/dotcode/decoder.ts +467 -0
- package/src/ts/dotcode/detector.d.ts +63 -0
- package/src/ts/dotcode/detector.ts +263 -0
- package/src/ts/dotcode/encoder.d.ts +69 -0
- package/src/ts/dotcode/encoder.ts +427 -0
- package/src/ts/dotcode/index.d.ts +37 -0
- package/src/ts/dotcode/index.ts +72 -0
- package/src/ts/dotcode/tables.d.ts +74 -0
- package/src/ts/dotcode/tables.ts +174 -0
- package/src/ts/hanxin/decoder.d.ts +48 -0
- package/src/ts/hanxin/decoder.ts +314 -0
- package/src/ts/hanxin/detector.d.ts +45 -0
- package/src/ts/hanxin/detector.ts +124 -0
- package/src/ts/hanxin/encoder.d.ts +41 -0
- package/src/ts/hanxin/encoder.ts +290 -0
- package/src/ts/hanxin/index.d.ts +16 -0
- package/src/ts/hanxin/index.ts +18 -0
- package/src/ts/hanxin/tables.d.ts +80 -0
- package/src/ts/hanxin/tables.ts +348 -0
- package/src/ts/image/height-coded.d.ts +101 -0
- package/src/ts/image/height-coded.ts +227 -0
- package/src/ts/index.d.ts +46 -2
- package/src/ts/index.ts +380 -13
- package/src/ts/maxicode/decoder.ts +265 -0
- package/src/ts/maxicode/detector.ts +109 -0
- package/src/ts/maxicode/encoder.ts +414 -0
- package/src/ts/maxicode/index.d.ts +96 -0
- package/src/ts/maxicode/index.ts +44 -0
- package/src/ts/maxicode/tables.ts +136 -0
- package/src/ts/oned/code25.d.ts +42 -0
- package/src/ts/oned/code25.ts +170 -0
- package/src/ts/oned/index.d.ts +7 -2
- package/src/ts/oned/index.ts +45 -3
- package/src/ts/oned/postal.d.ts +44 -0
- package/src/ts/oned/postal.ts +819 -0
- package/src/ts/oned/reader.d.ts +35 -0
- package/src/ts/oned/reader.ts +261 -7
- package/src/ts/oned/telepen.d.ts +65 -0
- package/src/ts/oned/telepen.ts +368 -0
- package/src/ts/oned/writers.d.ts +32 -0
- package/src/ts/oned/writers.ts +184 -13
- package/src/ts/stacked128/common.ts +225 -0
|
@@ -0,0 +1,414 @@
|
|
|
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
|
+
/** MaxiCode encoder for ISO/IEC 16023 modes 2 through 5. @module maxicode/encoder */
|
|
32
|
+
|
|
33
|
+
import { BitMatrix } from '../core/bit-matrix.js';
|
|
34
|
+
import { EncodeError } from '../core/errors.js';
|
|
35
|
+
import { GF64 } from '../core/galois-field.js';
|
|
36
|
+
import { rsEncode } from '../core/reed-solomon.js';
|
|
37
|
+
import {
|
|
38
|
+
MAXICODE_CODEWORDS,
|
|
39
|
+
MAXICODE_DATA_MODULES,
|
|
40
|
+
MAXICODE_GRID,
|
|
41
|
+
MAXICODE_HEIGHT,
|
|
42
|
+
MAXICODE_ORIENTATION_DARK,
|
|
43
|
+
MAXICODE_WIDTH,
|
|
44
|
+
maxicodeFinderValue,
|
|
45
|
+
} from './tables.js';
|
|
46
|
+
|
|
47
|
+
/** @typedef {'A'|'B'|'C'|'D'|'E'} MaxiCodeSet */
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @typedef {object} MaxiCodePrimary
|
|
51
|
+
* @property {string} postalCode Mode 2 numeric postal code, or Mode 3 text.
|
|
52
|
+
* @property {number} countryCode ISO numeric country code 0..999.
|
|
53
|
+
* @property {number} serviceClass Three-digit service class 0..999.
|
|
54
|
+
*/
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @typedef {object} MaxiCodeEncodeOptions
|
|
58
|
+
* @property {2|3|4|5} [mode] MaxiCode mode. Default 4.
|
|
59
|
+
* @property {MaxiCodePrimary} [primary] Required for modes 2 and 3.
|
|
60
|
+
* @property {'latin1'} [charset] Accepted for API clarity; ISO-8859-1 only.
|
|
61
|
+
*/
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* A matrix returned by the encoder carries non-enumerable-free metadata used
|
|
65
|
+
* by the focused reader tests and by applications that want the mode.
|
|
66
|
+
* @typedef {import('../core/bit-matrix.js').BitMatrix & {maxicode?: object}} MaxiCodeMatrix
|
|
67
|
+
*/
|
|
68
|
+
|
|
69
|
+
/** @param {number} cp @returns {number|null} Code Set A symbol value. */
|
|
70
|
+
export function codeSetAValue(cp) {
|
|
71
|
+
if (cp === 28 || cp === 29 || cp === 30) return cp;
|
|
72
|
+
if (cp === 32) return 32;
|
|
73
|
+
if (cp >= 34 && cp <= 58) return cp;
|
|
74
|
+
if (cp >= 48 && cp <= 57) return cp;
|
|
75
|
+
if (cp >= 65 && cp <= 90) return cp - 64;
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** @param {number} cp @returns {number|null} Code Set B symbol value. */
|
|
80
|
+
export function codeSetBValue(cp) {
|
|
81
|
+
if (cp === 96) return 0;
|
|
82
|
+
if (cp >= 97 && cp <= 122) return cp - 96;
|
|
83
|
+
if (cp === 28 || cp === 29 || cp === 30) return cp;
|
|
84
|
+
const values = {
|
|
85
|
+
32: 47, 33: 53, 44: 48, 46: 49, 47: 50, 58: 51,
|
|
86
|
+
59: 37, 60: 38, 61: 39, 62: 40, 63: 41, 64: 52,
|
|
87
|
+
91: 42, 92: 43, 93: 44, 94: 45, 95: 46,
|
|
88
|
+
123: 32, 124: 54, 125: 34, 126: 35, 127: 36,
|
|
89
|
+
};
|
|
90
|
+
return Object.prototype.hasOwnProperty.call(values, cp) ? values[cp] : null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** @param {number} value @returns {number|null} Code Set A value to ASCII. */
|
|
94
|
+
export function codeSetACharacter(value) {
|
|
95
|
+
if (value >= 1 && value <= 26) return value + 64;
|
|
96
|
+
if (value === 28 || value === 29 || value === 30 || value === 32) return value;
|
|
97
|
+
if (value >= 34 && value <= 58) return value;
|
|
98
|
+
if (value >= 48 && value <= 57) return value;
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** @param {number} value @returns {number|null} Code Set B value to ASCII. */
|
|
103
|
+
export function codeSetBCharacter(value) {
|
|
104
|
+
if (value === 0) return 96;
|
|
105
|
+
if (value >= 1 && value <= 26) return value + 96;
|
|
106
|
+
const chars = new Map([
|
|
107
|
+
[32, 123], [34, 125], [35, 126], [36, 127], [37, 59], [38, 60],
|
|
108
|
+
[39, 61], [40, 62], [41, 63], [42, 91], [43, 92], [44, 93],
|
|
109
|
+
[45, 94], [46, 95], [47, 32], [48, 44], [49, 46], [50, 47],
|
|
110
|
+
[51, 58], [52, 64], [53, 33], [54, 124],
|
|
111
|
+
]);
|
|
112
|
+
return chars.get(value) ?? null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** @param {number} cp @returns {number|null} Code Set C symbol value. */
|
|
116
|
+
export function codeSetCValue(cp) {
|
|
117
|
+
if (cp >= 192 && cp <= 218) return cp - 192;
|
|
118
|
+
if (cp >= 219 && cp <= 223) return cp - 187;
|
|
119
|
+
const values = {
|
|
120
|
+
128: 48, 129: 49, 130: 50, 131: 51, 132: 52, 133: 53, 134: 54, 135: 55, 136: 56, 137: 57,
|
|
121
|
+
170: 37, 172: 38, 177: 39, 178: 40, 179: 41, 181: 42, 185: 43, 186: 44, 188: 45, 189: 46, 190: 47,
|
|
122
|
+
};
|
|
123
|
+
return Object.prototype.hasOwnProperty.call(values, cp) ? values[cp] : null;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** @param {number} cp @returns {number|null} Code Set D symbol value. */
|
|
127
|
+
export function codeSetDValue(cp) {
|
|
128
|
+
if (cp >= 224 && cp <= 250) return cp - 224;
|
|
129
|
+
if (cp >= 251 && cp <= 255) return cp - 219;
|
|
130
|
+
const values = {
|
|
131
|
+
138: 47, 139: 48, 140: 49, 141: 50, 142: 51, 143: 52, 144: 53, 145: 54, 146: 55, 147: 56, 148: 57,
|
|
132
|
+
161: 37, 168: 38, 171: 39, 175: 40, 176: 41, 180: 42, 183: 43, 184: 44, 187: 45, 191: 46,
|
|
133
|
+
};
|
|
134
|
+
return Object.prototype.hasOwnProperty.call(values, cp) ? values[cp] : null;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** @param {number} cp @returns {number|null} Code Set E symbol value. */
|
|
138
|
+
export function codeSetEValue(cp) {
|
|
139
|
+
if (cp >= 0 && cp <= 26) return cp;
|
|
140
|
+
if (cp === 27) return 30;
|
|
141
|
+
if (cp >= 28 && cp <= 30) return cp + 4;
|
|
142
|
+
if (cp === 31) return 35;
|
|
143
|
+
if (cp === 32) return 59;
|
|
144
|
+
if (cp >= 149 && cp <= 158) return cp - 101;
|
|
145
|
+
const values = {
|
|
146
|
+
159: 36, 160: 37, 162: 38, 163: 39, 164: 40, 165: 41, 166: 42, 167: 43,
|
|
147
|
+
169: 44, 173: 45, 174: 46, 182: 47,
|
|
148
|
+
};
|
|
149
|
+
return Object.prototype.hasOwnProperty.call(values, cp) ? values[cp] : null;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** @param {number} value @returns {number|null} Code Set C value to ISO-8859-1. */
|
|
153
|
+
export function codeSetCCharacter(value) {
|
|
154
|
+
if (value >= 0 && value <= 26) return value + 192;
|
|
155
|
+
if (value >= 32 && value <= 36) return value + 187;
|
|
156
|
+
const chars = new Map([
|
|
157
|
+
[37, 170], [38, 172], [39, 177], [40, 178], [41, 179], [42, 181], [43, 185], [44, 186], [45, 188], [46, 189], [47, 190],
|
|
158
|
+
[48, 128], [49, 129], [50, 130], [51, 131], [52, 132], [53, 133], [54, 134], [55, 135], [56, 136], [57, 137],
|
|
159
|
+
]);
|
|
160
|
+
return chars.get(value) ?? null;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** @param {number} value @returns {number|null} Code Set D value to ISO-8859-1. */
|
|
164
|
+
export function codeSetDCharacter(value) {
|
|
165
|
+
if (value >= 0 && value <= 26) return value + 224;
|
|
166
|
+
if (value >= 32 && value <= 36) return value + 219;
|
|
167
|
+
const chars = new Map([
|
|
168
|
+
[37, 161], [38, 168], [39, 171], [40, 175], [41, 176], [42, 180], [43, 183], [44, 184], [45, 187], [46, 191],
|
|
169
|
+
[47, 138], [48, 139], [49, 140], [50, 141], [51, 142], [52, 143], [53, 144], [54, 145], [55, 146], [56, 147], [57, 148],
|
|
170
|
+
]);
|
|
171
|
+
return chars.get(value) ?? null;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** @param {number} value @returns {number|null} Code Set E value to ISO-8859-1. */
|
|
175
|
+
export function codeSetECharacter(value) {
|
|
176
|
+
if (value >= 0 && value <= 26) return value;
|
|
177
|
+
if (value === 30) return 27;
|
|
178
|
+
if (value >= 32 && value <= 34) return value - 4;
|
|
179
|
+
if (value === 35) return 31;
|
|
180
|
+
if (value === 36) return 159;
|
|
181
|
+
if (value === 37) return 160;
|
|
182
|
+
if (value >= 38 && value <= 43) return value + 124;
|
|
183
|
+
if (value === 44) return 169;
|
|
184
|
+
if (value === 45) return 173;
|
|
185
|
+
if (value === 46) return 174;
|
|
186
|
+
if (value === 47) return 182;
|
|
187
|
+
if (value >= 48 && value <= 57) return value + 101;
|
|
188
|
+
if (value === 59) return 32;
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** @param {number} cp @returns {{set:'C'|'D'|'E', value:number}|null} */
|
|
193
|
+
function extendedCodeSetValue(cp) {
|
|
194
|
+
const c = codeSetCValue(cp);
|
|
195
|
+
if (c !== null) return { set: 'C', value: c };
|
|
196
|
+
const d = codeSetDValue(cp);
|
|
197
|
+
if (d !== null) return { set: 'D', value: d };
|
|
198
|
+
const e = codeSetEValue(cp);
|
|
199
|
+
return e === null ? null : { set: 'E', value: e };
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/** @param {string|Uint8Array|number[]} value @returns {number[]} Latin-1 bytes. */
|
|
203
|
+
function inputBytes(value) {
|
|
204
|
+
if (value instanceof Uint8Array) return Array.from(value);
|
|
205
|
+
if (Array.isArray(value)) {
|
|
206
|
+
if (value.some((v) => !Number.isInteger(v) || v < 0 || v > 255)) {
|
|
207
|
+
throw new EncodeError('MaxiCode: numeric byte input must contain values from 0 to 255');
|
|
208
|
+
}
|
|
209
|
+
return value.slice();
|
|
210
|
+
}
|
|
211
|
+
if (typeof value !== 'string') throw new EncodeError('MaxiCode: value must be text or bytes');
|
|
212
|
+
const bytes = [];
|
|
213
|
+
for (const character of value) {
|
|
214
|
+
const cp = character.codePointAt(0);
|
|
215
|
+
if (cp === undefined || cp > 255) {
|
|
216
|
+
throw new EncodeError('MaxiCode: text must be representable in ISO-8859-1');
|
|
217
|
+
}
|
|
218
|
+
bytes.push(cp);
|
|
219
|
+
}
|
|
220
|
+
return bytes;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/** Convert source bytes to MaxiCode codewords using explicit A/B latches. */
|
|
224
|
+
export function encodeMaxiCodeText(value) {
|
|
225
|
+
const bytes = inputBytes(value);
|
|
226
|
+
const out = [];
|
|
227
|
+
/** @type {MaxiCodeSet} */
|
|
228
|
+
let current = 'A';
|
|
229
|
+
|
|
230
|
+
for (const cp of bytes) {
|
|
231
|
+
const a = codeSetAValue(cp);
|
|
232
|
+
const b = codeSetBValue(cp);
|
|
233
|
+
if (current === 'A') {
|
|
234
|
+
if (a !== null) {
|
|
235
|
+
out.push(a);
|
|
236
|
+
} else if (b !== null) {
|
|
237
|
+
// Latch B is deterministic and keeps the stream easy to audit. A
|
|
238
|
+
// following A character emits Latch A, so no implicit state leaks.
|
|
239
|
+
out.push(63, b);
|
|
240
|
+
current = 'B';
|
|
241
|
+
} else {
|
|
242
|
+
const extended = extendedCodeSetValue(cp);
|
|
243
|
+
if (extended === null) throw new EncodeError(`MaxiCode: character U+${cp.toString(16).padStart(4, '0')} is not encodable`);
|
|
244
|
+
out.push(extended.set === 'C' ? 60 : extended.set === 'D' ? 61 : 62, extended.value);
|
|
245
|
+
}
|
|
246
|
+
} else if (b !== null) {
|
|
247
|
+
out.push(b);
|
|
248
|
+
} else if (a !== null) {
|
|
249
|
+
out.push(63, a);
|
|
250
|
+
current = 'A';
|
|
251
|
+
} else {
|
|
252
|
+
const extended = extendedCodeSetValue(cp);
|
|
253
|
+
if (extended === null) throw new EncodeError(`MaxiCode: character U+${cp.toString(16).padStart(4, '0')} is not encodable`);
|
|
254
|
+
out.push(extended.set === 'C' ? 60 : extended.set === 'D' ? 61 : 62, extended.value);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return out;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/** @param {string} postal @param {number} country @param {number} service */
|
|
261
|
+
function mode2Primary(postal, country, service) {
|
|
262
|
+
if (!/^\d{1,9}$/.test(postal)) throw new EncodeError('MaxiCode Mode 2: postalCode must contain 1 to 9 digits');
|
|
263
|
+
const numericPostal = Number(postal);
|
|
264
|
+
return [
|
|
265
|
+
((numericPostal & 0x03) << 4) | 2,
|
|
266
|
+
(numericPostal & 0xfc) >> 2,
|
|
267
|
+
(numericPostal & 0x3f00) >> 8,
|
|
268
|
+
(numericPostal & 0xfc000) >> 14,
|
|
269
|
+
(numericPostal & 0x3f00000) >> 20,
|
|
270
|
+
((numericPostal & 0x3c000000) >> 26) | ((postal.length & 0x3) << 4),
|
|
271
|
+
((postal.length & 0x3c) >> 2) | ((country & 0x3) << 4),
|
|
272
|
+
(country & 0xfc) >> 2,
|
|
273
|
+
((country & 0x300) >> 8) | ((service & 0xf) << 2),
|
|
274
|
+
(service & 0x3f0) >> 4,
|
|
275
|
+
].map((word) => word & 0x3f);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/** @param {string} postal @param {number} country @param {number} service */
|
|
279
|
+
function mode3Primary(postal, country, service) {
|
|
280
|
+
if (typeof postal !== 'string' || postal.length < 1 || postal.length > 6) {
|
|
281
|
+
throw new EncodeError('MaxiCode Mode 3: postalCode must contain 1 to 6 Code Set A characters');
|
|
282
|
+
}
|
|
283
|
+
const values = postal.toUpperCase().padEnd(6, ' ').split('').map((c) => codeSetAValue(c.charCodeAt(0)));
|
|
284
|
+
if (values.some((value) => value === null)) throw new EncodeError('MaxiCode Mode 3: postalCode contains an unsupported character');
|
|
285
|
+
const v = /** @type {number[]} */ (values);
|
|
286
|
+
return [
|
|
287
|
+
((v[5] & 0x03) << 4) | 3,
|
|
288
|
+
((v[4] & 0x03) << 4) | ((v[5] & 0x3c) >> 2),
|
|
289
|
+
((v[3] & 0x03) << 4) | ((v[4] & 0x3c) >> 2),
|
|
290
|
+
((v[2] & 0x03) << 4) | ((v[3] & 0x3c) >> 2),
|
|
291
|
+
((v[1] & 0x03) << 4) | ((v[2] & 0x3c) >> 2),
|
|
292
|
+
((v[0] & 0x03) << 4) | ((v[1] & 0x3c) >> 2),
|
|
293
|
+
((v[0] & 0x3c) >> 2) | ((country & 0x3) << 4),
|
|
294
|
+
(country & 0xfc) >> 2,
|
|
295
|
+
((country & 0x300) >> 8) | ((service & 0xf) << 2),
|
|
296
|
+
(service & 0x3f0) >> 4,
|
|
297
|
+
].map((word) => word & 0x3f);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/** @param {MaxiCodeEncodeOptions} options @returns {2|3|4|5} */
|
|
301
|
+
function normalizeMode(options) {
|
|
302
|
+
const mode = options.mode ?? 4;
|
|
303
|
+
if (!Number.isInteger(mode) || mode < 2 || mode > 5) {
|
|
304
|
+
throw new EncodeError('MaxiCode: mode must be 2, 3, 4 or 5');
|
|
305
|
+
}
|
|
306
|
+
return /** @type {2|3|4|5} */ (mode);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/** @param {MaxiCodeEncodeOptions} options @param {2|3|4|5} mode @param {number[]} [payload] */
|
|
310
|
+
function primaryCodewords(options, mode, payload = []) {
|
|
311
|
+
if (mode === 4 || mode === 5) {
|
|
312
|
+
const primary = [mode, ...payload.slice(0, 9)];
|
|
313
|
+
while (primary.length < 10) primary.push(33);
|
|
314
|
+
return primary;
|
|
315
|
+
}
|
|
316
|
+
const primary = options.primary;
|
|
317
|
+
if (!primary || typeof primary.postalCode !== 'string') {
|
|
318
|
+
throw new EncodeError(`MaxiCode Mode ${mode}: primary { postalCode, countryCode, serviceClass } is required`);
|
|
319
|
+
}
|
|
320
|
+
const country = primary.countryCode;
|
|
321
|
+
const service = primary.serviceClass;
|
|
322
|
+
if (!Number.isInteger(country) || country < 0 || country > 999 ||
|
|
323
|
+
!Number.isInteger(service) || service < 0 || service > 999) {
|
|
324
|
+
throw new EncodeError('MaxiCode: countryCode and serviceClass must be integers from 0 to 999');
|
|
325
|
+
}
|
|
326
|
+
return mode === 2
|
|
327
|
+
? mode2Primary(primary.postalCode, country, service)
|
|
328
|
+
: mode3Primary(primary.postalCode, country, service);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/** @param {number[]} data @param {number} eccLength @returns {number[]} */
|
|
332
|
+
function secondaryErrorCorrection(data, eccLength) {
|
|
333
|
+
const halfData = data.length / 2;
|
|
334
|
+
const even = [];
|
|
335
|
+
const odd = [];
|
|
336
|
+
for (let i = 0; i < data.length; i++) (i % 2 === 0 ? even : odd).push(data[i]);
|
|
337
|
+
if (even.length !== halfData || odd.length !== halfData) throw new EncodeError('MaxiCode: secondary data must have an even length');
|
|
338
|
+
const evenParity = rsEncode(even, eccLength / 2, GF64, 1);
|
|
339
|
+
const oddParity = rsEncode(odd, eccLength / 2, GF64, 1);
|
|
340
|
+
const parity = [];
|
|
341
|
+
for (let i = 0; i < evenParity.length; i++) {
|
|
342
|
+
parity.push(evenParity[i], oddParity[i]);
|
|
343
|
+
}
|
|
344
|
+
return parity;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/** @param {number[]} codewords @returns {BitMatrix} */
|
|
348
|
+
export function placeMaxiCodeCodewords(codewords) {
|
|
349
|
+
if (!Array.isArray(codewords) || codewords.length !== MAXICODE_CODEWORDS ||
|
|
350
|
+
codewords.some((word) => !Number.isInteger(word) || word < 0 || word > 63)) {
|
|
351
|
+
throw new EncodeError('MaxiCode: internal codeword stream must contain 144 six-bit values');
|
|
352
|
+
}
|
|
353
|
+
const matrix = new BitMatrix(MAXICODE_WIDTH, MAXICODE_HEIGHT);
|
|
354
|
+
for (let y = 0; y < MAXICODE_HEIGHT; y++) {
|
|
355
|
+
for (let x = 0; x < MAXICODE_WIDTH; x++) {
|
|
356
|
+
const sequence = MAXICODE_GRID[y * MAXICODE_WIDTH + x];
|
|
357
|
+
if (sequence === 0) continue;
|
|
358
|
+
const wire = sequence + 5;
|
|
359
|
+
const codeword = Math.floor(wire / 6) - 1;
|
|
360
|
+
const bit = 5 - (wire % 6);
|
|
361
|
+
matrix.setValue(x, y, ((codewords[codeword] >>> bit) & 1) !== 0);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Finder: the standard's hexagonal bull's-eye is sampled as concentric
|
|
366
|
+
// rings by the square-module interchange representation used by this SDK.
|
|
367
|
+
for (let y = 0; y < MAXICODE_HEIGHT; y++) for (let x = 0; x < MAXICODE_WIDTH; x++) {
|
|
368
|
+
const value = maxicodeFinderValue(x, y);
|
|
369
|
+
if (value !== null) matrix.setValue(x, y, value);
|
|
370
|
+
}
|
|
371
|
+
for (const [x, y] of MAXICODE_ORIENTATION_DARK) matrix.set(x, y);
|
|
372
|
+
return matrix;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Encode a MaxiCode symbol. Modes 2 and 3 carry a structured primary message;
|
|
377
|
+
* modes 4 and 5 encode an unstructured secondary text message.
|
|
378
|
+
*
|
|
379
|
+
* @param {string|Uint8Array|number[]} value
|
|
380
|
+
* @param {MaxiCodeEncodeOptions} [options]
|
|
381
|
+
* @returns {MaxiCodeMatrix}
|
|
382
|
+
*/
|
|
383
|
+
export function encodeMaxiCode(value, options = {}) {
|
|
384
|
+
const mode = normalizeMode(options);
|
|
385
|
+
const data = encodeMaxiCodeText(value);
|
|
386
|
+
const secondaryLength = mode === 5 ? 68 : 84;
|
|
387
|
+
const secondaryEccLength = mode === 5 ? 56 : 40;
|
|
388
|
+
const primaryDataLength = mode === 4 || mode === 5 ? 9 : 0;
|
|
389
|
+
if (data.length > primaryDataLength + secondaryLength) {
|
|
390
|
+
throw new EncodeError(
|
|
391
|
+
`MaxiCode Mode ${mode}: payload needs ${data.length} codewords, maximum is ` +
|
|
392
|
+
`${primaryDataLength + secondaryLength}`
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
const primary = primaryCodewords(options, mode, data);
|
|
396
|
+
const secondary = data.slice(primaryDataLength);
|
|
397
|
+
while (secondary.length < secondaryLength) secondary.push(33);
|
|
398
|
+
const primaryParity = rsEncode(primary, 10, GF64, 1);
|
|
399
|
+
const codewords = primary.concat(primaryParity, secondary, secondaryErrorCorrection(secondary, secondaryEccLength));
|
|
400
|
+
if (codewords.length !== MAXICODE_CODEWORDS) throw new EncodeError('MaxiCode: internal codeword length mismatch');
|
|
401
|
+
const matrix = /** @type {MaxiCodeMatrix} */ (placeMaxiCodeCodewords(codewords));
|
|
402
|
+
matrix.maxicode = {
|
|
403
|
+
mode,
|
|
404
|
+
codewords: codewords.slice(),
|
|
405
|
+
dataCodewords: secondary.slice(),
|
|
406
|
+
dataModules: MAXICODE_DATA_MODULES,
|
|
407
|
+
moduleShape: 'hexagonal',
|
|
408
|
+
width: MAXICODE_WIDTH,
|
|
409
|
+
height: MAXICODE_HEIGHT,
|
|
410
|
+
};
|
|
411
|
+
return matrix;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
export { mode2Primary, mode3Primary, primaryCodewords };
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Sythos Barcode Suite
|
|
3
|
+
*
|
|
4
|
+
* MIT License
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) 2026 Sythos
|
|
7
|
+
* SPDX-FileCopyrightText: 2026 Sythos (https://www.sythos.net)
|
|
8
|
+
* SPDX-License-Identifier: MIT
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { BitMatrix } from '../core/bit-matrix.js';
|
|
12
|
+
|
|
13
|
+
export interface MaxiCodePrimary {
|
|
14
|
+
postalCode: string;
|
|
15
|
+
countryCode: number;
|
|
16
|
+
serviceClass: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface MaxiCodeEncodeOptions {
|
|
20
|
+
mode?: 2 | 3 | 4 | 5;
|
|
21
|
+
primary?: MaxiCodePrimary;
|
|
22
|
+
charset?: 'latin1';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface MaxiCodeDecodeOptions {
|
|
26
|
+
inverted?: boolean | 'auto';
|
|
27
|
+
rotation?: 0 | 180 | 'auto';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface MaxiCodeDecodeResult {
|
|
31
|
+
readonly format: 'maxicode';
|
|
32
|
+
readonly text: string;
|
|
33
|
+
readonly bytes: Uint8Array;
|
|
34
|
+
readonly mode: 2 | 3 | 4 | 5;
|
|
35
|
+
readonly corrections: number;
|
|
36
|
+
readonly rows: 33;
|
|
37
|
+
readonly columns: 30;
|
|
38
|
+
readonly inverted: boolean;
|
|
39
|
+
readonly rotation: 0 | 180;
|
|
40
|
+
readonly primary?: MaxiCodePrimary;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface MaxiCodeDetection extends MaxiCodeDecodeResult {
|
|
44
|
+
readonly corners: readonly [
|
|
45
|
+
{ readonly x: number; readonly y: number },
|
|
46
|
+
{ readonly x: number; readonly y: number },
|
|
47
|
+
{ readonly x: number; readonly y: number },
|
|
48
|
+
{ readonly x: number; readonly y: number },
|
|
49
|
+
];
|
|
50
|
+
readonly moduleSize: number;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Encode a Latin-1 payload into a MaxiCode mode 2, 3, 4 or 5 matrix. */
|
|
54
|
+
export declare function encodeMaxiCode(
|
|
55
|
+
value: string | Uint8Array | number[],
|
|
56
|
+
options?: MaxiCodeEncodeOptions,
|
|
57
|
+
): BitMatrix;
|
|
58
|
+
|
|
59
|
+
/** Encode a payload into MaxiCode six-bit data codewords. */
|
|
60
|
+
export declare function encodeMaxiCodeText(
|
|
61
|
+
value: string | Uint8Array | number[],
|
|
62
|
+
): number[];
|
|
63
|
+
|
|
64
|
+
/** Place a complete 144-codeword stream into the MaxiCode module matrix. */
|
|
65
|
+
export declare function placeMaxiCodeCodewords(codewords: number[]): BitMatrix;
|
|
66
|
+
|
|
67
|
+
/** Decode an upright or 180-degree-rotated MaxiCode matrix. */
|
|
68
|
+
export declare function decodeMaxiCode(
|
|
69
|
+
matrix: BitMatrix,
|
|
70
|
+
options?: MaxiCodeDecodeOptions,
|
|
71
|
+
): MaxiCodeDecodeResult;
|
|
72
|
+
|
|
73
|
+
/** Decode a sequence of six-bit payload words. */
|
|
74
|
+
export declare function decodeMaxiCodeText(
|
|
75
|
+
words: number[],
|
|
76
|
+
): { text: string; bytes: Uint8Array };
|
|
77
|
+
|
|
78
|
+
/** Check fixed finder and orientation modules. */
|
|
79
|
+
export declare function maxicodeStructureMatches(matrix: BitMatrix): boolean;
|
|
80
|
+
|
|
81
|
+
/** Read the 144 six-bit codewords from a canonical module matrix. */
|
|
82
|
+
export declare function readMaxiCodeCodewords(matrix: BitMatrix): number[];
|
|
83
|
+
|
|
84
|
+
/** Detect a scaled, inverted or full-frame MaxiCode raster. */
|
|
85
|
+
export declare function detectMaxiCode(binaryImage: BitMatrix): MaxiCodeDetection | null;
|
|
86
|
+
|
|
87
|
+
/** Detect and decode a MaxiCode, or return null. */
|
|
88
|
+
export declare function detectAndDecodeMaxiCode(binaryImage: BitMatrix): MaxiCodeDetection | null;
|
|
89
|
+
|
|
90
|
+
export declare const MAXICODE_WIDTH: 30;
|
|
91
|
+
export declare const MAXICODE_HEIGHT: 33;
|
|
92
|
+
export declare const MAXICODE_CODEWORDS: 144;
|
|
93
|
+
export declare const MAXICODE_DATA_MODULES: 864;
|
|
94
|
+
export declare const MAXICODE_FIELD_SIZE: 64;
|
|
95
|
+
export declare const MAXICODE_GRID: readonly number[];
|
|
96
|
+
export declare const validateMaxiCodeTables: () => string[];
|
|
@@ -0,0 +1,44 @@
|
|
|
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
|
+
/** MaxiCode public entry points. @module maxicode */
|
|
32
|
+
|
|
33
|
+
export { encodeMaxiCode, encodeMaxiCodeText, placeMaxiCodeCodewords } from './encoder.js';
|
|
34
|
+
export { decodeMaxiCode, decodeMaxiCodeText, maxicodeStructureMatches, readMaxiCodeCodewords } from './decoder.js';
|
|
35
|
+
export { detectMaxiCode, detectAndDecodeMaxiCode } from './detector.js';
|
|
36
|
+
export {
|
|
37
|
+
MAXICODE_CODEWORDS,
|
|
38
|
+
MAXICODE_DATA_MODULES,
|
|
39
|
+
MAXICODE_FIELD_SIZE,
|
|
40
|
+
MAXICODE_GRID,
|
|
41
|
+
MAXICODE_HEIGHT,
|
|
42
|
+
MAXICODE_WIDTH,
|
|
43
|
+
validateMaxiCodeTables,
|
|
44
|
+
} from './tables.js';
|