@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
package/src/ts/oned/reader.d.ts
CHANGED
|
@@ -92,6 +92,41 @@ export declare function decodeMSI(row: Uint8Array, options?: {
|
|
|
92
92
|
format: 'msi';
|
|
93
93
|
text: string;
|
|
94
94
|
} | null;
|
|
95
|
+
/** Decode Italian Code 32 from a Code 39-shaped scanline. */
|
|
96
|
+
export declare function decodeCode32(row: Uint8Array): {
|
|
97
|
+
format: 'code32';
|
|
98
|
+
text: string;
|
|
99
|
+
checkDigit: boolean;
|
|
100
|
+
} | null;
|
|
101
|
+
/** Decode PZN-7 or PZN-8 from a Code 39-shaped scanline. */
|
|
102
|
+
export declare function decodePZN(row: Uint8Array): {
|
|
103
|
+
format: 'pzn';
|
|
104
|
+
text: string;
|
|
105
|
+
pznVariant: 'pzn7' | 'pzn8';
|
|
106
|
+
checkDigit: boolean;
|
|
107
|
+
} | null;
|
|
108
|
+
/** Decode Industrial/Standard 2 of 5. */
|
|
109
|
+
export declare function decodeIndustrial2of5(row: Uint8Array, options?: {
|
|
110
|
+
checkDigit?: boolean;
|
|
111
|
+
profile?: 'camera';
|
|
112
|
+
}): {
|
|
113
|
+
format: 'industrial2of5';
|
|
114
|
+
text: string;
|
|
115
|
+
checkDigit: boolean;
|
|
116
|
+
} | null;
|
|
117
|
+
/** Decode IATA 2 of 5. */
|
|
118
|
+
export declare function decodeIATA2of5(row: Uint8Array, options?: {
|
|
119
|
+
checkDigit?: boolean;
|
|
120
|
+
profile?: 'camera';
|
|
121
|
+
}): {
|
|
122
|
+
format: 'iata2of5';
|
|
123
|
+
text: string;
|
|
124
|
+
checkDigit: boolean;
|
|
125
|
+
} | null;
|
|
126
|
+
/** Decode the canonical Standard 2 of 5 frame. */
|
|
127
|
+
export declare const decodeStandard2of5: typeof decodeIndustrial2of5;
|
|
128
|
+
/** Decode any supported Code 25 family frame. */
|
|
129
|
+
export declare const decodeCode25: typeof decodeIndustrial2of5;
|
|
95
130
|
/**
|
|
96
131
|
* Read every linear symbol found in a binarized image.
|
|
97
132
|
*
|
package/src/ts/oned/reader.ts
CHANGED
|
@@ -59,12 +59,21 @@ import {
|
|
|
59
59
|
ITF, CODABAR, CODABAR_START_STOP,
|
|
60
60
|
CODE11, CODE11_START_STOP, MSI_START, MSI_STOP, MSI_BIT,
|
|
61
61
|
} from './patterns.js';
|
|
62
|
-
import {
|
|
62
|
+
import {
|
|
63
|
+
ean13CheckDigit, upceToUpcaBody,
|
|
64
|
+
decodeCode32Payload, decodePZNPayload,
|
|
65
|
+
} from './writers.js';
|
|
63
66
|
import {
|
|
64
67
|
EAN2_PARITY, EAN5_PARITY, EAN_ADDON_START, EAN_ADDON_SEPARATOR,
|
|
65
68
|
ean5Checksum,
|
|
66
69
|
} from './addons.js';
|
|
67
70
|
import { decodeDataBar14Scanline } from '../databar/decoder.js';
|
|
71
|
+
import { decodeTelepen } from './telepen.js';
|
|
72
|
+
import {
|
|
73
|
+
CODE25_DIGIT_PATTERNS, CODE25_VARIANTS, CODE25_MAX_DIGITS,
|
|
74
|
+
code25CheckDigit,
|
|
75
|
+
} from './code25.js';
|
|
76
|
+
import { decodePostal } from './postal.js';
|
|
68
77
|
|
|
69
78
|
/* ------------------------------------------------------------------ *
|
|
70
79
|
* Pattern matching primitives
|
|
@@ -933,6 +942,31 @@ function decodeCode39(row, options = {}) {
|
|
|
933
942
|
return { format: 'code39', text };
|
|
934
943
|
}
|
|
935
944
|
|
|
945
|
+
/** Decode Italian Code 32 after validating its Code 39/base-32 payload. */
|
|
946
|
+
export function decodeCode32(row) {
|
|
947
|
+
const base = decodeCode39(row);
|
|
948
|
+
if (!base) return null;
|
|
949
|
+
const parsed = decodeCode32Payload(base.text);
|
|
950
|
+
return parsed
|
|
951
|
+
? { format: 'code32', text: parsed.text, checkDigit: true }
|
|
952
|
+
: null;
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
/** Decode PZN-7 or PZN-8 after validating its Code 39 payload/check digit. */
|
|
956
|
+
export function decodePZN(row) {
|
|
957
|
+
const base = decodeCode39(row);
|
|
958
|
+
if (!base) return null;
|
|
959
|
+
const parsed = decodePZNPayload(base.text);
|
|
960
|
+
return parsed
|
|
961
|
+
? {
|
|
962
|
+
format: 'pzn',
|
|
963
|
+
text: parsed.text,
|
|
964
|
+
pznVariant: parsed.variant,
|
|
965
|
+
checkDigit: true,
|
|
966
|
+
}
|
|
967
|
+
: null;
|
|
968
|
+
}
|
|
969
|
+
|
|
936
970
|
/* ------------------------------------------------------------------ *
|
|
937
971
|
* Code 93
|
|
938
972
|
* ------------------------------------------------------------------ */
|
|
@@ -1062,6 +1096,166 @@ function decodeITF(row) {
|
|
|
1062
1096
|
return { format: 'itf', text: digits.join('') };
|
|
1063
1097
|
}
|
|
1064
1098
|
|
|
1099
|
+
/* ------------------------------------------------------------------ *
|
|
1100
|
+
* Code 25 family
|
|
1101
|
+
* ------------------------------------------------------------------ */
|
|
1102
|
+
|
|
1103
|
+
/** Convert a Code 25 width description into the measured module widths. */
|
|
1104
|
+
function code25Widths(pattern, ratio) {
|
|
1105
|
+
return [...pattern].map((width) => Number(width) > 1 ? ratio : 1);
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
/**
|
|
1109
|
+
* Match a Code 25 guard or digit at an exact offset. The writer permits a
|
|
1110
|
+
* configurable wide-bar ratio, so a small bounded set of ratios is considered
|
|
1111
|
+
* while matching rather than assuming one printer's dimensions.
|
|
1112
|
+
*
|
|
1113
|
+
* @param {Uint8Array} row
|
|
1114
|
+
* @param {number} start
|
|
1115
|
+
* @param {string} pattern
|
|
1116
|
+
* @param {number|undefined} preferredRatio
|
|
1117
|
+
* @returns {{end:number, score:number, ratio:number}|null}
|
|
1118
|
+
*/
|
|
1119
|
+
function matchCode25(row, start, pattern, preferredRatio) {
|
|
1120
|
+
const counters = new Array(pattern.length).fill(0);
|
|
1121
|
+
if (!recordPattern(row, start, counters)) return null;
|
|
1122
|
+
const candidates = preferredRatio
|
|
1123
|
+
? [preferredRatio]
|
|
1124
|
+
: [2, 3, 4, 5, 6, 7, 8];
|
|
1125
|
+
let best = null;
|
|
1126
|
+
for (const ratio of candidates) {
|
|
1127
|
+
const score = patternVariance(counters, code25Widths(pattern, ratio), 0.75);
|
|
1128
|
+
if (!Number.isFinite(score)) continue;
|
|
1129
|
+
if (!best || score < best.score) best = { score, ratio };
|
|
1130
|
+
}
|
|
1131
|
+
if (!best || best.score >= 0.38) return null;
|
|
1132
|
+
return {
|
|
1133
|
+
end: start + counters.reduce((sum, width) => sum + width, 0),
|
|
1134
|
+
score: best.score,
|
|
1135
|
+
ratio: best.ratio,
|
|
1136
|
+
};
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
/** Find the strongest Code 25 start guard in a scanline. */
|
|
1140
|
+
function findCode25Start(row, startPattern) {
|
|
1141
|
+
let best = null;
|
|
1142
|
+
for (let offset = 0; offset < row.length; offset++) {
|
|
1143
|
+
if (row[offset] !== 1 || (offset > 0 && row[offset - 1] === 1)) continue;
|
|
1144
|
+
const found = matchCode25(row, offset, startPattern);
|
|
1145
|
+
if (found && (!best || found.score < best.score)) {
|
|
1146
|
+
best = { start: offset, ...found };
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
return best;
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
/**
|
|
1153
|
+
* Decode a Code 25/Industrial 2 of 5/IATA 2 of 5 scanline.
|
|
1154
|
+
*
|
|
1155
|
+
* Standard 2 of 5 and Industrial 2 of 5 intentionally share the canonical
|
|
1156
|
+
* industrial frame in this SDK; the public variant labels remain explicit so
|
|
1157
|
+
* callers can select the terminology used by their data source.
|
|
1158
|
+
*
|
|
1159
|
+
* @param {Uint8Array} row
|
|
1160
|
+
* @param {'standard'|'industrial'|'iata'} variant
|
|
1161
|
+
* @param {object} options
|
|
1162
|
+
* @returns {{format:'industrial2of5'|'iata2of5',text:string,checkDigit:boolean}|null}
|
|
1163
|
+
*/
|
|
1164
|
+
function decodeCode25Variant(row, variant, options = {}) {
|
|
1165
|
+
const profile = CODE25_VARIANTS[variant];
|
|
1166
|
+
const start = findCode25Start(row, profile.start);
|
|
1167
|
+
if (!start) return null;
|
|
1168
|
+
|
|
1169
|
+
let offset = start.end;
|
|
1170
|
+
let digits = '';
|
|
1171
|
+
let stop = null;
|
|
1172
|
+
// The IATA start guard is all narrow bars and therefore carries no ratio
|
|
1173
|
+
// information. Infer its ratio from the first data digit instead of
|
|
1174
|
+
// hard-coding the first candidate (which would make a 3:1 symbol look like
|
|
1175
|
+
// a truncated stop pattern).
|
|
1176
|
+
let ratio = variant === 'iata' ? undefined : start.ratio;
|
|
1177
|
+
const counters = new Array(10).fill(0);
|
|
1178
|
+
|
|
1179
|
+
for (let count = 0; count < CODE25_MAX_DIGITS; count++) {
|
|
1180
|
+
const candidateStop = matchCode25(row, offset, profile.stop, ratio);
|
|
1181
|
+
const stopGap = candidateStop
|
|
1182
|
+
? (() => {
|
|
1183
|
+
let nextDark = candidateStop.end;
|
|
1184
|
+
while (nextDark < row.length && row[nextDark] === 0) nextDark++;
|
|
1185
|
+
return nextDark === row.length ? Infinity : nextDark - candidateStop.end;
|
|
1186
|
+
})()
|
|
1187
|
+
: 0;
|
|
1188
|
+
if (candidateStop && stopGap >= Math.max(3, Math.ceil((ratio ?? candidateStop.ratio) * 3))) {
|
|
1189
|
+
stop = candidateStop;
|
|
1190
|
+
break;
|
|
1191
|
+
}
|
|
1192
|
+
if (!recordPattern(row, offset, counters)) return null;
|
|
1193
|
+
|
|
1194
|
+
let bestDigit = null;
|
|
1195
|
+
for (let digit = 0; digit < CODE25_DIGIT_PATTERNS.length; digit++) {
|
|
1196
|
+
const candidates = ratio ? [ratio] : [2, 3, 4, 5, 6, 7, 8];
|
|
1197
|
+
for (const candidateRatio of candidates) {
|
|
1198
|
+
const score = patternVariance(
|
|
1199
|
+
counters,
|
|
1200
|
+
code25Widths(CODE25_DIGIT_PATTERNS[digit], candidateRatio),
|
|
1201
|
+
0.75,
|
|
1202
|
+
);
|
|
1203
|
+
if (!Number.isFinite(score)) continue;
|
|
1204
|
+
if (!bestDigit || score < bestDigit.score) {
|
|
1205
|
+
bestDigit = { digit, score, ratio: candidateRatio };
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
if (!bestDigit || bestDigit.score >= 0.38) return null;
|
|
1210
|
+
ratio ??= bestDigit.ratio;
|
|
1211
|
+
digits += String(bestDigit.digit);
|
|
1212
|
+
offset += counters.reduce((sum, width) => sum + width, 0);
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
if (!stop || digits.length === 0) return null;
|
|
1216
|
+
const stopEnd = stop.end;
|
|
1217
|
+
let nextDark = stopEnd;
|
|
1218
|
+
while (nextDark < row.length && row[nextDark] === 0) nextDark++;
|
|
1219
|
+
if (nextDark !== row.length && nextDark - stopEnd < Math.max(3, Math.ceil(start.ratio * 3))) {
|
|
1220
|
+
return null;
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
let checkDigit = false;
|
|
1224
|
+
if (options.checkDigit === true || options.profile === 'camera') {
|
|
1225
|
+
if (digits.length < 2) return null;
|
|
1226
|
+
const body = digits.slice(0, -1);
|
|
1227
|
+
if (code25CheckDigit(body) !== Number(digits.at(-1))) return null;
|
|
1228
|
+
digits = body;
|
|
1229
|
+
checkDigit = true;
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
return {
|
|
1233
|
+
format: profile.id,
|
|
1234
|
+
text: digits,
|
|
1235
|
+
checkDigit,
|
|
1236
|
+
};
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
/** Decode the canonical Standard/Industrial 2 of 5 frame. */
|
|
1240
|
+
export function decodeIndustrial2of5(row, options = {}) {
|
|
1241
|
+
return decodeCode25Variant(row, 'industrial', options);
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
/** Decode IATA 2 of 5 with its shorter guard frame. */
|
|
1245
|
+
export function decodeIATA2of5(row, options = {}) {
|
|
1246
|
+
return decodeCode25Variant(row, 'iata', options);
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
/** Decode the canonical Standard 2 of 5 frame. */
|
|
1250
|
+
export function decodeStandard2of5(row, options = {}) {
|
|
1251
|
+
return decodeCode25Variant(row, 'standard', options);
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
/** Decode any Code 25 family frame using the canonical Standard profile. */
|
|
1255
|
+
export function decodeCode25(row, options = {}) {
|
|
1256
|
+
return decodeCode25Variant(row, 'standard', options);
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1065
1259
|
/* ------------------------------------------------------------------ *
|
|
1066
1260
|
* Codabar
|
|
1067
1261
|
* ------------------------------------------------------------------ */
|
|
@@ -1134,9 +1328,15 @@ const DECODERS = [
|
|
|
1134
1328
|
['code128', decodeCode128],
|
|
1135
1329
|
['code11', decodeCode11],
|
|
1136
1330
|
['msi', decodeMSI],
|
|
1331
|
+
['telepen', decodeTelepen],
|
|
1332
|
+
['telepennumeric', (row, options = {}) => decodeTelepen(row, { ...options, numeric: true })],
|
|
1137
1333
|
['gs1databar14', decodeDataBar14Scanline],
|
|
1334
|
+
['code32', decodeCode32],
|
|
1335
|
+
['pzn', decodePZN],
|
|
1138
1336
|
['code39', decodeCode39],
|
|
1139
1337
|
['code93', decodeCode93],
|
|
1338
|
+
['industrial2of5', decodeIndustrial2of5],
|
|
1339
|
+
['iata2of5', decodeIATA2of5],
|
|
1140
1340
|
['itf', decodeITF],
|
|
1141
1341
|
['codabar', decodeCodabar],
|
|
1142
1342
|
];
|
|
@@ -1184,19 +1384,26 @@ function cameraRowGeometry(row) {
|
|
|
1184
1384
|
}
|
|
1185
1385
|
|
|
1186
1386
|
/** @param {string} format @param {object} options @returns {boolean|null} */
|
|
1187
|
-
function checksumStatus(format, options) {
|
|
1387
|
+
function checksumStatus(format, options, result = null) {
|
|
1188
1388
|
if (format === 'ean13' || format === 'ean8' || format === 'upca' || format === 'upce' ||
|
|
1189
1389
|
format === 'code93' || format === 'code128' || format === 'gs1128' ||
|
|
1190
1390
|
format === 'gs1databar14') return true;
|
|
1191
1391
|
if (format === 'code11' || format === 'msi' || format === 'code39') {
|
|
1192
1392
|
return options.profile === 'camera' || options.checkDigit === true ? true : null;
|
|
1193
1393
|
}
|
|
1394
|
+
if (format === 'code32' || format === 'pzn') return true;
|
|
1395
|
+
if (format === 'industrial2of5' || format === 'iata2of5') {
|
|
1396
|
+
return result?.checkDigit === true ? true : null;
|
|
1397
|
+
}
|
|
1398
|
+
if (format === 'telepen' || format === 'telepennumeric') return true;
|
|
1399
|
+
if (format === 'postnet' || format === 'planet' || format === 'rm4scc'
|
|
1400
|
+
|| format === 'auspost' || format === 'japanpost' || format === 'imb') return true;
|
|
1194
1401
|
return null;
|
|
1195
1402
|
}
|
|
1196
1403
|
|
|
1197
1404
|
/** @param {object} result @param {object} geometry @param {Set<number>} rows @param {object} options @returns {object} */
|
|
1198
1405
|
function cameraMetadata(result, geometry, rows, options) {
|
|
1199
|
-
const checksum = checksumStatus(result.format, options);
|
|
1406
|
+
const checksum = checksumStatus(result.format, options, result);
|
|
1200
1407
|
const consistency = Math.min(1, rows.size / 3);
|
|
1201
1408
|
const confidence = Math.min(1, 0.4 + (geometry.quietZone ? 0.2 : 0) +
|
|
1202
1409
|
(checksum === true ? 0.2 : 0) + consistency * 0.2);
|
|
@@ -1231,8 +1438,21 @@ export function decodeOneD(image, options = {}) {
|
|
|
1231
1438
|
const cameraProfile = profile === 'camera';
|
|
1232
1439
|
const enabled = formats ? new Set(formats) : null;
|
|
1233
1440
|
const active = DECODERS.filter(([id]) => {
|
|
1234
|
-
|
|
1441
|
+
// Telepen Numeric uses the same guards as Telepen Alpha and is therefore
|
|
1442
|
+
// only attempted when the caller explicitly requests the numeric mode.
|
|
1443
|
+
// Auto-detecting it as ASCII would turn valid digit pairs into plausible
|
|
1444
|
+
// but incorrect control characters.
|
|
1445
|
+
if (!enabled) return id !== 'telepennumeric';
|
|
1235
1446
|
if (enabled.has(id)) return true;
|
|
1447
|
+
if (id === 'telepen') return enabled.has('telepen-alpha');
|
|
1448
|
+
if (id === 'telepennumeric') return enabled.has('telepen-numeric');
|
|
1449
|
+
if (id === 'code32') return enabled.has('italian-pharmacode');
|
|
1450
|
+
if (id === 'pzn') return enabled.has('pzn7') || enabled.has('pzn8');
|
|
1451
|
+
if (id === 'industrial2of5') {
|
|
1452
|
+
return enabled.has('code2of5') || enabled.has('standard2of5')
|
|
1453
|
+
|| enabled.has('standard-2-of-5') || enabled.has('industrial-2-of-5');
|
|
1454
|
+
}
|
|
1455
|
+
if (id === 'iata2of5') return enabled.has('iata-2-of-5');
|
|
1236
1456
|
if (id === 'ean13' || id === 'ean8' || id === 'upca' || id === 'upce') {
|
|
1237
1457
|
return enabled.has('ean2') || enabled.has('ean5') ||
|
|
1238
1458
|
(id === 'ean13' && enabled.has('isbn'));
|
|
@@ -1241,10 +1461,27 @@ export function decodeOneD(image, options = {}) {
|
|
|
1241
1461
|
if (id === 'gs1databar14') return enabled.has('databar') || enabled.has('gs1-databar14');
|
|
1242
1462
|
return false;
|
|
1243
1463
|
});
|
|
1244
|
-
if (active.length === 0) return [];
|
|
1245
|
-
|
|
1246
1464
|
const results = [];
|
|
1247
1465
|
const seen = new Set();
|
|
1466
|
+
// Postal symbols carry information in the vertical bar state rather than
|
|
1467
|
+
// in a horizontal run-width alphabet. Decode that shared path once before
|
|
1468
|
+
// the ordinary scanline readers; an explicit non-postal filter yields no
|
|
1469
|
+
// postal attempt and therefore cannot broaden a caller's format request.
|
|
1470
|
+
const postalResults = decodePostal(image, {
|
|
1471
|
+
...options,
|
|
1472
|
+
profile: cameraProfile ? 'camera' : undefined,
|
|
1473
|
+
formats: formats ? formats : undefined,
|
|
1474
|
+
});
|
|
1475
|
+
const postalRow = image.height >> 1;
|
|
1476
|
+
for (const result of postalResults) {
|
|
1477
|
+
const key = `${result.format}:${result.text}`;
|
|
1478
|
+
if (seen.has(key)) continue;
|
|
1479
|
+
seen.add(key);
|
|
1480
|
+
results.push({ ...result, row: postalRow });
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
if (active.length === 0) return results;
|
|
1484
|
+
|
|
1248
1485
|
const height = image.height;
|
|
1249
1486
|
const buffer = new Uint8Array(image.width);
|
|
1250
1487
|
const cameraCandidates = new Map();
|
|
@@ -1270,7 +1507,8 @@ export function decodeOneD(image, options = {}) {
|
|
|
1270
1507
|
try {
|
|
1271
1508
|
// Code 11 and MSI checks are optional in their base standards, but
|
|
1272
1509
|
// a camera frame cannot safely promote their short unchecked forms.
|
|
1273
|
-
const decoderOptions = cameraProfile && (id === 'code11' || id === 'msi'
|
|
1510
|
+
const decoderOptions = cameraProfile && (id === 'code11' || id === 'msi'
|
|
1511
|
+
|| id === 'industrial2of5' || id === 'iata2of5')
|
|
1274
1512
|
? { ...options, checkDigit: true }
|
|
1275
1513
|
: options;
|
|
1276
1514
|
result = decoder(scan, decoderOptions);
|
|
@@ -1298,6 +1536,22 @@ export function decodeOneD(image, options = {}) {
|
|
|
1298
1536
|
if (!enabled.has('gs1128') && !enabled.has('code128')) continue;
|
|
1299
1537
|
} else if (result.format === 'gs1databar14') {
|
|
1300
1538
|
if (!enabled.has('gs1databar14') && !enabled.has('databar') && !enabled.has('gs1-databar14')) continue;
|
|
1539
|
+
} else if (result.format === 'telepen') {
|
|
1540
|
+
if (!enabled.has('telepen') && !enabled.has('telepen-alpha')) continue;
|
|
1541
|
+
} else if (result.format === 'telepennumeric') {
|
|
1542
|
+
if (!enabled.has('telepennumeric') && !enabled.has('telepen-numeric')) continue;
|
|
1543
|
+
} else if (result.format === 'code32') {
|
|
1544
|
+
if (!enabled.has('code32') && !enabled.has('italian-pharmacode')) continue;
|
|
1545
|
+
} else if (result.format === 'pzn') {
|
|
1546
|
+
if (!enabled.has('pzn') && !enabled.has('pzn7') && !enabled.has('pzn8')) continue;
|
|
1547
|
+
if (enabled.has('pzn7') && result.pznVariant !== 'pzn7') continue;
|
|
1548
|
+
if (enabled.has('pzn8') && result.pznVariant !== 'pzn8') continue;
|
|
1549
|
+
} else if (result.format === 'industrial2of5') {
|
|
1550
|
+
if (!enabled.has('industrial2of5') && !enabled.has('industrial-2-of-5')
|
|
1551
|
+
&& !enabled.has('code2of5') && !enabled.has('standard2of5')
|
|
1552
|
+
&& !enabled.has('standard-2-of-5')) continue;
|
|
1553
|
+
} else if (result.format === 'iata2of5') {
|
|
1554
|
+
if (!enabled.has('iata2of5') && !enabled.has('iata-2-of-5')) continue;
|
|
1301
1555
|
} else if (!enabled.has(result.format)) {
|
|
1302
1556
|
continue;
|
|
1303
1557
|
}
|
|
@@ -0,0 +1,65 @@
|
|
|
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
|
+
*
|
|
9
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
10
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
11
|
+
* in the Software without restriction, including without limitation the rights
|
|
12
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
13
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
14
|
+
* furnished to do so, subject to the following conditions:
|
|
15
|
+
*
|
|
16
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
17
|
+
* copies or substantial portions of the Software.
|
|
18
|
+
*
|
|
19
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
20
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
21
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
22
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
23
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
24
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
25
|
+
* SOFTWARE.
|
|
26
|
+
*
|
|
27
|
+
* SPDX-License-Identifier: MIT
|
|
28
|
+
*
|
|
29
|
+
* Original work. No code from any other barcode implementation.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
export declare const TELEPEN_START_VALUE = 95;
|
|
33
|
+
export declare const TELEPEN_STOP_VALUE = 122;
|
|
34
|
+
export declare const TELEPEN_MAX_LENGTH = 500;
|
|
35
|
+
|
|
36
|
+
/** Return the original Telepen run-width pattern for one seven-bit value. */
|
|
37
|
+
export declare function telepenPattern(value: number): string;
|
|
38
|
+
|
|
39
|
+
/** Encode Telepen Alpha or Telepen Numeric when `numeric` is true. */
|
|
40
|
+
export declare function encodeTelepen(value: string, options?: {
|
|
41
|
+
numeric?: boolean;
|
|
42
|
+
mode?: 'ascii' | 'numeric';
|
|
43
|
+
telepenMode?: 'ascii' | 'numeric';
|
|
44
|
+
}): import('../core/bit-matrix.js').BitMatrix;
|
|
45
|
+
|
|
46
|
+
/** Encode a strict even-length Telepen Numeric payload. */
|
|
47
|
+
export declare function encodeTelepenNumeric(value: string): import('../core/bit-matrix.js').BitMatrix;
|
|
48
|
+
|
|
49
|
+
/** Decode a binarized Telepen scanline. */
|
|
50
|
+
export declare function decodeTelepen(row: Uint8Array, options?: {
|
|
51
|
+
numeric?: boolean;
|
|
52
|
+
mode?: 'ascii' | 'numeric';
|
|
53
|
+
telepenMode?: 'ascii' | 'numeric';
|
|
54
|
+
}): {
|
|
55
|
+
format: 'telepen' | 'telepennumeric';
|
|
56
|
+
text: string;
|
|
57
|
+
mode: 'ascii' | 'numeric';
|
|
58
|
+
} | null;
|
|
59
|
+
|
|
60
|
+
/** Decode Telepen Numeric explicitly. */
|
|
61
|
+
export declare function decodeTelepenNumeric(row: Uint8Array): {
|
|
62
|
+
format: 'telepennumeric';
|
|
63
|
+
text: string;
|
|
64
|
+
mode: 'numeric';
|
|
65
|
+
} | null;
|