tapirscan 1.0.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/LICENSE +21 -0
- package/README.md +241 -0
- package/THIRD_PARTY_NOTICES.md +656 -0
- package/dist/detail-20260914/continuity.d.mts +2 -0
- package/dist/detail-20260914/continuity.mjs +62 -0
- package/dist/detail-20260914/detail-proposals-rich.d.mts +13 -0
- package/dist/detail-20260914/detail-proposals-rich.mjs +53 -0
- package/dist/detail-20260914/direct-recovery.d.mts +28 -0
- package/dist/detail-20260914/direct-recovery.mjs +139 -0
- package/dist/detail-20260914/host.d.mts +40 -0
- package/dist/detail-20260914/host.mjs +280 -0
- package/dist/detail-20260914/scanner.d.mts +12 -0
- package/dist/detail-20260914/scanner.mjs +64 -0
- package/dist/detail-20260914/source-evidence.d.mts +10 -0
- package/dist/detail-20260914/source-evidence.mjs +94 -0
- package/dist/detail-canvas.d.ts +35 -0
- package/dist/detail-canvas.js +73 -0
- package/dist/detail.d.ts +19 -0
- package/dist/detail.js +87 -0
- package/dist/host.d.ts +112 -0
- package/dist/host.js +184 -0
- package/dist/host64.d.ts +112 -0
- package/dist/host64.js +184 -0
- package/dist/index.d.ts +92 -0
- package/dist/index.js +199 -0
- package/dist/multiformat/formats.d.ts +26 -0
- package/dist/multiformat/formats.js +59 -0
- package/dist/multiformat/geometry.d.ts +32 -0
- package/dist/multiformat/geometry.js +122 -0
- package/dist/multiformat/pixels.d.ts +3 -0
- package/dist/multiformat/pixels.js +36 -0
- package/dist/multiformat/scanner.d.ts +51 -0
- package/dist/multiformat/scanner.js +258 -0
- package/dist/multiformat-host.d.ts +121 -0
- package/dist/multiformat-host.js +207 -0
- package/dist/policy.d.ts +12 -0
- package/dist/policy.js +12 -0
- package/package.json +52 -0
- package/wasm/high-release-20260915.wasm +0 -0
- package/wasm/low-release-20260915.wasm +0 -0
- package/wasm/medium-release-20260915.wasm +0 -0
- package/wasm/multiformat.json +6 -0
- package/wasm/multiformat.wasm +0 -0
- package/wasm/very-high-release-20260915.wasm +0 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Florian Nick
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
# Tapirscan for JavaScript and TypeScript
|
|
2
|
+
|
|
3
|
+
Scan image pixels in a browser or Node with the same Rust/WASM core.
|
|
4
|
+
[Quick start](#quick-start) · [WASM loading](#wasm-loading) · [Functions](#functions) · [All options](#all-options) · [Results](#results)
|
|
5
|
+
|
|
6
|
+
## Quick start
|
|
7
|
+
|
|
8
|
+
```sh
|
|
9
|
+
npm install tapirscan
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Registry publication is pending; until then, install a tarball from the
|
|
13
|
+
[local build guide](../../docs/DEVELOPMENT.md). TypeScript declarations and WASM
|
|
14
|
+
binaries are included. Your runtime must support WebAssembly SIMD.
|
|
15
|
+
|
|
16
|
+
Pass a canvas's `ImageData` directly:
|
|
17
|
+
|
|
18
|
+
```js
|
|
19
|
+
import { scan } from "tapirscan";
|
|
20
|
+
|
|
21
|
+
// Using an existing canvas and its 2D context:
|
|
22
|
+
const image = context.getImageData(0, 0, canvas.width, canvas.height);
|
|
23
|
+
const result = await scan(image);
|
|
24
|
+
console.log(result.values); // e.g. ["4006381333931"]
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Defaults are Medium effort, EAN13, multiple results, and debug disabled.
|
|
28
|
+
`result.barcodes` also gives each read's text, format, polygon and rectangle.
|
|
29
|
+
The helper creates and disposes a scanner automatically. Browser apps need to
|
|
30
|
+
serve its [WASM assets](#wasm-loading); Node loads the packaged files automatically.
|
|
31
|
+
|
|
32
|
+
For more control or repeated images, reuse a scanner:
|
|
33
|
+
|
|
34
|
+
```js
|
|
35
|
+
import { Scanner } from "tapirscan";
|
|
36
|
+
|
|
37
|
+
const scanner = await Scanner.create({ mode: "high", formats: "1D" });
|
|
38
|
+
try {
|
|
39
|
+
const result = scanner.scan(image);
|
|
40
|
+
for (const barcode of result.barcodes) {
|
|
41
|
+
console.log(barcode.text, barcode.format, barcode.polygon);
|
|
42
|
+
}
|
|
43
|
+
} finally {
|
|
44
|
+
scanner.dispose();
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Here `image` is the `ImageData` above. `formats: "1D"` enables all supported linear
|
|
49
|
+
formats; additional readers are experimental. Settings also work with the helper:
|
|
50
|
+
`await scan(image, { mode: "high", formats: "1D" })`.
|
|
51
|
+
|
|
52
|
+
## WASM loading
|
|
53
|
+
|
|
54
|
+
In Node, the default loader reads assets from the installed package. Decode your
|
|
55
|
+
image with an image library first, then pass grayscale, RGB or RGBA bytes:
|
|
56
|
+
|
|
57
|
+
```js
|
|
58
|
+
const result = await scan({ data: pixels, width, height, channels: 1, stride: width });
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Here `pixels` is a Uint8Array of decoded grayscale pixels. Image codecs are not
|
|
62
|
+
included; filenames, URLs and encoded JPEG/PNG bytes are not scan inputs.
|
|
63
|
+
|
|
64
|
+
In a browser, the default loader fetches assets relative to the module. If your
|
|
65
|
+
bundler relocates modules, copy the WASMs into your public directory:
|
|
66
|
+
|
|
67
|
+
```sh
|
|
68
|
+
mkdir -p public/tapirscan
|
|
69
|
+
cp node_modules/tapirscan/wasm/*.wasm public/tapirscan/
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Then supply a loader pointing at those files:
|
|
73
|
+
|
|
74
|
+
```js
|
|
75
|
+
const scanner = await Scanner.create({
|
|
76
|
+
loadWasm: async (url) => {
|
|
77
|
+
const filename = url.pathname.split("/").pop();
|
|
78
|
+
const response = await fetch(`/tapirscan/${filename}`);
|
|
79
|
+
if (!response.ok) throw new Error(`Could not load scanner: ${response.status}`);
|
|
80
|
+
return response.arrayBuffer();
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
try {
|
|
84
|
+
console.log(scanner.scan(image).values);
|
|
85
|
+
} finally {
|
|
86
|
+
scanner.dispose();
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Use the deployed base path if your app is hosted below a subpath. Copy all current
|
|
91
|
+
WASMs: Medium/High/Very high also load the Low recovery decoder. The demo and its
|
|
92
|
+
comparison engines are not needed in your app.
|
|
93
|
+
|
|
94
|
+
## Camera and worker use
|
|
95
|
+
|
|
96
|
+
Initialization is asynchronous; scanning is synchronous. For a responsive browser
|
|
97
|
+
UI, initialize one scanner inside a Web Worker and transfer an owned frame buffer.
|
|
98
|
+
Capture the next frame after the previous result arrives. Avoid racing scanner
|
|
99
|
+
initialization or modifying pixels during scanning. The [demo worker](../../demo/src/lib/scan.worker.ts)
|
|
100
|
+
shows a complete integration. Camera capture belongs to your app and requires
|
|
101
|
+
HTTPS (localhost works for development).
|
|
102
|
+
|
|
103
|
+
## Moving from ZXing
|
|
104
|
+
|
|
105
|
+
For [`zxing-wasm`](https://github.com/Sec-ant/zxing-wasm), you can keep your
|
|
106
|
+
existing ImageData and replace the decoding call:
|
|
107
|
+
|
|
108
|
+
```js
|
|
109
|
+
// Before:
|
|
110
|
+
import { readBarcodes } from "zxing-wasm/reader";
|
|
111
|
+
const reads = await readBarcodes(image, { formats: ["EAN13"] });
|
|
112
|
+
const oldValues = reads.filter((read) => read.isValid).map((read) => read.text);
|
|
113
|
+
|
|
114
|
+
// After:
|
|
115
|
+
import { scan } from "tapirscan";
|
|
116
|
+
const result = await scan(image, { formats: ["EAN13"] });
|
|
117
|
+
const values = result.values;
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Here `image` is decoded ImageData. Keep an explicit format selection during
|
|
121
|
+
migration; Tapirscan defaults to EAN13. For successive frames, initialize a
|
|
122
|
+
`Scanner` once and scan inside a worker, as shown above.
|
|
123
|
+
|
|
124
|
+
| Existing ZXing integration | Tapirscan equivalent or difference |
|
|
125
|
+
| ---------------------------------------- | ------------------------------------------------------------------------------------------- |
|
|
126
|
+
| Array of reads | `result.barcodes`, or `result.values` for strings. |
|
|
127
|
+
| `read.text` | `barcode.text`. |
|
|
128
|
+
| `read.position` | `barcode.polygon` as four [x, y] corners, or `barcode.rect`. |
|
|
129
|
+
| `tryHarder`, `tryRotate`, `tryDownscale` | No direct option mapping. Select an effort mode and measure your images. |
|
|
130
|
+
| `maxNumberOfSymbols` | No arbitrary count limit; `multiple: false` selects one after the scan, without early exit. |
|
|
131
|
+
| Blob or encoded image input | Decode to ImageData or a supported byte buffer before scanning. |
|
|
132
|
+
| WASM overrides/asset paths | Use Tapirscan's `loadWasm` and packaged assets. |
|
|
133
|
+
|
|
134
|
+
[`@zxing/browser`](https://github.com/zxing-js/browser) also manages browser image
|
|
135
|
+
and video acquisition. Tapirscan's scanner accepts pixels; it does not replace
|
|
136
|
+
camera-device helpers or continuous-scan callbacks. Keep your capture loop,
|
|
137
|
+
draw frames to a canvas, and pass ImageData to a reusable scanner. Stop media
|
|
138
|
+
tracks when capture ends, and dispose the scanner when finished. See the
|
|
139
|
+
[demo](../../demo/README.md) for capture behavior.
|
|
140
|
+
|
|
141
|
+
Check supported formats and reader-specific metadata before switching. Additional
|
|
142
|
+
formats are experimental, and Tapirscan is not a drop-in replacement for every
|
|
143
|
+
ZXing package. Run both readers on representative inputs before replacing one.
|
|
144
|
+
|
|
145
|
+
## Functions
|
|
146
|
+
|
|
147
|
+
| Function | Return type | Behavior |
|
|
148
|
+
| ----------------------------------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------- |
|
|
149
|
+
| `scan(image, options = {})` | `Promise<ScanResult>` | One image with automatic scanner creation and disposal, including on failure. Accepts creation and scan options together. |
|
|
150
|
+
| `Scanner.create(options = {})` | `Promise<Scanner>` | Initialize a reusable scanner. Mode and formats are fixed for its lifetime. |
|
|
151
|
+
| `scanner.scan(image, options = {})` | `ScanResult` | Synchronously scan pixels. Accepts scan options only. |
|
|
152
|
+
| `scanner.best(result)` | `Barcode \| undefined` | Convenience alias for `result.best`. |
|
|
153
|
+
| `scanner.dispose()` | `void` | Release WASM sessions. Repeated disposal is safe; do not scan after disposal. |
|
|
154
|
+
|
|
155
|
+
`image` is required for either scan function. All options are optional. Reuse a
|
|
156
|
+
scanner for successive frames to avoid repeated initialization; create another
|
|
157
|
+
to change effort or formats. Previously returned results survive disposal.
|
|
158
|
+
|
|
159
|
+
## All options
|
|
160
|
+
|
|
161
|
+
| Option | Where | Default | Meaning |
|
|
162
|
+
| ---------------- | -------- | ---------------------- | ---------------------------------------------------------------------------------------------------------- |
|
|
163
|
+
| `mode` | Creation | `"medium"` | `"low"`, `"medium"`, `"high"`, `"very-high"`. |
|
|
164
|
+
| `formats` | Creation | `["EAN13"]` | `"1D"`, `"2D"`, `"all"`, or a nonempty array of exact identifiers. |
|
|
165
|
+
| `loadWasm` | Creation | Module-relative loader | `(url: URL) => Promise<ArrayBuffer>`. Uses HTTP fetch in browsers and filesystem reads for Node file URLs. |
|
|
166
|
+
| `multiple` | Scan | `true` | False keeps at most the highest-support read after scanning; it does not provide an early exit. |
|
|
167
|
+
| `debug` | Scan | `false` | Include search evidence under `result.debug`. Decoded polygons are always returned. |
|
|
168
|
+
| `includeRegions` | Scan | Unset | Compatibility alias for `debug`; prefer `debug` in new code. Conflicting values are rejected. |
|
|
169
|
+
|
|
170
|
+
Format presets cover supported symbologies. Exports `linearFormats`, `matrixFormats`
|
|
171
|
+
and `retailFormats` let you compose custom selections; `formatBits` provides their
|
|
172
|
+
native bit mapping. See [identifiers and coverage](../../docs/FORMATS.md).
|
|
173
|
+
The four effort modes tune EAN13/UPCA; additional readers use fixed effort.
|
|
174
|
+
|
|
175
|
+
Resolution, camera capture, preprocessing rotation, ROI, confidence thresholds,
|
|
176
|
+
timeouts and exact work budgets are not public scan options. Demo capture and
|
|
177
|
+
resize settings belong to the application.
|
|
178
|
+
|
|
179
|
+
## Image input
|
|
180
|
+
|
|
181
|
+
`PixelImage` accepts `ImageData` (or its data/width/height fields) or an explicit
|
|
182
|
+
`Image` buffer:
|
|
183
|
+
|
|
184
|
+
| Field | Type | Meaning |
|
|
185
|
+
| ----------------- | ------------- | ------------------------------------------------------------------------------------------------------- |
|
|
186
|
+
| `data` | `Uint8Array` | Decoded pixels. ImageData instead uses `Uint8ClampedArray` and implies tightly packed RGBA. |
|
|
187
|
+
| `width`, `height` | `number` | Integer input dimensions, at least 3 pixels each. |
|
|
188
|
+
| `channels` | `1 \| 3 \| 4` | Grayscale, RGB or RGBA. Alpha is ignored. Required for explicit buffers. |
|
|
189
|
+
| `stride` | `number` | Bytes between row starts, at least width × channels. Required for explicit buffers; padding is allowed. |
|
|
190
|
+
|
|
191
|
+
Input is limited to 128 MiB of addressed pixels. Keep the buffer stable during the
|
|
192
|
+
call. Convert DOM image elements or encoded images to pixels before scanning.
|
|
193
|
+
|
|
194
|
+
## Results
|
|
195
|
+
|
|
196
|
+
| Field | Type | Meaning |
|
|
197
|
+
| ------------------- | -------------------------------------------------------------- | --------------------------------------------------------------------------------- |
|
|
198
|
+
| `result.values` | `string[]` | Decoded strings. |
|
|
199
|
+
| `result.barcodes` | `Barcode[]` | Decoded values with format and geometry. |
|
|
200
|
+
| `result.best` | `Barcode \| undefined` | Highest-support read, or undefined when empty. |
|
|
201
|
+
| `result.image` | `{ width: number, height: number }` | Dimensions of supplied pixels. |
|
|
202
|
+
| `result.mode` | `Mode` | Selected effort. |
|
|
203
|
+
| `result.elapsedMs` | `number` | Host scan time in milliseconds; excludes file loading and scanner initialization. |
|
|
204
|
+
| `result.unfinished` | `boolean` | Incomplete work; returned reads may still be useful. |
|
|
205
|
+
| `result.debug` | `Diagnostics \| undefined` | Requested diagnostic evidence; absent by default. |
|
|
206
|
+
| `barcode.text` | `string` | Decoded text. |
|
|
207
|
+
| `barcode.format` | `Format \| "Unknown"` | Symbology identifier. |
|
|
208
|
+
| `barcode.polygon` | `Quad` | Four `[x, y]` corners in input-image coordinates. |
|
|
209
|
+
| `barcode.rect` | `{ left: number, top: number, width: number, height: number }` | Enclosing integer rectangle. |
|
|
210
|
+
|
|
211
|
+
Both result arrays are empty when nothing is decoded. Coordinates start at the
|
|
212
|
+
top left, x rightward and y downward. Geometry is returned, not a cropped bitmap.
|
|
213
|
+
Map coordinates back yourself if you resize/rotate before scanning. Support is a
|
|
214
|
+
ranking heuristic, not a probability.
|
|
215
|
+
|
|
216
|
+
The package exports `ScannerOptions`, `ScanOptions`, `ScanResult`, `Barcode`,
|
|
217
|
+
`PixelImage`, `Image`, `Quad`, `Mode`, `Format`, `FormatSelection`, `Diagnostics`
|
|
218
|
+
and `DiagnosticBarcode` types. TypeScript infers results from calls; runtime
|
|
219
|
+
checks still validate pixel buffers and dimensions.
|
|
220
|
+
|
|
221
|
+
## Diagnostics and errors
|
|
222
|
+
|
|
223
|
+
```js
|
|
224
|
+
const result = scanner.scan(image, { debug: true });
|
|
225
|
+
if (result.debug) {
|
|
226
|
+
console.log(result.debug.localization, result.debug.searchWindows);
|
|
227
|
+
console.log(result.debug.scan.barcodes);
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Diagnostics retain the raw schema-2 result: `scan` includes support and candidate
|
|
232
|
+
evidence, and `localizationLimited` reports localization limits. Depending on the
|
|
233
|
+
reader, `localization`, `searchWindows`, `recovery` and `detailRegions` may be
|
|
234
|
+
present. Raw metadata includes GS1 and structured append where supported.
|
|
235
|
+
Candidate indices inside recovery crops are local to the crop and are not
|
|
236
|
+
identifiers for tracking between frames.
|
|
237
|
+
|
|
238
|
+
Invalid options can raise TypeError. Scanner validation and engine failures can
|
|
239
|
+
raise the exported `ScannerError` with a `.code` and `.message`. Loader/fetch
|
|
240
|
+
errors propagate to the caller; creation and the one-shot helper reject their
|
|
241
|
+
promises on failure. Always dispose reusable scanners with `finally`.
|