@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.
- package/LICENSE +5 -4
- package/NOTICE.md +2 -2
- package/README.md +17 -13
- package/bundle/sythos-barcode.esm.js +11658 -11244
- package/bundle/sythos-barcode.js +11658 -11244
- package/package.json +10 -3
- package/src/index.d.ts +1 -1
- package/src/index.js +508 -516
- package/src/js/aztec/decoder.js +208 -199
- package/src/js/aztec/detector.js +153 -126
- package/src/js/aztec/encoder.js +172 -153
- package/src/js/aztec/high-level.js +106 -101
- package/src/js/aztec/index.js +1 -12
- package/src/js/aztec/tables.js +106 -120
- package/src/js/aztecrune/decoder.js +76 -85
- package/src/js/aztecrune/detector.js +107 -93
- package/src/js/aztecrune/encoder.js +50 -68
- package/src/js/aztecrune/index.js +1 -16
- package/src/js/aztecrune/tables.js +59 -54
- package/src/js/compactpdf417/decoder.js +69 -74
- package/src/js/compactpdf417/detector.js +74 -75
- package/src/js/compactpdf417/encoder.js +68 -81
- package/src/js/compactpdf417/index.js +1 -13
- package/src/js/compactpdf417/tables.js +71 -80
- package/src/js/core/bit-buffer.js +106 -122
- package/src/js/core/bit-matrix.js +196 -194
- package/src/js/core/errors.js +13 -15
- package/src/js/core/galois-field.js +119 -133
- package/src/js/core/index.js +2 -19
- package/src/js/core/reed-solomon.js +195 -204
- package/src/js/databar/codec.js +93 -108
- package/src/js/databar/decoder.js +149 -140
- package/src/js/databar/encoder.js +47 -55
- package/src/js/databar/gs1.js +122 -123
- package/src/js/databar/index.js +4 -36
- package/src/js/databar/patterns.js +81 -85
- package/src/js/databar/tables.js +51 -52
- package/src/js/datamatrix/decoder.js +216 -182
- package/src/js/datamatrix/detector.js +161 -132
- package/src/js/datamatrix/encoder.js +192 -133
- package/src/js/datamatrix/index.js +1 -9
- package/src/js/datamatrix/tables.js +68 -69
- package/src/js/frameqr/decoder.js +102 -125
- package/src/js/frameqr/detector.js +101 -94
- package/src/js/frameqr/encoder.js +67 -80
- package/src/js/frameqr/index.js +1 -10
- package/src/js/frameqr/tables.js +145 -163
- package/src/js/image/binarizer.js +146 -162
- package/src/js/image/grid-sampler.js +63 -79
- package/src/js/image/index.js +0 -2
- package/src/js/image/luminance.js +129 -140
- package/src/js/image/perspective.js +112 -149
- package/src/js/micropdf417/compaction.js +64 -65
- package/src/js/micropdf417/decoder.js +106 -109
- package/src/js/micropdf417/detector.js +73 -70
- package/src/js/micropdf417/encoder.js +129 -154
- package/src/js/micropdf417/error-correction.js +6 -11
- package/src/js/micropdf417/index.js +2 -16
- package/src/js/micropdf417/tables.js +112 -99
- package/src/js/microqr/decoder.js +177 -182
- package/src/js/microqr/detector.js +240 -256
- package/src/js/microqr/encoder.js +239 -194
- package/src/js/microqr/index.js +0 -2
- package/src/js/microqr/tables.js +208 -200
- package/src/js/oned/addons.js +167 -191
- package/src/js/oned/index.js +27 -58
- package/src/js/oned/patterns.js +194 -213
- package/src/js/oned/reader.js +1003 -984
- package/src/js/oned/writers.js +411 -466
- package/src/js/pdf417/compaction.js +315 -222
- package/src/js/pdf417/decoder.js +71 -36
- package/src/js/pdf417/detector.js +377 -361
- package/src/js/pdf417/encoder.js +57 -46
- package/src/js/pdf417/error-correction.js +5 -8
- package/src/js/pdf417/tables.js +248 -250
- package/src/js/qr/decoder.js +311 -347
- package/src/js/qr/detector.js +378 -395
- package/src/js/qr/encoder.js +514 -599
- package/src/js/qr/index.js +0 -2
- package/src/js/qr/tables.js +375 -422
- package/src/js/render/image-data.js +57 -62
- package/src/js/render/index.js +21 -23
- package/src/js/render/options.js +82 -90
- package/src/js/render/png.js +144 -169
- package/src/js/render/svg.js +41 -44
- package/src/js/render/webgl.js +103 -109
- package/src/js/render/webgpu.js +205 -217
- package/src/js/rmqr/decoder.js +161 -53
- package/src/js/rmqr/detector.js +73 -41
- package/src/js/rmqr/encoder.js +253 -104
- package/src/js/rmqr/index.js +1 -5
- package/src/js/rmqr/tables.js +108 -85
- package/src/ts/aztec/decoder.ts +317 -0
- package/src/ts/aztec/detector.ts +224 -0
- package/src/ts/aztec/encoder.ts +257 -0
- package/src/ts/aztec/high-level.ts +211 -0
- package/src/ts/aztec/index.ts +45 -0
- package/src/ts/aztec/tables.ts +210 -0
- package/src/ts/aztecrune/decoder.ts +155 -0
- package/src/ts/aztecrune/detector.ts +166 -0
- package/src/ts/aztecrune/encoder.ts +121 -0
- package/src/ts/aztecrune/index.ts +49 -0
- package/src/ts/aztecrune/tables.ts +137 -0
- package/src/ts/compactpdf417/decoder.ts +128 -0
- package/src/ts/compactpdf417/detector.ts +139 -0
- package/src/ts/compactpdf417/encoder.ts +140 -0
- package/src/ts/compactpdf417/index.ts +17 -0
- package/src/ts/compactpdf417/tables.ts +173 -0
- package/src/ts/core/bit-buffer.ts +174 -0
- package/src/ts/core/bit-matrix.ts +241 -0
- package/src/ts/core/errors.ts +61 -0
- package/src/ts/core/galois-field.ts +207 -0
- package/src/ts/core/index.ts +56 -0
- package/src/ts/core/reed-solomon.ts +327 -0
- package/src/ts/databar/codec.ts +181 -0
- package/src/ts/databar/decoder.ts +215 -0
- package/src/ts/databar/encoder.ts +96 -0
- package/src/ts/databar/gs1.ts +177 -0
- package/src/ts/databar/index.ts +68 -0
- package/src/ts/databar/patterns.ts +143 -0
- package/src/ts/databar/tables.ts +124 -0
- package/src/ts/datamatrix/decoder.ts +262 -0
- package/src/ts/datamatrix/detector.ts +225 -0
- package/src/ts/datamatrix/encoder.ts +191 -0
- package/src/ts/datamatrix/index.ts +42 -0
- package/src/ts/datamatrix/tables.ts +123 -0
- package/src/ts/frameqr/decoder.ts +239 -0
- package/src/ts/frameqr/detector.ts +192 -0
- package/src/ts/frameqr/encoder.ts +156 -0
- package/src/ts/frameqr/index.ts +42 -0
- package/src/ts/frameqr/tables.ts +270 -0
- package/src/ts/image/binarizer.ts +270 -0
- package/src/ts/image/grid-sampler.ts +164 -0
- package/src/ts/image/index.ts +40 -0
- package/src/ts/image/luminance.ts +196 -0
- package/src/ts/image/perspective.ts +195 -0
- package/src/ts/index.d.ts +1 -1
- package/src/ts/index.ts +790 -0
- package/src/ts/micropdf417/compaction.ts +116 -0
- package/src/ts/micropdf417/decoder.ts +183 -0
- package/src/ts/micropdf417/detector.ts +149 -0
- package/src/ts/micropdf417/encoder.ts +209 -0
- package/src/ts/micropdf417/error-correction.ts +55 -0
- package/src/ts/micropdf417/index.ts +49 -0
- package/src/ts/micropdf417/tables.ts +184 -0
- package/src/ts/microqr/decoder.ts +245 -0
- package/src/ts/microqr/detector.ts +355 -0
- package/src/ts/microqr/encoder.ts +269 -0
- package/src/ts/microqr/index.ts +36 -0
- package/src/ts/microqr/tables.ts +316 -0
- package/src/ts/oned/addons.ts +420 -0
- package/src/ts/oned/index.ts +106 -0
- package/src/ts/oned/patterns.ts +384 -0
- package/src/ts/oned/reader.ts +1364 -0
- package/src/ts/oned/writers.ts +746 -0
- package/src/ts/pdf417/compaction.ts +298 -0
- package/src/ts/pdf417/decoder.ts +75 -0
- package/src/ts/pdf417/detector.ts +468 -0
- package/src/ts/pdf417/encoder.ts +91 -0
- package/src/ts/pdf417/error-correction.ts +47 -0
- package/src/ts/pdf417/index.ts +6 -0
- package/src/ts/pdf417/tables.ts +317 -0
- package/src/ts/qr/decoder.ts +575 -0
- package/src/ts/qr/detector.ts +630 -0
- package/src/ts/qr/encoder.ts +958 -0
- package/src/ts/qr/index.ts +44 -0
- package/src/ts/qr/tables.ts +737 -0
- package/src/ts/render/image-data.ts +125 -0
- package/src/ts/render/index.ts +130 -0
- package/src/ts/render/options.ts +160 -0
- package/src/ts/render/png.ts +295 -0
- package/src/ts/render/svg.ts +120 -0
- package/src/ts/render/webgl.ts +206 -0
- package/src/ts/render/webgpu.ts +369 -0
- package/src/ts/rmqr/decoder.ts +101 -0
- package/src/ts/rmqr/detector.ts +90 -0
- package/src/ts/rmqr/encoder.ts +172 -0
- package/src/ts/rmqr/index.ts +37 -0
- package/src/ts/rmqr/tables.ts +154 -0
package/src/index.js
CHANGED
|
@@ -28,7 +28,6 @@
|
|
|
28
28
|
*
|
|
29
29
|
* Original work. No code from any other barcode implementation.
|
|
30
30
|
*/
|
|
31
|
-
|
|
32
31
|
/**
|
|
33
32
|
* Sythos Barcode Suite — public API.
|
|
34
33
|
*
|
|
@@ -45,7 +44,6 @@
|
|
|
45
44
|
*
|
|
46
45
|
* @module @sythos/js_barcode_universal
|
|
47
46
|
*/
|
|
48
|
-
|
|
49
47
|
import { BitMatrix } from './js/core/bit-matrix.js';
|
|
50
48
|
import { EncodeError, NotFoundError } from './js/core/errors.js';
|
|
51
49
|
import { LuminanceSource } from './js/image/luminance.js';
|
|
@@ -63,11 +61,8 @@ import * as frameqr from './js/frameqr/index.js';
|
|
|
63
61
|
import * as aztecRune from './js/aztecrune/index.js';
|
|
64
62
|
import * as compactPdf417 from './js/compactpdf417/index.js';
|
|
65
63
|
import * as databar from './js/databar/index.js';
|
|
66
|
-
|
|
67
64
|
export { BitMatrix };
|
|
68
|
-
export {
|
|
69
|
-
BarcodeError, EncodeError, NotFoundError, FormatError, ChecksumError,
|
|
70
|
-
} from './js/core/errors.js';
|
|
65
|
+
export { BarcodeError, EncodeError, NotFoundError, FormatError, ChecksumError, } from './js/core/errors.js';
|
|
71
66
|
export { LuminanceSource } from './js/image/luminance.js';
|
|
72
67
|
export { binarize, binarizeGlobal, binarizeHybrid } from './js/image/binarizer.js';
|
|
73
68
|
export * from './js/oned/index.js';
|
|
@@ -77,25 +72,18 @@ export { toPNG, toPNGDataURI } from './js/render/png.js';
|
|
|
77
72
|
export { renderToCanvasAuto, isWebGL2Available } from './js/render/index.js';
|
|
78
73
|
export { renderToCanvasAutoAsync, isWebGPUAvailable } from './js/render/index.js';
|
|
79
74
|
export { encodeQR, decodeQR, detectQR, detectAndDecodeQR } from './js/qr/index.js';
|
|
80
|
-
export {
|
|
81
|
-
encodeDataMatrix, decodeDataMatrix, detectDataMatrix, detectAndDecodeDataMatrix,
|
|
82
|
-
} from './js/datamatrix/index.js';
|
|
75
|
+
export { encodeDataMatrix, decodeDataMatrix, detectDataMatrix, detectAndDecodeDataMatrix, } from './js/datamatrix/index.js';
|
|
83
76
|
export { encodeAztec, decodeAztec, detectAztec, detectAndDecodeAztec } from './js/aztec/index.js';
|
|
84
77
|
export * from './js/aztecrune/index.js';
|
|
85
78
|
export { encodePDF417, decodePDF417, detectPDF417, detectAndDecodePDF417 } from './js/pdf417/index.js';
|
|
86
79
|
export * from './js/compactpdf417/index.js';
|
|
87
|
-
|
|
88
|
-
|
|
80
|
+
// DataBar exports include both the verified GTIN/data layer and the
|
|
81
|
+
// Omnidirectional/Truncated physical image path.
|
|
89
82
|
export * from './js/databar/index.js';
|
|
90
|
-
export {
|
|
91
|
-
encodeMicroPDF417, decodeMicroPDF417, detectMicroPDF417, detectAndDecodeMicroPDF417,
|
|
92
|
-
} from './js/micropdf417/index.js';
|
|
83
|
+
export { encodeMicroPDF417, decodeMicroPDF417, detectMicroPDF417, detectAndDecodeMicroPDF417, } from './js/micropdf417/index.js';
|
|
93
84
|
export { encodeMicroQR, decodeMicroQR, detectMicroQR, detectAndDecodeMicroQR } from './js/microqr/index.js';
|
|
94
85
|
export { encodeRMQR, decodeRMQR, detectRMQR, detectAndDecodeRMQR } from './js/rmqr/index.js';
|
|
95
|
-
export {
|
|
96
|
-
encodeFrameQR, decodeFrameQR, detectFrameQR, detectAndDecodeFrameQR,
|
|
97
|
-
} from './js/frameqr/index.js';
|
|
98
|
-
|
|
86
|
+
export { encodeFrameQR, decodeFrameQR, detectFrameQR, detectAndDecodeFrameQR, } from './js/frameqr/index.js';
|
|
99
87
|
/**
|
|
100
88
|
* @typedef {object} FormatInfo
|
|
101
89
|
* @property {string} id
|
|
@@ -105,7 +93,6 @@ export {
|
|
|
105
93
|
* @property {'1D' | '2D'} kind
|
|
106
94
|
* @property {'supplement'} [role]
|
|
107
95
|
*/
|
|
108
|
-
|
|
109
96
|
// Writing and reading a format are separate capabilities that can land at
|
|
110
97
|
// different times, so they are reported separately rather than collapsed into
|
|
111
98
|
// one "supported" flag that would be wrong in one direction or the other.
|
|
@@ -116,9 +103,9 @@ export {
|
|
|
116
103
|
// value.
|
|
117
104
|
const qrPresent = qr.QR_PLACEHOLDER !== true;
|
|
118
105
|
const qrCanEncode = qrPresent &&
|
|
119
|
-
|
|
106
|
+
typeof qr.encodeQR === 'function' && qr.QR_CAN_ENCODE !== false;
|
|
120
107
|
const qrCanDecode = qrPresent &&
|
|
121
|
-
|
|
108
|
+
typeof qr.detectAndDecodeQR === 'function' && qr.QR_CAN_DECODE !== false;
|
|
122
109
|
const dataMatrixCanEncode = typeof datamatrix.encodeDataMatrix === 'function';
|
|
123
110
|
const dataMatrixCanDecode = typeof datamatrix.detectAndDecodeDataMatrix === 'function';
|
|
124
111
|
const aztecCanEncode = typeof aztec.encodeAztec === 'function';
|
|
@@ -143,7 +130,6 @@ const compactPdf417CanEncode = typeof compactPdf417.encodeCompactPDF417 === 'fun
|
|
|
143
130
|
const compactPdf417CanDecode = typeof compactPdf417.detectAndDecodeCompactPDF417 === 'function';
|
|
144
131
|
const dataBarCanEncode = typeof databar.encodeDataBar14 === 'function';
|
|
145
132
|
const dataBarCanDecode = typeof databar.decodeDataBar14Scanline === 'function';
|
|
146
|
-
|
|
147
133
|
/**
|
|
148
134
|
* Every format this build supports.
|
|
149
135
|
*
|
|
@@ -155,96 +141,93 @@ const dataBarCanDecode = typeof databar.decodeDataBar14Scanline === 'function';
|
|
|
155
141
|
* @returns {FormatInfo[]}
|
|
156
142
|
*/
|
|
157
143
|
export function listFormats() {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
return formats;
|
|
144
|
+
const formats = Object.entries(ONED_FORMATS).map(([id, info]) => ({
|
|
145
|
+
id,
|
|
146
|
+
label: info.label,
|
|
147
|
+
canWrite: true,
|
|
148
|
+
canRead: info.readable,
|
|
149
|
+
kind: /** @type {'1D'} */ ('1D'),
|
|
150
|
+
...(info.role ? { role: info.role } : {}),
|
|
151
|
+
}));
|
|
152
|
+
formats.push({
|
|
153
|
+
id: 'qr',
|
|
154
|
+
label: 'QR Code',
|
|
155
|
+
canWrite: qrCanEncode,
|
|
156
|
+
canRead: qrCanDecode,
|
|
157
|
+
kind: /** @type {'2D'} */ ('2D'),
|
|
158
|
+
});
|
|
159
|
+
formats.push({
|
|
160
|
+
id: 'datamatrix',
|
|
161
|
+
label: 'Data Matrix ECC 200',
|
|
162
|
+
canWrite: dataMatrixCanEncode,
|
|
163
|
+
canRead: dataMatrixCanDecode,
|
|
164
|
+
kind: /** @type {'2D'} */ ('2D'),
|
|
165
|
+
});
|
|
166
|
+
formats.push({
|
|
167
|
+
id: 'aztec',
|
|
168
|
+
label: 'Aztec Code',
|
|
169
|
+
canWrite: aztecCanEncode,
|
|
170
|
+
canRead: aztecCanDecode,
|
|
171
|
+
kind: /** @type {'2D'} */ ('2D'),
|
|
172
|
+
});
|
|
173
|
+
formats.push({
|
|
174
|
+
id: 'aztecrune',
|
|
175
|
+
label: 'Aztec Rune',
|
|
176
|
+
canWrite: aztecRuneCanEncode,
|
|
177
|
+
canRead: aztecRuneCanDecode,
|
|
178
|
+
kind: /** @type {'2D'} */ ('2D'),
|
|
179
|
+
});
|
|
180
|
+
formats.push({
|
|
181
|
+
id: 'pdf417',
|
|
182
|
+
label: 'PDF417',
|
|
183
|
+
canWrite: pdf417CanEncode,
|
|
184
|
+
canRead: pdf417CanDecode,
|
|
185
|
+
kind: /** @type {'2D'} */ ('2D'),
|
|
186
|
+
});
|
|
187
|
+
formats.push({
|
|
188
|
+
id: 'compactpdf417',
|
|
189
|
+
label: 'Compact PDF417',
|
|
190
|
+
canWrite: compactPdf417CanEncode,
|
|
191
|
+
canRead: compactPdf417CanDecode,
|
|
192
|
+
kind: /** @type {'2D'} */ ('2D'),
|
|
193
|
+
});
|
|
194
|
+
formats.push({
|
|
195
|
+
id: 'micropdf417',
|
|
196
|
+
label: 'MicroPDF417',
|
|
197
|
+
canWrite: microPdf417CanEncode,
|
|
198
|
+
canRead: microPdf417CanDecode,
|
|
199
|
+
kind: /** @type {'2D'} */ ('2D'),
|
|
200
|
+
});
|
|
201
|
+
formats.push({
|
|
202
|
+
id: 'microqr',
|
|
203
|
+
label: 'Micro QR Code',
|
|
204
|
+
canWrite: microQrCanEncode,
|
|
205
|
+
canRead: microQrCanDecode,
|
|
206
|
+
kind: /** @type {'2D'} */ ('2D'),
|
|
207
|
+
});
|
|
208
|
+
formats.push({
|
|
209
|
+
id: 'rmqr',
|
|
210
|
+
label: 'rMQR Code',
|
|
211
|
+
canWrite: rmqrCanEncode,
|
|
212
|
+
canRead: rmqrCanDecode,
|
|
213
|
+
kind: /** @type {'2D'} */ ('2D'),
|
|
214
|
+
});
|
|
215
|
+
formats.push({
|
|
216
|
+
id: 'frameqr',
|
|
217
|
+
label: 'Sythos Canvas QR profile',
|
|
218
|
+
canWrite: frameQrCanEncode,
|
|
219
|
+
canRead: frameQrCanDecode,
|
|
220
|
+
kind: /** @type {'2D'} */ ('2D'),
|
|
221
|
+
});
|
|
222
|
+
formats.push({
|
|
223
|
+
id: 'gs1databar14',
|
|
224
|
+
label: 'GS1 DataBar Omnidirectional / Truncated',
|
|
225
|
+
canWrite: dataBarCanEncode,
|
|
226
|
+
canRead: dataBarCanDecode,
|
|
227
|
+
kind: /** @type {'1D'} */ ('1D'),
|
|
228
|
+
});
|
|
229
|
+
return formats;
|
|
246
230
|
}
|
|
247
|
-
|
|
248
231
|
/**
|
|
249
232
|
* Encode a payload into a barcode matrix.
|
|
250
233
|
*
|
|
@@ -282,51 +265,48 @@ export function listFormats() {
|
|
|
282
265
|
* @returns {BitMatrix}
|
|
283
266
|
*/
|
|
284
267
|
export function encode(text, options = {}) {
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
}
|
|
327
|
-
return entry.encode(value, options);
|
|
268
|
+
const format = String(options.format ?? 'qr').toLowerCase();
|
|
269
|
+
const value = typeof text === 'number' ? String(text) : text;
|
|
270
|
+
if (format === 'qr' || format === 'qrcode') {
|
|
271
|
+
return qr.encodeQR(value, options);
|
|
272
|
+
}
|
|
273
|
+
if (format === 'datamatrix' || format === 'data-matrix') {
|
|
274
|
+
return datamatrix.encodeDataMatrix(value, options);
|
|
275
|
+
}
|
|
276
|
+
if (format === 'aztec' || format === 'aztec-code') {
|
|
277
|
+
return aztec.encodeAztec(value, options);
|
|
278
|
+
}
|
|
279
|
+
if (format === 'aztecrune' || format === 'aztec-rune' || format === 'rune') {
|
|
280
|
+
return aztecRune.encodeAztecRune(value, options);
|
|
281
|
+
}
|
|
282
|
+
if (format === 'pdf417' || format === 'pdf-417') {
|
|
283
|
+
return pdf417.encodePDF417(value, options);
|
|
284
|
+
}
|
|
285
|
+
if (format === 'compactpdf417' || format === 'compact-pdf417' || format === 'compact-pdf-417') {
|
|
286
|
+
return compactPdf417.encodeCompactPDF417(value, options);
|
|
287
|
+
}
|
|
288
|
+
if (format === 'micropdf417' || format === 'micro-pdf417' || format === 'micro-pdf-417') {
|
|
289
|
+
return micropdf417.encodeMicroPDF417(value, options);
|
|
290
|
+
}
|
|
291
|
+
if (format === 'microqr' || format === 'micro-qr') {
|
|
292
|
+
return microqr.encodeMicroQR(value, options);
|
|
293
|
+
}
|
|
294
|
+
if (format === 'rmqr' || format === 'r-mqr' || format === 'rectangular-micro-qr') {
|
|
295
|
+
return rmqr.encodeRMQR(value, options);
|
|
296
|
+
}
|
|
297
|
+
if (format === 'frameqr' || format === 'frame-qr' || format === 'canvas-qr') {
|
|
298
|
+
return frameqr.encodeFrameQR(value, options);
|
|
299
|
+
}
|
|
300
|
+
if (format === 'gs1databar14' || format === 'gs1-databar14' || format === 'databar') {
|
|
301
|
+
return databar.encodeDataBar14(value, options);
|
|
302
|
+
}
|
|
303
|
+
const entry = ONED_FORMATS[format];
|
|
304
|
+
if (!entry) {
|
|
305
|
+
const known = [...Object.keys(ONED_FORMATS), 'qr', 'datamatrix', 'aztec', 'aztecrune', 'pdf417', 'compactpdf417', 'micropdf417', 'microqr', 'rmqr', 'frameqr', 'gs1databar14'].join(', ');
|
|
306
|
+
throw new EncodeError(`Unknown format "${format}". Known formats: ${known}`);
|
|
307
|
+
}
|
|
308
|
+
return entry.encode(value, options);
|
|
328
309
|
}
|
|
329
|
-
|
|
330
310
|
/**
|
|
331
311
|
* @typedef {object} DecodeResult
|
|
332
312
|
* @property {string} text
|
|
@@ -359,7 +339,6 @@ export function encode(text, options = {}) {
|
|
|
359
339
|
* @property {string} [gtin] GS1 DataBar GTIN-14 payload.
|
|
360
340
|
* @property {boolean} [linkage] GS1 DataBar linkage flag.
|
|
361
341
|
*/
|
|
362
|
-
|
|
363
342
|
/**
|
|
364
343
|
* Find and decode every barcode in an image.
|
|
365
344
|
*
|
|
@@ -378,302 +357,314 @@ export function encode(text, options = {}) {
|
|
|
378
357
|
* @returns {DecodeResult[]}
|
|
379
358
|
*/
|
|
380
359
|
export function decode(image, options = {}) {
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
360
|
+
const { formats = null, tryHarder = true, binarizer = 'auto', profile = null } = options;
|
|
361
|
+
const want = formats ? new Set(formats.map((f) => f.toLowerCase())) : null;
|
|
362
|
+
const wantQR = !want || want.has('qr') || want.has('qrcode');
|
|
363
|
+
const wantDataMatrix = !want || want.has('datamatrix') || want.has('data-matrix');
|
|
364
|
+
const wantAztec = !want || want.has('aztec') || want.has('aztec-code');
|
|
365
|
+
const wantAztecRune = !want || want.has('aztecrune') || want.has('aztec-rune') || want.has('rune');
|
|
366
|
+
const wantPDF417 = !want || want.has('pdf417') || want.has('pdf-417');
|
|
367
|
+
const wantCompactPDF417 = !want || want.has('compactpdf417') || want.has('compact-pdf417') || want.has('compact-pdf-417');
|
|
368
|
+
const wantMicroPDF417 = !want || want.has('micropdf417') || want.has('micro-pdf417') || want.has('micro-pdf-417');
|
|
369
|
+
const wantMicroQR = !want || want.has('microqr') || want.has('micro-qr');
|
|
370
|
+
const wantRMQR = !want || want.has('rmqr') || want.has('r-mqr') || want.has('rectangular-micro-qr');
|
|
371
|
+
const wantFrameQR = !want || want.has('frameqr') || want.has('frame-qr') || want.has('canvas-qr');
|
|
372
|
+
const oneDAliases = new Set(['gs1databar14', 'databar', 'gs1-databar14']);
|
|
373
|
+
const wantOneD = !want || [...want].some((f) => f in ONED_FORMATS || oneDAliases.has(f));
|
|
374
|
+
const wantTwoD = wantQR || wantDataMatrix || wantAztec || wantAztecRune || wantPDF417
|
|
375
|
+
|| wantCompactPDF417 || wantMicroPDF417 || wantMicroQR || wantRMQR || wantFrameQR;
|
|
376
|
+
const source = LuminanceSource.fromImageData(image);
|
|
377
|
+
const results = [];
|
|
378
|
+
// Light-on-dark symbols are common on screens and packaging, so a second
|
|
379
|
+
// inverted pass is worth the cost when the first finds nothing.
|
|
380
|
+
const passes = tryHarder ? [source, source.invert()] : [source];
|
|
381
|
+
for (const pass of passes) {
|
|
382
|
+
const bits = binarize(pass, binarizer);
|
|
383
|
+
// Keep all two-dimensional detector calls in one helper so camera-only
|
|
384
|
+
// orientation retries use exactly the same validation path as the native
|
|
385
|
+
// orientation. Arbitrary-angle retries remain opt-in to the camera
|
|
386
|
+
// profile because resampling is intentionally not part of ordinary decode.
|
|
387
|
+
const readTwoD = (candidateBits, cameraRotation = 0, candidateSource = pass) => {
|
|
388
|
+
const before = results.length;
|
|
389
|
+
const add = (found, format) => {
|
|
390
|
+
if (!found)
|
|
391
|
+
return;
|
|
392
|
+
const publicFound = { ...found, format };
|
|
393
|
+
if (profile === 'camera' && cameraRotation !== 0)
|
|
394
|
+
publicFound.rotation = cameraRotation;
|
|
395
|
+
results.push(publicFound);
|
|
396
|
+
};
|
|
397
|
+
if (wantQR && qrCanDecode) {
|
|
398
|
+
try {
|
|
399
|
+
for (const found of qr.detectAndDecodeQR(candidateBits))
|
|
400
|
+
add(found, 'qr');
|
|
401
|
+
}
|
|
402
|
+
catch {
|
|
403
|
+
/* no QR in this pass */
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
if (wantDataMatrix && dataMatrixCanDecode) {
|
|
407
|
+
// Hybrid thresholding can erase the interior of very large, perfectly
|
|
408
|
+
// uniform modules. In auto mode keep the local-threshold attempt, then
|
|
409
|
+
// retry Data Matrix once with the global threshold before giving up.
|
|
410
|
+
const dataMatrixBits = binarizer === 'auto'
|
|
411
|
+
? [candidateBits, binarize(candidateSource, 'global')]
|
|
412
|
+
: [candidateBits];
|
|
413
|
+
for (const thresholdBits of dataMatrixBits) {
|
|
414
|
+
try {
|
|
415
|
+
const found = datamatrix.detectAndDecodeDataMatrix(thresholdBits);
|
|
416
|
+
if (found) {
|
|
417
|
+
add(found, 'datamatrix');
|
|
418
|
+
break;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
catch {
|
|
422
|
+
/* no Data Matrix with this threshold */
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
if (wantAztec && aztecCanDecode) {
|
|
427
|
+
// The central bull's-eye is a small, high-contrast target. Hybrid
|
|
428
|
+
// thresholding can flatten it on clean rendered symbols, so mirror the
|
|
429
|
+
// Data Matrix global fallback in auto mode.
|
|
430
|
+
const aztecBits = binarizer === 'auto'
|
|
431
|
+
? [candidateBits, binarize(candidateSource, 'global')]
|
|
432
|
+
: [candidateBits];
|
|
433
|
+
for (const thresholdBits of aztecBits) {
|
|
434
|
+
try {
|
|
435
|
+
const found = aztec.detectAndDecodeAztec(thresholdBits);
|
|
436
|
+
if (found) {
|
|
437
|
+
add(found, 'aztec');
|
|
438
|
+
break;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
catch {
|
|
442
|
+
/* no Aztec code with this threshold */
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
if (wantAztecRune && aztecRuneCanDecode) {
|
|
447
|
+
try {
|
|
448
|
+
const found = aztecRune.detectAndDecodeAztecRune(candidateBits);
|
|
449
|
+
if (found)
|
|
450
|
+
add(found, 'aztecrune');
|
|
451
|
+
}
|
|
452
|
+
catch {
|
|
453
|
+
/* no Aztec Rune in this pass */
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
if (wantPDF417 && pdf417CanDecode) {
|
|
457
|
+
try {
|
|
458
|
+
const found = pdf417.detectAndDecodePDF417(candidateBits);
|
|
459
|
+
if (found)
|
|
460
|
+
add(found, 'pdf417');
|
|
461
|
+
}
|
|
462
|
+
catch {
|
|
463
|
+
/* no PDF417 in this pass */
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
if (wantCompactPDF417 && compactPdf417CanDecode) {
|
|
467
|
+
try {
|
|
468
|
+
const found = compactPdf417.detectAndDecodeCompactPDF417(candidateBits);
|
|
469
|
+
if (found)
|
|
470
|
+
add(found, 'compactpdf417');
|
|
471
|
+
}
|
|
472
|
+
catch {
|
|
473
|
+
/* no Compact PDF417 in this pass */
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
if (wantMicroPDF417 && microPdf417CanDecode) {
|
|
477
|
+
// MicroPDF417 detection measures runs across the whole raster. Hybrid
|
|
478
|
+
// thresholding can alter uniform modules near local-window boundaries,
|
|
479
|
+
// so retry the global threshold in auto mode as for the other 2D codes.
|
|
480
|
+
const microPdf417Bits = binarizer === 'auto'
|
|
481
|
+
? [candidateBits, binarize(candidateSource, 'global')]
|
|
482
|
+
: [candidateBits];
|
|
483
|
+
for (const thresholdBits of microPdf417Bits) {
|
|
484
|
+
try {
|
|
485
|
+
const found = micropdf417.detectAndDecodeMicroPDF417(thresholdBits);
|
|
486
|
+
if (found) {
|
|
487
|
+
add(found, 'micropdf417');
|
|
488
|
+
break;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
catch {
|
|
492
|
+
/* no MicroPDF417 with this threshold */
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
if (wantMicroQR && microQrCanDecode) {
|
|
497
|
+
try {
|
|
498
|
+
for (const found of microqr.detectAndDecodeMicroQR(candidateBits))
|
|
499
|
+
add(found, 'microqr');
|
|
500
|
+
}
|
|
501
|
+
catch {
|
|
502
|
+
/* no Micro QR in this pass */
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
if (wantRMQR && rmqrCanDecode) {
|
|
506
|
+
try {
|
|
507
|
+
const found = rmqr.detectAndDecodeRMQR(candidateBits);
|
|
508
|
+
if (found)
|
|
509
|
+
add(found, 'rmqr');
|
|
510
|
+
}
|
|
511
|
+
catch {
|
|
512
|
+
/* no rMQR in this pass */
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
if (wantFrameQR && frameQrCanDecode) {
|
|
516
|
+
try {
|
|
517
|
+
for (const found of frameqr.detectAndDecodeFrameQR(candidateBits, options.frameqr ?? {}))
|
|
518
|
+
add(found, 'frameqr');
|
|
519
|
+
}
|
|
520
|
+
catch {
|
|
521
|
+
/* no Sythos Canvas QR profile in this pass */
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
return results.length > before;
|
|
525
|
+
};
|
|
526
|
+
const twoDFound = readTwoD(bits);
|
|
527
|
+
if (profile === 'camera' && wantTwoD && !twoDFound) {
|
|
528
|
+
// Normalize a camera frame through the full eight-angle set only after
|
|
529
|
+
// the native orientation has failed. This preserves the fast path while
|
|
530
|
+
// limiting resampling to frames that need it.
|
|
531
|
+
const cameraRotations = [45, 90, 135, 180, 225, 270, 315];
|
|
532
|
+
for (const rotation of cameraRotations) {
|
|
533
|
+
const inverse = (360 - rotation) % 360;
|
|
534
|
+
const orientedSource = inverse % 90 === 0
|
|
535
|
+
? inverse === 0
|
|
536
|
+
? pass
|
|
537
|
+
: inverse === 90
|
|
538
|
+
? pass.rotate90()
|
|
539
|
+
: inverse === 180
|
|
540
|
+
? rotateLuminanceSourceByDegrees(pass, 180)
|
|
541
|
+
: pass.rotate90().rotate90().rotate90()
|
|
542
|
+
: rotateLuminanceSourceByDegrees(pass, inverse);
|
|
543
|
+
if (readTwoD(binarize(orientedSource, binarizer), rotation, orientedSource))
|
|
544
|
+
break;
|
|
545
|
+
}
|
|
469
546
|
}
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
547
|
+
if (wantOneD) {
|
|
548
|
+
const oneDFormats = want
|
|
549
|
+
? [...want].filter((f) => f in ONED_FORMATS || oneDAliases.has(f))
|
|
550
|
+
: null;
|
|
551
|
+
const oneDPasses = [{ bits, rotation: 0 }];
|
|
552
|
+
// A linear symbol rotated away from the horizontal has no usable
|
|
553
|
+
// horizontal scanline. The strict camera profile adds normalized
|
|
554
|
+
// orientations only when the native orientation found no validated 1D
|
|
555
|
+
// result. Keep the two quarter-turns first: they are the established
|
|
556
|
+
// path and avoid paying for diagonal resampling on the common case.
|
|
557
|
+
const readOneD = (candidateBits, rotation) => decodeOneD(candidateBits, {
|
|
558
|
+
...options, formats: oneDFormats, tryHarder, profile, cameraRotation: rotation,
|
|
559
|
+
});
|
|
560
|
+
let oneDResults = readOneD(bits, 0);
|
|
561
|
+
if (profile === 'camera' && oneDResults.length === 0) {
|
|
562
|
+
// `rotation` describes the supplied raster, clockwise from the
|
|
563
|
+
// canonical horizontal orientation. To normalize it, apply the
|
|
564
|
+
// inverse rotation. Include 180 explicitly so the full eight-angle
|
|
565
|
+
// set still works when tryHarder is disabled (the normal reversed-row
|
|
566
|
+
// pass also handles it without a second raster transform).
|
|
567
|
+
const cameraRotations = [90, 270, 180, 45, 135, 225, 315];
|
|
568
|
+
for (const rotation of cameraRotations) {
|
|
569
|
+
const inverse = (360 - rotation) % 360;
|
|
570
|
+
const oriented = inverse % 90 === 0
|
|
571
|
+
? inverse === 0
|
|
572
|
+
? bits.clone()
|
|
573
|
+
: inverse === 180
|
|
574
|
+
? (() => { const copy = bits.clone(); copy.rotate180(); return copy; })()
|
|
575
|
+
: rotateBitMatrix90(bits, inverse === 90)
|
|
576
|
+
: rotateBitMatrixByDegrees(bits, inverse);
|
|
577
|
+
oneDPasses.push({ bits: oriented, rotation });
|
|
578
|
+
}
|
|
579
|
+
for (let i = 1; i < oneDPasses.length && oneDResults.length === 0; i++) {
|
|
580
|
+
oneDResults = readOneD(oneDPasses[i].bits, oneDPasses[i].rotation);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
for (const found of oneDResults) {
|
|
584
|
+
const { row, ...publicFound } = found;
|
|
585
|
+
void row;
|
|
586
|
+
if (publicFound.gs1) {
|
|
587
|
+
const semanticText = publicFound.format === 'gs1databar14'
|
|
588
|
+
? `01${publicFound.gtin ?? publicFound.text}`
|
|
589
|
+
: publicFound.text;
|
|
590
|
+
try {
|
|
591
|
+
publicFound.elements = databar.decodeGS1ElementString(semanticText);
|
|
592
|
+
}
|
|
593
|
+
catch (error) {
|
|
594
|
+
publicFound.gs1ParseError = error instanceof Error ? error.message : String(error);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
results.push(publicFound);
|
|
598
|
+
}
|
|
478
599
|
}
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
if (wantCompactPDF417 && compactPdf417CanDecode) {
|
|
482
|
-
try {
|
|
483
|
-
const found = compactPdf417.detectAndDecodeCompactPDF417(candidateBits);
|
|
484
|
-
if (found) add(found, 'compactpdf417');
|
|
485
|
-
} catch {
|
|
486
|
-
/* no Compact PDF417 in this pass */
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
if (wantMicroPDF417 && microPdf417CanDecode) {
|
|
491
|
-
// MicroPDF417 detection measures runs across the whole raster. Hybrid
|
|
492
|
-
// thresholding can alter uniform modules near local-window boundaries,
|
|
493
|
-
// so retry the global threshold in auto mode as for the other 2D codes.
|
|
494
|
-
const microPdf417Bits = binarizer === 'auto'
|
|
495
|
-
? [candidateBits, binarize(candidateSource, 'global')]
|
|
496
|
-
: [candidateBits];
|
|
497
|
-
for (const thresholdBits of microPdf417Bits) {
|
|
498
|
-
try {
|
|
499
|
-
const found = micropdf417.detectAndDecodeMicroPDF417(thresholdBits);
|
|
500
|
-
if (found) { add(found, 'micropdf417'); break; }
|
|
501
|
-
} catch {
|
|
502
|
-
/* no MicroPDF417 with this threshold */
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
if (wantMicroQR && microQrCanDecode) {
|
|
508
|
-
try {
|
|
509
|
-
for (const found of microqr.detectAndDecodeMicroQR(candidateBits)) add(found, 'microqr');
|
|
510
|
-
} catch {
|
|
511
|
-
/* no Micro QR in this pass */
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
if (wantRMQR && rmqrCanDecode) {
|
|
516
|
-
try {
|
|
517
|
-
const found = rmqr.detectAndDecodeRMQR(candidateBits);
|
|
518
|
-
if (found) add(found, 'rmqr');
|
|
519
|
-
} catch {
|
|
520
|
-
/* no rMQR in this pass */
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
if (wantFrameQR && frameQrCanDecode) {
|
|
525
|
-
try {
|
|
526
|
-
for (const found of frameqr.detectAndDecodeFrameQR(candidateBits, options.frameqr ?? {})) add(found, 'frameqr');
|
|
527
|
-
} catch {
|
|
528
|
-
/* no Sythos Canvas QR profile in this pass */
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
return results.length > before;
|
|
533
|
-
};
|
|
534
|
-
|
|
535
|
-
const twoDFound = readTwoD(bits);
|
|
536
|
-
if (profile === 'camera' && wantTwoD && !twoDFound) {
|
|
537
|
-
// Normalize a camera frame through the full eight-angle set only after
|
|
538
|
-
// the native orientation has failed. This preserves the fast path while
|
|
539
|
-
// limiting resampling to frames that need it.
|
|
540
|
-
const cameraRotations = [45, 90, 135, 180, 225, 270, 315];
|
|
541
|
-
for (const rotation of cameraRotations) {
|
|
542
|
-
const inverse = (360 - rotation) % 360;
|
|
543
|
-
const orientedSource = inverse % 90 === 0
|
|
544
|
-
? inverse === 0
|
|
545
|
-
? pass
|
|
546
|
-
: inverse === 90
|
|
547
|
-
? pass.rotate90()
|
|
548
|
-
: inverse === 180
|
|
549
|
-
? rotateLuminanceSourceByDegrees(pass, 180)
|
|
550
|
-
: pass.rotate90().rotate90().rotate90()
|
|
551
|
-
: rotateLuminanceSourceByDegrees(pass, inverse);
|
|
552
|
-
if (readTwoD(binarize(orientedSource, binarizer), rotation, orientedSource)) break;
|
|
553
|
-
}
|
|
600
|
+
if (results.length > 0)
|
|
601
|
+
break;
|
|
554
602
|
}
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
? (() => { const copy = bits.clone(); copy.rotate180(); return copy; })()
|
|
584
|
-
: rotateBitMatrix90(bits, inverse === 90)
|
|
585
|
-
: rotateBitMatrixByDegrees(bits, inverse);
|
|
586
|
-
oneDPasses.push({ bits: oriented, rotation });
|
|
587
|
-
}
|
|
588
|
-
for (let i = 1; i < oneDPasses.length && oneDResults.length === 0; i++) {
|
|
589
|
-
oneDResults = readOneD(oneDPasses[i].bits, oneDPasses[i].rotation);
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
for (const found of oneDResults) {
|
|
593
|
-
const { row, ...publicFound } = found;
|
|
594
|
-
void row;
|
|
595
|
-
if (publicFound.gs1) {
|
|
596
|
-
const semanticText = publicFound.format === 'gs1databar14'
|
|
597
|
-
? `01${publicFound.gtin ?? publicFound.text}`
|
|
598
|
-
: publicFound.text;
|
|
599
|
-
try {
|
|
600
|
-
publicFound.elements = databar.decodeGS1ElementString(semanticText);
|
|
601
|
-
} catch (error) {
|
|
602
|
-
publicFound.gs1ParseError = error instanceof Error ? error.message : String(error);
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
results.push(publicFound);
|
|
606
|
-
}
|
|
603
|
+
// De-duplicate: the same symbol is often read on several scan rows.
|
|
604
|
+
const seen = new Set();
|
|
605
|
+
const unique = results.filter((r) => {
|
|
606
|
+
const key = `${r.format}:${r.text}`;
|
|
607
|
+
if (seen.has(key))
|
|
608
|
+
return false;
|
|
609
|
+
seen.add(key);
|
|
610
|
+
return true;
|
|
611
|
+
});
|
|
612
|
+
// On large clean rasters, hybrid thresholding can erase otherwise uniform
|
|
613
|
+
// QR/PDF417 modules. Keep auto/hybrid as the primary strategy, then make one
|
|
614
|
+
// focused global retry only when the complete primary pass found nothing.
|
|
615
|
+
// This deliberately leaves an explicit global request single-pass.
|
|
616
|
+
const retryFormats = formats
|
|
617
|
+
? formats.filter((format) => {
|
|
618
|
+
const id = String(format).toLowerCase();
|
|
619
|
+
return id === 'qr' || id === 'qrcode'
|
|
620
|
+
|| id === 'pdf417' || id === 'pdf-417'
|
|
621
|
+
|| id === 'compactpdf417' || id === 'compact-pdf417' || id === 'compact-pdf-417';
|
|
622
|
+
})
|
|
623
|
+
: ['qr', 'pdf417', 'compactpdf417'];
|
|
624
|
+
const shouldRetryGlobal = unique.length === 0
|
|
625
|
+
&& (binarizer === 'auto' || binarizer === 'hybrid')
|
|
626
|
+
&& retryFormats.length > 0;
|
|
627
|
+
if (!shouldRetryGlobal) {
|
|
628
|
+
return profile === 'camera'
|
|
629
|
+
? unique.sort((a, b) => (b.confidence ?? 0) - (a.confidence ?? 0))
|
|
630
|
+
: unique;
|
|
607
631
|
}
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
// On large clean rasters, hybrid thresholding can erase otherwise uniform
|
|
622
|
-
// QR/PDF417 modules. Keep auto/hybrid as the primary strategy, then make one
|
|
623
|
-
// focused global retry only when the complete primary pass found nothing.
|
|
624
|
-
// This deliberately leaves an explicit global request single-pass.
|
|
625
|
-
const retryFormats = formats
|
|
626
|
-
? formats.filter((format) => {
|
|
627
|
-
const id = String(format).toLowerCase();
|
|
628
|
-
return id === 'qr' || id === 'qrcode'
|
|
629
|
-
|| id === 'pdf417' || id === 'pdf-417'
|
|
630
|
-
|| id === 'compactpdf417' || id === 'compact-pdf417' || id === 'compact-pdf-417';
|
|
631
|
-
})
|
|
632
|
-
: ['qr', 'pdf417', 'compactpdf417'];
|
|
633
|
-
const shouldRetryGlobal = unique.length === 0
|
|
634
|
-
&& (binarizer === 'auto' || binarizer === 'hybrid')
|
|
635
|
-
&& retryFormats.length > 0;
|
|
636
|
-
|
|
637
|
-
if (!shouldRetryGlobal) {
|
|
632
|
+
const fallback = decode(image, {
|
|
633
|
+
...options,
|
|
634
|
+
formats: retryFormats,
|
|
635
|
+
binarizer: 'global',
|
|
636
|
+
});
|
|
637
|
+
const fallbackSeen = new Set();
|
|
638
|
+
const merged = [...unique, ...fallback].filter((r) => {
|
|
639
|
+
const key = `${r.format}:${r.text}`;
|
|
640
|
+
if (fallbackSeen.has(key))
|
|
641
|
+
return false;
|
|
642
|
+
fallbackSeen.add(key);
|
|
643
|
+
return true;
|
|
644
|
+
});
|
|
638
645
|
return profile === 'camera'
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
const fallback = decode(image, {
|
|
644
|
-
...options,
|
|
645
|
-
formats: retryFormats,
|
|
646
|
-
binarizer: 'global',
|
|
647
|
-
});
|
|
648
|
-
const fallbackSeen = new Set();
|
|
649
|
-
const merged = [...unique, ...fallback].filter((r) => {
|
|
650
|
-
const key = `${r.format}:${r.text}`;
|
|
651
|
-
if (fallbackSeen.has(key)) return false;
|
|
652
|
-
fallbackSeen.add(key);
|
|
653
|
-
return true;
|
|
654
|
-
});
|
|
655
|
-
return profile === 'camera'
|
|
656
|
-
? merged.sort((a, b) => (b.confidence ?? 0) - (a.confidence ?? 0))
|
|
657
|
-
: merged;
|
|
646
|
+
? merged.sort((a, b) => (b.confidence ?? 0) - (a.confidence ?? 0))
|
|
647
|
+
: merged;
|
|
658
648
|
}
|
|
659
|
-
|
|
660
649
|
/**
|
|
661
650
|
* @param {BitMatrix} matrix
|
|
662
651
|
* @param {boolean} clockwise
|
|
663
652
|
* @returns {BitMatrix}
|
|
664
653
|
*/
|
|
665
654
|
function rotateBitMatrix90(matrix, clockwise) {
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
655
|
+
const rotated = new BitMatrix(matrix.height, matrix.width);
|
|
656
|
+
for (let y = 0; y < matrix.height; y++) {
|
|
657
|
+
for (let x = 0; x < matrix.width; x++) {
|
|
658
|
+
if (!matrix.get(x, y))
|
|
659
|
+
continue;
|
|
660
|
+
if (clockwise)
|
|
661
|
+
rotated.set(matrix.height - 1 - y, x);
|
|
662
|
+
else
|
|
663
|
+
rotated.set(y, matrix.width - 1 - x);
|
|
664
|
+
}
|
|
672
665
|
}
|
|
673
|
-
|
|
674
|
-
return rotated;
|
|
666
|
+
return rotated;
|
|
675
667
|
}
|
|
676
|
-
|
|
677
668
|
/**
|
|
678
669
|
* Rotate a binarized raster by an arbitrary clockwise angle without changing
|
|
679
670
|
* the source matrix. This is intentionally used only by the strict camera
|
|
@@ -685,45 +676,45 @@ function rotateBitMatrix90(matrix, clockwise) {
|
|
|
685
676
|
* @returns {BitMatrix}
|
|
686
677
|
*/
|
|
687
678
|
function rotateBitMatrixByDegrees(matrix, degrees) {
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
const radians = angle * Math.PI / 180;
|
|
699
|
-
const sin = Math.sin(radians);
|
|
700
|
-
const cos = Math.cos(radians);
|
|
701
|
-
const width = Math.ceil(Math.abs(matrix.width * cos) + Math.abs(matrix.height * sin));
|
|
702
|
-
const height = Math.ceil(Math.abs(matrix.width * sin) + Math.abs(matrix.height * cos));
|
|
703
|
-
const rotated = new BitMatrix(width, height);
|
|
704
|
-
const sourceCenterX = (matrix.width - 1) / 2;
|
|
705
|
-
const sourceCenterY = (matrix.height - 1) / 2;
|
|
706
|
-
const destinationCenterX = (width - 1) / 2;
|
|
707
|
-
const destinationCenterY = (height - 1) / 2;
|
|
708
|
-
|
|
709
|
-
// Inverse-map destination pixels into the source. Sampling the already
|
|
710
|
-
// binarized raster keeps the operation deterministic and leaves the caller's
|
|
711
|
-
// original image and threshold result untouched.
|
|
712
|
-
for (let y = 0; y < height; y++) {
|
|
713
|
-
const dy = y - destinationCenterY;
|
|
714
|
-
for (let x = 0; x < width; x++) {
|
|
715
|
-
const dx = x - destinationCenterX;
|
|
716
|
-
const sourceX = Math.round(cos * dx + sin * dy + sourceCenterX);
|
|
717
|
-
const sourceY = Math.round(-sin * dx + cos * dy + sourceCenterY);
|
|
718
|
-
if (sourceX >= 0 && sourceX < matrix.width && sourceY >= 0 && sourceY < matrix.height &&
|
|
719
|
-
matrix.get(sourceX, sourceY)) {
|
|
720
|
-
rotated.set(x, y);
|
|
721
|
-
}
|
|
679
|
+
const angle = ((degrees % 360) + 360) % 360;
|
|
680
|
+
if (angle === 0)
|
|
681
|
+
return matrix.clone();
|
|
682
|
+
if (angle === 90)
|
|
683
|
+
return rotateBitMatrix90(matrix, true);
|
|
684
|
+
if (angle === 180) {
|
|
685
|
+
const rotated = matrix.clone();
|
|
686
|
+
rotated.rotate180();
|
|
687
|
+
return rotated;
|
|
722
688
|
}
|
|
723
|
-
|
|
724
|
-
|
|
689
|
+
if (angle === 270)
|
|
690
|
+
return rotateBitMatrix90(matrix, false);
|
|
691
|
+
const radians = angle * Math.PI / 180;
|
|
692
|
+
const sin = Math.sin(radians);
|
|
693
|
+
const cos = Math.cos(radians);
|
|
694
|
+
const width = Math.ceil(Math.abs(matrix.width * cos) + Math.abs(matrix.height * sin));
|
|
695
|
+
const height = Math.ceil(Math.abs(matrix.width * sin) + Math.abs(matrix.height * cos));
|
|
696
|
+
const rotated = new BitMatrix(width, height);
|
|
697
|
+
const sourceCenterX = (matrix.width - 1) / 2;
|
|
698
|
+
const sourceCenterY = (matrix.height - 1) / 2;
|
|
699
|
+
const destinationCenterX = (width - 1) / 2;
|
|
700
|
+
const destinationCenterY = (height - 1) / 2;
|
|
701
|
+
// Inverse-map destination pixels into the source. Sampling the already
|
|
702
|
+
// binarized raster keeps the operation deterministic and leaves the caller's
|
|
703
|
+
// original image and threshold result untouched.
|
|
704
|
+
for (let y = 0; y < height; y++) {
|
|
705
|
+
const dy = y - destinationCenterY;
|
|
706
|
+
for (let x = 0; x < width; x++) {
|
|
707
|
+
const dx = x - destinationCenterX;
|
|
708
|
+
const sourceX = Math.round(cos * dx + sin * dy + sourceCenterX);
|
|
709
|
+
const sourceY = Math.round(-sin * dx + cos * dy + sourceCenterY);
|
|
710
|
+
if (sourceX >= 0 && sourceX < matrix.width && sourceY >= 0 && sourceY < matrix.height &&
|
|
711
|
+
matrix.get(sourceX, sourceY)) {
|
|
712
|
+
rotated.set(x, y);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
return rotated;
|
|
725
717
|
}
|
|
726
|
-
|
|
727
718
|
/**
|
|
728
719
|
* Rotate the greyscale source before thresholding. Resampling luminance rather
|
|
729
720
|
* than an already-binarized matrix preserves module contrast at diagonal
|
|
@@ -735,44 +726,45 @@ function rotateBitMatrixByDegrees(matrix, degrees) {
|
|
|
735
726
|
* @returns {LuminanceSource}
|
|
736
727
|
*/
|
|
737
728
|
function rotateLuminanceSourceByDegrees(source, degrees) {
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
const
|
|
758
|
-
for (let
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
729
|
+
const angle = ((degrees % 360) + 360) % 360;
|
|
730
|
+
if (angle === 0)
|
|
731
|
+
return source;
|
|
732
|
+
if (angle === 90)
|
|
733
|
+
return source.rotate90();
|
|
734
|
+
if (angle === 180)
|
|
735
|
+
return source.rotate90().rotate90();
|
|
736
|
+
if (angle === 270)
|
|
737
|
+
return source.rotate90().rotate90().rotate90();
|
|
738
|
+
const radians = angle * Math.PI / 180;
|
|
739
|
+
const sin = Math.sin(radians);
|
|
740
|
+
const cos = Math.cos(radians);
|
|
741
|
+
const width = Math.ceil(Math.abs(source.width * cos) + Math.abs(source.height * sin));
|
|
742
|
+
const height = Math.ceil(Math.abs(source.width * sin) + Math.abs(source.height * cos));
|
|
743
|
+
const rotated = new Uint8Array(width * height);
|
|
744
|
+
rotated.fill(255);
|
|
745
|
+
const sourceCenterX = (source.width - 1) / 2;
|
|
746
|
+
const sourceCenterY = (source.height - 1) / 2;
|
|
747
|
+
const destinationCenterX = (width - 1) / 2;
|
|
748
|
+
const destinationCenterY = (height - 1) / 2;
|
|
749
|
+
for (let y = 0; y < height; y++) {
|
|
750
|
+
const dy = y - destinationCenterY;
|
|
751
|
+
for (let x = 0; x < width; x++) {
|
|
752
|
+
const dx = x - destinationCenterX;
|
|
753
|
+
const sourceX = cos * dx + sin * dy + sourceCenterX;
|
|
754
|
+
const sourceY = -sin * dx + cos * dy + sourceCenterY;
|
|
755
|
+
if (sourceX >= 0 && sourceX < source.width && sourceY >= 0 && sourceY < source.height) {
|
|
756
|
+
const left = Math.floor(sourceX), top = Math.floor(sourceY);
|
|
757
|
+
const right = Math.min(source.width - 1, left + 1);
|
|
758
|
+
const bottom = Math.min(source.height - 1, top + 1);
|
|
759
|
+
const fx = sourceX - left, fy = sourceY - top;
|
|
760
|
+
const topValue = source.get(left, top) * (1 - fx) + source.get(right, top) * fx;
|
|
761
|
+
const bottomValue = source.get(left, bottom) * (1 - fx) + source.get(right, bottom) * fx;
|
|
762
|
+
rotated[y * width + x] = Math.round(topValue * (1 - fy) + bottomValue * fy);
|
|
763
|
+
}
|
|
764
|
+
}
|
|
771
765
|
}
|
|
772
|
-
|
|
773
|
-
return LuminanceSource.fromGrey(rotated, width, height);
|
|
766
|
+
return LuminanceSource.fromGrey(rotated, width, height);
|
|
774
767
|
}
|
|
775
|
-
|
|
776
768
|
/**
|
|
777
769
|
* Decode, or throw if nothing is found.
|
|
778
770
|
*
|
|
@@ -781,10 +773,10 @@ function rotateLuminanceSourceByDegrees(source, degrees) {
|
|
|
781
773
|
* @returns {DecodeResult}
|
|
782
774
|
*/
|
|
783
775
|
export function decodeStrict(image, options) {
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
776
|
+
const results = decode(image, options);
|
|
777
|
+
if (results.length === 0)
|
|
778
|
+
throw new NotFoundError('No barcode found in image');
|
|
779
|
+
return results[0];
|
|
787
780
|
}
|
|
788
|
-
|
|
789
781
|
/** Library version, matching package.json. */
|
|
790
|
-
export const VERSION = '1.5.
|
|
782
|
+
export const VERSION = '1.5.9';
|