@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.
Files changed (137) hide show
  1. package/LICENSE +22 -4
  2. package/NOTICE.md +10 -1
  3. package/README.md +416 -22
  4. package/bundle/sythos-barcode.esm.js +22176 -11991
  5. package/bundle/sythos-barcode.js +22059 -11991
  6. package/licenses/README.md +12 -1
  7. package/licenses/codablockf.license +51 -0
  8. package/licenses/code16k.license +52 -0
  9. package/licenses/code25.license +58 -0
  10. package/licenses/code32.license +51 -0
  11. package/licenses/dotcode.license +66 -0
  12. package/licenses/gs1-composite.license +106 -0
  13. package/licenses/gs1-databar.license +21 -14
  14. package/licenses/hanxin.license +95 -0
  15. package/licenses/maxicode.license +62 -0
  16. package/licenses/postal.license +61 -0
  17. package/licenses/pzn.license +53 -0
  18. package/licenses/telepen.license +73 -0
  19. package/llms.txt +62 -6
  20. package/package.json +47 -3
  21. package/src/index.d.ts +111 -6
  22. package/src/index.js +362 -13
  23. package/src/js/codablockf/decoder.js +198 -0
  24. package/src/js/codablockf/encoder.js +97 -0
  25. package/src/js/codablockf/index.js +3 -0
  26. package/src/js/code16k/decoder.js +303 -0
  27. package/src/js/code16k/detector.js +187 -0
  28. package/src/js/code16k/encoder.js +153 -0
  29. package/src/js/code16k/index.js +14 -0
  30. package/src/js/code16k/tables.js +152 -0
  31. package/src/js/composite/index.js +570 -0
  32. package/src/js/core/detection-contract.js +185 -0
  33. package/src/js/core/symbol-layout.js +155 -0
  34. package/src/js/databar/expanded.js +968 -0
  35. package/src/js/databar/index.js +4 -0
  36. package/src/js/databar/layout.js +192 -0
  37. package/src/js/databar/limited.js +533 -0
  38. package/src/js/databar/stacked-omnidirectional.js +548 -0
  39. package/src/js/databar/stacked.js +528 -0
  40. package/src/js/dotcode/decoder.js +548 -0
  41. package/src/js/dotcode/detector.js +251 -0
  42. package/src/js/dotcode/encoder.js +453 -0
  43. package/src/js/dotcode/index.js +34 -0
  44. package/src/js/dotcode/tables.js +168 -0
  45. package/src/js/hanxin/decoder.js +299 -0
  46. package/src/js/hanxin/detector.js +122 -0
  47. package/src/js/hanxin/encoder.js +259 -0
  48. package/src/js/hanxin/index.js +16 -0
  49. package/src/js/hanxin/tables.js +316 -0
  50. package/src/js/image/height-coded.js +164 -0
  51. package/src/js/maxicode/decoder.js +275 -0
  52. package/src/js/maxicode/detector.js +118 -0
  53. package/src/js/maxicode/encoder.js +438 -0
  54. package/src/js/maxicode/index.js +34 -0
  55. package/src/js/maxicode/tables.js +132 -0
  56. package/src/js/oned/code25.js +148 -0
  57. package/src/js/oned/index.js +21 -3
  58. package/src/js/oned/postal.js +889 -0
  59. package/src/js/oned/reader.js +272 -7
  60. package/src/js/oned/telepen.js +361 -0
  61. package/src/js/oned/writers.js +186 -12
  62. package/src/js/stacked128/common.js +208 -0
  63. package/src/ts/codablockf/decoder.ts +212 -0
  64. package/src/ts/codablockf/encoder.ts +128 -0
  65. package/src/ts/codablockf/index.d.ts +31 -0
  66. package/src/ts/codablockf/index.ts +6 -0
  67. package/src/ts/code16k/decoder.d.ts +25 -0
  68. package/src/ts/code16k/decoder.ts +313 -0
  69. package/src/ts/code16k/detector.d.ts +21 -0
  70. package/src/ts/code16k/detector.ts +203 -0
  71. package/src/ts/code16k/encoder.d.ts +26 -0
  72. package/src/ts/code16k/encoder.ts +198 -0
  73. package/src/ts/code16k/index.d.ts +10 -0
  74. package/src/ts/code16k/index.ts +47 -0
  75. package/src/ts/code16k/tables.d.ts +54 -0
  76. package/src/ts/code16k/tables.ts +195 -0
  77. package/src/ts/composite/index.d.ts +74 -0
  78. package/src/ts/composite/index.ts +547 -0
  79. package/src/ts/core/detection-contract.d.ts +119 -0
  80. package/src/ts/core/detection-contract.ts +267 -0
  81. package/src/ts/core/symbol-layout.d.ts +108 -0
  82. package/src/ts/core/symbol-layout.ts +236 -0
  83. package/src/ts/databar/expanded.d.ts +40 -0
  84. package/src/ts/databar/expanded.ts +948 -0
  85. package/src/ts/databar/index.d.ts +48 -0
  86. package/src/ts/databar/index.ts +48 -0
  87. package/src/ts/databar/layout.d.ts +122 -0
  88. package/src/ts/databar/layout.ts +275 -0
  89. package/src/ts/databar/limited.d.ts +85 -0
  90. package/src/ts/databar/limited.ts +553 -0
  91. package/src/ts/databar/stacked-omnidirectional.d.ts +128 -0
  92. package/src/ts/databar/stacked-omnidirectional.ts +559 -0
  93. package/src/ts/databar/stacked.d.ts +96 -0
  94. package/src/ts/databar/stacked.ts +549 -0
  95. package/src/ts/dotcode/decoder.d.ts +58 -0
  96. package/src/ts/dotcode/decoder.ts +467 -0
  97. package/src/ts/dotcode/detector.d.ts +63 -0
  98. package/src/ts/dotcode/detector.ts +263 -0
  99. package/src/ts/dotcode/encoder.d.ts +69 -0
  100. package/src/ts/dotcode/encoder.ts +427 -0
  101. package/src/ts/dotcode/index.d.ts +37 -0
  102. package/src/ts/dotcode/index.ts +72 -0
  103. package/src/ts/dotcode/tables.d.ts +74 -0
  104. package/src/ts/dotcode/tables.ts +174 -0
  105. package/src/ts/hanxin/decoder.d.ts +48 -0
  106. package/src/ts/hanxin/decoder.ts +314 -0
  107. package/src/ts/hanxin/detector.d.ts +45 -0
  108. package/src/ts/hanxin/detector.ts +124 -0
  109. package/src/ts/hanxin/encoder.d.ts +41 -0
  110. package/src/ts/hanxin/encoder.ts +290 -0
  111. package/src/ts/hanxin/index.d.ts +16 -0
  112. package/src/ts/hanxin/index.ts +18 -0
  113. package/src/ts/hanxin/tables.d.ts +80 -0
  114. package/src/ts/hanxin/tables.ts +348 -0
  115. package/src/ts/image/height-coded.d.ts +101 -0
  116. package/src/ts/image/height-coded.ts +227 -0
  117. package/src/ts/index.d.ts +46 -2
  118. package/src/ts/index.ts +380 -13
  119. package/src/ts/maxicode/decoder.ts +265 -0
  120. package/src/ts/maxicode/detector.ts +109 -0
  121. package/src/ts/maxicode/encoder.ts +414 -0
  122. package/src/ts/maxicode/index.d.ts +96 -0
  123. package/src/ts/maxicode/index.ts +44 -0
  124. package/src/ts/maxicode/tables.ts +136 -0
  125. package/src/ts/oned/code25.d.ts +42 -0
  126. package/src/ts/oned/code25.ts +170 -0
  127. package/src/ts/oned/index.d.ts +7 -2
  128. package/src/ts/oned/index.ts +45 -3
  129. package/src/ts/oned/postal.d.ts +44 -0
  130. package/src/ts/oned/postal.ts +819 -0
  131. package/src/ts/oned/reader.d.ts +35 -0
  132. package/src/ts/oned/reader.ts +261 -7
  133. package/src/ts/oned/telepen.d.ts +65 -0
  134. package/src/ts/oned/telepen.ts +368 -0
  135. package/src/ts/oned/writers.d.ts +32 -0
  136. package/src/ts/oned/writers.ts +184 -13
  137. package/src/ts/stacked128/common.ts +225 -0
@@ -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
  *
@@ -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 { ean13CheckDigit, upceToUpcaBody } from './writers.js';
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
- if (!enabled) return true;
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;