@sythos/js_barcode_universal 1.5.8 → 1.5.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (179) hide show
  1. package/LICENSE +5 -4
  2. package/NOTICE.md +2 -2
  3. package/README.md +17 -13
  4. package/bundle/sythos-barcode.esm.js +11658 -11244
  5. package/bundle/sythos-barcode.js +11658 -11244
  6. package/package.json +10 -3
  7. package/src/index.d.ts +1 -1
  8. package/src/index.js +508 -516
  9. package/src/js/aztec/decoder.js +208 -199
  10. package/src/js/aztec/detector.js +153 -126
  11. package/src/js/aztec/encoder.js +172 -153
  12. package/src/js/aztec/high-level.js +106 -101
  13. package/src/js/aztec/index.js +1 -12
  14. package/src/js/aztec/tables.js +106 -120
  15. package/src/js/aztecrune/decoder.js +76 -85
  16. package/src/js/aztecrune/detector.js +107 -93
  17. package/src/js/aztecrune/encoder.js +50 -68
  18. package/src/js/aztecrune/index.js +1 -16
  19. package/src/js/aztecrune/tables.js +59 -54
  20. package/src/js/compactpdf417/decoder.js +69 -74
  21. package/src/js/compactpdf417/detector.js +74 -75
  22. package/src/js/compactpdf417/encoder.js +68 -81
  23. package/src/js/compactpdf417/index.js +1 -13
  24. package/src/js/compactpdf417/tables.js +71 -80
  25. package/src/js/core/bit-buffer.js +106 -122
  26. package/src/js/core/bit-matrix.js +196 -194
  27. package/src/js/core/errors.js +13 -15
  28. package/src/js/core/galois-field.js +119 -133
  29. package/src/js/core/index.js +2 -19
  30. package/src/js/core/reed-solomon.js +195 -204
  31. package/src/js/databar/codec.js +93 -108
  32. package/src/js/databar/decoder.js +149 -140
  33. package/src/js/databar/encoder.js +47 -55
  34. package/src/js/databar/gs1.js +122 -123
  35. package/src/js/databar/index.js +4 -36
  36. package/src/js/databar/patterns.js +81 -85
  37. package/src/js/databar/tables.js +51 -52
  38. package/src/js/datamatrix/decoder.js +216 -182
  39. package/src/js/datamatrix/detector.js +161 -132
  40. package/src/js/datamatrix/encoder.js +192 -133
  41. package/src/js/datamatrix/index.js +1 -9
  42. package/src/js/datamatrix/tables.js +68 -69
  43. package/src/js/frameqr/decoder.js +102 -125
  44. package/src/js/frameqr/detector.js +101 -94
  45. package/src/js/frameqr/encoder.js +67 -80
  46. package/src/js/frameqr/index.js +1 -10
  47. package/src/js/frameqr/tables.js +145 -163
  48. package/src/js/image/binarizer.js +146 -162
  49. package/src/js/image/grid-sampler.js +63 -79
  50. package/src/js/image/index.js +0 -2
  51. package/src/js/image/luminance.js +129 -140
  52. package/src/js/image/perspective.js +112 -149
  53. package/src/js/micropdf417/compaction.js +64 -65
  54. package/src/js/micropdf417/decoder.js +106 -109
  55. package/src/js/micropdf417/detector.js +73 -70
  56. package/src/js/micropdf417/encoder.js +129 -154
  57. package/src/js/micropdf417/error-correction.js +6 -11
  58. package/src/js/micropdf417/index.js +2 -16
  59. package/src/js/micropdf417/tables.js +112 -99
  60. package/src/js/microqr/decoder.js +177 -182
  61. package/src/js/microqr/detector.js +240 -256
  62. package/src/js/microqr/encoder.js +239 -194
  63. package/src/js/microqr/index.js +0 -2
  64. package/src/js/microqr/tables.js +208 -200
  65. package/src/js/oned/addons.js +167 -191
  66. package/src/js/oned/index.js +27 -58
  67. package/src/js/oned/patterns.js +194 -213
  68. package/src/js/oned/reader.js +1003 -984
  69. package/src/js/oned/writers.js +411 -466
  70. package/src/js/pdf417/compaction.js +315 -222
  71. package/src/js/pdf417/decoder.js +71 -36
  72. package/src/js/pdf417/detector.js +377 -361
  73. package/src/js/pdf417/encoder.js +57 -46
  74. package/src/js/pdf417/error-correction.js +5 -8
  75. package/src/js/pdf417/tables.js +248 -250
  76. package/src/js/qr/decoder.js +311 -347
  77. package/src/js/qr/detector.js +378 -395
  78. package/src/js/qr/encoder.js +514 -599
  79. package/src/js/qr/index.js +0 -2
  80. package/src/js/qr/tables.js +375 -422
  81. package/src/js/render/image-data.js +57 -62
  82. package/src/js/render/index.js +21 -23
  83. package/src/js/render/options.js +82 -90
  84. package/src/js/render/png.js +144 -169
  85. package/src/js/render/svg.js +41 -44
  86. package/src/js/render/webgl.js +103 -109
  87. package/src/js/render/webgpu.js +205 -217
  88. package/src/js/rmqr/decoder.js +161 -53
  89. package/src/js/rmqr/detector.js +73 -41
  90. package/src/js/rmqr/encoder.js +253 -104
  91. package/src/js/rmqr/index.js +1 -5
  92. package/src/js/rmqr/tables.js +108 -85
  93. package/src/ts/aztec/decoder.ts +317 -0
  94. package/src/ts/aztec/detector.ts +224 -0
  95. package/src/ts/aztec/encoder.ts +257 -0
  96. package/src/ts/aztec/high-level.ts +211 -0
  97. package/src/ts/aztec/index.ts +45 -0
  98. package/src/ts/aztec/tables.ts +210 -0
  99. package/src/ts/aztecrune/decoder.ts +155 -0
  100. package/src/ts/aztecrune/detector.ts +166 -0
  101. package/src/ts/aztecrune/encoder.ts +121 -0
  102. package/src/ts/aztecrune/index.ts +49 -0
  103. package/src/ts/aztecrune/tables.ts +137 -0
  104. package/src/ts/compactpdf417/decoder.ts +128 -0
  105. package/src/ts/compactpdf417/detector.ts +139 -0
  106. package/src/ts/compactpdf417/encoder.ts +140 -0
  107. package/src/ts/compactpdf417/index.ts +17 -0
  108. package/src/ts/compactpdf417/tables.ts +173 -0
  109. package/src/ts/core/bit-buffer.ts +174 -0
  110. package/src/ts/core/bit-matrix.ts +241 -0
  111. package/src/ts/core/errors.ts +61 -0
  112. package/src/ts/core/galois-field.ts +207 -0
  113. package/src/ts/core/index.ts +56 -0
  114. package/src/ts/core/reed-solomon.ts +327 -0
  115. package/src/ts/databar/codec.ts +181 -0
  116. package/src/ts/databar/decoder.ts +215 -0
  117. package/src/ts/databar/encoder.ts +96 -0
  118. package/src/ts/databar/gs1.ts +177 -0
  119. package/src/ts/databar/index.ts +68 -0
  120. package/src/ts/databar/patterns.ts +143 -0
  121. package/src/ts/databar/tables.ts +124 -0
  122. package/src/ts/datamatrix/decoder.ts +262 -0
  123. package/src/ts/datamatrix/detector.ts +225 -0
  124. package/src/ts/datamatrix/encoder.ts +191 -0
  125. package/src/ts/datamatrix/index.ts +42 -0
  126. package/src/ts/datamatrix/tables.ts +123 -0
  127. package/src/ts/frameqr/decoder.ts +239 -0
  128. package/src/ts/frameqr/detector.ts +192 -0
  129. package/src/ts/frameqr/encoder.ts +156 -0
  130. package/src/ts/frameqr/index.ts +42 -0
  131. package/src/ts/frameqr/tables.ts +270 -0
  132. package/src/ts/image/binarizer.ts +270 -0
  133. package/src/ts/image/grid-sampler.ts +164 -0
  134. package/src/ts/image/index.ts +40 -0
  135. package/src/ts/image/luminance.ts +196 -0
  136. package/src/ts/image/perspective.ts +195 -0
  137. package/src/ts/index.d.ts +1 -1
  138. package/src/ts/index.ts +790 -0
  139. package/src/ts/micropdf417/compaction.ts +116 -0
  140. package/src/ts/micropdf417/decoder.ts +183 -0
  141. package/src/ts/micropdf417/detector.ts +149 -0
  142. package/src/ts/micropdf417/encoder.ts +209 -0
  143. package/src/ts/micropdf417/error-correction.ts +55 -0
  144. package/src/ts/micropdf417/index.ts +49 -0
  145. package/src/ts/micropdf417/tables.ts +184 -0
  146. package/src/ts/microqr/decoder.ts +245 -0
  147. package/src/ts/microqr/detector.ts +355 -0
  148. package/src/ts/microqr/encoder.ts +269 -0
  149. package/src/ts/microqr/index.ts +36 -0
  150. package/src/ts/microqr/tables.ts +316 -0
  151. package/src/ts/oned/addons.ts +420 -0
  152. package/src/ts/oned/index.ts +106 -0
  153. package/src/ts/oned/patterns.ts +384 -0
  154. package/src/ts/oned/reader.ts +1364 -0
  155. package/src/ts/oned/writers.ts +746 -0
  156. package/src/ts/pdf417/compaction.ts +298 -0
  157. package/src/ts/pdf417/decoder.ts +75 -0
  158. package/src/ts/pdf417/detector.ts +468 -0
  159. package/src/ts/pdf417/encoder.ts +91 -0
  160. package/src/ts/pdf417/error-correction.ts +47 -0
  161. package/src/ts/pdf417/index.ts +6 -0
  162. package/src/ts/pdf417/tables.ts +317 -0
  163. package/src/ts/qr/decoder.ts +575 -0
  164. package/src/ts/qr/detector.ts +630 -0
  165. package/src/ts/qr/encoder.ts +958 -0
  166. package/src/ts/qr/index.ts +44 -0
  167. package/src/ts/qr/tables.ts +737 -0
  168. package/src/ts/render/image-data.ts +125 -0
  169. package/src/ts/render/index.ts +130 -0
  170. package/src/ts/render/options.ts +160 -0
  171. package/src/ts/render/png.ts +295 -0
  172. package/src/ts/render/svg.ts +120 -0
  173. package/src/ts/render/webgl.ts +206 -0
  174. package/src/ts/render/webgpu.ts +369 -0
  175. package/src/ts/rmqr/decoder.ts +101 -0
  176. package/src/ts/rmqr/detector.ts +90 -0
  177. package/src/ts/rmqr/encoder.ts +172 -0
  178. package/src/ts/rmqr/index.ts +37 -0
  179. package/src/ts/rmqr/tables.ts +154 -0
@@ -0,0 +1,327 @@
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
+ * Reed-Solomon encoding and decoding over an arbitrary finite field.
33
+ *
34
+ * Systematic encoding: the output is the message followed by parity symbols,
35
+ * so the data is readable without decoding when the symbol is undamaged.
36
+ *
37
+ * Decoding is syndromes -> Berlekamp-Massey -> Chien search -> Forney.
38
+ * It corrects up to floor(eccLen / 2) symbol errors at unknown positions.
39
+ *
40
+ * Every arithmetic operation routes through the field object. There is
41
+ * deliberately not a single bare `^` in this file: XOR is correct for binary
42
+ * fields and wrong for GF(929), and the resulting bug is invisible to any test
43
+ * that only exercises QR or Data Matrix. See core/galois-field.js.
44
+ *
45
+ * Polynomial convention in this module: **coefficient index 0 is the highest
46
+ * degree**, matching the wire order of a codeword. The decoder converts to
47
+ * degree-ascending internally where the algorithms are stated that way.
48
+ *
49
+ * @module core/reed-solomon
50
+ */
51
+
52
+ import { ChecksumError } from './errors.js';
53
+
54
+ /**
55
+ * Build the generator polynomial for `eccLen` parity symbols.
56
+ *
57
+ * g(x) = product over i of (x - a^(base + i)), i = 0 .. eccLen-1
58
+ *
59
+ * `base` is 0 for QR; 1 for Aztec, Data Matrix and PDF417.
60
+ *
61
+ * @param {number} eccLen
62
+ * @param {import('./galois-field.js').GaloisField} field
63
+ * @param {number} [base]
64
+ * @returns {number[]} Monic, degree-descending, length eccLen + 1.
65
+ */
66
+ export function generatorPoly(eccLen, field, base = 0) {
67
+ let g = [1];
68
+ for (let i = 0; i < eccLen; i++) {
69
+ const root = field.exp(base + i);
70
+ const next = new Array(g.length + 1).fill(0);
71
+ for (let j = 0; j < g.length; j++) {
72
+ // g[j]*x lands at next[j]; g[j]*(-root) lands at next[j+1].
73
+ next[j] = field.add(next[j], g[j]);
74
+ next[j + 1] = field.sub(next[j + 1], field.mul(g[j], root));
75
+ }
76
+ g = next;
77
+ }
78
+ return g;
79
+ }
80
+
81
+ const generatorCache = new Map();
82
+
83
+ /**
84
+ * Cached {@link generatorPoly}. Encoding the same format repeatedly is the
85
+ * common case and rebuilding the polynomial each time is pure waste.
86
+ *
87
+ * @param {number} eccLen
88
+ * @param {import('./galois-field.js').GaloisField} field
89
+ * @param {number} [base]
90
+ * @returns {number[]}
91
+ */
92
+ function cachedGenerator(eccLen, field, base) {
93
+ const key = `${field.name}|${eccLen}|${base}`;
94
+ let g = generatorCache.get(key);
95
+ if (!g) {
96
+ g = generatorPoly(eccLen, field, base);
97
+ generatorCache.set(key, g);
98
+ }
99
+ return g;
100
+ }
101
+
102
+ /**
103
+ * Compute `eccLen` parity symbols for `data`.
104
+ *
105
+ * @param {ArrayLike<number>} data
106
+ * @param {number} eccLen
107
+ * @param {import('./galois-field.js').GaloisField} field
108
+ * @param {number} [base]
109
+ * @returns {number[]} The parity symbols alone, length eccLen.
110
+ */
111
+ export function rsEncode(data, eccLen, field, base = 0) {
112
+ if (eccLen <= 0) return [];
113
+ const gen = cachedGenerator(eccLen, field, base);
114
+ const res = new Array(data.length + eccLen).fill(0);
115
+ for (let i = 0; i < data.length; i++) res[i] = data[i];
116
+
117
+ // Synthetic division by a monic divisor: the leading coefficient is
118
+ // annihilated each step and its multiple subtracted from the tail.
119
+ for (let i = 0; i < data.length; i++) {
120
+ const coef = res[i];
121
+ if (coef === 0) continue;
122
+ for (let j = 1; j <= eccLen; j++) {
123
+ res[i + j] = field.sub(res[i + j], field.mul(gen[j], coef));
124
+ }
125
+ }
126
+
127
+ const remainder = res.slice(data.length);
128
+
129
+ // The codeword is data(x)*x^eccLen MINUS the remainder, so the parity
130
+ // symbols are the negated remainder. In a binary field negation is the
131
+ // identity and this is invisible; in GF(929) omitting it produces a
132
+ // codeword that is not divisible by the generator, and every symbol fails
133
+ // to decode. Exactly the class of bug the field abstraction exists to stop.
134
+ if (field.prime) {
135
+ for (let i = 0; i < remainder.length; i++) remainder[i] = field.neg(remainder[i]);
136
+ }
137
+ return remainder;
138
+ }
139
+
140
+ /**
141
+ * Evaluate a degree-descending polynomial at x (Horner).
142
+ *
143
+ * @param {ArrayLike<number>} poly
144
+ * @param {number} x
145
+ * @param {import('./galois-field.js').GaloisField} field
146
+ * @returns {number}
147
+ */
148
+ function evalPoly(poly, x, field) {
149
+ let acc = 0;
150
+ for (let i = 0; i < poly.length; i++) {
151
+ acc = field.add(field.mul(acc, x), poly[i]);
152
+ }
153
+ return acc;
154
+ }
155
+
156
+ function multiplyAscending(left, right, field, limit) {
157
+ const out = new Array(Math.min(limit, left.length + right.length - 1)).fill(0);
158
+ for (let i = 0; i < left.length; i++) for (let j = 0; j < right.length && i + j < out.length; j++) {
159
+ out[i + j] = field.add(out[i + j], field.mul(left[i], right[j]));
160
+ }
161
+ return out;
162
+ }
163
+
164
+ function berlekampMassey(syndromes, field) {
165
+ const limit = syndromes.length;
166
+ const lambda = new Array(limit + 1).fill(0);
167
+ const previous = new Array(limit + 1).fill(0);
168
+ const temporary = new Array(limit + 1).fill(0);
169
+ lambda[0] = 1;
170
+ previous[0] = 1;
171
+ let errorCount = 0;
172
+ let shift = 1;
173
+ let lastDiscrepancy = 1;
174
+
175
+ for (let step = 0; step < limit; step++) {
176
+ let discrepancy = syndromes[step];
177
+ for (let i = 1; i <= errorCount; i++) discrepancy = field.add(discrepancy, field.mul(lambda[i], syndromes[step - i]));
178
+ if (discrepancy === 0) { shift++; continue; }
179
+ const scale = field.div(discrepancy, lastDiscrepancy);
180
+ for (let i = 0; i <= limit; i++) temporary[i] = lambda[i];
181
+ for (let i = 0; i + shift <= limit; i++) if (previous[i] !== 0) {
182
+ lambda[i + shift] = field.sub(lambda[i + shift], field.mul(scale, previous[i]));
183
+ }
184
+ if (2 * errorCount <= step) {
185
+ errorCount = step + 1 - errorCount;
186
+ for (let i = 0; i <= limit; i++) previous[i] = temporary[i];
187
+ lastDiscrepancy = discrepancy;
188
+ shift = 1;
189
+ } else shift++;
190
+ }
191
+ return { locator: lambda.slice(0, errorCount + 1), errorCount };
192
+ }
193
+
194
+ /**
195
+ * Correct errors in a received codeword, in place.
196
+ *
197
+ * @param {number[]} received Data followed by parity, degree-descending.
198
+ * @param {number} eccLen
199
+ * @param {import('./galois-field.js').GaloisField} field
200
+ * @param {number} [base]
201
+ * @param {number[]} [erasures] Known damaged indexes, counted from wire order.
202
+ * @returns {number} Number of symbols corrected.
203
+ * @throws {ChecksumError} If the damage exceeds the correction capacity.
204
+ */
205
+ export function rsDecode(received, eccLen, field, base = 0, erasures = []) {
206
+ const n = received.length;
207
+ if (!Array.isArray(erasures) || new Set(erasures).size !== erasures.length || erasures.some((index) => !Number.isInteger(index) || index < 0 || index >= n)) {
208
+ throw new ChecksumError('Reed-Solomon: erasure positions must be unique codeword indexes');
209
+ }
210
+ if (erasures.length > eccLen) throw new ChecksumError(`Reed-Solomon: ${erasures.length} erasures exceeds correction capacity ${eccLen} (${field.name})`);
211
+
212
+ // --- Syndromes. S[i] = R(a^(base+i)); all zero means an intact codeword.
213
+ const syn = new Array(eccLen).fill(0);
214
+ let damaged = false;
215
+ for (let i = 0; i < eccLen; i++) {
216
+ const s = evalPoly(received, field.exp(base + i), field);
217
+ syn[i] = s;
218
+ if (s !== 0) damaged = true;
219
+ }
220
+ if (!damaged) return 0;
221
+
222
+ // Remove the known roots before locating unknown errors. The leading
223
+ // erasureCount terms contain only the known-location transient and are not
224
+ // part of the error-only recurrence.
225
+ let erasureLocator = [1];
226
+ for (const index of erasures) {
227
+ const location = field.exp(n - 1 - index);
228
+ erasureLocator = multiplyAscending(erasureLocator, [1, field.neg(location)], field, eccLen + 1);
229
+ }
230
+ const modified = multiplyAscending(syn, erasureLocator, field, eccLen).slice(erasures.length);
231
+ const { locator: errorLocator, errorCount } = berlekampMassey(modified, field);
232
+ if (2 * errorCount + erasures.length > eccLen) {
233
+ throw new ChecksumError(
234
+ `Reed-Solomon: ${errorCount} errors and ${erasures.length} erasures exceed correction capacity ` +
235
+ `${eccLen} (${field.name})`
236
+ );
237
+ }
238
+ const lambda = multiplyAscending(erasureLocator, errorLocator, field, eccLen + 1);
239
+ const totalCount = errorCount + erasures.length;
240
+
241
+ // --- Chien search. Position p (counted from the low-order end) is in error
242
+ // when lambda(a^-p) == 0.
243
+ const positions = [];
244
+ for (let p = 0; p < n; p++) {
245
+ const xInv = field.exp(-p);
246
+ let acc = 0;
247
+ let term = 1;
248
+ for (let i = 0; i <= totalCount; i++) {
249
+ acc = field.add(acc, field.mul(lambda[i], term));
250
+ term = field.mul(term, xInv);
251
+ }
252
+ if (acc === 0) positions.push(p);
253
+ }
254
+
255
+ if (positions.length !== totalCount) {
256
+ throw new ChecksumError(
257
+ `Reed-Solomon: located ${positions.length} of ${totalCount} error positions`
258
+ );
259
+ }
260
+
261
+ // --- Error evaluator. omega(x) = [S(x) * lambda(x)] mod x^eccLen,
262
+ // with S degree-ascending.
263
+ const omega = new Array(eccLen).fill(0);
264
+ for (let i = 0; i < eccLen; i++) {
265
+ let acc = 0;
266
+ for (let j = 0; j <= i && j <= totalCount; j++) {
267
+ acc = field.add(acc, field.mul(lambda[j], syn[i - j]));
268
+ }
269
+ omega[i] = acc;
270
+ }
271
+
272
+ // --- Forney. For an error at position p, with X = a^p:
273
+ // magnitude = -X^(1-base) * omega(X^-1) / lambda'(X^-1)
274
+ // The sign is a no-op in binary fields and load-bearing in GF(929).
275
+ let corrected = 0;
276
+ for (const p of positions) {
277
+ const xInv = field.exp(-p);
278
+
279
+ let num = 0;
280
+ let term = 1;
281
+ for (let i = 0; i < eccLen; i++) {
282
+ num = field.add(num, field.mul(omega[i], term));
283
+ term = field.mul(term, xInv);
284
+ }
285
+
286
+ // Formal derivative: only odd-index terms survive in a binary field, but
287
+ // in a prime field every term contributes with an integer multiplier.
288
+ let den = 0;
289
+ term = 1;
290
+ for (let i = 1; i <= totalCount; i++) {
291
+ if (field.prime) {
292
+ // i * lambda[i] * x^(i-1), where `i` is repeated addition.
293
+ let mult = 0;
294
+ const t = field.mul(lambda[i], term);
295
+ for (let k = 0; k < i; k++) mult = field.add(mult, t);
296
+ den = field.add(den, mult);
297
+ } else if (i % 2 === 1) {
298
+ den = field.add(den, field.mul(lambda[i], term));
299
+ }
300
+ term = field.mul(term, xInv);
301
+ }
302
+
303
+ if (den === 0) {
304
+ throw new ChecksumError('Reed-Solomon: singular error locator derivative');
305
+ }
306
+
307
+ let magnitude = field.div(num, den);
308
+ // X^(1-base): one factor of X when base is 0, none when base is 1.
309
+ if (base === 0) magnitude = field.mul(magnitude, field.exp(p));
310
+ else if (base !== 1) magnitude = field.mul(magnitude, field.exp(p * (1 - base)));
311
+ magnitude = field.neg(magnitude);
312
+
313
+ const idx = n - 1 - p;
314
+ received[idx] = field.sub(received[idx], magnitude);
315
+ corrected++;
316
+ }
317
+
318
+ // Verify: a genuine correction zeroes every syndrome. Without this check,
319
+ // damage beyond capacity can produce a plausible-looking wrong answer.
320
+ for (let i = 0; i < eccLen; i++) {
321
+ if (evalPoly(received, field.exp(base + i), field) !== 0) {
322
+ throw new ChecksumError('Reed-Solomon: correction failed verification');
323
+ }
324
+ }
325
+
326
+ return corrected;
327
+ }
@@ -0,0 +1,181 @@
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
+ * GTIN validation and numeric compaction for GS1 DataBar GTIN-only symbols.
33
+ *
34
+ * This is intentionally not a bar-pattern encoder. It emits the exact symbol
35
+ * character values required by ISO/IEC 24724:2011; a later encoder can turn
36
+ * those values into widths without reinterpreting a GTIN or linkage flag.
37
+ *
38
+ * @module databar/codec
39
+ */
40
+
41
+ import {
42
+ DATABAR14_INSIDE_RADIX,
43
+ DATABAR14_PAIR_RADIX,
44
+ DATABAR14_SYMBOL_LIMIT,
45
+ DATABAR_LIMITED_DATA_LIMIT,
46
+ DATABAR_LIMITED_LINKAGE_OFFSET,
47
+ DATABAR_LIMITED_PAIR_RADIX,
48
+ } from './tables.js';
49
+
50
+ const GTIN_LENGTHS = new Set([8, 12, 13, 14]);
51
+
52
+ function digits(value, label) {
53
+ const text = String(value);
54
+ if (!/^\d+$/.test(text)) throw new TypeError(`${label} must contain only decimal digits`);
55
+ return text;
56
+ }
57
+
58
+ function asCharacterValue(value, label, maximum) {
59
+ if (!Number.isInteger(value) || value < 0 || value > maximum) {
60
+ throw new RangeError(`${label} must be an integer from 0 to ${maximum}`);
61
+ }
62
+ return value;
63
+ }
64
+
65
+ /** Calculate the GS1 modulo-10 check digit for a GTIN body. */
66
+ export function gtinCheckDigit(body) {
67
+ const text = digits(body, 'GTIN body');
68
+ if (text.length < 1 || text.length > 13) throw new RangeError('GTIN body must contain 1 to 13 digits');
69
+ let sum = 0;
70
+ for (let i = text.length - 1, weight = 3; i >= 0; i--, weight = weight === 3 ? 1 : 3) {
71
+ sum += Number(text[i]) * weight;
72
+ }
73
+ return String((10 - (sum % 10)) % 10);
74
+ }
75
+
76
+ /** Return a checked 14-digit GTIN, preserving only its standardized digits. */
77
+ export function normalizeGTIN(value) {
78
+ const input = digits(value, 'GTIN');
79
+ if (!GTIN_LENGTHS.has(input.length)) {
80
+ throw new RangeError('GTIN must contain 8, 12, 13, or 14 digits including its check digit');
81
+ }
82
+ if (gtinCheckDigit(input.slice(0, -1)) !== input.at(-1)) {
83
+ throw new RangeError(`GTIN check digit is invalid for ${input}`);
84
+ }
85
+ return input.padStart(14, '0');
86
+ }
87
+
88
+ /** Build a checked GTIN-14 from its thirteen-digit data body. */
89
+ export function makeGTIN14(body) {
90
+ const input = digits(body, 'GTIN-14 body');
91
+ if (input.length > 13) throw new RangeError('GTIN-14 body must contain at most 13 digits');
92
+ const padded = input.padStart(13, '0');
93
+ return padded + gtinCheckDigit(padded);
94
+ }
95
+
96
+ function gtinFromSymbolValue(symbolValue) {
97
+ const value = BigInt(symbolValue);
98
+ if (value < 0n || value >= DATABAR14_SYMBOL_LIMIT) throw new RangeError('GS1 DataBar-14 symbol value is out of range');
99
+ const linkage = value >= 10000000000000n;
100
+ const body = (linkage ? value - 10000000000000n : value).toString().padStart(13, '0');
101
+ return { linkage, gtin: body + gtinCheckDigit(body) };
102
+ }
103
+
104
+ /**
105
+ * Compact a GTIN into the four values shared by Omnidirectional, Truncated,
106
+ * Stacked and Stacked Omnidirectional. The `physicalCharacters` sequence is
107
+ * left-to-right on a linear DataBar-14 row: 1, 2, 4, 3.
108
+ */
109
+ export function encodeDataBar14GTIN(value, options = {}) {
110
+ const gtin = normalizeGTIN(value);
111
+ const linkage = options.linkage === true;
112
+ if (options.linkage !== undefined && typeof options.linkage !== 'boolean') {
113
+ throw new TypeError('GS1 DataBar linkage must be a boolean');
114
+ }
115
+ const dataBody = gtin.slice(0, -1);
116
+ const symbolValue = BigInt(dataBody) + (linkage ? 10000000000000n : 0n);
117
+ const leftPair = symbolValue / BigInt(DATABAR14_PAIR_RADIX);
118
+ const rightPair = symbolValue % BigInt(DATABAR14_PAIR_RADIX);
119
+ const outerLeft = Number(leftPair / BigInt(DATABAR14_INSIDE_RADIX));
120
+ const innerLeft = Number(leftPair % BigInt(DATABAR14_INSIDE_RADIX));
121
+ const outerRight = Number(rightPair / BigInt(DATABAR14_INSIDE_RADIX));
122
+ const innerRight = Number(rightPair % BigInt(DATABAR14_INSIDE_RADIX));
123
+
124
+ return Object.freeze({
125
+ gtin, linkage, symbolValue, leftPair, rightPair,
126
+ logicalCharacters: Object.freeze({ outerLeft, innerLeft, outerRight, innerRight }),
127
+ physicalCharacters: Object.freeze([outerLeft, innerLeft, innerRight, outerRight]),
128
+ });
129
+ }
130
+
131
+ /** Reconstruct and validate a GTIN from the four DataBar-14 character values. */
132
+ export function decodeDataBar14GTIN(values) {
133
+ if (values === null || typeof values !== 'object') throw new TypeError('GS1 DataBar-14 characters must be an object');
134
+ const outerLeft = asCharacterValue(values.outerLeft, 'outerLeft', 2840);
135
+ const innerLeft = asCharacterValue(values.innerLeft, 'innerLeft', 1596);
136
+ const outerRight = asCharacterValue(values.outerRight, 'outerRight', 2840);
137
+ const innerRight = asCharacterValue(values.innerRight, 'innerRight', 1596);
138
+ const leftPair = BigInt(outerLeft) * BigInt(DATABAR14_INSIDE_RADIX) + BigInt(innerLeft);
139
+ const rightPair = BigInt(outerRight) * BigInt(DATABAR14_INSIDE_RADIX) + BigInt(innerRight);
140
+ return Object.freeze({
141
+ ...gtinFromSymbolValue(leftPair * BigInt(DATABAR14_PAIR_RADIX) + rightPair),
142
+ leftPair, rightPair,
143
+ });
144
+ }
145
+
146
+ /** Compact a DataBar Limited-eligible GTIN into its two data character values. */
147
+ export function encodeDataBarLimitedGTIN(value, options = {}) {
148
+ const gtin = normalizeGTIN(value);
149
+ const linkage = options.linkage === true;
150
+ if (options.linkage !== undefined && typeof options.linkage !== 'boolean') {
151
+ throw new TypeError('GS1 DataBar linkage must be a boolean');
152
+ }
153
+ const dataBody = BigInt(gtin.slice(0, -1));
154
+ if (dataBody >= DATABAR_LIMITED_DATA_LIMIT) {
155
+ throw new RangeError('GS1 DataBar Limited requires a GTIN whose indicator digit is 0 or 1');
156
+ }
157
+ const symbolValue = dataBody + (linkage ? DATABAR_LIMITED_LINKAGE_OFFSET : 0n);
158
+ const left = Number(symbolValue / BigInt(DATABAR_LIMITED_PAIR_RADIX));
159
+ const right = Number(symbolValue % BigInt(DATABAR_LIMITED_PAIR_RADIX));
160
+ return Object.freeze({ gtin, linkage, symbolValue, left, right });
161
+ }
162
+
163
+ /** Reconstruct and validate a GTIN from DataBar Limited data character values. */
164
+ export function decodeDataBarLimitedGTIN(values) {
165
+ if (values === null || typeof values !== 'object') throw new TypeError('GS1 DataBar Limited characters must be an object');
166
+ const left = asCharacterValue(values.left, 'left', 1994036);
167
+ const right = asCharacterValue(values.right, 'right', DATABAR_LIMITED_PAIR_RADIX - 1);
168
+ let symbolValue = BigInt(left) * BigInt(DATABAR_LIMITED_PAIR_RADIX) + BigInt(right);
169
+ const linkage = symbolValue >= DATABAR_LIMITED_LINKAGE_OFFSET;
170
+ if (linkage) symbolValue -= DATABAR_LIMITED_LINKAGE_OFFSET;
171
+ if (symbolValue < 0n || symbolValue >= DATABAR_LIMITED_DATA_LIMIT) {
172
+ throw new RangeError('GS1 DataBar Limited character values do not encode a permitted GTIN');
173
+ }
174
+ const body = symbolValue.toString().padStart(13, '0');
175
+ return Object.freeze({ linkage, gtin: body + gtinCheckDigit(body), symbolValue: BigInt(left) * BigInt(DATABAR_LIMITED_PAIR_RADIX) + BigInt(right) });
176
+ }
177
+
178
+ /** GS1 transmitted form for the GTIN-only DataBar variants. */
179
+ export function dataBarGtinTransmission(value) {
180
+ return `]e001${normalizeGTIN(value)}`;
181
+ }