tapirscan 1.1.0 → 1.2.0
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/README.md +37 -27
- package/dist/index.d.ts +3 -2
- package/dist/index.js +20 -23
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
# Tapirscan for JavaScript and TypeScript
|
|
2
2
|
|
|
3
|
-
This guide describes
|
|
3
|
+
This guide describes the 1.2.0 API revision. See [migration](../../docs/API_MIGRATION.md).
|
|
4
|
+
Build/install this checkout using [the development guide](../../docs/DEVELOPMENT.md)
|
|
5
|
+
to use these changes before publication; older registry packages use their own
|
|
6
|
+
versioned API.
|
|
4
7
|
|
|
5
8
|
Scan image pixels in a browser or Node with the same Rust/WASM core.
|
|
6
9
|
[Try the live demo](https://tapirscan.netlify.app) · [Quick start](#quick-start) · [WASM loading](#wasm-loading) · [Functions](#functions) · [All options](#all-options) · [Results](#results)
|
|
@@ -48,7 +51,7 @@ try {
|
|
|
48
51
|
```
|
|
49
52
|
|
|
50
53
|
Here `image` is the `ImageData` above. `formats: "1D"` enables all supported linear
|
|
51
|
-
formats;
|
|
54
|
+
formats; readers outside the retail group remain experimental. Settings also work with the helper:
|
|
52
55
|
`await scan(image, { mode: "high", formats: "1D" })`.
|
|
53
56
|
|
|
54
57
|
`formats: "retail"` selects EAN13, UPCA,
|
|
@@ -135,15 +138,15 @@ call and does not change the default formats. Previously returned results surviv
|
|
|
135
138
|
|
|
136
139
|
## All options
|
|
137
140
|
|
|
138
|
-
| Option
|
|
139
|
-
|
|
|
140
|
-
| `mode`
|
|
141
|
-
| `formats`
|
|
142
|
-
| `wasmBaseUrl`
|
|
143
|
-
| `loadWasm`
|
|
144
|
-
| `eanAddOnPolicy`
|
|
145
|
-
| `
|
|
146
|
-
| `debug`
|
|
141
|
+
| Option | Where | Default | Meaning |
|
|
142
|
+
| ---------------- | ------------------- | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
143
|
+
| `mode` | Creation | `"medium"` | `"low"`, `"medium"`, `"high"`, `"very-high"`. |
|
|
144
|
+
| `formats` | Creation / scan | `["EAN13"]` | A single identifier, `"retail"`, `"common1D"`, `"common"`, `"1D"`, `"2D"`, `"all"`, or a nonempty array. Per-call selections must be subsets of creation formats. |
|
|
145
|
+
| `wasmBaseUrl` | Creation | Module-relative assets | Directory URL for packaged WASMs. Use this for normal browser hosting. |
|
|
146
|
+
| `loadWasm` | Creation | Module-relative loader | `(url: URL) => Promise<ArrayBuffer>`. Uses HTTP fetch in browsers and filesystem reads for Node file URLs. |
|
|
147
|
+
| `eanAddOnPolicy` | Creation / one-shot | `"Ignore"` | `"Ignore"`, `"Read"`, `"Require"`; optional EAN/UPC supplement policy. |
|
|
148
|
+
| `extendedBudget` | Scan / one-shot | `false` | Allow extra reader work for any format. Exact budgets may evolve. |
|
|
149
|
+
| `debug` | Scan | `false` | Include search evidence under `result.debug`. Decoded polygons are always returned. |
|
|
147
150
|
|
|
148
151
|
Format presets cover supported symbologies. Exports `commonFormats`, `commonLinearFormats`, `linearFormats`, `matrixFormats`
|
|
149
152
|
and `retailFormats` let you compose custom selections; `formatBits` provides their
|
|
@@ -180,6 +183,7 @@ call. Convert DOM image elements or encoded images to pixels before scanning.
|
|
|
180
183
|
| `result.mode` | `Mode` | Selected effort. |
|
|
181
184
|
| `result.elapsedMs` | `number` | Host scan time in milliseconds; excludes file loading and scanner initialization. |
|
|
182
185
|
| `result.unfinished` | `boolean` | Incomplete work; returned reads may still be useful. |
|
|
186
|
+
| `result.undecoded` | `readonly UndecodedRegion[]` | Localized proposals without accepted decodes; always available. |
|
|
183
187
|
| `result.debug` | `Diagnostics \| undefined` | Requested diagnostic evidence; absent by default. |
|
|
184
188
|
| `barcode.payloadBytes` | `readonly number[] \| undefined` | Original decoded matrix payload bytes when available; use `Uint8Array.from(...)` for an owned byte buffer. |
|
|
185
189
|
| `barcode.text` | `string` | Decoded text. |
|
|
@@ -194,7 +198,8 @@ call. Convert DOM image elements or encoded images to pixels before scanning.
|
|
|
194
198
|
|
|
195
199
|
Results, including nested geometry and requested diagnostics, are immutable at
|
|
196
200
|
runtime and in TypeScript. Use `structuredClone(result)` if you need a mutable
|
|
197
|
-
copy.
|
|
201
|
+
copy. `barcodes` and `values` are empty when nothing is decoded; `undecoded` may still
|
|
202
|
+
contain proposals. Use `result.best` for
|
|
198
203
|
one read, or `undefined` when empty. All decoded instances remain available,
|
|
199
204
|
including separate copies of the same value. Coordinates start at the
|
|
200
205
|
top left, x rightward and y downward. Geometry is returned, not a cropped bitmap.
|
|
@@ -221,9 +226,9 @@ The policy is fixed for that scanner; its default is `"Ignore"`.
|
|
|
221
226
|
supplement. Supplement geometry is not exposed separately.
|
|
222
227
|
|
|
223
228
|
The supplement appears separately in `barcode.eanAddOn`; `barcode.text` remains
|
|
224
|
-
the main payload. Reading supplements enables additional
|
|
225
|
-
work independently of the effort mode.
|
|
226
|
-
by `"Require"` remain available
|
|
229
|
+
the main payload. Reading supplements enables additional decoding
|
|
230
|
+
work independently of the effort mode. Retail reads rejected
|
|
231
|
+
by `"Require"` remain available in `result.undecoded`.
|
|
227
232
|
|
|
228
233
|
## Evidence and work limits
|
|
229
234
|
|
|
@@ -294,17 +299,22 @@ raise the exported `ScannerError` with a `.code` and `.message`. Loader/fetch
|
|
|
294
299
|
errors propagate to the caller; creation and the one-shot helper reject their
|
|
295
300
|
promises on failure. Always dispose reusable scanners with `finally`.
|
|
296
301
|
|
|
297
|
-
##
|
|
302
|
+
## Extended work budget
|
|
298
303
|
|
|
299
|
-
Use `scanner.scan(image, {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
budgets stopping later candidates. The default is `false`; at least one of
|
|
303
|
-
`EAN13` or `UPCA` must be selected. This is a per-scan option.
|
|
304
|
+
Use `scanner.scan(image, { extendedBudget: true })` to allow additional reader work. The default
|
|
305
|
+
is false. This option is valid for every format; the exact budgets and stages are
|
|
306
|
+
implementation details that may evolve. Effort mode remains a separate setting.
|
|
304
307
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
308
|
+
Today this relaxes shared EAN-13/UPC-A retry and association limits. Other readers
|
|
309
|
+
currently retain their existing budgets. Per-candidate limits and intentional
|
|
310
|
+
deferrals remain; `unfinished` can still be true. This is not unlimited search,
|
|
311
|
+
an exhaustiveness guarantee or a wall-clock deadline. Custom primary-reader
|
|
312
|
+
engines must support the extended-work capability or report an error.
|
|
313
|
+
|
|
314
|
+
## Undecoded regions
|
|
315
|
+
|
|
316
|
+
`result.undecoded` is always available, independently of `debug`. Each entry has
|
|
317
|
+
a source-image polygon and a format hint. It is a localized proposal without an
|
|
318
|
+
accepted decode, not proof of a real or permanently unreadable barcode. Entries
|
|
319
|
+
can overlap or describe false candidates. An empty collection does not prove
|
|
320
|
+
that every barcode was found. Raw candidate attempts remain in debug diagnostics.
|
package/dist/index.d.ts
CHANGED
|
@@ -25,8 +25,8 @@ type RawDiagnosticBarcode = FormatBarcode | (ScanFrame["barcodes"][number] & {
|
|
|
25
25
|
});
|
|
26
26
|
export type Mode = "low" | "medium" | "high" | "very-high";
|
|
27
27
|
export interface ScanOptions {
|
|
28
|
-
/**
|
|
29
|
-
|
|
28
|
+
/** Allow reader-specific extra work. Supported for every format; exact budgets may evolve. */
|
|
29
|
+
extendedBudget?: boolean;
|
|
30
30
|
/** Per-call subset of the formats configured at creation. */
|
|
31
31
|
formats?: FormatSelection;
|
|
32
32
|
debug?: boolean;
|
|
@@ -117,6 +117,7 @@ export interface ScanResult {
|
|
|
117
117
|
readonly mode: Mode;
|
|
118
118
|
readonly elapsedMs: number;
|
|
119
119
|
readonly unfinished: boolean;
|
|
120
|
+
readonly undecoded: readonly UndecodedRegion[];
|
|
120
121
|
readonly debug?: Diagnostics;
|
|
121
122
|
}
|
|
122
123
|
export type EanAddOnPolicy = "Ignore" | "Read" | "Require";
|
package/dist/index.js
CHANGED
|
@@ -111,15 +111,17 @@ function publicResult(raw, image, debug) {
|
|
|
111
111
|
for (let i = 0; i < barcodes.length; i++)
|
|
112
112
|
if (bestIndex < 0 || raw.scan.barcodes[i].support > raw.scan.barcodes[bestIndex].support)
|
|
113
113
|
bestIndex = i;
|
|
114
|
+
const regions = regionEvidence(raw);
|
|
114
115
|
return freeze({
|
|
115
116
|
barcodes,
|
|
116
117
|
values: barcodes.map((b) => b.text),
|
|
117
118
|
best: barcodes[bestIndex],
|
|
119
|
+
undecoded: regions.undecoded,
|
|
118
120
|
image: { width: image.width, height: image.height },
|
|
119
121
|
mode: raw.mode,
|
|
120
122
|
elapsedMs: raw.elapsedMs,
|
|
121
123
|
unfinished: raw.scan.unfinished || raw.localizationLimited || (raw.localization?.omitted ?? 0) > 0,
|
|
122
|
-
...(debug ? { debug: { ...raw, regions
|
|
124
|
+
...(debug ? { debug: { ...raw, regions } } : {}),
|
|
123
125
|
});
|
|
124
126
|
}
|
|
125
127
|
const modes = {
|
|
@@ -196,23 +198,22 @@ export class Scanner {
|
|
|
196
198
|
if (input === null || typeof input !== "object" || Array.isArray(input))
|
|
197
199
|
throw new TypeError("Invalid scan options");
|
|
198
200
|
for (const key of Object.keys(options))
|
|
199
|
-
if (key !== "debug" && key !== "formats" && key !== "
|
|
201
|
+
if (key !== "debug" && key !== "formats" && key !== "extendedBudget")
|
|
200
202
|
throw new TypeError(`Unknown scan option: ${key}`);
|
|
201
203
|
if (options.debug !== undefined && typeof options.debug !== "boolean")
|
|
202
204
|
throw new TypeError("debug must be a boolean");
|
|
203
|
-
if (options.
|
|
204
|
-
throw new TypeError("
|
|
205
|
+
if (options.extendedBudget !== undefined && typeof options.extendedBudget !== "boolean")
|
|
206
|
+
throw new TypeError("extendedBudget must be a boolean");
|
|
207
|
+
const finishCandidates = options.extendedBudget ?? false;
|
|
205
208
|
const debug = options.debug ?? false;
|
|
206
|
-
const scanPolicy = { ...policy, finishCandidates
|
|
209
|
+
const scanPolicy = { ...policy, finishCandidates };
|
|
207
210
|
const formats = options.formats === undefined ? this.formats : resolveFormats(options.formats);
|
|
208
211
|
if (formats.some((format) => !this.formats.includes(format)))
|
|
209
212
|
throw new TypeError(`Scan formats must be a subset of configured formats. Requested: ${formats.join(", ")}; configured: ${this.formats.join(", ")}`);
|
|
210
213
|
if (this.host instanceof MediumMultiformatScanner) {
|
|
211
|
-
if (options.finishCandidates && !formats.some((f) => f === "EAN13" || f === "UPCA"))
|
|
212
|
-
throw new TypeError("finishCandidates requires EAN13 or UPCA");
|
|
213
214
|
const frame = this.host.scan(image, formats, {
|
|
214
215
|
eanAddOnSymbol: this.eanAddOnPolicy,
|
|
215
|
-
finishCandidates
|
|
216
|
+
finishCandidates,
|
|
216
217
|
});
|
|
217
218
|
const barcodes = frame.barcodes;
|
|
218
219
|
const primary = frame.primary;
|
|
@@ -225,12 +226,12 @@ export class Scanner {
|
|
|
225
226
|
// Preserve the aggregate limit flag when additional readers cannot separate causes.
|
|
226
227
|
localizationLimited: frame.unfinished,
|
|
227
228
|
scan: {
|
|
228
|
-
...
|
|
229
|
+
...primary?.scan,
|
|
229
230
|
barcodes,
|
|
230
231
|
unfinished: frame.unfinished,
|
|
231
|
-
|
|
232
|
+
regions: frame.regions,
|
|
232
233
|
},
|
|
233
|
-
...(
|
|
234
|
+
...(primary
|
|
234
235
|
? {
|
|
235
236
|
localization,
|
|
236
237
|
searchWindows: primary.searchWindows,
|
|
@@ -253,17 +254,13 @@ export class Scanner {
|
|
|
253
254
|
multiple: true,
|
|
254
255
|
elapsedMs: full.scanMs,
|
|
255
256
|
localizationLimited: localization.workLimited || localization.omitted > 0,
|
|
256
|
-
scan:
|
|
257
|
-
|
|
257
|
+
scan: { ...full.scan, barcodes },
|
|
258
|
+
localization,
|
|
259
|
+
searchWindows: full.searchWindows,
|
|
260
|
+
...("recovery" in full && "detailRegions" in full
|
|
258
261
|
? {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
...("recovery" in full && "detailRegions" in full
|
|
262
|
-
? {
|
|
263
|
-
recovery: full.recovery,
|
|
264
|
-
detailRegions: full.detailRegions,
|
|
265
|
-
}
|
|
266
|
-
: {}),
|
|
262
|
+
recovery: full.recovery,
|
|
263
|
+
detailRegions: full.detailRegions,
|
|
267
264
|
}
|
|
268
265
|
: {}),
|
|
269
266
|
}, image, debug);
|
|
@@ -277,12 +274,12 @@ export async function scan(image, options = {}) {
|
|
|
277
274
|
const input = options;
|
|
278
275
|
if (input === null || typeof input !== "object" || Array.isArray(input))
|
|
279
276
|
throw new TypeError("Invalid scan options");
|
|
280
|
-
const { debug,
|
|
277
|
+
const { debug, extendedBudget, ...creation } = options;
|
|
281
278
|
if (debug !== undefined && typeof debug !== "boolean")
|
|
282
279
|
throw new TypeError("debug must be a boolean");
|
|
283
280
|
const scanner = await Scanner.create(creation);
|
|
284
281
|
try {
|
|
285
|
-
return scanner.scan(image, { debug,
|
|
282
|
+
return scanner.scan(image, { debug, extendedBudget });
|
|
286
283
|
}
|
|
287
284
|
finally {
|
|
288
285
|
scanner.dispose();
|