@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,548 @@
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
+ /** Strict DotCode matrix decoder. @module dotcode/decoder */
31
+ import { BitMatrix } from '../core/bit-matrix.js';
32
+ import { ChecksumError, FormatError } from '../core/errors.js';
33
+ import { rsDecode } from '../core/reed-solomon.js';
34
+ import { DOTCODE_CODEWORD_COUNT, DOTCODE_FIELD_SIZE, DOTCODE_MASK_STEPS, DOTCODE_MAX_DIMENSION, DOTCODE_MIN_DIMENSION, GF113_DOTCODE, dotCodeActivePositions, dotCodeCodeword, dotCodeCodewordCapacity, dotCodeCornerOrder, dotCodeDataCapacity, dotCodeIsCorner, dotCodeIsDataPosition, } from './tables.js';
35
+ function checkMatrix(matrix) {
36
+ if (!matrix || !Number.isInteger(matrix.width) || !Number.isInteger(matrix.height) || typeof matrix.get !== 'function') {
37
+ throw new FormatError('DotCode: no matrix supplied');
38
+ }
39
+ if (matrix.width < DOTCODE_MIN_DIMENSION || matrix.height < DOTCODE_MIN_DIMENSION ||
40
+ matrix.width > DOTCODE_MAX_DIMENSION || matrix.height > DOTCODE_MAX_DIMENSION) {
41
+ throw new FormatError(`DotCode: dimensions must be in ${DOTCODE_MIN_DIMENSION}..${DOTCODE_MAX_DIMENSION}`);
42
+ }
43
+ if (((matrix.width + matrix.height) & 1) === 0) {
44
+ throw new FormatError('DotCode: width plus height must be odd');
45
+ }
46
+ }
47
+ function readStream(matrix) {
48
+ const width = matrix.width;
49
+ const height = matrix.height;
50
+ const stream = [];
51
+ const read = (column, row) => stream.push(matrix.get(column, row));
52
+ // Odd-height symbols are folded horizontally; even-height symbols are
53
+ // folded vertically. The six reserved corners are read last in wire order.
54
+ if (height & 1) {
55
+ for (let row = 0; row < height; row++)
56
+ for (let column = 0; column < width; column++) {
57
+ if (!dotCodeIsDataPosition(column, row) || dotCodeIsCorner(column, row, width, height))
58
+ continue;
59
+ read(column, height - row - 1);
60
+ }
61
+ }
62
+ else {
63
+ for (let column = 0; column < width; column++)
64
+ for (let row = 0; row < height; row++) {
65
+ if (!dotCodeIsDataPosition(column, row) || dotCodeIsCorner(column, row, width, height))
66
+ continue;
67
+ read(column, row);
68
+ }
69
+ }
70
+ for (const [column, row] of dotCodeCornerOrder(width, height))
71
+ read(column, row);
72
+ const expected = dotCodeActivePositions(width, height);
73
+ if (stream.length !== expected)
74
+ throw new FormatError('DotCode: matrix fold has an invalid active-position count');
75
+ return stream;
76
+ }
77
+ function unmaskAndCorrect(stream, width, height) {
78
+ const slots = dotCodeCodewordCapacity(width, height);
79
+ const dataLength = dotCodeDataCapacity(slots);
80
+ if (!dataLength)
81
+ throw new FormatError('DotCode: dimensions cannot carry data and error correction');
82
+ const errorCodewords = 3 + Math.floor(dataLength / 2);
83
+ const symbolCodewords = dataLength + errorCodewords;
84
+ const usedBits = 2 + symbolCodewords * 9;
85
+ if (usedBits > stream.length)
86
+ throw new FormatError('DotCode: codeword stream is truncated');
87
+ for (let bit = usedBits; bit < stream.length; bit++) {
88
+ if (!stream[bit])
89
+ throw new FormatError('DotCode: padding bits must be dark');
90
+ }
91
+ const wireMask = (stream[0] ? 2 : 0) | (stream[1] ? 1 : 0);
92
+ if (wireMask < 0 || wireMask > 3)
93
+ throw new FormatError('DotCode: invalid mask bits');
94
+ const received = [wireMask];
95
+ for (let word = 0; word < symbolCodewords; word++) {
96
+ const offset = 2 + word * 9;
97
+ let pattern = 0;
98
+ for (let bit = 0; bit < 9; bit++)
99
+ pattern = (pattern << 1) | (stream[offset + bit] ? 1 : 0);
100
+ const codeword = dotCodeCodeword(pattern);
101
+ if (codeword < 0 || codeword >= DOTCODE_CODEWORD_COUNT) {
102
+ throw new FormatError(`DotCode: unassigned 5-of-9 pattern 0x${pattern.toString(16)}`);
103
+ }
104
+ received.push(codeword);
105
+ }
106
+ const protectedLength = dataLength + 1;
107
+ const totalLength = protectedLength + errorCodewords;
108
+ const blockCount = Math.ceil(totalLength / (DOTCODE_FIELD_SIZE - 1));
109
+ const corrected = received.slice();
110
+ let corrections = 0;
111
+ for (let block = 0; block < blockCount; block++) {
112
+ const blockData = Math.ceil((protectedLength - block) / blockCount);
113
+ const blockTotal = Math.ceil((totalLength - block) / blockCount);
114
+ const blockEcc = blockTotal - blockData;
115
+ if (blockData < 1 || blockEcc < 1)
116
+ throw new FormatError('DotCode: invalid interleaved RS block layout');
117
+ const blockValues = [];
118
+ // The first protected value is the two-bit mask itself, so protected data
119
+ // starts at received[0], exactly as it does in the encoder's wire array.
120
+ for (let index = 0; index < blockData; index++)
121
+ blockValues.push(received[block + index * blockCount]);
122
+ for (let index = 0; index < blockEcc; index++) {
123
+ // The encoder's wire indexes include the mask at position zero. Data
124
+ // is consequently read at 1+offset, while parity starts at the raw
125
+ // interleaved offset returned by the standard layout.
126
+ const value = received[block + blockData * blockCount + index * blockCount];
127
+ if (value === undefined)
128
+ throw new FormatError('DotCode: truncated RS parity block');
129
+ blockValues.push(value);
130
+ }
131
+ try {
132
+ corrections += rsDecode(blockValues, blockEcc, GF113_DOTCODE, 1);
133
+ }
134
+ catch (error) {
135
+ if (error instanceof ChecksumError)
136
+ throw error;
137
+ throw new ChecksumError('DotCode: Reed-Solomon verification failed');
138
+ }
139
+ for (let index = 0; index < blockData; index++)
140
+ corrected[block + index * blockCount] = blockValues[index];
141
+ }
142
+ const correctedMask = corrected[0];
143
+ if (!Number.isInteger(correctedMask) || correctedMask < 0 || correctedMask > 3) {
144
+ throw new ChecksumError('DotCode: Reed-Solomon corrected an invalid mask value');
145
+ }
146
+ const correctedMaskValue = correctedMask;
147
+ const step = DOTCODE_MASK_STEPS[correctedMaskValue];
148
+ const data = [];
149
+ for (let index = 0; index < dataLength; index++) {
150
+ const value = corrected[1 + index];
151
+ const unmapped = (value - step * index) % DOTCODE_FIELD_SIZE;
152
+ data.push((unmapped + DOTCODE_FIELD_SIZE) % DOTCODE_FIELD_SIZE);
153
+ }
154
+ return { data, mask: correctedMaskValue, errorCodewords, corrections };
155
+ }
156
+ function appendBytes(output, values) {
157
+ if (typeof values === 'number')
158
+ output.push(values);
159
+ else
160
+ output.push(...values);
161
+ }
162
+ function aCharacter(codeword) {
163
+ if (codeword < 0 || codeword > 95)
164
+ throw new FormatError('DotCode: invalid Code Set A character');
165
+ return codeword < 64 ? codeword + 32 : codeword - 64;
166
+ }
167
+ function bCharacter(codeword) {
168
+ if (codeword < 0 || codeword > 95)
169
+ throw new FormatError('DotCode: invalid Code Set B character');
170
+ return codeword + 32;
171
+ }
172
+ function bSymbols(codeword) {
173
+ if (codeword <= 95)
174
+ return bCharacter(codeword);
175
+ if (codeword === 96)
176
+ return [13, 10];
177
+ if (codeword === 97)
178
+ return 9;
179
+ if (codeword === 98)
180
+ return 28;
181
+ if (codeword === 99)
182
+ return 29;
183
+ if (codeword === 100)
184
+ return 30;
185
+ throw new FormatError('DotCode: invalid Code Set B character');
186
+ }
187
+ function cCharacters(codeword) {
188
+ if (codeword < 0 || codeword > 99)
189
+ throw new FormatError('DotCode: invalid Code Set C pair');
190
+ return [48 + Math.floor(codeword / 10), 48 + (codeword % 10)];
191
+ }
192
+ function binaryDigitsToBytes(words) {
193
+ if (!words.length || words.length > 6 || words.length === 1) {
194
+ throw new FormatError('DotCode: incomplete binary radix group');
195
+ }
196
+ if (words.some((word) => word < 0 || word > 102))
197
+ throw new FormatError('DotCode: binary group contains a control codeword');
198
+ let value = 0;
199
+ for (const word of words)
200
+ value = value * 103 + word;
201
+ const digitCount = words.length - 1;
202
+ const bytes = new Array(digitCount).fill(0);
203
+ for (let index = digitCount - 1; index >= 0; index--) {
204
+ const digit = value % 259;
205
+ value = Math.floor(value / 259);
206
+ if (digit > 255)
207
+ throw new FormatError('DotCode: ECI or reserved binary value is not supported');
208
+ bytes[index] = digit;
209
+ }
210
+ if (value !== 0)
211
+ throw new FormatError('DotCode: binary radix group overflows five base-259 digits');
212
+ return bytes;
213
+ }
214
+ function decodePayload(data) {
215
+ const words = data.slice();
216
+ while (words.length && words[words.length - 1] === 106)
217
+ words.pop();
218
+ if (!words.length)
219
+ throw new FormatError('DotCode: data contains padding only');
220
+ const bytes = [];
221
+ let mode = 'C';
222
+ let position = 0;
223
+ let lead = true;
224
+ let gs1 = false;
225
+ let binarySeen = false;
226
+ const markData = () => { lead = false; };
227
+ const markControl = () => { if (lead)
228
+ lead = false; };
229
+ const readWord = (message) => {
230
+ if (position >= words.length)
231
+ throw new FormatError(`DotCode: ${message} is truncated`);
232
+ return words[position++];
233
+ };
234
+ const readB = () => appendBytes(bytes, bSymbols(readWord('Code Set B shift')));
235
+ const readA = () => appendBytes(bytes, aCharacter(readWord('Code Set A shift')));
236
+ const readC = () => appendBytes(bytes, cCharacters(readWord('Code Set C shift')));
237
+ const readBShift = (count) => { for (let index = 0; index < count; index++)
238
+ readB(); };
239
+ const readCShift = (count) => {
240
+ for (let index = 0; index < count; index++) {
241
+ const value = readWord('Code Set C shift');
242
+ appendBytes(bytes, cCharacters(value));
243
+ }
244
+ };
245
+ while (position < words.length) {
246
+ const codeword = words[position++];
247
+ if (mode === 'X') {
248
+ const group = [codeword];
249
+ while (group[group.length - 1] <= 102 && position < words.length && words[position] <= 102)
250
+ group.push(words[position++]);
251
+ if (group[0] <= 102) {
252
+ // The final short group has 2..5 codewords; a full group has six.
253
+ if (group.length % 6 === 1) {
254
+ // A control follows a complete group and is handled below. This
255
+ // branch is only reached when the symbol ends on one stray value.
256
+ throw new FormatError('DotCode: binary radix group has an invalid length');
257
+ }
258
+ for (let offset = 0; offset < group.length; offset += 6) {
259
+ const size = Math.min(6, group.length - offset);
260
+ if (size === 1)
261
+ throw new FormatError('DotCode: binary radix group has an invalid length');
262
+ appendBytes(bytes, binaryDigitsToBytes(group.slice(offset, offset + size)));
263
+ }
264
+ }
265
+ // A control codeword interrupts binary mode. The group was collected
266
+ // only from data codewords, so the control remains at `codeword`.
267
+ const control = group[0] > 102 ? group[0] : null;
268
+ if (control === null)
269
+ continue;
270
+ markControl();
271
+ if (control >= 103 && control <= 108) {
272
+ readCShift(control - 101);
273
+ mode = 'X';
274
+ }
275
+ else if (control === 109)
276
+ mode = 'A';
277
+ else if (control === 110)
278
+ mode = 'B';
279
+ else if (control === 111)
280
+ mode = 'C';
281
+ else if (control === 112)
282
+ throw new FormatError('DotCode: structured binary separation is not supported');
283
+ else
284
+ throw new FormatError('DotCode: unsupported binary control codeword');
285
+ continue;
286
+ }
287
+ if (mode === 'C') {
288
+ if (codeword <= 99) {
289
+ if (lead)
290
+ gs1 = true;
291
+ appendBytes(bytes, cCharacters(codeword));
292
+ markData();
293
+ }
294
+ else if (codeword === 100) {
295
+ markControl();
296
+ throw new FormatError('DotCode: Code Set C macro/AI 17 encoding is not supported');
297
+ }
298
+ else if (codeword === 101) {
299
+ mode = 'A';
300
+ }
301
+ else if (codeword >= 102 && codeword <= 105) {
302
+ readBShift(codeword - 101);
303
+ markControl();
304
+ }
305
+ else if (codeword === 106) {
306
+ mode = 'B';
307
+ }
308
+ else if (codeword === 107) {
309
+ if (lead) {
310
+ lead = false;
311
+ }
312
+ else
313
+ appendBytes(bytes, 29);
314
+ }
315
+ else if (codeword === 108 || codeword === 109) {
316
+ markControl();
317
+ throw new FormatError(`DotCode: ${codeword === 108 ? 'FNC2' : 'FNC3'} is not supported`);
318
+ }
319
+ else if (codeword === 110 || codeword === 111) {
320
+ const shifted = readWord('upper shift');
321
+ const value = codeword === 110 ? shifted + 64 : shifted + 160;
322
+ if ((codeword === 110 && (shifted < 64 || shifted > 95)) || (codeword === 111 && (shifted < 0 || shifted > 95)) || value > 255) {
323
+ throw new FormatError('DotCode: invalid upper-shift operand');
324
+ }
325
+ appendBytes(bytes, value);
326
+ markData();
327
+ }
328
+ else if (codeword === 112) {
329
+ mode = 'X';
330
+ binarySeen = true;
331
+ markControl();
332
+ }
333
+ else
334
+ throw new FormatError(`DotCode: unsupported Code Set C codeword ${codeword}`);
335
+ continue;
336
+ }
337
+ if (mode === 'A') {
338
+ if (codeword <= 95) {
339
+ appendBytes(bytes, aCharacter(codeword));
340
+ markData();
341
+ }
342
+ else if (codeword === 96) {
343
+ appendBytes(bytes, [13, 10]);
344
+ markData();
345
+ }
346
+ else if (codeword >= 97 && codeword <= 101) {
347
+ readBShift(codeword - 95);
348
+ markControl();
349
+ }
350
+ else if (codeword === 102)
351
+ mode = 'B';
352
+ else if (codeword >= 103 && codeword <= 105) {
353
+ readCShift(codeword - 101);
354
+ markControl();
355
+ }
356
+ else if (codeword === 106)
357
+ mode = 'C';
358
+ else if (codeword === 107) {
359
+ appendBytes(bytes, lead ? [] : 29);
360
+ lead = false;
361
+ }
362
+ else if (codeword === 108 || codeword === 109) {
363
+ markControl();
364
+ throw new FormatError(`DotCode: ${codeword === 108 ? 'FNC2' : 'FNC3'} is not supported`);
365
+ }
366
+ else if (codeword === 110 || codeword === 111) {
367
+ const shifted = readWord('upper shift');
368
+ const value = codeword === 110 ? shifted + 64 : shifted + 160;
369
+ if ((codeword === 110 && (shifted < 64 || shifted > 95)) || (codeword === 111 && (shifted < 0 || shifted > 95)) || value > 255) {
370
+ throw new FormatError('DotCode: invalid upper-shift operand');
371
+ }
372
+ appendBytes(bytes, value);
373
+ markData();
374
+ }
375
+ else if (codeword === 112) {
376
+ mode = 'X';
377
+ binarySeen = true;
378
+ markControl();
379
+ }
380
+ else
381
+ throw new FormatError(`DotCode: unsupported Code Set A codeword ${codeword}`);
382
+ continue;
383
+ }
384
+ // Code Set B.
385
+ if (codeword <= 95) {
386
+ appendBytes(bytes, bCharacter(codeword));
387
+ markData();
388
+ }
389
+ else if (codeword === 96) {
390
+ appendBytes(bytes, [13, 10]);
391
+ markData();
392
+ }
393
+ else if (codeword === 97) {
394
+ appendBytes(bytes, 9);
395
+ markData();
396
+ }
397
+ else if (codeword === 98) {
398
+ appendBytes(bytes, 28);
399
+ markData();
400
+ }
401
+ else if (codeword === 99) {
402
+ appendBytes(bytes, 29);
403
+ markData();
404
+ }
405
+ else if (codeword === 100) {
406
+ appendBytes(bytes, 30);
407
+ markData();
408
+ }
409
+ else if (codeword === 101) {
410
+ readA();
411
+ markControl();
412
+ }
413
+ else if (codeword === 102)
414
+ mode = 'A';
415
+ else if (codeword >= 103 && codeword <= 105) {
416
+ readBShift(codeword - 101);
417
+ markControl();
418
+ }
419
+ else if (codeword === 106) {
420
+ mode = 'C';
421
+ }
422
+ else if (codeword === 107) {
423
+ appendBytes(bytes, lead ? [] : 29);
424
+ lead = false;
425
+ }
426
+ else if (codeword === 108 || codeword === 109) {
427
+ markControl();
428
+ throw new FormatError(`DotCode: ${codeword === 108 ? 'FNC2' : 'FNC3'} is not supported`);
429
+ }
430
+ else if (codeword === 110 || codeword === 111) {
431
+ const shifted = readWord('upper shift');
432
+ const value = codeword === 110 ? shifted + 64 : shifted + 160;
433
+ if ((codeword === 110 && (shifted < 64 || shifted > 95)) || (codeword === 111 && (shifted < 0 || shifted > 95)) || value > 255) {
434
+ throw new FormatError('DotCode: invalid upper-shift operand');
435
+ }
436
+ appendBytes(bytes, value);
437
+ markData();
438
+ }
439
+ else if (codeword === 112) {
440
+ mode = 'X';
441
+ binarySeen = true;
442
+ markControl();
443
+ }
444
+ else
445
+ throw new FormatError(`DotCode: unsupported Code Set B codeword ${codeword}`);
446
+ }
447
+ if (!bytes.length)
448
+ throw new FormatError('DotCode: decoded payload is empty');
449
+ let text = '';
450
+ let encoding = 'latin1';
451
+ try {
452
+ text = new TextDecoder('utf-8', { fatal: true }).decode(Uint8Array.from(bytes));
453
+ encoding = 'utf8';
454
+ }
455
+ catch {
456
+ for (const byte of bytes)
457
+ text += String.fromCharCode(byte);
458
+ }
459
+ return { text, bytes: Uint8Array.from(bytes), gs1, encoding, binarySeen };
460
+ }
461
+ function rotate(matrix, degrees) {
462
+ if (degrees === 0)
463
+ return matrix.clone();
464
+ const output = degrees === 90 || degrees === 270 ? new BitMatrix(matrix.height, matrix.width) : new BitMatrix(matrix.width, matrix.height);
465
+ for (let y = 0; y < matrix.height; y++)
466
+ for (let x = 0; x < matrix.width; x++) {
467
+ let nx = x;
468
+ let ny = y;
469
+ if (degrees === 90) {
470
+ nx = matrix.height - 1 - y;
471
+ ny = x;
472
+ }
473
+ else if (degrees === 180) {
474
+ nx = matrix.width - 1 - x;
475
+ ny = matrix.height - 1 - y;
476
+ }
477
+ else {
478
+ nx = y;
479
+ ny = matrix.width - 1 - x;
480
+ }
481
+ output.setValue(nx, ny, matrix.get(x, y));
482
+ }
483
+ return output;
484
+ }
485
+ function inverted(matrix) {
486
+ const output = matrix.clone();
487
+ for (let y = 0; y < output.height; y++)
488
+ for (let x = 0; x < output.width; x++)
489
+ output.flip(x, y);
490
+ return output;
491
+ }
492
+ function decodeCanonical(matrix) {
493
+ checkMatrix(matrix);
494
+ // Inactive checkerboard positions are structural whitespace. Accepting a
495
+ // dark bit there would turn arbitrary dense artwork into a false positive.
496
+ for (let y = 0; y < matrix.height; y++)
497
+ for (let x = 0; x < matrix.width; x++) {
498
+ // The specification deliberately places two of the six tail/corner bits
499
+ // on the opposite checkerboard parity. They are the only legal exception.
500
+ if (!dotCodeIsDataPosition(x, y) && !dotCodeIsCorner(x, y, matrix.width, matrix.height) && matrix.get(x, y)) {
501
+ throw new FormatError('DotCode: inactive alternating position is dark');
502
+ }
503
+ }
504
+ const stream = readStream(matrix);
505
+ const corrected = unmaskAndCorrect(stream, matrix.width, matrix.height);
506
+ const payload = decodePayload(corrected.data);
507
+ return { ...payload, ...corrected, width: matrix.width, height: matrix.height };
508
+ }
509
+ /** Decode a sampled DotCode matrix, trying quarter turns and polarity. */
510
+ export function decodeDotCode(matrix, options = {}) {
511
+ checkMatrix(matrix);
512
+ if (options.rotation !== undefined && options.rotation !== 'auto' &&
513
+ options.rotation !== 0 && options.rotation !== 90 && options.rotation !== 180 && options.rotation !== 270) {
514
+ throw new FormatError('DotCode: rotation must be 0, 90, 180, 270 or auto');
515
+ }
516
+ if (options.inverted !== undefined && options.inverted !== 'auto' && typeof options.inverted !== 'boolean') {
517
+ throw new FormatError('DotCode: inverted must be true, false or auto');
518
+ }
519
+ const rotations = options.rotation === undefined || options.rotation === 'auto'
520
+ ? [0, 90, 180, 270] : [options.rotation];
521
+ const polarities = options.inverted === undefined || options.inverted === 'auto'
522
+ ? [false, true] : [options.inverted];
523
+ let firstError = null;
524
+ for (const rotation of rotations)
525
+ for (const isInverted of polarities) {
526
+ try {
527
+ let oriented = rotate(matrix, rotation);
528
+ if (isInverted)
529
+ oriented = inverted(oriented);
530
+ const decoded = decodeCanonical(oriented);
531
+ return {
532
+ format: 'dotcode', text: decoded.text, bytes: decoded.bytes,
533
+ width: decoded.width, height: decoded.height, mask: decoded.mask,
534
+ dataCodewords: decoded.data.length, errorCodewords: decoded.errorCodewords,
535
+ corrections: decoded.corrections, gs1: decoded.gs1, encoding: decoded.encoding,
536
+ rotation, inverted: isInverted,
537
+ };
538
+ }
539
+ catch (error) {
540
+ if (firstError === null)
541
+ firstError = error;
542
+ }
543
+ }
544
+ if (firstError instanceof Error)
545
+ throw firstError;
546
+ throw new FormatError('DotCode: matrix could not be decoded');
547
+ }
548
+ export { ChecksumError, FormatError };