@kitsy/pdf 0.0.0-stage → 0.1.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/CHANGELOG.md +22 -0
- package/LICENSE +201 -0
- package/NOTICE +16 -0
- package/README.md +229 -2
- package/THIRD_PARTY_NOTICES.md +48 -0
- package/dist/chunk-2K5X7L5J.js +43 -0
- package/dist/chunk-2K5X7L5J.js.map +1 -0
- package/dist/chunk-4EAYBJ6G.js +362 -0
- package/dist/chunk-4EAYBJ6G.js.map +1 -0
- package/dist/chunk-4LBJTDV5.js +165 -0
- package/dist/chunk-4LBJTDV5.js.map +1 -0
- package/dist/chunk-4USCGC24.js +304 -0
- package/dist/chunk-4USCGC24.js.map +1 -0
- package/dist/chunk-5OIMR74G.js +273 -0
- package/dist/chunk-5OIMR74G.js.map +1 -0
- package/dist/chunk-6NFNAKWA.js +257 -0
- package/dist/chunk-6NFNAKWA.js.map +1 -0
- package/dist/chunk-7MTEMQLL.js +47 -0
- package/dist/chunk-7MTEMQLL.js.map +1 -0
- package/dist/chunk-7V4SWTUK.js +83 -0
- package/dist/chunk-7V4SWTUK.js.map +1 -0
- package/dist/chunk-7XH7MFZ4.js +295 -0
- package/dist/chunk-7XH7MFZ4.js.map +1 -0
- package/dist/chunk-BK3MYSUS.js +61 -0
- package/dist/chunk-BK3MYSUS.js.map +1 -0
- package/dist/chunk-BLHNKR3S.js +35 -0
- package/dist/chunk-BLHNKR3S.js.map +1 -0
- package/dist/chunk-CCJ43SIX.js +342 -0
- package/dist/chunk-CCJ43SIX.js.map +1 -0
- package/dist/chunk-CNEDWATL.js +210 -0
- package/dist/chunk-CNEDWATL.js.map +1 -0
- package/dist/chunk-CYTWDJE2.js +152 -0
- package/dist/chunk-CYTWDJE2.js.map +1 -0
- package/dist/chunk-DKVOICYZ.js +45 -0
- package/dist/chunk-DKVOICYZ.js.map +1 -0
- package/dist/chunk-DPEJU4P6.js +79 -0
- package/dist/chunk-DPEJU4P6.js.map +1 -0
- package/dist/chunk-DQPABZTU.js +22 -0
- package/dist/chunk-DQPABZTU.js.map +1 -0
- package/dist/chunk-EKHEY3LI.js +46 -0
- package/dist/chunk-EKHEY3LI.js.map +1 -0
- package/dist/chunk-G467LFKC.js +191 -0
- package/dist/chunk-G467LFKC.js.map +1 -0
- package/dist/chunk-GLYWSVSB.js +250 -0
- package/dist/chunk-GLYWSVSB.js.map +1 -0
- package/dist/chunk-GXBIMUO7.js +135 -0
- package/dist/chunk-GXBIMUO7.js.map +1 -0
- package/dist/chunk-H6EERRCR.js +180 -0
- package/dist/chunk-H6EERRCR.js.map +1 -0
- package/dist/chunk-HDTM3LXD.js +35 -0
- package/dist/chunk-HDTM3LXD.js.map +1 -0
- package/dist/chunk-IGMX56C3.js +180 -0
- package/dist/chunk-IGMX56C3.js.map +1 -0
- package/dist/chunk-J4IRICCJ.js +74 -0
- package/dist/chunk-J4IRICCJ.js.map +1 -0
- package/dist/chunk-JXKQ7S6W.js +331 -0
- package/dist/chunk-JXKQ7S6W.js.map +1 -0
- package/dist/chunk-KY6DXS6S.js +34 -0
- package/dist/chunk-KY6DXS6S.js.map +1 -0
- package/dist/chunk-NOFGCYGS.js +22 -0
- package/dist/chunk-NOFGCYGS.js.map +1 -0
- package/dist/chunk-OSHUJRKD.js +131 -0
- package/dist/chunk-OSHUJRKD.js.map +1 -0
- package/dist/chunk-PMH7OWPF.js +40 -0
- package/dist/chunk-PMH7OWPF.js.map +1 -0
- package/dist/chunk-PRBKSX3A.js +299 -0
- package/dist/chunk-PRBKSX3A.js.map +1 -0
- package/dist/chunk-SVPUX7AM.js +48 -0
- package/dist/chunk-SVPUX7AM.js.map +1 -0
- package/dist/chunk-TR6HJ4NO.js +659 -0
- package/dist/chunk-TR6HJ4NO.js.map +1 -0
- package/dist/chunk-TV3E4YUF.js +69 -0
- package/dist/chunk-TV3E4YUF.js.map +1 -0
- package/dist/chunk-U2ROTOPF.js +256 -0
- package/dist/chunk-U2ROTOPF.js.map +1 -0
- package/dist/chunk-VCTN77K2.js +112 -0
- package/dist/chunk-VCTN77K2.js.map +1 -0
- package/dist/chunk-WEOPDD3P.js +120 -0
- package/dist/chunk-WEOPDD3P.js.map +1 -0
- package/dist/chunk-ZBZ7OC6I.js +228 -0
- package/dist/chunk-ZBZ7OC6I.js.map +1 -0
- package/dist/config-CJYo46X9.d.ts +19 -0
- package/dist/index.d.ts +487 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/react/certificate-sign.d.ts +5 -0
- package/dist/react/certificate-sign.js +14 -0
- package/dist/react/certificate-sign.js.map +1 -0
- package/dist/react/compress.d.ts +5 -0
- package/dist/react/compress.js +8 -0
- package/dist/react/compress.js.map +1 -0
- package/dist/react/fill-form.d.ts +5 -0
- package/dist/react/fill-form.js +8 -0
- package/dist/react/fill-form.js.map +1 -0
- package/dist/react/images-to-pdf.d.ts +5 -0
- package/dist/react/images-to-pdf.js +7 -0
- package/dist/react/images-to-pdf.js.map +1 -0
- package/dist/react/merge.d.ts +5 -0
- package/dist/react/merge.js +8 -0
- package/dist/react/merge.js.map +1 -0
- package/dist/react/metadata.d.ts +5 -0
- package/dist/react/metadata.js +8 -0
- package/dist/react/metadata.js.map +1 -0
- package/dist/react/organize.d.ts +5 -0
- package/dist/react/organize.js +12 -0
- package/dist/react/organize.js.map +1 -0
- package/dist/react/pdf-to-images.d.ts +5 -0
- package/dist/react/pdf-to-images.js +13 -0
- package/dist/react/pdf-to-images.js.map +1 -0
- package/dist/react/protect.d.ts +5 -0
- package/dist/react/protect.js +8 -0
- package/dist/react/protect.js.map +1 -0
- package/dist/react/sign.d.ts +5 -0
- package/dist/react/sign.js +13 -0
- package/dist/react/sign.js.map +1 -0
- package/dist/react/split.d.ts +5 -0
- package/dist/react/split.js +11 -0
- package/dist/react/split.js.map +1 -0
- package/dist/react/verify-signatures.d.ts +5 -0
- package/dist/react/verify-signatures.js +8 -0
- package/dist/react/verify-signatures.js.map +1 -0
- package/dist/react/watermark.d.ts +5 -0
- package/dist/react/watermark.js +10 -0
- package/dist/react/watermark.js.map +1 -0
- package/dist/react.d.ts +40 -0
- package/dist/react.js +40 -0
- package/dist/react.js.map +1 -0
- package/dist/styles.css +2 -0
- package/dist/vite.d.ts +5 -0
- package/dist/vite.js +9 -0
- package/dist/vite.js.map +1 -0
- package/package.json +163 -4
|
@@ -0,0 +1,659 @@
|
|
|
1
|
+
import { useRenderDocument } from './chunk-J4IRICCJ.js';
|
|
2
|
+
import { opaqueBounds, whiteToTransparent } from './chunk-KY6DXS6S.js';
|
|
3
|
+
import { applySignatures } from './chunk-SVPUX7AM.js';
|
|
4
|
+
import { renderPageToCanvas, canvasToBlob } from './chunk-DPEJU4P6.js';
|
|
5
|
+
import { useSinglePdf } from './chunk-BK3MYSUS.js';
|
|
6
|
+
import { PdfToolPage, PdfToolColumns, cardClass, secondaryButtonClass, iconButtonClass, SideTips, SelectedPdfCard, FileDropzone, StatusBanner, primaryButtonClass, BackToPdfTools, fieldLabelClass, inputClass } from './chunk-H6EERRCR.js';
|
|
7
|
+
import { downloadBlob, toBaseName, formatBytes } from './chunk-EKHEY3LI.js';
|
|
8
|
+
import { useState, useRef, useEffect, useCallback } from 'react';
|
|
9
|
+
import '@fontsource/dancing-script/400.css';
|
|
10
|
+
import '@fontsource/great-vibes/400.css';
|
|
11
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
12
|
+
|
|
13
|
+
var TABS = [
|
|
14
|
+
{ id: "draw", label: "Draw", icon: "fa-solid fa-pen-nib" },
|
|
15
|
+
{ id: "type", label: "Type", icon: "fa-solid fa-keyboard" },
|
|
16
|
+
{ id: "upload", label: "Upload", icon: "fa-solid fa-upload" }
|
|
17
|
+
];
|
|
18
|
+
var FONTS = [
|
|
19
|
+
{ family: "Dancing Script", label: "Handwritten" },
|
|
20
|
+
{ family: "Great Vibes", label: "Elegant" },
|
|
21
|
+
{ family: "Helvetica, Arial, sans-serif", label: "Plain (dates, initials)" }
|
|
22
|
+
];
|
|
23
|
+
var INKS = [
|
|
24
|
+
{ value: "#0f172a", label: "Black" },
|
|
25
|
+
{ value: "#1d4ed8", label: "Blue" }
|
|
26
|
+
];
|
|
27
|
+
async function trimToPng(canvas, padding = 8) {
|
|
28
|
+
const context = canvas.getContext("2d");
|
|
29
|
+
const { data } = context.getImageData(0, 0, canvas.width, canvas.height);
|
|
30
|
+
const bounds = opaqueBounds(data, canvas.width, canvas.height);
|
|
31
|
+
if (!bounds) return null;
|
|
32
|
+
const x = Math.max(0, bounds.x - padding);
|
|
33
|
+
const y = Math.max(0, bounds.y - padding);
|
|
34
|
+
const width = Math.min(canvas.width - x, bounds.width + padding * 2);
|
|
35
|
+
const height = Math.min(canvas.height - y, bounds.height + padding * 2);
|
|
36
|
+
const out = document.createElement("canvas");
|
|
37
|
+
out.width = width;
|
|
38
|
+
out.height = height;
|
|
39
|
+
out.getContext("2d").drawImage(canvas, x, y, width, height, 0, 0, width, height);
|
|
40
|
+
const blob = await canvasToBlob(out, "image/png");
|
|
41
|
+
return {
|
|
42
|
+
bytes: new Uint8Array(await blob.arrayBuffer()),
|
|
43
|
+
url: URL.createObjectURL(blob),
|
|
44
|
+
aspect: width / height
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function DrawPad({ ink, onReady }) {
|
|
48
|
+
const canvasRef = useRef(null);
|
|
49
|
+
const drawing = useRef(false);
|
|
50
|
+
const last = useRef(null);
|
|
51
|
+
const [hasInk, setHasInk] = useState(false);
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
const canvas = canvasRef.current;
|
|
54
|
+
const ratio = Math.min(3, window.devicePixelRatio || 1);
|
|
55
|
+
canvas.width = canvas.clientWidth * ratio;
|
|
56
|
+
canvas.height = canvas.clientHeight * ratio;
|
|
57
|
+
const context = canvas.getContext("2d");
|
|
58
|
+
context.scale(ratio, ratio);
|
|
59
|
+
context.lineCap = "round";
|
|
60
|
+
context.lineJoin = "round";
|
|
61
|
+
}, []);
|
|
62
|
+
const point = (event) => {
|
|
63
|
+
const rect = canvasRef.current.getBoundingClientRect();
|
|
64
|
+
return { x: event.clientX - rect.left, y: event.clientY - rect.top };
|
|
65
|
+
};
|
|
66
|
+
const start = (event) => {
|
|
67
|
+
event.preventDefault();
|
|
68
|
+
canvasRef.current.setPointerCapture(event.pointerId);
|
|
69
|
+
drawing.current = true;
|
|
70
|
+
last.current = point(event);
|
|
71
|
+
};
|
|
72
|
+
const moveTo = (event) => {
|
|
73
|
+
if (!drawing.current) return;
|
|
74
|
+
const context = canvasRef.current.getContext("2d");
|
|
75
|
+
const next = point(event);
|
|
76
|
+
const distance = Math.hypot(next.x - last.current.x, next.y - last.current.y);
|
|
77
|
+
context.strokeStyle = ink;
|
|
78
|
+
context.lineWidth = Math.max(1.6, 3.2 - distance / 18);
|
|
79
|
+
context.beginPath();
|
|
80
|
+
context.moveTo(last.current.x, last.current.y);
|
|
81
|
+
context.lineTo(next.x, next.y);
|
|
82
|
+
context.stroke();
|
|
83
|
+
last.current = next;
|
|
84
|
+
setHasInk(true);
|
|
85
|
+
};
|
|
86
|
+
const stop = () => {
|
|
87
|
+
drawing.current = false;
|
|
88
|
+
};
|
|
89
|
+
const clear = () => {
|
|
90
|
+
const canvas = canvasRef.current;
|
|
91
|
+
canvas.getContext("2d").clearRect(0, 0, canvas.width, canvas.height);
|
|
92
|
+
setHasInk(false);
|
|
93
|
+
};
|
|
94
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
95
|
+
/* @__PURE__ */ jsx(
|
|
96
|
+
"canvas",
|
|
97
|
+
{
|
|
98
|
+
ref: canvasRef,
|
|
99
|
+
onPointerDown: start,
|
|
100
|
+
onPointerMove: moveTo,
|
|
101
|
+
onPointerUp: stop,
|
|
102
|
+
onPointerCancel: stop,
|
|
103
|
+
"aria-label": "Signature drawing area",
|
|
104
|
+
className: "h-40 w-full touch-none rounded-xl border border-dashed border-slate-300 bg-white dark:border-slate-600"
|
|
105
|
+
}
|
|
106
|
+
),
|
|
107
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-3 flex flex-wrap gap-2", children: [
|
|
108
|
+
/* @__PURE__ */ jsxs("button", { type: "button", onClick: () => onReady(canvasRef.current), disabled: !hasInk, className: primaryButtonClass, children: [
|
|
109
|
+
/* @__PURE__ */ jsx("i", { className: "fa-solid fa-check" }),
|
|
110
|
+
"Use signature"
|
|
111
|
+
] }),
|
|
112
|
+
/* @__PURE__ */ jsxs("button", { type: "button", onClick: clear, disabled: !hasInk, className: secondaryButtonClass, children: [
|
|
113
|
+
/* @__PURE__ */ jsx("i", { className: "fa-solid fa-eraser" }),
|
|
114
|
+
"Clear"
|
|
115
|
+
] })
|
|
116
|
+
] })
|
|
117
|
+
] });
|
|
118
|
+
}
|
|
119
|
+
function TypePad({ ink, onReady }) {
|
|
120
|
+
const [text, setText] = useState("");
|
|
121
|
+
const [font, setFont] = useState(FONTS[0].family);
|
|
122
|
+
const render = async () => {
|
|
123
|
+
const size = 120;
|
|
124
|
+
const cssFont = `${size}px ${font.includes(",") ? font : `"${font}"`}`;
|
|
125
|
+
await document.fonts.load(cssFont, text);
|
|
126
|
+
const canvas = document.createElement("canvas");
|
|
127
|
+
const context = canvas.getContext("2d");
|
|
128
|
+
context.font = cssFont;
|
|
129
|
+
const width = Math.ceil(context.measureText(text).width) + size;
|
|
130
|
+
canvas.width = width;
|
|
131
|
+
canvas.height = Math.ceil(size * 1.8);
|
|
132
|
+
context.font = cssFont;
|
|
133
|
+
context.fillStyle = ink;
|
|
134
|
+
context.textBaseline = "middle";
|
|
135
|
+
context.fillText(text, size / 2, canvas.height / 2);
|
|
136
|
+
onReady(canvas);
|
|
137
|
+
};
|
|
138
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
139
|
+
/* @__PURE__ */ jsxs("div", { className: "grid gap-3 sm:grid-cols-2", children: [
|
|
140
|
+
/* @__PURE__ */ jsxs("label", { className: fieldLabelClass, children: [
|
|
141
|
+
"Your name or text",
|
|
142
|
+
/* @__PURE__ */ jsx(
|
|
143
|
+
"input",
|
|
144
|
+
{
|
|
145
|
+
type: "text",
|
|
146
|
+
value: text,
|
|
147
|
+
maxLength: 60,
|
|
148
|
+
onChange: (event) => setText(event.target.value),
|
|
149
|
+
placeholder: "Jane Doe",
|
|
150
|
+
className: `mt-2 ${inputClass}`
|
|
151
|
+
}
|
|
152
|
+
)
|
|
153
|
+
] }),
|
|
154
|
+
/* @__PURE__ */ jsxs("label", { className: fieldLabelClass, children: [
|
|
155
|
+
"Style",
|
|
156
|
+
/* @__PURE__ */ jsx("select", { value: font, onChange: (event) => setFont(event.target.value), className: `mt-2 ${inputClass}`, children: FONTS.map((option) => /* @__PURE__ */ jsx("option", { value: option.family, children: option.label }, option.family)) })
|
|
157
|
+
] })
|
|
158
|
+
] }),
|
|
159
|
+
/* @__PURE__ */ jsx(
|
|
160
|
+
"div",
|
|
161
|
+
{
|
|
162
|
+
className: "mt-3 flex h-24 items-center justify-center overflow-hidden rounded-xl border border-dashed border-slate-300 bg-white px-4 text-4xl dark:border-slate-600",
|
|
163
|
+
style: { fontFamily: font.includes(",") ? font : `"${font}"`, color: ink },
|
|
164
|
+
children: text || /* @__PURE__ */ jsx("span", { className: "text-base text-slate-400", children: "Preview" })
|
|
165
|
+
}
|
|
166
|
+
),
|
|
167
|
+
/* @__PURE__ */ jsxs("button", { type: "button", onClick: render, disabled: !text.trim(), className: `mt-3 ${primaryButtonClass}`, children: [
|
|
168
|
+
/* @__PURE__ */ jsx("i", { className: "fa-solid fa-check" }),
|
|
169
|
+
"Use signature"
|
|
170
|
+
] })
|
|
171
|
+
] });
|
|
172
|
+
}
|
|
173
|
+
function UploadPad({ onReady, onError }) {
|
|
174
|
+
const [removeBackground, setRemoveBackground] = useState(true);
|
|
175
|
+
const inputRef = useRef(null);
|
|
176
|
+
const handleFile = async (file) => {
|
|
177
|
+
if (!file) return;
|
|
178
|
+
try {
|
|
179
|
+
const bitmap = await createImageBitmap(file);
|
|
180
|
+
const scale = Math.min(1, 1200 / bitmap.width);
|
|
181
|
+
const canvas = document.createElement("canvas");
|
|
182
|
+
canvas.width = Math.round(bitmap.width * scale);
|
|
183
|
+
canvas.height = Math.round(bitmap.height * scale);
|
|
184
|
+
const context = canvas.getContext("2d");
|
|
185
|
+
context.drawImage(bitmap, 0, 0, canvas.width, canvas.height);
|
|
186
|
+
bitmap.close?.();
|
|
187
|
+
if (removeBackground) {
|
|
188
|
+
const image = context.getImageData(0, 0, canvas.width, canvas.height);
|
|
189
|
+
whiteToTransparent(image.data);
|
|
190
|
+
context.putImageData(image, 0, 0);
|
|
191
|
+
}
|
|
192
|
+
onReady(canvas);
|
|
193
|
+
} catch {
|
|
194
|
+
onError("That image could not be read. Try a PNG or JPG.");
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
198
|
+
/* @__PURE__ */ jsx(
|
|
199
|
+
"input",
|
|
200
|
+
{
|
|
201
|
+
ref: inputRef,
|
|
202
|
+
type: "file",
|
|
203
|
+
accept: "image/png,image/jpeg,image/webp",
|
|
204
|
+
className: "hidden",
|
|
205
|
+
onChange: (event) => {
|
|
206
|
+
handleFile(event.target.files?.[0]);
|
|
207
|
+
event.target.value = "";
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
),
|
|
211
|
+
/* @__PURE__ */ jsxs("button", { type: "button", onClick: () => inputRef.current?.click(), className: secondaryButtonClass, children: [
|
|
212
|
+
/* @__PURE__ */ jsx("i", { className: "fa-solid fa-image" }),
|
|
213
|
+
"Choose signature image"
|
|
214
|
+
] }),
|
|
215
|
+
/* @__PURE__ */ jsxs("label", { className: "mt-3 flex items-center gap-3 text-sm text-slate-700 dark:text-slate-300", children: [
|
|
216
|
+
/* @__PURE__ */ jsx(
|
|
217
|
+
"input",
|
|
218
|
+
{
|
|
219
|
+
type: "checkbox",
|
|
220
|
+
checked: removeBackground,
|
|
221
|
+
onChange: (event) => setRemoveBackground(event.target.checked),
|
|
222
|
+
className: "h-4 w-4 accent-[#1F8CFF]"
|
|
223
|
+
}
|
|
224
|
+
),
|
|
225
|
+
"Remove white background (for photos or scans of paper)"
|
|
226
|
+
] })
|
|
227
|
+
] });
|
|
228
|
+
}
|
|
229
|
+
function SignatureCreator({ onCreate, onError }) {
|
|
230
|
+
const [tab, setTab] = useState("draw");
|
|
231
|
+
const [ink, setInk] = useState(INKS[0].value);
|
|
232
|
+
const finish = async (canvas) => {
|
|
233
|
+
const result = await trimToPng(canvas);
|
|
234
|
+
if (!result) {
|
|
235
|
+
onError("The signature is empty.");
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
onCreate(result);
|
|
239
|
+
};
|
|
240
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
241
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [
|
|
242
|
+
/* @__PURE__ */ jsx("div", { role: "tablist", className: "inline-flex rounded-xl border border-slate-200 bg-slate-50 p-1 dark:border-slate-700 dark:bg-slate-900", children: TABS.map((option) => /* @__PURE__ */ jsxs(
|
|
243
|
+
"button",
|
|
244
|
+
{
|
|
245
|
+
type: "button",
|
|
246
|
+
role: "tab",
|
|
247
|
+
"aria-selected": tab === option.id,
|
|
248
|
+
onClick: () => setTab(option.id),
|
|
249
|
+
className: `inline-flex items-center gap-2 rounded-lg px-3 py-1.5 text-xs font-semibold uppercase tracking-[0.12em] transition ${tab === option.id ? "bg-white text-slate-900 shadow-sm dark:bg-slate-800 dark:text-slate-100" : "text-slate-500 hover:text-slate-800 dark:text-slate-400"}`,
|
|
250
|
+
children: [
|
|
251
|
+
/* @__PURE__ */ jsx("i", { className: option.icon }),
|
|
252
|
+
option.label
|
|
253
|
+
]
|
|
254
|
+
},
|
|
255
|
+
option.id
|
|
256
|
+
)) }),
|
|
257
|
+
tab !== "upload" ? /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", "aria-label": "Ink colour", children: INKS.map((option) => /* @__PURE__ */ jsx(
|
|
258
|
+
"button",
|
|
259
|
+
{
|
|
260
|
+
type: "button",
|
|
261
|
+
onClick: () => setInk(option.value),
|
|
262
|
+
"aria-label": `${option.label} ink`,
|
|
263
|
+
"aria-pressed": ink === option.value,
|
|
264
|
+
className: `h-6 w-6 rounded-full border-2 ${ink === option.value ? "border-[#1F8CFF]" : "border-transparent"}`,
|
|
265
|
+
style: { backgroundColor: option.value }
|
|
266
|
+
},
|
|
267
|
+
option.value
|
|
268
|
+
)) }) : null
|
|
269
|
+
] }),
|
|
270
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-4", children: [
|
|
271
|
+
tab === "draw" ? /* @__PURE__ */ jsx(DrawPad, { ink, onReady: finish }) : null,
|
|
272
|
+
tab === "type" ? /* @__PURE__ */ jsx(TypePad, { ink, onReady: finish }) : null,
|
|
273
|
+
tab === "upload" ? /* @__PURE__ */ jsx(UploadPad, { onReady: finish, onError }) : null
|
|
274
|
+
] })
|
|
275
|
+
] });
|
|
276
|
+
}
|
|
277
|
+
var PREVIEW_WIDTH = 900;
|
|
278
|
+
var nextId = 0;
|
|
279
|
+
var clamp = (value, min, max) => Math.min(max, Math.max(min, value));
|
|
280
|
+
function usePagePreview(doc, pageIndex) {
|
|
281
|
+
const [preview, setPreview] = useState(null);
|
|
282
|
+
useEffect(() => {
|
|
283
|
+
if (!doc) {
|
|
284
|
+
setPreview(null);
|
|
285
|
+
return void 0;
|
|
286
|
+
}
|
|
287
|
+
let cancelled = false;
|
|
288
|
+
let url = null;
|
|
289
|
+
(async () => {
|
|
290
|
+
const page = await doc.getPage(pageIndex + 1);
|
|
291
|
+
const base = page.getViewport({ scale: 1 });
|
|
292
|
+
const ratio = Math.min(2, window.devicePixelRatio || 1);
|
|
293
|
+
const render = await renderPageToCanvas(doc, pageIndex + 1, {
|
|
294
|
+
scale: PREVIEW_WIDTH / base.width * ratio
|
|
295
|
+
});
|
|
296
|
+
const blob = await canvasToBlob(render.canvas, "image/jpeg", 0.85);
|
|
297
|
+
if (cancelled) return;
|
|
298
|
+
url = URL.createObjectURL(blob);
|
|
299
|
+
setPreview({ url, width: render.width, height: render.height });
|
|
300
|
+
})().catch(() => {
|
|
301
|
+
});
|
|
302
|
+
return () => {
|
|
303
|
+
cancelled = true;
|
|
304
|
+
if (url) URL.revokeObjectURL(url);
|
|
305
|
+
};
|
|
306
|
+
}, [doc, pageIndex]);
|
|
307
|
+
return preview;
|
|
308
|
+
}
|
|
309
|
+
function PlacementBox({ placement, signature, pageAspect, selected, onSelect, onChange, onRemove, containerRef }) {
|
|
310
|
+
const startDrag = (event, mode) => {
|
|
311
|
+
event.preventDefault();
|
|
312
|
+
event.stopPropagation();
|
|
313
|
+
onSelect();
|
|
314
|
+
const rect = containerRef.current.getBoundingClientRect();
|
|
315
|
+
const origin = { x: event.clientX, y: event.clientY, placement };
|
|
316
|
+
const handleMove = (moveEvent) => {
|
|
317
|
+
const dx = (moveEvent.clientX - origin.x) / rect.width;
|
|
318
|
+
const dy = (moveEvent.clientY - origin.y) / rect.height;
|
|
319
|
+
const start = origin.placement;
|
|
320
|
+
if (mode === "move") {
|
|
321
|
+
onChange({
|
|
322
|
+
x: clamp(start.x + dx, 0, 1 - start.width),
|
|
323
|
+
y: clamp(start.y + dy, 0, 1 - start.height)
|
|
324
|
+
});
|
|
325
|
+
} else {
|
|
326
|
+
const width = clamp(start.width + dx, 0.04, 1 - start.x);
|
|
327
|
+
const height = width * pageAspect / signature.aspect;
|
|
328
|
+
if (start.y + height <= 1) onChange({ width, height });
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
const handleUp = () => {
|
|
332
|
+
window.removeEventListener("pointermove", handleMove);
|
|
333
|
+
window.removeEventListener("pointerup", handleUp);
|
|
334
|
+
};
|
|
335
|
+
window.addEventListener("pointermove", handleMove);
|
|
336
|
+
window.addEventListener("pointerup", handleUp);
|
|
337
|
+
};
|
|
338
|
+
const nudge = (event) => {
|
|
339
|
+
const step = event.shiftKey ? 0.02 : 5e-3;
|
|
340
|
+
const moves = { ArrowLeft: [-step, 0], ArrowRight: [step, 0], ArrowUp: [0, -step], ArrowDown: [0, step] };
|
|
341
|
+
if (event.key === "Delete" || event.key === "Backspace") {
|
|
342
|
+
event.preventDefault();
|
|
343
|
+
onRemove();
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
const move = moves[event.key];
|
|
347
|
+
if (!move) return;
|
|
348
|
+
event.preventDefault();
|
|
349
|
+
onChange({
|
|
350
|
+
x: clamp(placement.x + move[0], 0, 1 - placement.width),
|
|
351
|
+
y: clamp(placement.y + move[1], 0, 1 - placement.height)
|
|
352
|
+
});
|
|
353
|
+
};
|
|
354
|
+
return /* @__PURE__ */ jsxs(
|
|
355
|
+
"div",
|
|
356
|
+
{
|
|
357
|
+
role: "button",
|
|
358
|
+
tabIndex: 0,
|
|
359
|
+
"aria-label": "Placed signature. Drag to move, use arrow keys to nudge, Delete to remove.",
|
|
360
|
+
onPointerDown: (event) => startDrag(event, "move"),
|
|
361
|
+
onKeyDown: nudge,
|
|
362
|
+
onFocus: onSelect,
|
|
363
|
+
className: `absolute cursor-move touch-none select-none outline-none ${selected ? "ring-2 ring-[#1F8CFF]" : "ring-1 ring-[#1F8CFF]/40 hover:ring-[#1F8CFF]"}`,
|
|
364
|
+
style: {
|
|
365
|
+
left: `${placement.x * 100}%`,
|
|
366
|
+
top: `${placement.y * 100}%`,
|
|
367
|
+
width: `${placement.width * 100}%`,
|
|
368
|
+
height: `${placement.height * 100}%`
|
|
369
|
+
},
|
|
370
|
+
children: [
|
|
371
|
+
/* @__PURE__ */ jsx("img", { src: signature.url, alt: "", draggable: false, className: "pointer-events-none h-full w-full" }),
|
|
372
|
+
selected ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
373
|
+
/* @__PURE__ */ jsx(
|
|
374
|
+
"button",
|
|
375
|
+
{
|
|
376
|
+
type: "button",
|
|
377
|
+
onPointerDown: (event) => event.stopPropagation(),
|
|
378
|
+
onClick: onRemove,
|
|
379
|
+
"aria-label": "Remove signature",
|
|
380
|
+
className: "absolute -right-3 -top-3 inline-flex h-6 w-6 items-center justify-center rounded-full bg-rose-600 text-[10px] text-white shadow",
|
|
381
|
+
children: /* @__PURE__ */ jsx("i", { className: "fa-solid fa-xmark" })
|
|
382
|
+
}
|
|
383
|
+
),
|
|
384
|
+
/* @__PURE__ */ jsx(
|
|
385
|
+
"span",
|
|
386
|
+
{
|
|
387
|
+
onPointerDown: (event) => startDrag(event, "resize"),
|
|
388
|
+
"aria-hidden": "true",
|
|
389
|
+
className: "absolute -bottom-2 -right-2 h-4 w-4 cursor-nwse-resize rounded-sm border-2 border-white bg-[#1F8CFF] shadow"
|
|
390
|
+
}
|
|
391
|
+
)
|
|
392
|
+
] }) : null
|
|
393
|
+
]
|
|
394
|
+
}
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
function SignPdf() {
|
|
398
|
+
const [signatures, setSignatures] = useState([]);
|
|
399
|
+
const [placements, setPlacements] = useState([]);
|
|
400
|
+
const [selectedId, setSelectedId] = useState(null);
|
|
401
|
+
const [pageIndex, setPageIndex] = useState(0);
|
|
402
|
+
const [isSaving, setIsSaving] = useState(false);
|
|
403
|
+
const [status, setStatus] = useState({
|
|
404
|
+
tone: "info",
|
|
405
|
+
text: "Add a PDF, create your signature, then place it on the page."
|
|
406
|
+
});
|
|
407
|
+
const containerRef = useRef(null);
|
|
408
|
+
const signaturesRef = useRef(signatures);
|
|
409
|
+
signaturesRef.current = signatures;
|
|
410
|
+
useEffect(() => {
|
|
411
|
+
document.title = "Sign PDF | Apps";
|
|
412
|
+
return () => signaturesRef.current.forEach((signature) => URL.revokeObjectURL(signature.url));
|
|
413
|
+
}, []);
|
|
414
|
+
const onError = useCallback((text) => setStatus({ tone: "error", text }), []);
|
|
415
|
+
const onLoaded = useCallback(({ file, pageCount }) => {
|
|
416
|
+
setPlacements([]);
|
|
417
|
+
setPageIndex(0);
|
|
418
|
+
setStatus({
|
|
419
|
+
tone: "success",
|
|
420
|
+
text: `${file.name} loaded with ${pageCount} page${pageCount === 1 ? "" : "s"}. Create a signature to place it.`
|
|
421
|
+
});
|
|
422
|
+
}, []);
|
|
423
|
+
const pdf = useSinglePdf({ onError, onLoaded });
|
|
424
|
+
const { doc, error: renderError } = useRenderDocument(pdf.bytes);
|
|
425
|
+
const preview = usePagePreview(doc, pageIndex);
|
|
426
|
+
const pageAspect = preview ? preview.width / preview.height : 1 / 1.414;
|
|
427
|
+
const placeSignature = (signature, onPage = pageIndex) => {
|
|
428
|
+
const width = 0.3;
|
|
429
|
+
const height = Math.min(width * pageAspect / signature.aspect, 1);
|
|
430
|
+
const step = placements.filter((placement2) => placement2.pageIndex === onPage).length % 6;
|
|
431
|
+
const placement = {
|
|
432
|
+
id: `place-${++nextId}`,
|
|
433
|
+
signatureId: signature.id,
|
|
434
|
+
pageIndex: onPage,
|
|
435
|
+
x: clamp(0.6 - width / 2 - step * 0.04, 0, 1 - width),
|
|
436
|
+
y: clamp(0.8 - height / 2 - step * 0.08, 0, 1 - height),
|
|
437
|
+
width,
|
|
438
|
+
height
|
|
439
|
+
};
|
|
440
|
+
setPlacements((current) => [...current, placement]);
|
|
441
|
+
setSelectedId(placement.id);
|
|
442
|
+
};
|
|
443
|
+
const addSignature = (created) => {
|
|
444
|
+
const signature = { id: `sig-${++nextId}`, ...created };
|
|
445
|
+
setSignatures((current) => [...current, signature]);
|
|
446
|
+
if (pdf.pageCount) {
|
|
447
|
+
placeSignature(signature);
|
|
448
|
+
setStatus({ tone: "success", text: "Signature placed. Drag it into position, or resize it from the corner." });
|
|
449
|
+
}
|
|
450
|
+
};
|
|
451
|
+
const updatePlacement = (id, patch) => {
|
|
452
|
+
setPlacements((current) => current.map((placement) => placement.id === id ? { ...placement, ...patch } : placement));
|
|
453
|
+
};
|
|
454
|
+
const removePlacement = (id) => {
|
|
455
|
+
setPlacements((current) => current.filter((placement) => placement.id !== id));
|
|
456
|
+
setSelectedId(null);
|
|
457
|
+
};
|
|
458
|
+
const removeSignature = (id) => {
|
|
459
|
+
setSignatures((current) => {
|
|
460
|
+
const removed = current.find((signature) => signature.id === id);
|
|
461
|
+
if (removed) URL.revokeObjectURL(removed.url);
|
|
462
|
+
return current.filter((signature) => signature.id !== id);
|
|
463
|
+
});
|
|
464
|
+
setPlacements((current) => current.filter((placement) => placement.signatureId !== id));
|
|
465
|
+
};
|
|
466
|
+
const clearFile = () => {
|
|
467
|
+
pdf.clear();
|
|
468
|
+
setPlacements([]);
|
|
469
|
+
};
|
|
470
|
+
const handleSave = async () => {
|
|
471
|
+
if (!pdf.bytes || !placements.length) return;
|
|
472
|
+
setIsSaving(true);
|
|
473
|
+
setStatus({ tone: "info", text: "Signing your PDF..." });
|
|
474
|
+
try {
|
|
475
|
+
const byId2 = new Map(signatures.map((signature) => [signature.id, signature]));
|
|
476
|
+
const bytes = await applySignatures(
|
|
477
|
+
pdf.bytes,
|
|
478
|
+
placements.map((placement) => ({ ...placement, image: byId2.get(placement.signatureId).bytes }))
|
|
479
|
+
);
|
|
480
|
+
const blob = new Blob([bytes], { type: "application/pdf" });
|
|
481
|
+
downloadBlob(blob, `${toBaseName(pdf.file.name, "document")}-signed.pdf`);
|
|
482
|
+
setStatus({
|
|
483
|
+
tone: "success",
|
|
484
|
+
text: `Signed with ${placements.length} signature${placements.length === 1 ? "" : "s"} (${formatBytes(blob.size)}). Your download has started.`
|
|
485
|
+
});
|
|
486
|
+
} catch (error) {
|
|
487
|
+
setStatus({ tone: "error", text: `Signing failed: ${error?.message || "Please try again."}` });
|
|
488
|
+
} finally {
|
|
489
|
+
setIsSaving(false);
|
|
490
|
+
}
|
|
491
|
+
};
|
|
492
|
+
const busy = isSaving || pdf.isReading;
|
|
493
|
+
const pagePlacements = placements.filter((placement) => placement.pageIndex === pageIndex);
|
|
494
|
+
const byId = new Map(signatures.map((signature) => [signature.id, signature]));
|
|
495
|
+
return /* @__PURE__ */ jsx(
|
|
496
|
+
PdfToolPage,
|
|
497
|
+
{
|
|
498
|
+
title: "Sign PDF",
|
|
499
|
+
description: "Draw, type, or upload your signature and place it anywhere on the page. Runs in your browser; your document and signature never leave your device.",
|
|
500
|
+
stats: [
|
|
501
|
+
{ value: pdf.pageCount || "-", label: "Pages" },
|
|
502
|
+
{ value: placements.length || "-", label: "Placed" }
|
|
503
|
+
],
|
|
504
|
+
children: /* @__PURE__ */ jsx(
|
|
505
|
+
PdfToolColumns,
|
|
506
|
+
{
|
|
507
|
+
main: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
508
|
+
pdf.file ? /* @__PURE__ */ jsx(
|
|
509
|
+
SelectedPdfCard,
|
|
510
|
+
{
|
|
511
|
+
signed: pdf.signed,
|
|
512
|
+
file: pdf.file,
|
|
513
|
+
pageCount: pdf.pageCount,
|
|
514
|
+
onRemove: clearFile,
|
|
515
|
+
disabled: isSaving
|
|
516
|
+
}
|
|
517
|
+
) : /* @__PURE__ */ jsx(
|
|
518
|
+
FileDropzone,
|
|
519
|
+
{
|
|
520
|
+
accept: ".pdf,application/pdf",
|
|
521
|
+
disabled: busy,
|
|
522
|
+
onFiles: pdf.selectFiles,
|
|
523
|
+
title: "Drag and drop a PDF here",
|
|
524
|
+
hint: "or click to browse from your device"
|
|
525
|
+
}
|
|
526
|
+
),
|
|
527
|
+
renderError ? /* @__PURE__ */ jsx("p", { className: "mt-3 text-xs text-rose-700 dark:text-rose-300", children: renderError }) : null,
|
|
528
|
+
pdf.pageCount ? /* @__PURE__ */ jsxs("div", { className: "mt-5", children: [
|
|
529
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-3 flex flex-wrap items-center justify-between gap-2", children: [
|
|
530
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
531
|
+
/* @__PURE__ */ jsx(
|
|
532
|
+
"button",
|
|
533
|
+
{
|
|
534
|
+
type: "button",
|
|
535
|
+
onClick: () => setPageIndex((index) => Math.max(0, index - 1)),
|
|
536
|
+
disabled: pageIndex === 0,
|
|
537
|
+
className: iconButtonClass,
|
|
538
|
+
"aria-label": "Previous page",
|
|
539
|
+
children: /* @__PURE__ */ jsx("i", { className: "fa-solid fa-chevron-left" })
|
|
540
|
+
}
|
|
541
|
+
),
|
|
542
|
+
/* @__PURE__ */ jsxs("span", { className: "text-sm text-slate-700 dark:text-slate-300", children: [
|
|
543
|
+
"Page ",
|
|
544
|
+
pageIndex + 1,
|
|
545
|
+
" of ",
|
|
546
|
+
pdf.pageCount
|
|
547
|
+
] }),
|
|
548
|
+
/* @__PURE__ */ jsx(
|
|
549
|
+
"button",
|
|
550
|
+
{
|
|
551
|
+
type: "button",
|
|
552
|
+
onClick: () => setPageIndex((index) => Math.min(pdf.pageCount - 1, index + 1)),
|
|
553
|
+
disabled: pageIndex === pdf.pageCount - 1,
|
|
554
|
+
className: iconButtonClass,
|
|
555
|
+
"aria-label": "Next page",
|
|
556
|
+
children: /* @__PURE__ */ jsx("i", { className: "fa-solid fa-chevron-right" })
|
|
557
|
+
}
|
|
558
|
+
)
|
|
559
|
+
] }),
|
|
560
|
+
/* @__PURE__ */ jsxs("span", { className: "text-xs text-slate-500 dark:text-slate-400", children: [
|
|
561
|
+
pagePlacements.length,
|
|
562
|
+
" on this page"
|
|
563
|
+
] })
|
|
564
|
+
] }),
|
|
565
|
+
/* @__PURE__ */ jsx("div", { className: "flex justify-center rounded-2xl bg-slate-100 p-3 dark:bg-slate-800", children: /* @__PURE__ */ jsxs(
|
|
566
|
+
"div",
|
|
567
|
+
{
|
|
568
|
+
ref: containerRef,
|
|
569
|
+
onPointerDown: () => setSelectedId(null),
|
|
570
|
+
className: "relative w-full max-w-[720px] bg-white shadow-md",
|
|
571
|
+
style: { aspectRatio: preview ? `${preview.width} / ${preview.height}` : "1 / 1.414" },
|
|
572
|
+
children: [
|
|
573
|
+
preview ? /* @__PURE__ */ jsx(
|
|
574
|
+
"img",
|
|
575
|
+
{
|
|
576
|
+
src: preview.url,
|
|
577
|
+
alt: `Page ${pageIndex + 1}`,
|
|
578
|
+
draggable: false,
|
|
579
|
+
className: "pointer-events-none absolute inset-0 h-full w-full select-none"
|
|
580
|
+
}
|
|
581
|
+
) : /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ jsx("i", { className: "fa-solid fa-spinner fa-spin text-slate-400", "aria-label": "Rendering page" }) }),
|
|
582
|
+
pagePlacements.map((placement) => /* @__PURE__ */ jsx(
|
|
583
|
+
PlacementBox,
|
|
584
|
+
{
|
|
585
|
+
placement,
|
|
586
|
+
signature: byId.get(placement.signatureId),
|
|
587
|
+
pageAspect,
|
|
588
|
+
selected: selectedId === placement.id,
|
|
589
|
+
onSelect: () => setSelectedId(placement.id),
|
|
590
|
+
onChange: (patch) => updatePlacement(placement.id, patch),
|
|
591
|
+
onRemove: () => removePlacement(placement.id),
|
|
592
|
+
containerRef
|
|
593
|
+
},
|
|
594
|
+
placement.id
|
|
595
|
+
))
|
|
596
|
+
]
|
|
597
|
+
}
|
|
598
|
+
) })
|
|
599
|
+
] }) : null,
|
|
600
|
+
/* @__PURE__ */ jsx(StatusBanner, { status }),
|
|
601
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-5 flex flex-wrap gap-3", children: [
|
|
602
|
+
/* @__PURE__ */ jsxs("button", { type: "button", onClick: handleSave, disabled: busy || !placements.length, className: primaryButtonClass, children: [
|
|
603
|
+
/* @__PURE__ */ jsx("i", { className: `fa-solid ${isSaving ? "fa-spinner fa-spin" : "fa-signature"}` }),
|
|
604
|
+
isSaving ? "Signing..." : "Sign & Download"
|
|
605
|
+
] }),
|
|
606
|
+
/* @__PURE__ */ jsx(BackToPdfTools, {})
|
|
607
|
+
] })
|
|
608
|
+
] }),
|
|
609
|
+
side: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
610
|
+
/* @__PURE__ */ jsx("h2", { className: "text-base font-semibold text-slate-900 dark:text-slate-100", children: "Your signature" }),
|
|
611
|
+
/* @__PURE__ */ jsx("div", { className: "mt-4", children: /* @__PURE__ */ jsx(SignatureCreator, { onCreate: addSignature, onError }) }),
|
|
612
|
+
signatures.length ? /* @__PURE__ */ jsx("ul", { className: "mt-5 space-y-2", children: signatures.map((signature) => /* @__PURE__ */ jsxs("li", { className: `${cardClass} !p-2`, children: [
|
|
613
|
+
/* @__PURE__ */ jsx("div", { className: "flex h-14 items-center justify-center rounded-lg bg-white", children: /* @__PURE__ */ jsx("img", { src: signature.url, alt: "Saved signature", className: "max-h-full max-w-full" }) }),
|
|
614
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-2 flex gap-2", children: [
|
|
615
|
+
/* @__PURE__ */ jsxs(
|
|
616
|
+
"button",
|
|
617
|
+
{
|
|
618
|
+
type: "button",
|
|
619
|
+
onClick: () => placeSignature(signature),
|
|
620
|
+
disabled: !pdf.pageCount,
|
|
621
|
+
className: `flex-1 justify-center ${secondaryButtonClass}`,
|
|
622
|
+
children: [
|
|
623
|
+
/* @__PURE__ */ jsx("i", { className: "fa-solid fa-plus" }),
|
|
624
|
+
"Place"
|
|
625
|
+
]
|
|
626
|
+
}
|
|
627
|
+
),
|
|
628
|
+
/* @__PURE__ */ jsx(
|
|
629
|
+
"button",
|
|
630
|
+
{
|
|
631
|
+
type: "button",
|
|
632
|
+
onClick: () => removeSignature(signature.id),
|
|
633
|
+
className: iconButtonClass,
|
|
634
|
+
"aria-label": "Delete signature",
|
|
635
|
+
children: /* @__PURE__ */ jsx("i", { className: "fa-solid fa-trash" })
|
|
636
|
+
}
|
|
637
|
+
)
|
|
638
|
+
] })
|
|
639
|
+
] }, signature.id)) }) : null,
|
|
640
|
+
/* @__PURE__ */ jsx(
|
|
641
|
+
SideTips,
|
|
642
|
+
{
|
|
643
|
+
tips: [
|
|
644
|
+
"Place the same signature on several pages, or add initials with Type \u2192 Plain.",
|
|
645
|
+
"Select a signature and press Delete to remove it; arrow keys nudge it.",
|
|
646
|
+
"This adds a visible signature image. It is not a certificate-based digital signature."
|
|
647
|
+
]
|
|
648
|
+
}
|
|
649
|
+
)
|
|
650
|
+
] })
|
|
651
|
+
}
|
|
652
|
+
)
|
|
653
|
+
}
|
|
654
|
+
);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
export { SignPdf };
|
|
658
|
+
//# sourceMappingURL=chunk-TR6HJ4NO.js.map
|
|
659
|
+
//# sourceMappingURL=chunk-TR6HJ4NO.js.map
|