@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
@@ -0,0 +1,136 @@
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
+ /**
32
+ * MaxiCode structural constants and the module sequence from ISO/IEC 16023
33
+ * Figure 5. The sequence is transcribed from the published standard figure,
34
+ * not imported from another implementation. Zero entries are the alternating
35
+ * row gaps, finder reservation and orientation markers; values 1..864 identify
36
+ * the six-bit data modules in wire order.
37
+ *
38
+ * @module maxicode/tables
39
+ */
40
+
41
+ export const MAXICODE_WIDTH = 30;
42
+ export const MAXICODE_HEIGHT = 33;
43
+ export const MAXICODE_CODEWORDS = 144;
44
+ export const MAXICODE_DATA_MODULES = 864;
45
+ export const MAXICODE_FIELD_SIZE = 64;
46
+
47
+ /** ISO 16023 Figure 5, laid out as 33 rows of 30 logical positions. */
48
+ const MAXICODE_GRID_ROWS = [
49
+ [122, 121, 128, 127, 134, 133, 140, 139, 146, 145, 152, 151, 158, 157, 164, 163, 170, 169, 176, 175, 182, 181, 188, 187, 194, 193, 200, 199, 0, 0],
50
+ [124, 123, 130, 129, 136, 135, 142, 141, 148, 147, 154, 153, 160, 159, 166, 165, 172, 171, 178, 177, 184, 183, 190, 189, 196, 195, 202, 201, 817, 0],
51
+ [126, 125, 132, 131, 138, 137, 144, 143, 150, 149, 156, 155, 162, 161, 168, 167, 174, 173, 180, 179, 186, 185, 192, 191, 198, 197, 204, 203, 819, 818],
52
+ [284, 283, 278, 277, 272, 271, 266, 265, 260, 259, 254, 253, 248, 247, 242, 241, 236, 235, 230, 229, 224, 223, 218, 217, 212, 211, 206, 205, 820, 0],
53
+ [286, 285, 280, 279, 274, 273, 268, 267, 262, 261, 256, 255, 250, 249, 244, 243, 238, 237, 232, 231, 226, 225, 220, 219, 214, 213, 208, 207, 822, 821],
54
+ [288, 287, 282, 281, 276, 275, 270, 269, 264, 263, 258, 257, 252, 251, 246, 245, 240, 239, 234, 233, 228, 227, 222, 221, 216, 215, 210, 209, 823, 0],
55
+ [290, 289, 296, 295, 302, 301, 308, 307, 314, 313, 320, 319, 326, 325, 332, 331, 338, 337, 344, 343, 350, 349, 356, 355, 362, 361, 368, 367, 825, 824],
56
+ [292, 291, 298, 297, 304, 303, 310, 309, 316, 315, 322, 321, 328, 327, 334, 333, 340, 339, 346, 345, 352, 351, 358, 357, 364, 363, 370, 369, 826, 0],
57
+ [294, 293, 300, 299, 306, 305, 312, 311, 318, 317, 324, 323, 330, 329, 336, 335, 342, 341, 348, 347, 354, 353, 360, 359, 366, 365, 372, 371, 828, 827],
58
+ [410, 409, 404, 403, 398, 397, 392, 391, 80, 79, 0, 0, 14, 13, 38, 37, 3, 0, 45, 44, 110, 109, 386, 385, 380, 379, 374, 373, 829, 0],
59
+ [412, 411, 406, 405, 400, 399, 394, 393, 82, 81, 41, 0, 16, 15, 40, 39, 4, 0, 0, 46, 112, 111, 388, 387, 382, 381, 376, 375, 831, 830],
60
+ [414, 413, 408, 407, 402, 401, 396, 395, 84, 83, 42, 0, 0, 0, 0, 0, 6, 5, 48, 47, 114, 113, 390, 389, 384, 383, 378, 377, 832, 0],
61
+ [416, 415, 422, 421, 428, 427, 104, 103, 56, 55, 17, 0, 0, 0, 0, 0, 0, 0, 21, 20, 86, 85, 434, 433, 440, 439, 446, 445, 834, 833],
62
+ [418, 417, 424, 423, 430, 429, 106, 105, 58, 57, 0, 0, 0, 0, 0, 0, 0, 0, 23, 22, 88, 87, 436, 435, 442, 441, 448, 447, 835, 0],
63
+ [420, 419, 426, 425, 432, 431, 108, 107, 60, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 90, 89, 438, 437, 444, 443, 450, 449, 837, 836],
64
+ [482, 481, 476, 475, 470, 469, 49, 0, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 54, 53, 464, 463, 458, 457, 452, 451, 838, 0],
65
+ [484, 483, 478, 477, 472, 471, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 466, 465, 460, 459, 454, 453, 840, 839],
66
+ [486, 485, 480, 479, 474, 473, 52, 51, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 43, 468, 467, 462, 461, 456, 455, 841, 0],
67
+ [488, 487, 494, 493, 500, 499, 98, 97, 62, 61, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 92, 91, 506, 505, 512, 511, 518, 517, 843, 842],
68
+ [490, 489, 496, 495, 502, 501, 100, 99, 64, 63, 0, 0, 0, 0, 0, 0, 0, 0, 29, 28, 94, 93, 508, 507, 514, 513, 520, 519, 844, 0],
69
+ [492, 491, 498, 497, 504, 503, 102, 101, 66, 65, 18, 0, 0, 0, 0, 0, 0, 0, 19, 30, 96, 95, 510, 509, 516, 515, 522, 521, 846, 845],
70
+ [560, 559, 554, 553, 548, 547, 542, 541, 74, 73, 33, 0, 0, 0, 0, 0, 0, 11, 68, 67, 116, 115, 536, 535, 530, 529, 524, 523, 847, 0],
71
+ [562, 561, 556, 555, 550, 549, 544, 543, 76, 75, 0, 0, 8, 7, 36, 35, 12, 0, 70, 69, 118, 117, 538, 537, 532, 531, 526, 525, 849, 848],
72
+ [564, 563, 558, 557, 552, 551, 546, 545, 78, 77, 0, 34, 10, 9, 26, 25, 0, 0, 72, 71, 120, 119, 540, 539, 534, 533, 528, 527, 850, 0],
73
+ [566, 565, 572, 571, 578, 577, 584, 583, 590, 589, 596, 595, 602, 601, 608, 607, 614, 613, 620, 619, 626, 625, 632, 631, 638, 637, 644, 643, 852, 851],
74
+ [568, 567, 574, 573, 580, 579, 586, 585, 592, 591, 598, 597, 604, 603, 610, 609, 616, 615, 622, 621, 628, 627, 634, 633, 640, 639, 646, 645, 853, 0],
75
+ [570, 569, 576, 575, 582, 581, 588, 587, 594, 593, 600, 599, 606, 605, 612, 611, 618, 617, 624, 623, 630, 629, 636, 635, 642, 641, 648, 647, 855, 854],
76
+ [728, 727, 722, 721, 716, 715, 710, 709, 704, 703, 698, 697, 692, 691, 686, 685, 680, 679, 674, 673, 668, 667, 662, 661, 656, 655, 650, 649, 856, 0],
77
+ [730, 729, 724, 723, 718, 717, 712, 711, 706, 705, 700, 699, 694, 693, 688, 687, 682, 681, 676, 675, 670, 669, 664, 663, 658, 657, 652, 651, 858, 857],
78
+ [732, 731, 726, 725, 720, 719, 714, 713, 708, 707, 702, 701, 696, 695, 690, 689, 684, 683, 678, 677, 672, 671, 666, 665, 660, 659, 654, 653, 859, 0],
79
+ [734, 733, 740, 739, 746, 745, 752, 751, 758, 757, 764, 763, 770, 769, 776, 775, 782, 781, 788, 787, 794, 793, 800, 799, 806, 805, 812, 811, 861, 860],
80
+ [736, 735, 742, 741, 748, 747, 754, 753, 760, 759, 766, 765, 772, 771, 778, 777, 784, 783, 790, 789, 796, 795, 802, 801, 808, 807, 814, 813, 862, 0],
81
+ [738, 737, 744, 743, 750, 749, 756, 755, 762, 761, 768, 767, 774, 773, 780, 779, 786, 785, 792, 791, 798, 797, 804, 803, 810, 809, 816, 815, 864, 863],
82
+ ];
83
+
84
+ /** Flattened standard module sequence. */
85
+ export const MAXICODE_GRID = MAXICODE_GRID_ROWS.flat();
86
+
87
+ /** Fixed orientation modules, expressed as [x, y] logical positions. */
88
+ export const MAXICODE_ORIENTATION_DARK = Object.freeze([
89
+ [28, 0], [29, 0],
90
+ [10, 9], [11, 9], [11, 10],
91
+ [7, 15], [8, 16],
92
+ [20, 16], [20, 17],
93
+ [10, 22], [10, 23],
94
+ [17, 22], [17, 23],
95
+ ]);
96
+
97
+ /**
98
+ * Return the fixed colour of a structural module, or null for a data module.
99
+ *
100
+ * MaxiCode's finder is drawn on a staggered hexagonal lattice. The logical
101
+ * interchange grid keeps one cell per module, so the standard ring radii are
102
+ * evaluated at the centre of each staggered cell. Using the structural zero
103
+ * entries from Figure 5 here is important: treating the entire circular area
104
+ * as finder cells would overwrite payload modules at the edge of the rings.
105
+ */
106
+ export function maxicodeFinderValue(x: number, y: number): boolean | null {
107
+ if (MAXICODE_GRID[y * MAXICODE_WIDTH + x] !== 0) return null;
108
+
109
+ // Coordinates are scaled only to preserve the 30:33 staggered lattice
110
+ // proportions. The constants are the normalized centres and radii of the
111
+ // three standard annuli; a scale factor cancels out of the classification.
112
+ const cellX = (y & 1) ? x + 34.5 / 35 : x + 16.5 / 35;
113
+ const cellY = y + 20 / 30;
114
+ const centreX = 507 / 35;
115
+ const centreY = 506 / 30;
116
+ const distance = Math.hypot((cellX - centreX) * 35, (cellY - centreY) * 30);
117
+ return (distance >= 20 && distance < 46) ||
118
+ (distance >= 73 && distance < 100) ||
119
+ (distance >= 126 && distance < 153);
120
+ }
121
+
122
+ /** Verify that the transcribed standard sequence is complete and one-to-one. */
123
+ export function validateMaxiCodeTables(): string[] {
124
+ const errors: string[] = [];
125
+ if (MAXICODE_GRID.length !== MAXICODE_WIDTH * MAXICODE_HEIGHT) {
126
+ errors.push(`grid length ${MAXICODE_GRID.length} is not ${MAXICODE_WIDTH * MAXICODE_HEIGHT}`);
127
+ }
128
+ const values = MAXICODE_GRID.filter((value) => value > 0);
129
+ if (values.length !== MAXICODE_DATA_MODULES) errors.push(`grid has ${values.length} data modules`);
130
+ const seen = new Set(values);
131
+ if (seen.size !== values.length) errors.push('grid contains duplicate module sequence numbers');
132
+ for (let value = 1; value <= MAXICODE_DATA_MODULES; value++) {
133
+ if (!seen.has(value)) errors.push(`grid is missing module sequence ${value}`);
134
+ }
135
+ return errors;
136
+ }
@@ -0,0 +1,42 @@
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
+ * Original work. No code from any other barcode implementation.
11
+ */
12
+ import { BitMatrix } from '../core/bit-matrix.js';
13
+ export type Code25Variant = 'standard' | 'industrial' | 'iata';
14
+ export declare const CODE25_DIGIT_PATTERNS: readonly [
15
+ '1111313111', '3111111131', '1131111131', '3131111111', '1111311131',
16
+ '3111311111', '1131311111', '1111113131', '3111113111', '1131113111'
17
+ ];
18
+ export declare const CODE25_VARIANTS: Readonly<Record<Code25Variant, {
19
+ readonly id: 'industrial2of5' | 'iata2of5';
20
+ readonly label: string;
21
+ readonly start: string;
22
+ readonly stop: string;
23
+ }>>;
24
+ export declare const CODE25_MAX_DIGITS: 500;
25
+ export declare function code25CheckDigit(value: string): number;
26
+ export declare function encodeCode25(value: string, options?: {
27
+ variant?: Code25Variant | string;
28
+ checkDigit?: boolean;
29
+ wideRatio?: number;
30
+ }): BitMatrix;
31
+ export declare function encodeStandard2of5(value: string, options?: {
32
+ checkDigit?: boolean;
33
+ wideRatio?: number;
34
+ }): BitMatrix;
35
+ export declare function encodeIndustrial2of5(value: string, options?: {
36
+ checkDigit?: boolean;
37
+ wideRatio?: number;
38
+ }): BitMatrix;
39
+ export declare function encodeIATA2of5(value: string, options?: {
40
+ checkDigit?: boolean;
41
+ wideRatio?: number;
42
+ }): BitMatrix;
@@ -0,0 +1,170 @@
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
+ /**
33
+ * Code 25 family writers.
34
+ *
35
+ * Code 25 (also called Standard or Industrial 2 of 5) represents each digit
36
+ * with five alternating bar/space elements, exactly two of the bars being wide.
37
+ * IATA 2 of 5 uses the same digit grammar with a shorter start/stop frame.
38
+ * The width descriptions below are expressed as module counts rather than
39
+ * copied implementation tables and are checked by the focused test suite.
40
+ *
41
+ * @module oned/code25
42
+ */
43
+
44
+ import { BitMatrix } from '../core/bit-matrix.js';
45
+ import { EncodeError } from '../core/errors.js';
46
+
47
+ /** Supported Code 25 framing profiles. */
48
+ export type Code25Variant = 'standard' | 'industrial' | 'iata';
49
+
50
+ /** The ten digit patterns, each with five bars and five spaces. */
51
+ export const CODE25_DIGIT_PATTERNS = [
52
+ '1111313111', '3111111131', '1131111131', '3131111111',
53
+ '1111311131', '3111311111', '1131311111', '1111113131',
54
+ '3111113111', '1131113111',
55
+ ] as const;
56
+
57
+ /** Start/stop run widths for the three public names. */
58
+ export const CODE25_VARIANTS: Readonly<Record<Code25Variant, {
59
+ readonly id: 'industrial2of5' | 'iata2of5';
60
+ readonly label: string;
61
+ readonly start: string;
62
+ readonly stop: string;
63
+ }>> = {
64
+ // Code 25 and Industrial 2 of 5 share the discrete two-wide-bar grammar
65
+ // and canonical industrial frame. `standard` is the friendly API alias.
66
+ standard: { id: 'industrial2of5', label: 'Standard 2 of 5 (Code 25)', start: '313111', stop: '31113' },
67
+ industrial: { id: 'industrial2of5', label: 'Industrial 2 of 5', start: '313111', stop: '31113' },
68
+ iata: { id: 'iata2of5', label: 'IATA 2 of 5', start: '1111', stop: '311' },
69
+ };
70
+
71
+ const MAX_CODE25_DIGITS = 500;
72
+
73
+ function requireDigits(value: string, label: string): string {
74
+ const text = String(value);
75
+ if (!/^[0-9]+$/u.test(text)) {
76
+ throw new EncodeError(`${label}: payload must be digits only, got "${value}"`);
77
+ }
78
+ if (text.length === 0 || text.length > MAX_CODE25_DIGITS) {
79
+ throw new EncodeError(`${label}: payload length must be in 1..${MAX_CODE25_DIGITS}`);
80
+ }
81
+ return text;
82
+ }
83
+
84
+ /** Modulo-10 Code 25 check digit (alternating weights from the right). */
85
+ export function code25CheckDigit(value: string): number {
86
+ let sum = 0;
87
+ for (let i = 0; i < value.length; i++) {
88
+ const fromRight = value.length - 1 - i;
89
+ sum += Number(value[i]) * (fromRight % 2 === 0 ? 3 : 1);
90
+ }
91
+ return (10 - (sum % 10)) % 10;
92
+ }
93
+
94
+ function expandWidths(widths: string, wideRatio: number): string {
95
+ let modules = '';
96
+ for (let i = 0; i < widths.length; i++) {
97
+ const width = Number(widths[i]);
98
+ const count = width > 1 ? wideRatio : 1;
99
+ modules += (i % 2 === 0 ? '1' : '0').repeat(count);
100
+ }
101
+ return modules;
102
+ }
103
+
104
+ function toMatrix(modules: string): BitMatrix {
105
+ const matrix = new BitMatrix(modules.length, 1);
106
+ for (let x = 0; x < modules.length; x++) {
107
+ if (modules[x] === '1') matrix.set(x, 0);
108
+ }
109
+ return matrix;
110
+ }
111
+
112
+ function resolveVariant(value: unknown): Code25Variant {
113
+ const variant = String(value ?? 'standard').toLowerCase();
114
+ if (variant === 'industrial' || variant === 'industrial2of5' || variant === 'industrial-2-of-5') return 'industrial';
115
+ if (variant === 'iata' || variant === 'iata2of5' || variant === 'iata-2-of-5') return 'iata';
116
+ if (variant === 'standard' || variant === 'code2of5' || variant === 'code-2-of-5' || variant === 'standard2of5' || variant === 'standard-2-of-5') return 'standard';
117
+ throw new EncodeError(`Code 25: unknown variant "${value}"`);
118
+ }
119
+
120
+ /**
121
+ * Encode one of the Code 25 family profiles.
122
+ *
123
+ * `checkDigit` is opt-in because the base symbologies do not require one. A
124
+ * camera-profile decoder will require and validate it before promoting a read.
125
+ *
126
+ * @param {string} value Numeric payload.
127
+ * @param {object} [options]
128
+ * @param {Code25Variant|string} [options.variant='standard'] Framing profile.
129
+ * @param {boolean} [options.checkDigit] Append a modulo-10 check digit.
130
+ * @param {number} [options.wideRatio=3] Number of modules in a wide bar.
131
+ * @returns {BitMatrix}
132
+ */
133
+ export function encodeCode25(value: string, options: {
134
+ variant?: Code25Variant | string;
135
+ checkDigit?: boolean;
136
+ wideRatio?: number;
137
+ } = {}): BitMatrix {
138
+ const variant = resolveVariant(options.variant);
139
+ const profile = CODE25_VARIANTS[variant];
140
+ const label = profile.label;
141
+ const ratio = options.wideRatio ?? 3;
142
+ if (!Number.isInteger(ratio) || ratio < 2 || ratio > 8) {
143
+ throw new EncodeError(`${label}: wideRatio must be an integer in 2..8`);
144
+ }
145
+ let digits = requireDigits(value, label);
146
+ if (options.checkDigit === true) digits += String(code25CheckDigit(digits));
147
+
148
+ let modules = expandWidths(profile.start, ratio);
149
+ for (const digit of digits) modules += expandWidths(CODE25_DIGIT_PATTERNS[Number(digit)], ratio);
150
+ modules += expandWidths(profile.stop, ratio);
151
+ return toMatrix(modules);
152
+ }
153
+
154
+ /** Encode Standard 2 of 5 (the canonical Code 25 frame). */
155
+ export function encodeStandard2of5(value: string, options: Omit<Parameters<typeof encodeCode25>[1], 'variant'> = {}): BitMatrix {
156
+ return encodeCode25(value, { ...options, variant: 'standard' });
157
+ }
158
+
159
+ /** Encode Industrial 2 of 5. */
160
+ export function encodeIndustrial2of5(value: string, options: Omit<Parameters<typeof encodeCode25>[1], 'variant'> = {}): BitMatrix {
161
+ return encodeCode25(value, { ...options, variant: 'industrial' });
162
+ }
163
+
164
+ /** Encode IATA 2 of 5. */
165
+ export function encodeIATA2of5(value: string, options: Omit<Parameters<typeof encodeCode25>[1], 'variant'> = {}): BitMatrix {
166
+ return encodeCode25(value, { ...options, variant: 'iata' });
167
+ }
168
+
169
+ /** Internal limit used by the scanline reader and its safety checks. */
170
+ export const CODE25_MAX_DIGITS = MAX_CODE25_DIGITS;
@@ -33,9 +33,14 @@
33
33
  *
34
34
  * @module oned
35
35
  */
36
- export { encodeEAN13, encodeEAN8, encodeUPCA, encodeUPCE, encodeISBN, encodeCode39, encodeCode93, encodeCode128, encodeITF, encodeITF14, encodeCodabar, encodeCode11, encodeMSI, encodePharmacode, ean13CheckDigit, } from './writers.js';
36
+ export { encodeEAN13, encodeEAN8, encodeUPCA, encodeUPCE, encodeISBN, encodeCode39, encodeCode93, encodeCode128, code128DataCodewords, encodeITF, encodeITF14, encodeCodabar, encodeCode11, encodeMSI, encodePharmacode, code32CheckDigit, encodeCode32, decodeCode32Payload, encodePZN, decodePZNPayload, ean13CheckDigit, } from './writers.js';
37
+ export { CODE25_DIGIT_PATTERNS, CODE25_VARIANTS, CODE25_MAX_DIGITS, code25CheckDigit, encodeCode25, encodeStandard2of5, encodeIndustrial2of5, encodeIATA2of5, } from './code25.js';
38
+ export type { Code25Variant } from './code25.js';
37
39
  export { EAN2_PARITY, EAN5_PARITY, EAN2_WIDTH, EAN5_WIDTH, EAN_ADDON_START, EAN_ADDON_SEPARATOR, ean2Parity, ean5Checksum, ean5CheckDigit, ean5Parity, encodeEAN2, encodeEAN5, encodeEANAddon, encodeEANAddOn, decodeEAN2, decodeEAN5, decodeEANAddon, decodeEANAddOn, composeEANAddon, encodeEAN13WithAddon, encodeEAN8WithAddon, encodeUPCAWithAddon, encodeUPCEWithAddon, } from './addons.js';
38
- export { decodeOneD, decodeOneDStrict, decodeCode11, decodeMSI, patternVariance, recordPattern, toNarrowWidePattern, } from './reader.js';
40
+ export { decodeOneD, decodeOneDStrict, decodeCode32, decodePZN, decodeCode25, decodeStandard2of5, decodeIndustrial2of5, decodeIATA2of5, decodeCode11, decodeMSI, patternVariance, recordPattern, toNarrowWidePattern, } from './reader.js';
41
+ export { TELEPEN_START_VALUE, TELEPEN_STOP_VALUE, TELEPEN_MAX_LENGTH, telepenPattern, encodeTelepen, encodeTelepenNumeric, decodeTelepen, decodeTelepenNumeric, } from './telepen.js';
42
+ export { POSTAL_FORMATS, POSTAL_ALIASES, STATE_PROFILES, encodePostnet, encodePlanet, encodeRM4SCC, encodeKIX, encodeAustraliaPost, encodeJapanPost, encodeIMB, decodePostal, } from './postal.js';
43
+ export type { PostalFormat, PostalOptions, PostalDecodeResult } from './postal.js';
39
44
  export { validateTables } from './patterns.js';
40
45
  /**
41
46
  * Writers by format id, for the top-level `encode()` dispatcher.
@@ -37,11 +37,26 @@
37
37
  export {
38
38
  encodeEAN13, encodeEAN8, encodeUPCA, encodeUPCE, encodeISBN,
39
39
  encodeCode39, encodeCode93, encodeCode128,
40
+ code128DataCodewords,
40
41
  encodeITF, encodeITF14, encodeCodabar, encodeCode11,
41
- encodeMSI, encodePharmacode,
42
+ encodeMSI, encodePharmacode, encodeCode32, encodePZN,
43
+ code32CheckDigit, decodeCode32Payload, decodePZNPayload,
42
44
  ean13CheckDigit,
43
45
  } from './writers.js';
44
46
 
47
+ export {
48
+ CODE25_DIGIT_PATTERNS, CODE25_VARIANTS, CODE25_MAX_DIGITS,
49
+ code25CheckDigit, encodeCode25, encodeStandard2of5,
50
+ encodeIndustrial2of5, encodeIATA2of5,
51
+ } from './code25.js';
52
+ export type { Code25Variant } from './code25.js';
53
+
54
+ export {
55
+ TELEPEN_START_VALUE, TELEPEN_STOP_VALUE, TELEPEN_MAX_LENGTH,
56
+ telepenPattern, encodeTelepen, encodeTelepenNumeric,
57
+ decodeTelepen, decodeTelepenNumeric,
58
+ } from './telepen.js';
59
+
45
60
  export {
46
61
  EAN2_PARITY, EAN5_PARITY, EAN2_WIDTH, EAN5_WIDTH,
47
62
  EAN_ADDON_START, EAN_ADDON_SEPARATOR,
@@ -54,20 +69,35 @@ export {
54
69
  } from './addons.js';
55
70
 
56
71
  export {
57
- decodeOneD, decodeOneDStrict,
72
+ decodeOneD, decodeOneDStrict, decodeCode32, decodePZN,
73
+ decodeCode25, decodeStandard2of5, decodeIndustrial2of5, decodeIATA2of5,
58
74
  decodeCode11, decodeMSI,
59
75
  patternVariance, recordPattern, toNarrowWidePattern,
60
76
  } from './reader.js';
61
77
 
78
+ export {
79
+ POSTAL_FORMATS, POSTAL_ALIASES, STATE_PROFILES,
80
+ encodePostnet, encodePlanet, encodeRM4SCC, encodeKIX,
81
+ encodeAustraliaPost, encodeJapanPost, encodeIMB,
82
+ decodePostal,
83
+ } from './postal.js';
84
+ export type { PostalFormat, PostalOptions, PostalDecodeResult } from './postal.js';
85
+
62
86
  export { validateTables } from './patterns.js';
63
87
 
64
88
  import {
65
89
  encodeEAN13, encodeEAN8, encodeUPCA, encodeUPCE, encodeISBN,
66
90
  encodeCode39, encodeCode93, encodeCode128,
67
91
  encodeITF, encodeITF14, encodeCodabar, encodeCode11,
68
- encodeMSI, encodePharmacode,
92
+ encodeMSI, encodePharmacode, encodeCode32, encodePZN,
69
93
  } from './writers.js';
70
94
  import { encodeEAN2, encodeEAN5 } from './addons.js';
95
+ import { encodeTelepen } from './telepen.js';
96
+ import { encodeIndustrial2of5, encodeIATA2of5 } from './code25.js';
97
+ import {
98
+ encodePostnet, encodePlanet, encodeRM4SCC, encodeKIX,
99
+ encodeAustraliaPost, encodeJapanPost, encodeIMB,
100
+ } from './postal.js';
71
101
 
72
102
  /**
73
103
  * Writers by format id, for the top-level `encode()` dispatcher.
@@ -95,12 +125,24 @@ export const ONED_FORMATS = {
95
125
  code93: { encode: encodeCode93, readable: true, label: 'Code 93' },
96
126
  itf: { encode: encodeITF, readable: true, label: 'ITF (Interleaved 2 of 5)' },
97
127
  itf14: { encode: encodeITF14, readable: true, label: 'ITF-14' },
128
+ industrial2of5: { encode: encodeIndustrial2of5, readable: true, label: 'Industrial 2 of 5' },
129
+ iata2of5: { encode: encodeIATA2of5, readable: true, label: 'IATA 2 of 5' },
98
130
  codabar: { encode: encodeCodabar, readable: true, label: 'Codabar' },
99
131
  code11: { encode: encodeCode11, readable: true, label: 'Code 11' },
100
132
  msi: { encode: encodeMSI, readable: true, label: 'MSI Plessey' },
133
+ code32: { encode: encodeCode32, readable: true, label: 'Code 32 (Italian Pharmacode)' },
134
+ pzn: { encode: encodePZN, readable: true, label: 'PZN (Pharmazentralnummer)' },
135
+ telepen: { encode: encodeTelepen, readable: true, label: 'Telepen' },
101
136
  pharmacode: { encode: encodePharmacode, readable: false, label: 'Pharmacode' },
102
137
  // Supplements are reported as readable capabilities, but the image reader
103
138
  // only accepts them when attached to a validated EAN/UPC parent symbol.
104
139
  ean2: { encode: encodeEAN2, readable: true, role: 'supplement', label: 'EAN-2 supplement' },
105
140
  ean5: { encode: encodeEAN5, readable: true, role: 'supplement', label: 'EAN-5 supplement' },
141
+ postnet: { encode: encodePostnet, readable: true, label: 'USPS POSTNET' },
142
+ planet: { encode: encodePlanet, readable: true, label: 'USPS PLANET' },
143
+ rm4scc: { encode: encodeRM4SCC, readable: true, label: 'Royal Mail 4-State (RM4SCC)' },
144
+ kix: { encode: encodeKIX, readable: true, label: 'KIX postal code' },
145
+ auspost: { encode: encodeAustraliaPost, readable: true, label: 'Australia Post 4-State' },
146
+ japanpost: { encode: encodeJapanPost, readable: true, label: 'Japan Post 4-State' },
147
+ imb: { encode: encodeIMB, readable: true, label: 'USPS Intelligent Mail (IMb)' },
106
148
  };
@@ -0,0 +1,44 @@
1
+ /*
2
+ * Sythos Barcode Suite — MIT License — Copyright (c) 2026 Sythos
3
+ * Original work. No code from any other barcode implementation.
4
+ */
5
+ import { BitMatrix } from '../core/bit-matrix.js';
6
+
7
+ export type PostalFormat =
8
+ | 'postnet'
9
+ | 'planet'
10
+ | 'rm4scc'
11
+ | 'kix'
12
+ | 'auspost'
13
+ | 'japanpost'
14
+ | 'imb';
15
+
16
+ export interface PostalOptions {
17
+ checkDigit?: boolean;
18
+ customerEncoding?: 'character' | 'numeric';
19
+ custinfoenc?: 'character' | 'numeric';
20
+ profile?: 'camera';
21
+ }
22
+
23
+ export interface PostalDecodeResult {
24
+ format: PostalFormat;
25
+ text: string;
26
+ checkDigit: boolean;
27
+ }
28
+
29
+ export declare const POSTAL_FORMATS: PostalFormat[];
30
+ export declare const POSTAL_ALIASES: Readonly<Record<string, PostalFormat>>;
31
+ export declare const STATE_PROFILES: readonly (readonly [number, number])[];
32
+
33
+ export declare function encodePostnet(value: string, options?: PostalOptions): BitMatrix;
34
+ export declare function encodePlanet(value: string, options?: PostalOptions): BitMatrix;
35
+ export declare function encodeRM4SCC(value: string, options?: PostalOptions): BitMatrix;
36
+ export declare function encodeKIX(value: string): BitMatrix;
37
+ export declare function encodeAustraliaPost(value: string, options?: PostalOptions): BitMatrix;
38
+ export declare function encodeJapanPost(value: string): BitMatrix;
39
+ export declare function encodeIMB(value: string): BitMatrix;
40
+
41
+ export declare function decodePostal(
42
+ image: BitMatrix,
43
+ options?: PostalOptions & { formats?: string[] },
44
+ ): PostalDecodeResult[];