@scanfit/browser 0.1.0-alpha.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/THIRD_PARTY_NOTICES.md +27 -0
- package/dist/assets/processor.worker-ILg2T47t.js +1456 -0
- package/dist/chunks/geometry-57LNKdIq.js +37 -0
- package/dist/chunks/types-Bf5l1UNl.js +35 -0
- package/dist/core.js +572 -0
- package/dist/detector.js +580 -0
- package/dist/pdf.js +493 -0
- package/dist/react.js +993 -0
- package/dist/styles.css +1 -0
- package/dist/trigger.js +59 -0
- package/dist/types/core/canvas-bridge.d.ts +12 -0
- package/dist/types/core/geometry.d.ts +8 -0
- package/dist/types/core/headers.d.ts +10 -0
- package/dist/types/core/index.d.ts +20 -0
- package/dist/types/core/jpeg.d.ts +2 -0
- package/dist/types/core/processor.worker.d.ts +1 -0
- package/dist/types/core/protocol.d.ts +34 -0
- package/dist/types/core/types.d.ts +132 -0
- package/dist/types/core/worker-client.d.ts +13 -0
- package/dist/types/detector/index.d.ts +11 -0
- package/dist/types/detector/scanfit-classical.d.ts +4 -0
- package/dist/types/pdf/index.d.ts +25 -0
- package/dist/types/react/Camera.d.ts +6 -0
- package/dist/types/react/CornerEditor.d.ts +8 -0
- package/dist/types/react/DocumentScanner.d.ts +28 -0
- package/dist/types/react/hooks.d.ts +10 -0
- package/dist/types/react/index.d.ts +5 -0
- package/dist/types/react/messages.d.ts +77 -0
- package/dist/types/trigger/ScannerTrigger.d.ts +7 -0
- package/dist/types/trigger/index.d.ts +1 -0
- package/package.json +72 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { S as n } from "./types-Bf5l1UNl.js";
|
|
2
|
+
function m(r) {
|
|
3
|
+
if (!Array.isArray(r) || r.length !== 4 || r.some(
|
|
4
|
+
(e) => !e || !Number.isFinite(e.x) || !Number.isFinite(e.y) || e.x < 0 || e.x > 1 || e.y < 0 || e.y > 1
|
|
5
|
+
))
|
|
6
|
+
throw new n(
|
|
7
|
+
"INVALID_CORNERS",
|
|
8
|
+
"Keep all four corners inside the image."
|
|
9
|
+
);
|
|
10
|
+
let t = 0;
|
|
11
|
+
for (let e = 0; e < 4; e++) {
|
|
12
|
+
const o = r[e], a = r[(e + 1) % 4], i = r[(e + 2) % 4];
|
|
13
|
+
if ((a.x - o.x) * (i.y - a.y) - (a.y - o.y) * (i.x - a.x) <= 1e-5)
|
|
14
|
+
throw new n(
|
|
15
|
+
"INVALID_CORNERS",
|
|
16
|
+
"Corners must form a clockwise, non-crossing document outline."
|
|
17
|
+
);
|
|
18
|
+
t += o.x * a.y - a.x * o.y;
|
|
19
|
+
}
|
|
20
|
+
if (t < 0.01)
|
|
21
|
+
throw new n("INVALID_CORNERS", "Select a larger document area.");
|
|
22
|
+
}
|
|
23
|
+
function c(r, t, e = 2400, o = 4e6) {
|
|
24
|
+
const a = Math.min(
|
|
25
|
+
1,
|
|
26
|
+
e / Math.max(r, t),
|
|
27
|
+
Math.sqrt(o / (r * t))
|
|
28
|
+
);
|
|
29
|
+
return {
|
|
30
|
+
width: Math.max(1, Math.floor(r * a)),
|
|
31
|
+
height: Math.max(1, Math.floor(t * a))
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export {
|
|
35
|
+
c as f,
|
|
36
|
+
m as v
|
|
37
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
class s extends Error {
|
|
2
|
+
constructor(a, n, o) {
|
|
3
|
+
super(n, o), this.code = a, this.name = "ScanError";
|
|
4
|
+
}
|
|
5
|
+
code;
|
|
6
|
+
}
|
|
7
|
+
const t = Object.freeze({
|
|
8
|
+
maxPages: 20,
|
|
9
|
+
maxFileBytes: 25 * 1024 ** 2,
|
|
10
|
+
maxPixels: 25e6,
|
|
11
|
+
maxSessionBytes: 100 * 1024 ** 2
|
|
12
|
+
}), c = [
|
|
13
|
+
{ x: 0, y: 0 },
|
|
14
|
+
{ x: 1, y: 0 },
|
|
15
|
+
{ x: 1, y: 1 },
|
|
16
|
+
{ x: 0, y: 1 }
|
|
17
|
+
], r = (e) => e.map((a) => ({ ...a }));
|
|
18
|
+
function i(e) {
|
|
19
|
+
if (e?.aborted)
|
|
20
|
+
throw new DOMException("Operation cancelled", "AbortError");
|
|
21
|
+
}
|
|
22
|
+
function x(e) {
|
|
23
|
+
return e instanceof s ? e : new s(
|
|
24
|
+
"PROCESSING_FAILED",
|
|
25
|
+
e instanceof Error ? e.message : "Document processing failed. Please try again."
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
export {
|
|
29
|
+
t as D,
|
|
30
|
+
c as F,
|
|
31
|
+
s as S,
|
|
32
|
+
i as a,
|
|
33
|
+
x as b,
|
|
34
|
+
r as c
|
|
35
|
+
};
|
package/dist/core.js
ADDED
|
@@ -0,0 +1,572 @@
|
|
|
1
|
+
import { S as w, a as _, D as M, b as C, c as T } from "./chunks/types-Bf5l1UNl.js";
|
|
2
|
+
import { F as K } from "./chunks/types-Bf5l1UNl.js";
|
|
3
|
+
import { f as B, v as R } from "./chunks/geometry-57LNKdIq.js";
|
|
4
|
+
const P = () => new w(
|
|
5
|
+
"INVALID_INPUT",
|
|
6
|
+
"The image header is incomplete or invalid. Choose another photo."
|
|
7
|
+
);
|
|
8
|
+
function A(t, e, i) {
|
|
9
|
+
try {
|
|
10
|
+
if (e + 8 > i) return;
|
|
11
|
+
const n = t.getUint32(e) === 1165519206 && t.getUint16(e + 4) === 0, f = e + (n ? 6 : 0);
|
|
12
|
+
if (f + 8 > i) return;
|
|
13
|
+
const g = t.getUint16(f) === 18761;
|
|
14
|
+
if (!g && t.getUint16(f) !== 19789 || t.getUint16(f + 2, g) !== 42)
|
|
15
|
+
return;
|
|
16
|
+
const s = f + t.getUint32(f + 4, g);
|
|
17
|
+
if (s < f || s + 2 > i) return;
|
|
18
|
+
const c = Math.min(t.getUint16(s, g), 256);
|
|
19
|
+
for (let r = 0; r < c; r++) {
|
|
20
|
+
const o = s + 2 + r * 12;
|
|
21
|
+
if (o + 12 > i) return;
|
|
22
|
+
if (t.getUint16(o, g) === 274 && t.getUint16(o + 2, g) === 3 && t.getUint32(o + 4, g) === 1) {
|
|
23
|
+
const h = t.getUint16(o + 8, g);
|
|
24
|
+
return h >= 1 && h <= 8 ? h : void 0;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
} catch {
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function z(t) {
|
|
31
|
+
const e = new DataView(t.buffer, t.byteOffset, t.byteLength);
|
|
32
|
+
let i = 0, n = 0, f = 1, g = !1, s = "";
|
|
33
|
+
const c = (r) => {
|
|
34
|
+
!g && r !== void 0 && (f = r, g = !0);
|
|
35
|
+
};
|
|
36
|
+
if (t.length >= 24 && e.getUint32(0) === 2303741511 && e.getUint32(4) === 218765834) {
|
|
37
|
+
if (e.getUint32(12) !== 1229472850 || e.getUint32(8) !== 13) throw P();
|
|
38
|
+
i = e.getUint32(16), n = e.getUint32(20), s = "image/png";
|
|
39
|
+
for (let r = 8; r + 12 <= t.length; ) {
|
|
40
|
+
const o = e.getUint32(r), h = e.getUint32(r + 4), l = r + 8;
|
|
41
|
+
if (l + o + 4 > t.length) throw P();
|
|
42
|
+
if (h === 1633899596)
|
|
43
|
+
throw new w(
|
|
44
|
+
"UNSUPPORTED_FORMAT",
|
|
45
|
+
"Animated PNG is not supported. Choose a still photo."
|
|
46
|
+
);
|
|
47
|
+
h === 1700284774 && c(A(e, l, l + o)), r = l + o + 4;
|
|
48
|
+
}
|
|
49
|
+
} else if (t.length >= 4 && e.getUint16(0) === 65496) {
|
|
50
|
+
s = "image/jpeg";
|
|
51
|
+
let r = 2;
|
|
52
|
+
for (; r + 3 < t.length; ) {
|
|
53
|
+
if (t[r++] !== 255) throw P();
|
|
54
|
+
for (; t[r] === 255; ) r++;
|
|
55
|
+
const o = t[r++];
|
|
56
|
+
if (o === 218 || o === 217) break;
|
|
57
|
+
if (o === 1 || o >= 208 && o <= 215) continue;
|
|
58
|
+
if (r + 2 > t.length) throw P();
|
|
59
|
+
const h = e.getUint16(r);
|
|
60
|
+
if (h < 2 || r + h > t.length) throw P();
|
|
61
|
+
if (o === 225 && h >= 8 && e.getUint32(r + 2) === 1165519206 && e.getUint16(r + 6) === 0 && c(A(e, r + 2, r + h)), o >= 192 && o <= 207 && ![196, 200, 204].includes(o)) {
|
|
62
|
+
if (h < 8) throw P();
|
|
63
|
+
const l = e.getUint16(r + 3), b = e.getUint16(r + 5);
|
|
64
|
+
if (i && i !== b || n && n !== l) throw P();
|
|
65
|
+
n = l, i = b;
|
|
66
|
+
}
|
|
67
|
+
r += h;
|
|
68
|
+
}
|
|
69
|
+
} else if (t.length >= 30 && e.getUint32(0) === 1380533830 && e.getUint32(8) === 1464156752) {
|
|
70
|
+
s = "image/webp";
|
|
71
|
+
for (let r = 12; r + 8 <= t.length; ) {
|
|
72
|
+
const o = e.getUint32(r), h = e.getUint32(r + 4, !0), l = r + 8;
|
|
73
|
+
if (l + h > t.length) throw P();
|
|
74
|
+
if (o === 1448097880 && h >= 10) {
|
|
75
|
+
if (t[l] & 2)
|
|
76
|
+
throw new w(
|
|
77
|
+
"UNSUPPORTED_FORMAT",
|
|
78
|
+
"Animated WebP is not supported. Choose a still photo."
|
|
79
|
+
);
|
|
80
|
+
i = Math.max(
|
|
81
|
+
i,
|
|
82
|
+
1 + t[l + 4] + (t[l + 5] << 8) + (t[l + 6] << 16)
|
|
83
|
+
), n = Math.max(
|
|
84
|
+
n,
|
|
85
|
+
1 + t[l + 7] + (t[l + 8] << 8) + (t[l + 9] << 16)
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
if (o === 1448097824 && h >= 10 && t[l + 3] === 157 && t[l + 4] === 1 && t[l + 5] === 42 && (i = Math.max(i, e.getUint16(l + 6, !0) & 16383), n = Math.max(n, e.getUint16(l + 8, !0) & 16383)), o === 1448097868 && h >= 5 && t[l] === 47) {
|
|
89
|
+
const b = e.getUint32(l + 1, !0);
|
|
90
|
+
i = Math.max(i, (b & 16383) + 1), n = Math.max(n, (b >>> 14 & 16383) + 1);
|
|
91
|
+
}
|
|
92
|
+
o === 1163413830 && c(A(e, l, l + h)), r = l + h + (h & 1);
|
|
93
|
+
}
|
|
94
|
+
} else
|
|
95
|
+
throw new w(
|
|
96
|
+
"UNSUPPORTED_FORMAT",
|
|
97
|
+
"Choose a JPEG, PNG, or WebP photo. PDF and HEIC imports are not included in this beta."
|
|
98
|
+
);
|
|
99
|
+
if (!i || !n || !Number.isSafeInteger(i * n)) throw P();
|
|
100
|
+
return { width: i, height: n, orientation: f, mime: s };
|
|
101
|
+
}
|
|
102
|
+
async function F(t, e) {
|
|
103
|
+
if (!t.size) throw P();
|
|
104
|
+
if (t.size > e.maxFileBytes)
|
|
105
|
+
throw new w(
|
|
106
|
+
"LIMIT_EXCEEDED",
|
|
107
|
+
"This photo exceeds the input file-size limit. Choose a smaller photo."
|
|
108
|
+
);
|
|
109
|
+
const i = z(new Uint8Array(await t.arrayBuffer()));
|
|
110
|
+
if (i.width * i.height > e.maxPixels)
|
|
111
|
+
throw new w(
|
|
112
|
+
"LIMIT_EXCEEDED",
|
|
113
|
+
"This photo exceeds the pixel limit. Export it at a lower resolution."
|
|
114
|
+
);
|
|
115
|
+
return i;
|
|
116
|
+
}
|
|
117
|
+
async function j(t) {
|
|
118
|
+
const e = new Uint8Array(await t.arrayBuffer());
|
|
119
|
+
if (e.length < 4 || e[0] !== 255 || e[1] !== 216)
|
|
120
|
+
return t;
|
|
121
|
+
const i = [];
|
|
122
|
+
let n = 2, f = 0, g = !1;
|
|
123
|
+
for (; n + 3 < e.length && e[n] === 255; ) {
|
|
124
|
+
const s = n;
|
|
125
|
+
for (; e[n] === 255; ) n++;
|
|
126
|
+
const c = e[n++];
|
|
127
|
+
if (c === 218 || c === 217) break;
|
|
128
|
+
if (c === 1 || c >= 208 && c <= 215) continue;
|
|
129
|
+
if (n + 2 > e.length) break;
|
|
130
|
+
const r = e[n] << 8 | e[n + 1];
|
|
131
|
+
if (r < 2 || n + r > e.length) break;
|
|
132
|
+
const o = n + r;
|
|
133
|
+
c === 225 && (i.push(e.slice(f, s)), f = o, g = !0), n = o;
|
|
134
|
+
}
|
|
135
|
+
return g ? (i.push(e.slice(f)), new Blob(i, { type: "image/jpeg" })) : t;
|
|
136
|
+
}
|
|
137
|
+
async function W(t) {
|
|
138
|
+
const e = document.createElement("canvas");
|
|
139
|
+
let i, n;
|
|
140
|
+
try {
|
|
141
|
+
if (t.kind === "decode") {
|
|
142
|
+
e.width = t.width, e.height = t.height;
|
|
143
|
+
const c = e.getContext("2d", { willReadFrequently: !0 });
|
|
144
|
+
if (!c)
|
|
145
|
+
throw new w("UNSUPPORTED_BROWSER", "A 2D canvas is required.");
|
|
146
|
+
if (typeof createImageBitmap == "function")
|
|
147
|
+
try {
|
|
148
|
+
const o = {
|
|
149
|
+
imageOrientation: "from-image",
|
|
150
|
+
resizeWidth: t.width,
|
|
151
|
+
resizeHeight: t.height,
|
|
152
|
+
resizeQuality: "high"
|
|
153
|
+
}, h = t.region;
|
|
154
|
+
i = h ? await createImageBitmap(
|
|
155
|
+
t.blob,
|
|
156
|
+
h.x,
|
|
157
|
+
h.y,
|
|
158
|
+
h.width,
|
|
159
|
+
h.height,
|
|
160
|
+
o
|
|
161
|
+
) : await createImageBitmap(t.blob, o);
|
|
162
|
+
} catch {
|
|
163
|
+
}
|
|
164
|
+
if (i) c.drawImage(i, 0, 0, e.width, e.height);
|
|
165
|
+
else {
|
|
166
|
+
n = URL.createObjectURL(t.blob);
|
|
167
|
+
const o = new Image();
|
|
168
|
+
o.src = n, await o.decode();
|
|
169
|
+
const h = t.region;
|
|
170
|
+
h ? c.drawImage(
|
|
171
|
+
o,
|
|
172
|
+
h.x,
|
|
173
|
+
h.y,
|
|
174
|
+
h.width,
|
|
175
|
+
h.height,
|
|
176
|
+
0,
|
|
177
|
+
0,
|
|
178
|
+
e.width,
|
|
179
|
+
e.height
|
|
180
|
+
) : c.drawImage(o, 0, 0, e.width, e.height), o.src = "";
|
|
181
|
+
}
|
|
182
|
+
const r = c.getImageData(0, 0, e.width, e.height);
|
|
183
|
+
return {
|
|
184
|
+
buffer: r.data.buffer,
|
|
185
|
+
width: r.width,
|
|
186
|
+
height: r.height
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
const f = B(t.width, t.height, t.longEdge);
|
|
190
|
+
e.width = t.width, e.height = t.height;
|
|
191
|
+
const g = e.getContext("2d");
|
|
192
|
+
if (!g)
|
|
193
|
+
throw new w("UNSUPPORTED_BROWSER", "A 2D canvas is required.");
|
|
194
|
+
g.putImageData(
|
|
195
|
+
new ImageData(
|
|
196
|
+
new Uint8ClampedArray(t.buffer),
|
|
197
|
+
t.width,
|
|
198
|
+
t.height
|
|
199
|
+
),
|
|
200
|
+
0,
|
|
201
|
+
0
|
|
202
|
+
);
|
|
203
|
+
const s = document.createElement("canvas");
|
|
204
|
+
s.width = f.width, s.height = f.height;
|
|
205
|
+
try {
|
|
206
|
+
const c = s.getContext("2d");
|
|
207
|
+
c.fillStyle = "#fff", c.fillRect(0, 0, s.width, s.height), c.drawImage(e, 0, 0, s.width, s.height);
|
|
208
|
+
const r = await new Promise(
|
|
209
|
+
(o, h) => s.toBlob(
|
|
210
|
+
(l) => l ? o(l) : h(new Error("JPEG encoding failed.")),
|
|
211
|
+
"image/jpeg",
|
|
212
|
+
t.quality
|
|
213
|
+
)
|
|
214
|
+
);
|
|
215
|
+
if (r.type !== "image/jpeg")
|
|
216
|
+
throw new w(
|
|
217
|
+
"UNSUPPORTED_BROWSER",
|
|
218
|
+
"This browser cannot encode JPEG images."
|
|
219
|
+
);
|
|
220
|
+
return { blob: await j(r), ...f };
|
|
221
|
+
} finally {
|
|
222
|
+
s.width = s.height = 0;
|
|
223
|
+
}
|
|
224
|
+
} finally {
|
|
225
|
+
i?.close(), n && URL.revokeObjectURL(n), e.width = e.height = 0;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
function V(t) {
|
|
229
|
+
return new Worker(
|
|
230
|
+
"" + new URL("assets/processor.worker-ILg2T47t.js", import.meta.url).href,
|
|
231
|
+
{
|
|
232
|
+
type: "module",
|
|
233
|
+
name: t?.name
|
|
234
|
+
}
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
class G {
|
|
238
|
+
constructor(e) {
|
|
239
|
+
this.url = e;
|
|
240
|
+
}
|
|
241
|
+
url;
|
|
242
|
+
worker = null;
|
|
243
|
+
sequence = 0;
|
|
244
|
+
generation = 0;
|
|
245
|
+
tail = Promise.resolve();
|
|
246
|
+
pending = /* @__PURE__ */ new Map();
|
|
247
|
+
ensure() {
|
|
248
|
+
if (this.worker) return this.worker;
|
|
249
|
+
if (typeof Worker > "u")
|
|
250
|
+
throw new w(
|
|
251
|
+
"UNSUPPORTED_BROWSER",
|
|
252
|
+
"This browser does not support processing workers."
|
|
253
|
+
);
|
|
254
|
+
if (this.url && new URL(this.url, location.href).origin !== location.origin)
|
|
255
|
+
throw new w(
|
|
256
|
+
"INVALID_INPUT",
|
|
257
|
+
"The processing worker must be served from the same origin."
|
|
258
|
+
);
|
|
259
|
+
const e = this.url ? new Worker(this.url, { type: "module" }) : new V({ name: "scanfit-processing" });
|
|
260
|
+
return this.worker = e, e.onmessage = async (i) => {
|
|
261
|
+
const n = i.data;
|
|
262
|
+
if (n.type === "bridge") {
|
|
263
|
+
try {
|
|
264
|
+
const g = await W(n.action);
|
|
265
|
+
this.worker === e && e.postMessage(
|
|
266
|
+
{ type: "bridge-result", id: n.id, result: g },
|
|
267
|
+
"buffer" in g ? [g.buffer] : []
|
|
268
|
+
);
|
|
269
|
+
} catch (g) {
|
|
270
|
+
this.worker === e && e.postMessage({
|
|
271
|
+
type: "bridge-result",
|
|
272
|
+
id: n.id,
|
|
273
|
+
error: g instanceof Error ? g.message : "Canvas bridge failed."
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
const f = this.pending.get(n.id);
|
|
279
|
+
if (f) {
|
|
280
|
+
if (n.type === "progress") {
|
|
281
|
+
f.progress?.(n.value);
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
this.pending.delete(n.id), n.error ? f.reject(
|
|
285
|
+
new w(n.error.code ?? "PROCESSING_FAILED", n.error.message)
|
|
286
|
+
) : f.resolve(n.result);
|
|
287
|
+
}
|
|
288
|
+
}, e.onerror = () => this.reset(
|
|
289
|
+
new w(
|
|
290
|
+
"PROCESSING_FAILED",
|
|
291
|
+
"The processing worker failed. Check worker/WASM permissions and try again."
|
|
292
|
+
)
|
|
293
|
+
), e.onmessageerror = () => this.reset(
|
|
294
|
+
new w(
|
|
295
|
+
"PROCESSING_FAILED",
|
|
296
|
+
"Could not read the worker response."
|
|
297
|
+
)
|
|
298
|
+
), e;
|
|
299
|
+
}
|
|
300
|
+
run(e, i, n) {
|
|
301
|
+
const f = this.generation, g = async () => {
|
|
302
|
+
if (_(i), f !== this.generation)
|
|
303
|
+
throw new DOMException("Operation cancelled", "AbortError");
|
|
304
|
+
const c = this.ensure(), r = ++this.sequence;
|
|
305
|
+
let o;
|
|
306
|
+
const h = () => this.reset(new DOMException("Operation cancelled", "AbortError"));
|
|
307
|
+
try {
|
|
308
|
+
return await new Promise((l, b) => {
|
|
309
|
+
this.pending.set(r, { resolve: l, reject: b, progress: n }), i?.addEventListener("abort", h, { once: !0 }), o = setTimeout(
|
|
310
|
+
() => this.reset(
|
|
311
|
+
new w(
|
|
312
|
+
"PROCESSING_FAILED",
|
|
313
|
+
"Processing timed out. Try fewer or smaller pages."
|
|
314
|
+
)
|
|
315
|
+
),
|
|
316
|
+
12e4
|
|
317
|
+
);
|
|
318
|
+
try {
|
|
319
|
+
c.postMessage({ id: r, task: e });
|
|
320
|
+
} catch (U) {
|
|
321
|
+
this.pending.delete(r), b(U);
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
} finally {
|
|
325
|
+
clearTimeout(o), i?.removeEventListener("abort", h);
|
|
326
|
+
}
|
|
327
|
+
}, s = this.tail.then(g, g);
|
|
328
|
+
return this.tail = s.catch(() => {
|
|
329
|
+
}), s;
|
|
330
|
+
}
|
|
331
|
+
reset(e = new DOMException("Operation cancelled", "AbortError")) {
|
|
332
|
+
this.generation++, this.worker?.terminate(), this.worker = null;
|
|
333
|
+
for (const i of this.pending.values()) i.reject(e);
|
|
334
|
+
this.pending.clear();
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
function X(t = {}) {
|
|
338
|
+
const e = { ...M, ...t.limits }, i = { ...t.detectorOptions };
|
|
339
|
+
if (Object.values(e).some((a) => !Number.isSafeInteger(a) || a < 1))
|
|
340
|
+
throw new w(
|
|
341
|
+
"INVALID_INPUT",
|
|
342
|
+
"Safety limits must be positive integers."
|
|
343
|
+
);
|
|
344
|
+
if (i.minConfidence !== void 0 && (!Number.isFinite(i.minConfidence) || i.minConfidence < 0.15 || i.minConfidence > 0.95) || i.maxComponents !== void 0 && (!Number.isSafeInteger(i.maxComponents) || i.maxComponents < 1 || i.maxComponents > 16) || i.maxCandidates !== void 0 && (!Number.isSafeInteger(i.maxCandidates) || i.maxCandidates < 1 || i.maxCandidates > 64))
|
|
345
|
+
throw new w(
|
|
346
|
+
"INVALID_INPUT",
|
|
347
|
+
"Detector options are outside their supported ranges."
|
|
348
|
+
);
|
|
349
|
+
if (t.detectorModule && (typeof location > "u" || new URL(t.detectorModule, location.href).origin !== location.origin))
|
|
350
|
+
throw new w(
|
|
351
|
+
"INVALID_INPUT",
|
|
352
|
+
"Detector modules must be served from the same origin."
|
|
353
|
+
);
|
|
354
|
+
const n = t.detectorModule ? new URL(t.detectorModule, location.href).href : void 0, f = new G(t.workerUrl), g = /* @__PURE__ */ new Set();
|
|
355
|
+
let s = [], c = 0, r = !1, o = null, h = {
|
|
356
|
+
pages: [],
|
|
357
|
+
status: "idle",
|
|
358
|
+
progress: 0,
|
|
359
|
+
result: null,
|
|
360
|
+
error: null
|
|
361
|
+
};
|
|
362
|
+
const l = () => {
|
|
363
|
+
if (r)
|
|
364
|
+
throw new w("DISPOSED", "This scan session has been disposed.");
|
|
365
|
+
}, b = (a) => Object.freeze({
|
|
366
|
+
id: a.id,
|
|
367
|
+
width: a.width,
|
|
368
|
+
height: a.height,
|
|
369
|
+
sourceBytes: a.sourceBytes,
|
|
370
|
+
preview: a.preview,
|
|
371
|
+
thumbnail: a.thumbnail,
|
|
372
|
+
warnings: Object.freeze([...a.warnings]),
|
|
373
|
+
detection: a.detection ? Object.freeze({ ...a.detection }) : void 0,
|
|
374
|
+
edits: Object.freeze({
|
|
375
|
+
...a.edits,
|
|
376
|
+
corners: Object.freeze(
|
|
377
|
+
T(a.edits.corners).map((d) => Object.freeze(d))
|
|
378
|
+
)
|
|
379
|
+
})
|
|
380
|
+
}), U = (a = {}) => {
|
|
381
|
+
if (!r) {
|
|
382
|
+
h = {
|
|
383
|
+
...h,
|
|
384
|
+
...a,
|
|
385
|
+
pages: Object.freeze(s.map(b))
|
|
386
|
+
};
|
|
387
|
+
for (const d of g) d();
|
|
388
|
+
}
|
|
389
|
+
}, y = () => {
|
|
390
|
+
c++, o?.abort(), o = null, U({ status: "idle", progress: 0, result: null, error: null });
|
|
391
|
+
}, v = (a) => {
|
|
392
|
+
const d = s.find((m) => m.id === a);
|
|
393
|
+
if (!d) throw new w("INVALID_INPUT", "This page no longer exists.");
|
|
394
|
+
return d;
|
|
395
|
+
};
|
|
396
|
+
function S(a, d) {
|
|
397
|
+
l(), y();
|
|
398
|
+
const m = c, u = new AbortController();
|
|
399
|
+
o = u;
|
|
400
|
+
const p = () => u.abort();
|
|
401
|
+
return d?.addEventListener("abort", p, { once: !0 }), d?.aborted && u.abort(), U({ status: a }), {
|
|
402
|
+
token: m,
|
|
403
|
+
signal: u.signal,
|
|
404
|
+
finish() {
|
|
405
|
+
d?.removeEventListener("abort", p), o === u && (o = null), !r && c === m && U({ status: "idle" });
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
function N(a, d) {
|
|
410
|
+
if (_(d), r || a !== c)
|
|
411
|
+
throw new DOMException("Operation cancelled", "AbortError");
|
|
412
|
+
}
|
|
413
|
+
return {
|
|
414
|
+
getSnapshot: () => h,
|
|
415
|
+
subscribe(a) {
|
|
416
|
+
return l(), g.add(a), () => {
|
|
417
|
+
g.delete(a);
|
|
418
|
+
};
|
|
419
|
+
},
|
|
420
|
+
async addFiles(a, { signal: d, replacePageId: m } = {}) {
|
|
421
|
+
const u = Array.from(a);
|
|
422
|
+
if (l(), !u.length) return [];
|
|
423
|
+
const p = m ? v(m) : void 0;
|
|
424
|
+
if (p && u.length !== 1)
|
|
425
|
+
throw new w("INVALID_INPUT", "Select one replacement photo.");
|
|
426
|
+
if (u.length + s.length - (p ? 1 : 0) > e.maxPages)
|
|
427
|
+
throw new w(
|
|
428
|
+
"LIMIT_EXCEEDED",
|
|
429
|
+
`The session allows at most ${e.maxPages} pages.`
|
|
430
|
+
);
|
|
431
|
+
if (u.some((I) => !(I instanceof Blob)))
|
|
432
|
+
throw new w("INVALID_INPUT", "Expected image files or blobs.");
|
|
433
|
+
if (u.reduce((I, D) => I + D.size, 0) + s.reduce((I, D) => I + D.sourceBytes, 0) - (p?.sourceBytes ?? 0) > e.maxSessionBytes)
|
|
434
|
+
throw new w(
|
|
435
|
+
"LIMIT_EXCEEDED",
|
|
436
|
+
"These photos exceed the session input limit. Choose fewer or smaller photos."
|
|
437
|
+
);
|
|
438
|
+
const E = S("importing", d), k = [];
|
|
439
|
+
try {
|
|
440
|
+
for (const I of u) {
|
|
441
|
+
N(E.token, E.signal);
|
|
442
|
+
const D = await F(I, e);
|
|
443
|
+
N(E.token, E.signal);
|
|
444
|
+
const x = await f.run(
|
|
445
|
+
{
|
|
446
|
+
kind: "analyze",
|
|
447
|
+
blob: I,
|
|
448
|
+
header: D,
|
|
449
|
+
options: {
|
|
450
|
+
detector: t.detector,
|
|
451
|
+
detectorModule: n,
|
|
452
|
+
detectorOptions: i
|
|
453
|
+
}
|
|
454
|
+
},
|
|
455
|
+
E.signal
|
|
456
|
+
);
|
|
457
|
+
N(E.token, E.signal), R(x.corners);
|
|
458
|
+
const O = {
|
|
459
|
+
id: p?.id ?? crypto.randomUUID(),
|
|
460
|
+
source: I,
|
|
461
|
+
sourceBytes: I.size,
|
|
462
|
+
width: x.width,
|
|
463
|
+
height: x.height,
|
|
464
|
+
preview: x.preview,
|
|
465
|
+
thumbnail: x.thumbnail,
|
|
466
|
+
warnings: x.warnings,
|
|
467
|
+
detection: x.detection,
|
|
468
|
+
edits: {
|
|
469
|
+
corners: T(x.corners),
|
|
470
|
+
rotation: 0,
|
|
471
|
+
filter: "natural"
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
s = p ? s.map((L) => L.id === p.id ? O : L) : [...s, O], k.push(b(O)), U({ progress: k.length / u.length });
|
|
475
|
+
}
|
|
476
|
+
return k;
|
|
477
|
+
} catch (I) {
|
|
478
|
+
if (I instanceof Error && I.name === "AbortError")
|
|
479
|
+
return k;
|
|
480
|
+
const D = C(I);
|
|
481
|
+
throw E.token === c && U({ error: D }), D;
|
|
482
|
+
} finally {
|
|
483
|
+
E.finish();
|
|
484
|
+
}
|
|
485
|
+
},
|
|
486
|
+
updatePage(a, d) {
|
|
487
|
+
l();
|
|
488
|
+
const m = v(a), u = { ...m.edits, ...d };
|
|
489
|
+
if (R(u.corners), ![0, 90, 180, 270].includes(u.rotation) || !["natural", "grayscale", "contrast"].includes(u.filter))
|
|
490
|
+
throw new w("INVALID_INPUT", "Invalid page edits.");
|
|
491
|
+
y(), s = s.map(
|
|
492
|
+
(p) => p.id === a ? { ...p, edits: { ...u, corners: T(u.corners) } } : p
|
|
493
|
+
), U();
|
|
494
|
+
},
|
|
495
|
+
movePage(a, d) {
|
|
496
|
+
l();
|
|
497
|
+
const m = v(a);
|
|
498
|
+
if (!Number.isInteger(d) || d < 0 || d >= s.length)
|
|
499
|
+
throw new w("INVALID_INPUT", "Invalid page position.");
|
|
500
|
+
y(), s = s.filter((u) => u.id !== a), s.splice(d, 0, m), U();
|
|
501
|
+
},
|
|
502
|
+
removePage(a) {
|
|
503
|
+
l(), v(a), y(), s = s.filter((d) => d.id !== a), U();
|
|
504
|
+
},
|
|
505
|
+
async renderPage(a, d) {
|
|
506
|
+
l();
|
|
507
|
+
const m = v(a), u = c, p = await f.run({ kind: "render", page: m }, d);
|
|
508
|
+
return N(u, d), p;
|
|
509
|
+
},
|
|
510
|
+
async exportPdf(a) {
|
|
511
|
+
if (l(), !a || typeof a != "object") throw new w("INVALID_INPUT", "Provide export options with an explicit maxBytes limit.");
|
|
512
|
+
if (!s.length)
|
|
513
|
+
throw new w(
|
|
514
|
+
"INVALID_INPUT",
|
|
515
|
+
"Add at least one page before exporting."
|
|
516
|
+
);
|
|
517
|
+
const d = S("exporting", a.signal);
|
|
518
|
+
try {
|
|
519
|
+
const { signal: m, ...u } = a, p = await f.run(
|
|
520
|
+
{ kind: "export", pages: [...s], options: u },
|
|
521
|
+
d.signal,
|
|
522
|
+
(I) => {
|
|
523
|
+
d.token === c && U({ progress: I });
|
|
524
|
+
}
|
|
525
|
+
);
|
|
526
|
+
N(d.token, d.signal);
|
|
527
|
+
const k = p.blob.size <= a.maxBytes ? {
|
|
528
|
+
status: "ready",
|
|
529
|
+
file: new File([p.blob], "document.pdf", {
|
|
530
|
+
type: "application/pdf"
|
|
531
|
+
}),
|
|
532
|
+
report: { ...p.report, bytes: p.blob.size }
|
|
533
|
+
} : {
|
|
534
|
+
status: "cannot-fit",
|
|
535
|
+
candidateBytes: p.blob.size,
|
|
536
|
+
report: p.report
|
|
537
|
+
};
|
|
538
|
+
return U({ result: k, progress: 1 }), k;
|
|
539
|
+
} catch (m) {
|
|
540
|
+
if (m instanceof Error && m.name === "AbortError")
|
|
541
|
+
return { status: "cancelled" };
|
|
542
|
+
const u = C(m);
|
|
543
|
+
throw d.token === c && U({ error: u }), u;
|
|
544
|
+
} finally {
|
|
545
|
+
d.finish();
|
|
546
|
+
}
|
|
547
|
+
},
|
|
548
|
+
cancel() {
|
|
549
|
+
l(), y(), f.reset();
|
|
550
|
+
},
|
|
551
|
+
dispose() {
|
|
552
|
+
r || (o?.abort(), r = !0, c++, f.reset(), s = [], h = {
|
|
553
|
+
pages: [],
|
|
554
|
+
status: "idle",
|
|
555
|
+
progress: 0,
|
|
556
|
+
result: null,
|
|
557
|
+
error: null
|
|
558
|
+
}, g.clear());
|
|
559
|
+
}
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
export {
|
|
563
|
+
M as DEFAULT_LIMITS,
|
|
564
|
+
K as FULL_QUAD,
|
|
565
|
+
w as ScanError,
|
|
566
|
+
_ as abortIfNeeded,
|
|
567
|
+
C as asScanError,
|
|
568
|
+
T as copyQuad,
|
|
569
|
+
X as createScanSession,
|
|
570
|
+
z as inspectImageBytes,
|
|
571
|
+
R as validateQuad
|
|
572
|
+
};
|