@embedpdf/plugin-signature 2.15.0 → 3.0.0-next.13
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 +192 -21
- package/README.md +38 -0
- package/dist/contract-BvHa9Zl_.d.cts +176 -0
- package/dist/contract-C3nQtwGf.js +14 -0
- package/dist/contract-C3nQtwGf.js.map +1 -0
- package/dist/contract-CojqGdMH.cjs +43 -0
- package/dist/contract-CojqGdMH.cjs.map +1 -0
- package/dist/contract-Dg50puf7.d.ts +176 -0
- package/dist/contract.cjs +7 -0
- package/dist/contract.d.cts +2 -0
- package/dist/contract.d.ts +2 -0
- package/dist/contract.js +2 -0
- package/dist/index.cjs +498 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +35 -0
- package/dist/index.d.ts +35 -1
- package/dist/index.js +450 -511
- package/dist/index.js.map +1 -1
- package/package.json +28 -58
- package/dist/lib/actions.d.ts +0 -14
- package/dist/lib/handlers/signature-ink.handler.d.ts +0 -4
- package/dist/lib/handlers/signature-stamp.handler.d.ts +0 -4
- package/dist/lib/index.d.ts +0 -12
- package/dist/lib/manifest.d.ts +0 -4
- package/dist/lib/reducer.d.ts +0 -5
- package/dist/lib/serialization.d.ts +0 -30
- package/dist/lib/signature-plugin.d.ts +0 -27
- package/dist/lib/tools.d.ts +0 -113
- package/dist/lib/types.d.ts +0 -114
- package/dist/preact/adapter.d.ts +0 -5
- package/dist/preact/annotation.d.ts +0 -1
- package/dist/preact/core.d.ts +0 -1
- package/dist/preact/index.cjs +0 -2
- package/dist/preact/index.cjs.map +0 -1
- package/dist/preact/index.d.ts +0 -1
- package/dist/preact/index.js +0 -464
- package/dist/preact/index.js.map +0 -1
- package/dist/react/adapter.d.ts +0 -2
- package/dist/react/annotation.d.ts +0 -1
- package/dist/react/core.d.ts +0 -1
- package/dist/react/index.cjs +0 -2
- package/dist/react/index.cjs.map +0 -1
- package/dist/react/index.d.ts +0 -1
- package/dist/react/index.js +0 -463
- package/dist/react/index.js.map +0 -1
- package/dist/shared/components/crop-canvas.d.ts +0 -14
- package/dist/shared/components/index.d.ts +0 -2
- package/dist/shared/components/signature-draw-pad.d.ts +0 -12
- package/dist/shared/components/signature-type-pad.d.ts +0 -14
- package/dist/shared/hooks/index.d.ts +0 -2
- package/dist/shared/hooks/use-signature-upload.d.ts +0 -21
- package/dist/shared/hooks/use-signature.d.ts +0 -16
- package/dist/shared/index.d.ts +0 -4
- package/dist/shared-preact/components/crop-canvas.d.ts +0 -14
- package/dist/shared-preact/components/index.d.ts +0 -2
- package/dist/shared-preact/components/signature-draw-pad.d.ts +0 -12
- package/dist/shared-preact/components/signature-type-pad.d.ts +0 -14
- package/dist/shared-preact/hooks/index.d.ts +0 -2
- package/dist/shared-preact/hooks/use-signature-upload.d.ts +0 -21
- package/dist/shared-preact/hooks/use-signature.d.ts +0 -16
- package/dist/shared-preact/index.d.ts +0 -4
- package/dist/shared-react/components/crop-canvas.d.ts +0 -14
- package/dist/shared-react/components/index.d.ts +0 -2
- package/dist/shared-react/components/signature-draw-pad.d.ts +0 -12
- package/dist/shared-react/components/signature-type-pad.d.ts +0 -14
- package/dist/shared-react/hooks/index.d.ts +0 -2
- package/dist/shared-react/hooks/use-signature-upload.d.ts +0 -21
- package/dist/shared-react/hooks/use-signature.d.ts +0 -16
- package/dist/shared-react/index.d.ts +0 -4
- package/dist/shared-vue/components/crop-canvas.d.ts +0 -14
- package/dist/svelte/components/SignatureDrawPad.svelte.d.ts +0 -12
- package/dist/svelte/components/SignatureTypePad.svelte.d.ts +0 -14
- package/dist/svelte/components/index.d.ts +0 -2
- package/dist/svelte/hooks/index.d.ts +0 -2
- package/dist/svelte/hooks/use-signature-upload.svelte.d.ts +0 -21
- package/dist/svelte/hooks/use-signature.svelte.d.ts +0 -17
- package/dist/svelte/index.cjs +0 -2
- package/dist/svelte/index.cjs.map +0 -1
- package/dist/svelte/index.d.ts +0 -4
- package/dist/svelte/index.js +0 -415
- package/dist/svelte/index.js.map +0 -1
- package/dist/vue/components/SignatureDrawPad.vue.d.ts +0 -15
- package/dist/vue/components/SignatureTypePad.vue.d.ts +0 -19
- package/dist/vue/components/index.d.ts +0 -2
- package/dist/vue/hooks/index.d.ts +0 -2
- package/dist/vue/hooks/use-signature-upload.d.ts +0 -20
- package/dist/vue/hooks/use-signature.d.ts +0 -176
- package/dist/vue/index.cjs +0 -2
- package/dist/vue/index.cjs.map +0 -1
- package/dist/vue/index.d.ts +0 -4
- package/dist/vue/index.js +0 -453
- package/dist/vue/index.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,516 +1,455 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
};
|
|
32
|
-
const ADD_SIGNATURE_ENTRY = "SIGNATURE/ADD_ENTRY";
|
|
33
|
-
const REMOVE_SIGNATURE_ENTRY = "SIGNATURE/REMOVE_ENTRY";
|
|
34
|
-
function addSignatureEntry(id) {
|
|
35
|
-
return { type: ADD_SIGNATURE_ENTRY, payload: id };
|
|
1
|
+
import { a as markRoleOf, i as SignatureToken, n as SIGNATURES_LIBRARY_KIND, r as SIGNATURE_MARK_NAME, t as INITIALS_MARK_NAME } from "./contract-C3nQtwGf.js";
|
|
2
|
+
import { createEventHook, definePlugin } from "@embedpdf/core";
|
|
3
|
+
import { FormToken } from "@embedpdf/plugin-form/contract";
|
|
4
|
+
import { InteractionToken } from "@embedpdf/plugin-interaction/contract";
|
|
5
|
+
import { StampToken } from "@embedpdf/plugin-stamp/contract";
|
|
6
|
+
import { ANNOTATION_EDIT_PRIORITY, ARMED_STAMP_TOOL_ID, AnnotationToken } from "@embedpdf/plugin-annotation/contract";
|
|
7
|
+
import { certificateCommonName, createTestSigner, indexedDbKeyStore, memoryKeyStore, personalSigner, remoteSigner, sign, validateSignatures, webCryptoSigner } from "@embedpdf/core-signature";
|
|
8
|
+
import { resolveBinarySource } from "@embedpdf/engine-core/runtime";
|
|
9
|
+
//#region src/blank-page.ts
|
|
10
|
+
const OBJECTS = [
|
|
11
|
+
"<< /Type /Catalog /Pages 2 0 R >>",
|
|
12
|
+
"<< /Type /Pages /Kids [3 0 R] /Count 1 >>",
|
|
13
|
+
"<< /Type /Page /Parent 2 0 R /MediaBox [0 0 200 60] /Contents 4 0 R /Resources << >> >>",
|
|
14
|
+
`<< /Length 3 >>\nstream\nq Q\nendstream`
|
|
15
|
+
];
|
|
16
|
+
let cached = null;
|
|
17
|
+
function blankPagePdf() {
|
|
18
|
+
if (cached) return new Uint8Array(cached);
|
|
19
|
+
let body = "%PDF-1.7\n";
|
|
20
|
+
const offsets = [];
|
|
21
|
+
OBJECTS.forEach((object, index) => {
|
|
22
|
+
offsets.push(body.length);
|
|
23
|
+
body += `${index + 1} 0 obj\n${object}\nendobj\n`;
|
|
24
|
+
});
|
|
25
|
+
const xref = body.length;
|
|
26
|
+
body += `xref\n0 ${OBJECTS.length + 1}\n0000000000 65535 f \n`;
|
|
27
|
+
for (const offset of offsets) body += `${String(offset).padStart(10, "0")} 00000 n \n`;
|
|
28
|
+
body += `trailer\n<< /Size ${OBJECTS.length + 1} /Root 1 0 R >>\nstartxref\n${xref}\n%%EOF\n`;
|
|
29
|
+
cached = new TextEncoder().encode(body);
|
|
30
|
+
return new Uint8Array(cached);
|
|
36
31
|
}
|
|
37
|
-
|
|
38
|
-
|
|
32
|
+
//#endregion
|
|
33
|
+
//#region src/capability.ts
|
|
34
|
+
const sameRef = (a, b) => a.kind === "objectNumber" && b.kind === "objectNumber" ? a.fieldObjectNumber === b.fieldObjectNumber : a.kind === "fqn" && b.kind === "fqn" ? a.name === b.name : false;
|
|
35
|
+
const sameProtection = (a, b) => JSON.stringify(a) === JSON.stringify(b);
|
|
36
|
+
function createSignatureCapability(ctx, config = {}) {
|
|
37
|
+
const form = ctx.get(FormToken);
|
|
38
|
+
const changed = createEventHook((error) => globalThis.console?.error("[signature] listener failed:", error));
|
|
39
|
+
ctx.cleanup(() => changed.dispose());
|
|
40
|
+
const state = () => ctx.getState();
|
|
41
|
+
const requireDoc = () => {
|
|
42
|
+
const doc = ctx.doc;
|
|
43
|
+
if (!doc) throw new Error("[signature] no document bound");
|
|
44
|
+
return doc;
|
|
45
|
+
};
|
|
46
|
+
const requireSignatures = () => {
|
|
47
|
+
const doc = requireDoc();
|
|
48
|
+
if (!doc.signatures) throw new Error("[signature] this engine does not implement signatures");
|
|
49
|
+
return {
|
|
50
|
+
doc,
|
|
51
|
+
signatures: doc.signatures
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
const stamp = () => ctx.tryGet(StampToken);
|
|
55
|
+
const documentId = () => {
|
|
56
|
+
const id = ctx.documentId;
|
|
57
|
+
if (!id) throw new Error("[signature] no document id");
|
|
58
|
+
return id;
|
|
59
|
+
};
|
|
60
|
+
const mode = () => config.mode ?? (config.signer ? "sign" : "visual");
|
|
61
|
+
const resolveSigner = async () => {
|
|
62
|
+
const signer = config.signer;
|
|
63
|
+
if (!signer) throw new Error("[signature] no signer configured (mode \"sign\" needs one)");
|
|
64
|
+
return typeof signer === "function" ? signer() : signer;
|
|
65
|
+
};
|
|
66
|
+
const allows = (cap) => ctx.doc?.security.allows(cap) ?? false;
|
|
67
|
+
const refresh = async () => {
|
|
68
|
+
const doc = ctx.doc;
|
|
69
|
+
if (!doc?.signatures) {
|
|
70
|
+
ctx.dispatch({
|
|
71
|
+
type: "SNAPSHOT",
|
|
72
|
+
snapshot: null
|
|
73
|
+
});
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
const before = state().snapshot?.protection ?? null;
|
|
77
|
+
const snapshot = await doc.signatures.list();
|
|
78
|
+
ctx.dispatch({
|
|
79
|
+
type: "SNAPSHOT",
|
|
80
|
+
snapshot
|
|
81
|
+
});
|
|
82
|
+
if (!sameProtection(before, snapshot.protection)) changed.emit({
|
|
83
|
+
type: "protectionChanged",
|
|
84
|
+
protection: snapshot.protection
|
|
85
|
+
});
|
|
86
|
+
return snapshot;
|
|
87
|
+
};
|
|
88
|
+
const validate = async (opts) => {
|
|
89
|
+
const doc = ctx.doc;
|
|
90
|
+
if (!doc?.signatures) return [];
|
|
91
|
+
const verdicts = await validateSignatures(doc, {
|
|
92
|
+
trust: config.trust ?? null,
|
|
93
|
+
at: opts?.at,
|
|
94
|
+
until: opts?.until ?? "working-copy"
|
|
95
|
+
});
|
|
96
|
+
const before = state().verdicts;
|
|
97
|
+
ctx.dispatch({
|
|
98
|
+
type: "VERDICTS",
|
|
99
|
+
verdicts
|
|
100
|
+
});
|
|
101
|
+
changed.emit({
|
|
102
|
+
type: "validated",
|
|
103
|
+
verdicts
|
|
104
|
+
});
|
|
105
|
+
for (const v of verdicts) {
|
|
106
|
+
if (v.modifications.basis !== "working-copy" || v.summary !== "invalid") continue;
|
|
107
|
+
const was = before?.find((b) => b.signature.index === v.signature.index);
|
|
108
|
+
if (was && was.summary === "invalid") continue;
|
|
109
|
+
changed.emit({
|
|
110
|
+
type: "invalidating",
|
|
111
|
+
field: v.signature.field,
|
|
112
|
+
detail: v.modifications.detail ?? ""
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
return verdicts;
|
|
116
|
+
};
|
|
117
|
+
let revalidateTimer = null;
|
|
118
|
+
const revalidateSoon = () => {
|
|
119
|
+
if (!state().snapshot?.signatures.some((s) => s.signed)) return;
|
|
120
|
+
if (revalidateTimer) clearTimeout(revalidateTimer);
|
|
121
|
+
revalidateTimer = setTimeout(() => {
|
|
122
|
+
revalidateTimer = null;
|
|
123
|
+
validate().catch((error) => globalThis.console?.error("[signature] re-validation failed:", error));
|
|
124
|
+
}, 300);
|
|
125
|
+
};
|
|
126
|
+
ctx.cleanup(() => {
|
|
127
|
+
if (revalidateTimer) clearTimeout(revalidateTimer);
|
|
128
|
+
});
|
|
129
|
+
const analyze = (input) => requireSignatures().signatures.analyze(input);
|
|
130
|
+
const signatureOf = (field) => {
|
|
131
|
+
const snapshot = state().snapshot;
|
|
132
|
+
if (!snapshot) return null;
|
|
133
|
+
if ("annotObjectNumber" in field) {
|
|
134
|
+
const byWidget = snapshot.signatures.find((s) => s.widget?.annotObjectNumber === field.annotObjectNumber);
|
|
135
|
+
if (byWidget) return byWidget;
|
|
136
|
+
const owner = form.fieldForWidget(field.annotObjectNumber);
|
|
137
|
+
return owner ? signatureOf(owner.ref) : null;
|
|
138
|
+
}
|
|
139
|
+
return snapshot.signatures.find((s) => field.kind === "fqn" ? s.fieldName === field.name : sameRef(s.field, field)) ?? null;
|
|
140
|
+
};
|
|
141
|
+
const verdictOf = (field) => {
|
|
142
|
+
const signature = signatureOf(field);
|
|
143
|
+
if (!signature) return null;
|
|
144
|
+
return state().verdicts?.find((v) => v.signature.index === signature.index) ?? null;
|
|
145
|
+
};
|
|
146
|
+
const bytesOf = async (source) => new Uint8Array((await resolveBinarySource(source)).bytes);
|
|
147
|
+
const markBytes = async (mark) => {
|
|
148
|
+
if ("assetId" in mark) {
|
|
149
|
+
const bytes = stamp()?.assetBytes(mark.assetId);
|
|
150
|
+
if (!bytes) throw new Error(`[signature] unknown mark '${mark.assetId}'`);
|
|
151
|
+
return bytes;
|
|
152
|
+
}
|
|
153
|
+
return bytesOf(mark.source);
|
|
154
|
+
};
|
|
155
|
+
const withBusy = async (work) => {
|
|
156
|
+
ctx.dispatch({
|
|
157
|
+
type: "BUSY",
|
|
158
|
+
busy: true
|
|
159
|
+
});
|
|
160
|
+
try {
|
|
161
|
+
return await work();
|
|
162
|
+
} finally {
|
|
163
|
+
ctx.dispatch({
|
|
164
|
+
type: "BUSY",
|
|
165
|
+
busy: false
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
const signField = (input) => withBusy(async () => {
|
|
170
|
+
const { doc } = requireSignatures();
|
|
171
|
+
const signer = await resolveSigner();
|
|
172
|
+
const subject = signer.kind === "raw" && signer.certificateChain[0] ? certificateCommonName(signer.certificateChain[0]) : null;
|
|
173
|
+
const attribution = {
|
|
174
|
+
...subject ? { name: subject } : {},
|
|
175
|
+
...input.attribution
|
|
176
|
+
};
|
|
177
|
+
const appearance = input.appearance ? await bytesOf(input.appearance) : await markBytes(input.mark);
|
|
178
|
+
const result = await sign(doc, {
|
|
179
|
+
field: input.field,
|
|
180
|
+
signer,
|
|
181
|
+
attribution,
|
|
182
|
+
certify: input.certify,
|
|
183
|
+
lock: input.lock,
|
|
184
|
+
appearance: {
|
|
185
|
+
pdf: appearance,
|
|
186
|
+
pageIndex: 0
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
if (state().target && sameRef(state().target, input.field)) setTarget(null);
|
|
190
|
+
await refresh();
|
|
191
|
+
validate().catch((error) => globalThis.console?.error("[signature] validation after signing failed:", error));
|
|
192
|
+
changed.emit({
|
|
193
|
+
type: "signed",
|
|
194
|
+
field: input.field,
|
|
195
|
+
result
|
|
196
|
+
});
|
|
197
|
+
return result;
|
|
198
|
+
});
|
|
199
|
+
const setAppearance = async (field, pdf) => {
|
|
200
|
+
const doc = requireDoc();
|
|
201
|
+
if (!doc.forms.setSignatureAppearance) throw new Error("[signature] this engine cannot draw into a signature field");
|
|
202
|
+
const signature = signatureOf(field);
|
|
203
|
+
if (signature?.signed) throw new Error(`[signature] '${signature.fieldName}' is signed`);
|
|
204
|
+
await doc.forms.setSignatureAppearance(field, {
|
|
205
|
+
pdf,
|
|
206
|
+
pageIndex: 0
|
|
207
|
+
});
|
|
208
|
+
await form.refresh();
|
|
209
|
+
};
|
|
210
|
+
const fillField = (field, mark) => withBusy(async () => {
|
|
211
|
+
await setAppearance(field, await markBytes(mark));
|
|
212
|
+
if (state().target && sameRef(state().target, field)) setTarget(null);
|
|
213
|
+
changed.emit({
|
|
214
|
+
type: "filled",
|
|
215
|
+
field
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
const clearField = (field) => withBusy(async () => {
|
|
219
|
+
await setAppearance(field, blankPagePdf());
|
|
220
|
+
changed.emit({
|
|
221
|
+
type: "cleared",
|
|
222
|
+
field
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
const placeMark = async (mark, target) => {
|
|
226
|
+
if (!("field" in target)) {
|
|
227
|
+
if ("assetId" in mark) {
|
|
228
|
+
const library = stamp();
|
|
229
|
+
if (!library) throw new Error("[signature] no stamp plugin to place an asset");
|
|
230
|
+
await library.placeAsset(documentId(), mark.assetId, target);
|
|
231
|
+
} else await ctx.get(AnnotationToken).placeStamp({ source: mark.source }, target);
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
switch (mode()) {
|
|
235
|
+
case "sign":
|
|
236
|
+
if (config.allowCertify && !state().snapshot?.signatures.some((s) => s.signed)) {
|
|
237
|
+
changed.emit({
|
|
238
|
+
type: "ask",
|
|
239
|
+
field: target.field,
|
|
240
|
+
mark
|
|
241
|
+
});
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
await signField({
|
|
245
|
+
field: target.field,
|
|
246
|
+
mark
|
|
247
|
+
});
|
|
248
|
+
return;
|
|
249
|
+
case "visual":
|
|
250
|
+
await fillField(target.field, mark);
|
|
251
|
+
return;
|
|
252
|
+
case "ask":
|
|
253
|
+
changed.emit({
|
|
254
|
+
type: "ask",
|
|
255
|
+
field: target.field,
|
|
256
|
+
mark
|
|
257
|
+
});
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
const setTarget = (field) => {
|
|
262
|
+
const current = state().target;
|
|
263
|
+
if (current === field || current && field && sameRef(current, field)) return;
|
|
264
|
+
ctx.dispatch({
|
|
265
|
+
type: "TARGET",
|
|
266
|
+
field
|
|
267
|
+
});
|
|
268
|
+
changed.emit({
|
|
269
|
+
type: "target",
|
|
270
|
+
field
|
|
271
|
+
});
|
|
272
|
+
};
|
|
273
|
+
const doc = ctx.doc;
|
|
274
|
+
if (doc) {
|
|
275
|
+
const unsubscribe = doc.events.subscribe((event) => {
|
|
276
|
+
switch (event.type) {
|
|
277
|
+
case "signature.prepared":
|
|
278
|
+
ctx.dispatch({
|
|
279
|
+
type: "PENDING",
|
|
280
|
+
pending: {
|
|
281
|
+
signingId: event.signingId,
|
|
282
|
+
field: event.field
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
return;
|
|
286
|
+
case "signature.aborted":
|
|
287
|
+
ctx.dispatch({
|
|
288
|
+
type: "PENDING",
|
|
289
|
+
pending: null
|
|
290
|
+
});
|
|
291
|
+
return;
|
|
292
|
+
case "signature.completed":
|
|
293
|
+
ctx.dispatch({
|
|
294
|
+
type: "PENDING",
|
|
295
|
+
pending: null
|
|
296
|
+
});
|
|
297
|
+
return;
|
|
298
|
+
case "document.versioned":
|
|
299
|
+
case "stream.desynced":
|
|
300
|
+
refresh().then(() => validate()).catch((error) => globalThis.console?.error("[signature] refresh failed:", error));
|
|
301
|
+
return;
|
|
302
|
+
case "form.fieldCreated":
|
|
303
|
+
case "form.fieldDeleted":
|
|
304
|
+
case "form.widgetAttached":
|
|
305
|
+
case "form.widgetDetached":
|
|
306
|
+
case "form.imported":
|
|
307
|
+
case "form.repaired":
|
|
308
|
+
refresh().then(() => revalidateSoon()).catch((error) => globalThis.console?.error("[signature] refresh failed:", error));
|
|
309
|
+
return;
|
|
310
|
+
case "annotation.created":
|
|
311
|
+
case "annotation.updated":
|
|
312
|
+
case "annotation.deleted":
|
|
313
|
+
case "annotation.moved":
|
|
314
|
+
case "form.valueChanged":
|
|
315
|
+
case "form.fieldUpdated":
|
|
316
|
+
case "form.effectsApplied":
|
|
317
|
+
revalidateSoon();
|
|
318
|
+
return;
|
|
319
|
+
default: return;
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
ctx.cleanup(unsubscribe);
|
|
323
|
+
}
|
|
324
|
+
return {
|
|
325
|
+
snapshot: () => state().snapshot,
|
|
326
|
+
verdicts: () => state().verdicts,
|
|
327
|
+
protection: () => state().snapshot?.protection ?? null,
|
|
328
|
+
pending: () => state().pending,
|
|
329
|
+
target: () => state().target,
|
|
330
|
+
busy: () => state().busy,
|
|
331
|
+
mode,
|
|
332
|
+
signatureOf,
|
|
333
|
+
verdictOf,
|
|
334
|
+
canSign: () => allows("doc.sign") && config.signer != null,
|
|
335
|
+
canFill: () => allows("doc.forms.fill") && ctx.doc?.forms.setSignatureAppearance != null,
|
|
336
|
+
canCertify: () => config.allowCertify === true && allows("doc.sign.certify"),
|
|
337
|
+
signField,
|
|
338
|
+
fillField,
|
|
339
|
+
clearField,
|
|
340
|
+
placeMark,
|
|
341
|
+
setTarget,
|
|
342
|
+
inspect: (field) => changed.emit({
|
|
343
|
+
type: "inspect",
|
|
344
|
+
field
|
|
345
|
+
}),
|
|
346
|
+
refresh,
|
|
347
|
+
validate,
|
|
348
|
+
analyze,
|
|
349
|
+
revisionBytes: (revisionIndex) => requireSignatures().signatures.revisionBytes(revisionIndex),
|
|
350
|
+
onChanged: changed.on
|
|
351
|
+
};
|
|
39
352
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const { width, height } = ctx.targetSize;
|
|
71
|
-
const rect = {
|
|
72
|
-
origin: { x: pos.x - width / 2, y: pos.y - height / 2 },
|
|
73
|
-
size: { width, height }
|
|
74
|
-
};
|
|
75
|
-
let anno = {
|
|
76
|
-
...tool.defaults,
|
|
77
|
-
rect,
|
|
78
|
-
type: PdfAnnotationSubtype.STAMP,
|
|
79
|
-
name: PdfAnnotationName.Custom,
|
|
80
|
-
subject: "Signature",
|
|
81
|
-
flags: tool.defaults.flags ?? ["print"],
|
|
82
|
-
pageIndex: context.pageIndex,
|
|
83
|
-
id: uuidV4(),
|
|
84
|
-
created: /* @__PURE__ */ new Date()
|
|
85
|
-
};
|
|
86
|
-
if ((_a = tool.behavior) == null ? void 0 : _a.insertUpright) {
|
|
87
|
-
anno = applyInsertUpright(anno, pageRotation, false);
|
|
88
|
-
}
|
|
89
|
-
anno = clampAnnotationToPage(anno, pageSize);
|
|
90
|
-
onCommit(anno, { data: ctx.imageData });
|
|
91
|
-
onPreview(null);
|
|
92
|
-
},
|
|
93
|
-
onPointerLeave: () => {
|
|
94
|
-
onPreview(null);
|
|
95
|
-
}
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
};
|
|
99
|
-
function translateAndScaleInk(inkData, targetSize, centerPos) {
|
|
100
|
-
const scaleX = targetSize.width / inkData.size.width;
|
|
101
|
-
const scaleY = targetSize.height / inkData.size.height;
|
|
102
|
-
const scale = Math.min(scaleX, scaleY);
|
|
103
|
-
const scaledW = inkData.size.width * scale;
|
|
104
|
-
const scaledH = inkData.size.height * scale;
|
|
105
|
-
const offsetX = centerPos.x - scaledW / 2;
|
|
106
|
-
const offsetY = centerPos.y - scaledH / 2;
|
|
107
|
-
const inkList = inkData.inkList.map((stroke) => ({
|
|
108
|
-
points: stroke.points.map((p) => ({
|
|
109
|
-
x: p.x * scale + offsetX,
|
|
110
|
-
y: p.y * scale + offsetY
|
|
111
|
-
}))
|
|
112
|
-
}));
|
|
113
|
-
return { inkList, strokeWidth: Math.round(inkData.strokeWidth * scale * 10) / 10 };
|
|
353
|
+
//#endregion
|
|
354
|
+
//#region src/handler.ts
|
|
355
|
+
/**
|
|
356
|
+
* The armed mark over a field. The annotation plugin places an armed stamp
|
|
357
|
+
* on click (`annotation-place`, priority 95) and selects an existing
|
|
358
|
+
* annotation under the pointer before that (`annotation-edit`, 100 — a
|
|
359
|
+
* widget is an annotation). This handler sits above BOTH, enabled for the
|
|
360
|
+
* armed stamp's tool only, and captures exactly when the armed asset is a
|
|
361
|
+
* person's mark (a library of kind `signatures`) and the pointer is over a
|
|
362
|
+
* signature field: then the destination decides — the mark goes INTO the
|
|
363
|
+
* field (`placeMark` by mode) instead of onto the page. Everywhere else it
|
|
364
|
+
* declines and the click stays a stamp placement, as in Preview.
|
|
365
|
+
*/
|
|
366
|
+
function createArmedMarkHandler(documentId, signature, form, stamp) {
|
|
367
|
+
return {
|
|
368
|
+
id: "signature:armed-mark-on-field",
|
|
369
|
+
priority: ANNOTATION_EDIT_PRIORITY + 1,
|
|
370
|
+
enabledFor: (tool) => tool.id === ARMED_STAMP_TOOL_ID,
|
|
371
|
+
onDown: (sample) => {
|
|
372
|
+
if (!sample.page) return false;
|
|
373
|
+
const armed = stamp.armedAsset(documentId);
|
|
374
|
+
if (!armed || stamp.library(armed.libraryId)?.kind !== "signatures") return false;
|
|
375
|
+
const hit = form.widgetAt(sample.page.pon, sample.page.point);
|
|
376
|
+
if (!hit || hit.field.family !== "signature") return false;
|
|
377
|
+
if (signature.signatureOf(hit.field.ref)?.signed || hit.field.valueEntry.kind !== "none") return true;
|
|
378
|
+
stamp.disarm(documentId);
|
|
379
|
+
signature.placeMark({ assetId: armed.id }, { field: hit.field.ref }).catch((error) => globalThis.console?.error("[signature] placing the mark failed:", error));
|
|
380
|
+
return true;
|
|
381
|
+
}
|
|
382
|
+
};
|
|
114
383
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
const { inkList, strokeWidth } = translateAndScaleInk(ctx.inkData, ctx.targetSize, pos);
|
|
124
|
-
const allPoints = inkList.flatMap((s) => s.points);
|
|
125
|
-
if (allPoints.length === 0) return;
|
|
126
|
-
const rect = expandRect(
|
|
127
|
-
{
|
|
128
|
-
origin: {
|
|
129
|
-
x: Math.min(...allPoints.map((p) => p.x)),
|
|
130
|
-
y: Math.min(...allPoints.map((p) => p.y))
|
|
131
|
-
},
|
|
132
|
-
size: {
|
|
133
|
-
width: Math.max(...allPoints.map((p) => p.x)) - Math.min(...allPoints.map((p) => p.x)),
|
|
134
|
-
height: Math.max(...allPoints.map((p) => p.y)) - Math.min(...allPoints.map((p) => p.y))
|
|
135
|
-
}
|
|
136
|
-
},
|
|
137
|
-
strokeWidth / 2
|
|
138
|
-
);
|
|
139
|
-
onPreview({
|
|
140
|
-
type: PdfAnnotationSubtype.INK,
|
|
141
|
-
bounds: rect,
|
|
142
|
-
data: {
|
|
143
|
-
rect,
|
|
144
|
-
inkList,
|
|
145
|
-
strokeWidth,
|
|
146
|
-
strokeColor: ctx.inkData.strokeColor,
|
|
147
|
-
opacity: 0.5
|
|
148
|
-
}
|
|
149
|
-
});
|
|
150
|
-
},
|
|
151
|
-
onPointerDown: (pos) => {
|
|
152
|
-
const tool = getTool();
|
|
153
|
-
const ctx = getToolContext();
|
|
154
|
-
if (!tool || !(ctx == null ? void 0 : ctx.inkData) || !(ctx == null ? void 0 : ctx.targetSize)) return;
|
|
155
|
-
const { inkList, strokeWidth } = translateAndScaleInk(ctx.inkData, ctx.targetSize, pos);
|
|
156
|
-
const allPoints = inkList.flatMap((s) => s.points);
|
|
157
|
-
if (allPoints.length === 0) return;
|
|
158
|
-
const rect = expandRect(
|
|
159
|
-
{
|
|
160
|
-
origin: {
|
|
161
|
-
x: Math.min(...allPoints.map((p) => p.x)),
|
|
162
|
-
y: Math.min(...allPoints.map((p) => p.y))
|
|
163
|
-
},
|
|
164
|
-
size: {
|
|
165
|
-
width: Math.max(...allPoints.map((p) => p.x)) - Math.min(...allPoints.map((p) => p.x)),
|
|
166
|
-
height: Math.max(...allPoints.map((p) => p.y)) - Math.min(...allPoints.map((p) => p.y))
|
|
167
|
-
}
|
|
168
|
-
},
|
|
169
|
-
strokeWidth / 2
|
|
170
|
-
);
|
|
171
|
-
let anno = {
|
|
172
|
-
...tool.defaults,
|
|
173
|
-
inkList,
|
|
174
|
-
rect,
|
|
175
|
-
strokeWidth,
|
|
176
|
-
strokeColor: ctx.inkData.strokeColor,
|
|
177
|
-
opacity: 1,
|
|
178
|
-
type: PdfAnnotationSubtype.INK,
|
|
179
|
-
subject: "Signature",
|
|
180
|
-
flags: tool.defaults.flags ?? ["print"],
|
|
181
|
-
pageIndex: context.pageIndex,
|
|
182
|
-
id: uuidV4(),
|
|
183
|
-
created: /* @__PURE__ */ new Date()
|
|
184
|
-
};
|
|
185
|
-
const unclamped = anno.rect;
|
|
186
|
-
anno = clampAnnotationToPage(anno, pageSize);
|
|
187
|
-
const dx = anno.rect.origin.x - unclamped.origin.x;
|
|
188
|
-
const dy = anno.rect.origin.y - unclamped.origin.y;
|
|
189
|
-
if (dx !== 0 || dy !== 0) {
|
|
190
|
-
anno = {
|
|
191
|
-
...anno,
|
|
192
|
-
inkList: anno.inkList.map((s) => ({
|
|
193
|
-
points: s.points.map((p) => ({ x: p.x + dx, y: p.y + dy }))
|
|
194
|
-
}))
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
onCommit(anno);
|
|
198
|
-
onPreview(null);
|
|
199
|
-
},
|
|
200
|
-
onPointerLeave: () => {
|
|
201
|
-
onPreview(null);
|
|
202
|
-
}
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
|
-
};
|
|
206
|
-
const SIGNATURE_STAMP_TOOL_ID = "signatureStamp";
|
|
207
|
-
const SIGNATURE_INK_TOOL_ID = "signatureInk";
|
|
208
|
-
const signatureStampTool = defineAnnotationTool({
|
|
209
|
-
id: SIGNATURE_STAMP_TOOL_ID,
|
|
210
|
-
name: "Signature Stamp",
|
|
211
|
-
labelKey: "signature.stamp",
|
|
212
|
-
categories: ["annotation", "signature", "insert"],
|
|
213
|
-
matchScore: () => 0,
|
|
214
|
-
interaction: {
|
|
215
|
-
exclusive: true,
|
|
216
|
-
cursor: "copy",
|
|
217
|
-
isDraggable: true,
|
|
218
|
-
isResizable: true,
|
|
219
|
-
isRotatable: false,
|
|
220
|
-
lockAspectRatio: true,
|
|
221
|
-
isGroupDraggable: true,
|
|
222
|
-
isGroupResizable: false,
|
|
223
|
-
isGroupRotatable: false
|
|
224
|
-
},
|
|
225
|
-
defaults: {
|
|
226
|
-
type: PdfAnnotationSubtype.STAMP
|
|
227
|
-
},
|
|
228
|
-
behavior: {
|
|
229
|
-
deactivateToolAfterCreate: true,
|
|
230
|
-
selectAfterCreate: true,
|
|
231
|
-
insertUpright: true,
|
|
232
|
-
useAppearanceStream: true
|
|
233
|
-
},
|
|
234
|
-
pointerHandler: signatureStampHandlerFactory
|
|
384
|
+
//#endregion
|
|
385
|
+
//#region src/reducer.ts
|
|
386
|
+
const initialSignatureState = () => ({
|
|
387
|
+
snapshot: null,
|
|
388
|
+
verdicts: null,
|
|
389
|
+
target: null,
|
|
390
|
+
pending: null,
|
|
391
|
+
busy: false
|
|
235
392
|
});
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
transform: patching.patchInk,
|
|
261
|
-
pointerHandler: signatureInkHandlerFactory
|
|
262
|
-
});
|
|
263
|
-
const signatureTools = [signatureStampTool, signatureInkTool];
|
|
264
|
-
const _SignaturePlugin = class _SignaturePlugin extends BasePlugin {
|
|
265
|
-
constructor(id, registry, config) {
|
|
266
|
-
var _a;
|
|
267
|
-
super(id, registry);
|
|
268
|
-
this.config = config;
|
|
269
|
-
this.entries = /* @__PURE__ */ new Map();
|
|
270
|
-
this.entriesChange$ = createEmitter();
|
|
271
|
-
this.activePlacement$ = createScopedEmitter((documentId, activePlacement) => ({ documentId, activePlacement }));
|
|
272
|
-
this.annotation = null;
|
|
273
|
-
this.toolChangeUnsubscribe = null;
|
|
274
|
-
this.currentGhostUrl = null;
|
|
275
|
-
this.annotation = ((_a = registry.getPlugin("annotation")) == null ? void 0 : _a.provides()) ?? null;
|
|
276
|
-
if (this.annotation) {
|
|
277
|
-
for (const tool of signatureTools) {
|
|
278
|
-
this.annotation.addTool(tool);
|
|
279
|
-
}
|
|
280
|
-
this.toolChangeUnsubscribe = this.annotation.onActiveToolChange(({ documentId, tool }) => {
|
|
281
|
-
if ((tool == null ? void 0 : tool.id) !== SIGNATURE_STAMP_TOOL_ID && (tool == null ? void 0 : tool.id) !== SIGNATURE_INK_TOOL_ID) {
|
|
282
|
-
this.revokeGhostUrl();
|
|
283
|
-
this.activePlacement$.emit(documentId, null);
|
|
284
|
-
}
|
|
285
|
-
});
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
async initialize() {
|
|
289
|
-
}
|
|
290
|
-
buildCapability() {
|
|
291
|
-
return {
|
|
292
|
-
mode: this.config.mode,
|
|
293
|
-
getEntries: () => this.getEntries(),
|
|
294
|
-
addEntry: (entry) => this.addEntry(entry),
|
|
295
|
-
removeEntry: (id) => this.removeEntry(id),
|
|
296
|
-
loadEntries: (entries) => this.loadEntries(entries),
|
|
297
|
-
exportEntries: () => this.exportEntries(),
|
|
298
|
-
onEntriesChange: this.entriesChange$.on,
|
|
299
|
-
forDocument: (documentId) => this.createSignatureScope(documentId),
|
|
300
|
-
onActivePlacementChange: this.activePlacement$.onGlobal
|
|
301
|
-
};
|
|
302
|
-
}
|
|
303
|
-
getEntries() {
|
|
304
|
-
return Array.from(this.entries.values());
|
|
305
|
-
}
|
|
306
|
-
addEntry(entry) {
|
|
307
|
-
const id = uuidV4();
|
|
308
|
-
const fullEntry = {
|
|
309
|
-
...entry,
|
|
310
|
-
id,
|
|
311
|
-
createdAt: Date.now()
|
|
312
|
-
};
|
|
313
|
-
this.entries.set(id, fullEntry);
|
|
314
|
-
this.dispatch(addSignatureEntry(id));
|
|
315
|
-
this.emitEntriesChange();
|
|
316
|
-
return id;
|
|
317
|
-
}
|
|
318
|
-
removeEntry(id) {
|
|
319
|
-
if (!this.entries.has(id)) return;
|
|
320
|
-
this.entries.delete(id);
|
|
321
|
-
this.dispatch(removeSignatureEntry(id));
|
|
322
|
-
this.emitEntriesChange();
|
|
323
|
-
}
|
|
324
|
-
loadEntries(entries) {
|
|
325
|
-
for (const entry of entries) {
|
|
326
|
-
this.entries.set(entry.id, entry);
|
|
327
|
-
this.dispatch(addSignatureEntry(entry.id));
|
|
328
|
-
}
|
|
329
|
-
this.emitEntriesChange();
|
|
330
|
-
}
|
|
331
|
-
exportEntries() {
|
|
332
|
-
return this.getEntries();
|
|
333
|
-
}
|
|
334
|
-
createSignatureScope(documentId) {
|
|
335
|
-
return {
|
|
336
|
-
activateSignaturePlacement: (entryId) => this.activatePlacement(documentId, entryId, SignatureFieldKind.Signature),
|
|
337
|
-
activateInitialsPlacement: (entryId) => this.activatePlacement(documentId, entryId, SignatureFieldKind.Initials),
|
|
338
|
-
deactivatePlacement: () => this.deactivatePlacement(documentId),
|
|
339
|
-
getActivePlacement: () => this.activePlacement$.getValue(documentId) ?? null,
|
|
340
|
-
onActivePlacementChange: this.activePlacement$.forScope(documentId)
|
|
341
|
-
};
|
|
342
|
-
}
|
|
343
|
-
activatePlacement(documentId, entryId, kind) {
|
|
344
|
-
const entry = this.entries.get(entryId);
|
|
345
|
-
if (!entry || !this.annotation) return;
|
|
346
|
-
const field = kind === SignatureFieldKind.Initials ? entry.initials : entry.signature;
|
|
347
|
-
if (!field) return;
|
|
348
|
-
this.revokeGhostUrl();
|
|
349
|
-
if (field.creationType === SignatureCreationType.Draw) {
|
|
350
|
-
if (entry.signature.creationType !== SignatureCreationType.Draw) return;
|
|
351
|
-
const defaultSize = this.config.defaultSize ?? { width: 150, height: 50 };
|
|
352
|
-
const referenceSize = entry.signature.inkData.size;
|
|
353
|
-
const scale = Math.min(
|
|
354
|
-
defaultSize.width / referenceSize.width,
|
|
355
|
-
defaultSize.height / referenceSize.height
|
|
356
|
-
);
|
|
357
|
-
const targetSize = {
|
|
358
|
-
width: field.inkData.size.width * scale,
|
|
359
|
-
height: field.inkData.size.height * scale
|
|
360
|
-
};
|
|
361
|
-
this.annotation.setActiveTool(SIGNATURE_INK_TOOL_ID, {
|
|
362
|
-
inkData: field.inkData,
|
|
363
|
-
targetSize,
|
|
364
|
-
entryId,
|
|
365
|
-
kind
|
|
366
|
-
});
|
|
367
|
-
} else {
|
|
368
|
-
if (!field.imageData) return;
|
|
369
|
-
const ghostUrl = URL.createObjectURL(
|
|
370
|
-
new Blob([field.imageData], { type: field.imageMimeType ?? "image/png" })
|
|
371
|
-
);
|
|
372
|
-
this.currentGhostUrl = ghostUrl;
|
|
373
|
-
const defaultSize = this.config.defaultSize ?? { width: 150, height: 50 };
|
|
374
|
-
let targetSize = defaultSize;
|
|
375
|
-
if (field.imageSize) {
|
|
376
|
-
const referenceSize = entry.signature.creationType !== SignatureCreationType.Draw ? entry.signature.imageSize ?? field.imageSize : field.imageSize;
|
|
377
|
-
const scale = Math.min(
|
|
378
|
-
defaultSize.width / referenceSize.width,
|
|
379
|
-
defaultSize.height / referenceSize.height
|
|
380
|
-
);
|
|
381
|
-
targetSize = {
|
|
382
|
-
width: field.imageSize.width * scale,
|
|
383
|
-
height: field.imageSize.height * scale
|
|
384
|
-
};
|
|
385
|
-
}
|
|
386
|
-
this.annotation.setActiveTool(SIGNATURE_STAMP_TOOL_ID, {
|
|
387
|
-
imageData: field.imageData,
|
|
388
|
-
ghostUrl,
|
|
389
|
-
targetSize,
|
|
390
|
-
entryId,
|
|
391
|
-
kind
|
|
392
|
-
});
|
|
393
|
-
}
|
|
394
|
-
this.activePlacement$.emit(documentId, { entryId, kind });
|
|
395
|
-
}
|
|
396
|
-
deactivatePlacement(documentId) {
|
|
397
|
-
if (!this.annotation) return;
|
|
398
|
-
this.annotation.setActiveTool(null);
|
|
399
|
-
this.revokeGhostUrl();
|
|
400
|
-
this.activePlacement$.emit(documentId, null);
|
|
401
|
-
}
|
|
402
|
-
revokeGhostUrl() {
|
|
403
|
-
if (this.currentGhostUrl) {
|
|
404
|
-
URL.revokeObjectURL(this.currentGhostUrl);
|
|
405
|
-
this.currentGhostUrl = null;
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
emitEntriesChange() {
|
|
409
|
-
this.entriesChange$.emit(this.getEntries());
|
|
410
|
-
}
|
|
411
|
-
async destroy() {
|
|
412
|
-
var _a;
|
|
413
|
-
this.revokeGhostUrl();
|
|
414
|
-
(_a = this.toolChangeUnsubscribe) == null ? void 0 : _a.call(this);
|
|
415
|
-
this.entries.clear();
|
|
416
|
-
}
|
|
417
|
-
};
|
|
418
|
-
_SignaturePlugin.id = SIGNATURE_PLUGIN_ID;
|
|
419
|
-
let SignaturePlugin = _SignaturePlugin;
|
|
420
|
-
const initialState = {
|
|
421
|
-
entryIds: []
|
|
422
|
-
};
|
|
423
|
-
const signatureReducer = (state = initialState, action) => {
|
|
424
|
-
switch (action.type) {
|
|
425
|
-
case ADD_SIGNATURE_ENTRY:
|
|
426
|
-
return {
|
|
427
|
-
...state,
|
|
428
|
-
entryIds: [...state.entryIds, action.payload]
|
|
429
|
-
};
|
|
430
|
-
case REMOVE_SIGNATURE_ENTRY:
|
|
431
|
-
return {
|
|
432
|
-
...state,
|
|
433
|
-
entryIds: state.entryIds.filter((id) => id !== action.payload)
|
|
434
|
-
};
|
|
435
|
-
default:
|
|
436
|
-
return state;
|
|
437
|
-
}
|
|
438
|
-
};
|
|
439
|
-
function arrayBufferToBase64(buffer) {
|
|
440
|
-
const bytes = new Uint8Array(buffer);
|
|
441
|
-
let binary = "";
|
|
442
|
-
for (let i = 0; i < bytes.length; i++) {
|
|
443
|
-
binary += String.fromCharCode(bytes[i]);
|
|
444
|
-
}
|
|
445
|
-
return btoa(binary);
|
|
446
|
-
}
|
|
447
|
-
function base64ToArrayBuffer(base64) {
|
|
448
|
-
const binary = atob(base64);
|
|
449
|
-
const bytes = new Uint8Array(binary.length);
|
|
450
|
-
for (let i = 0; i < binary.length; i++) {
|
|
451
|
-
bytes[i] = binary.charCodeAt(i);
|
|
452
|
-
}
|
|
453
|
-
return bytes.buffer;
|
|
454
|
-
}
|
|
455
|
-
function serializeField(field) {
|
|
456
|
-
if (field.creationType === SignatureCreationType.Draw) {
|
|
457
|
-
return field;
|
|
458
|
-
}
|
|
459
|
-
const { imageData, ...rest } = field;
|
|
460
|
-
return {
|
|
461
|
-
...rest,
|
|
462
|
-
imageData: imageData ? arrayBufferToBase64(imageData) : void 0
|
|
463
|
-
};
|
|
464
|
-
}
|
|
465
|
-
function deserializeField(field) {
|
|
466
|
-
if (field.creationType === SignatureCreationType.Draw) {
|
|
467
|
-
return field;
|
|
468
|
-
}
|
|
469
|
-
const { imageData, ...rest } = field;
|
|
470
|
-
return {
|
|
471
|
-
...rest,
|
|
472
|
-
imageData: imageData ? base64ToArrayBuffer(imageData) : void 0
|
|
473
|
-
};
|
|
393
|
+
function signatureReducer(state, action) {
|
|
394
|
+
switch (action.type) {
|
|
395
|
+
case "SNAPSHOT": return {
|
|
396
|
+
...state,
|
|
397
|
+
snapshot: action.snapshot
|
|
398
|
+
};
|
|
399
|
+
case "VERDICTS": return {
|
|
400
|
+
...state,
|
|
401
|
+
verdicts: action.verdicts
|
|
402
|
+
};
|
|
403
|
+
case "TARGET": return {
|
|
404
|
+
...state,
|
|
405
|
+
target: action.field
|
|
406
|
+
};
|
|
407
|
+
case "PENDING": return {
|
|
408
|
+
...state,
|
|
409
|
+
pending: action.pending
|
|
410
|
+
};
|
|
411
|
+
case "BUSY": return {
|
|
412
|
+
...state,
|
|
413
|
+
busy: action.busy
|
|
414
|
+
};
|
|
415
|
+
default: return state;
|
|
416
|
+
}
|
|
474
417
|
}
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
serializeEntries,
|
|
514
|
-
signatureReducer
|
|
515
|
-
};
|
|
516
|
-
//# sourceMappingURL=index.js.map
|
|
418
|
+
//#endregion
|
|
419
|
+
//#region src/signature.plugin.ts
|
|
420
|
+
/**
|
|
421
|
+
* The signature plugin — the ACT of signing, document-scoped. It owns no
|
|
422
|
+
* mark: marks are stamp-library assets (or bytes the embedder brings), and
|
|
423
|
+
* the mark IS the appearance the engine draws into the field. Signing goes
|
|
424
|
+
* through `@embedpdf/core-signature` with the configured signer port; the
|
|
425
|
+
* engine seals; on the cloud the server publishes the new version.
|
|
426
|
+
*
|
|
427
|
+
* With the interaction hub and the stamp plugin present, an armed mark from
|
|
428
|
+
* a `signatures` library dropped over an unsigned signature field goes into
|
|
429
|
+
* the field (sign / visual fill / ask, by mode) instead of onto the page.
|
|
430
|
+
*/
|
|
431
|
+
const signaturePlugin = (config = {}) => definePlugin({
|
|
432
|
+
id: "signature",
|
|
433
|
+
token: SignatureToken,
|
|
434
|
+
scope: "document",
|
|
435
|
+
requires: [FormToken],
|
|
436
|
+
optional: [
|
|
437
|
+
InteractionToken,
|
|
438
|
+
StampToken,
|
|
439
|
+
AnnotationToken
|
|
440
|
+
],
|
|
441
|
+
initialState: initialSignatureState,
|
|
442
|
+
reduce: signatureReducer,
|
|
443
|
+
capability: (ctx) => createSignatureCapability(ctx, config),
|
|
444
|
+
init: (ctx) => {
|
|
445
|
+
const signature = ctx.get(SignatureToken);
|
|
446
|
+
signature.refresh().then((snapshot) => snapshot?.signatures.some((s) => s.signed) ? signature.validate() : null).catch((error) => globalThis.console?.error("[signature] initial read failed:", error));
|
|
447
|
+
const interaction = ctx.tryGet(InteractionToken);
|
|
448
|
+
const stamp = ctx.tryGet(StampToken);
|
|
449
|
+
if (interaction && stamp && ctx.documentId) ctx.cleanup(interaction.registerHandler(createArmedMarkHandler(ctx.documentId, signature, ctx.get(FormToken), stamp)));
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
//#endregion
|
|
453
|
+
export { INITIALS_MARK_NAME, SIGNATURES_LIBRARY_KIND, SIGNATURE_MARK_NAME, SignatureToken, createArmedMarkHandler, createTestSigner, indexedDbKeyStore, markRoleOf, memoryKeyStore, personalSigner, remoteSigner, signaturePlugin, webCryptoSigner };
|
|
454
|
+
|
|
455
|
+
//# sourceMappingURL=index.js.map
|