@kitsy/pdf 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/LICENSE +201 -0
  3. package/NOTICE +16 -0
  4. package/README.md +256 -2
  5. package/THIRD_PARTY_NOTICES.md +48 -0
  6. package/dist/chunk-2K5X7L5J.js +43 -0
  7. package/dist/chunk-2K5X7L5J.js.map +1 -0
  8. package/dist/chunk-4EAYBJ6G.js +362 -0
  9. package/dist/chunk-4EAYBJ6G.js.map +1 -0
  10. package/dist/chunk-4LBJTDV5.js +165 -0
  11. package/dist/chunk-4LBJTDV5.js.map +1 -0
  12. package/dist/chunk-4ZEZ2GLD.js +353 -0
  13. package/dist/chunk-4ZEZ2GLD.js.map +1 -0
  14. package/dist/chunk-5OIMR74G.js +273 -0
  15. package/dist/chunk-5OIMR74G.js.map +1 -0
  16. package/dist/chunk-6NFNAKWA.js +257 -0
  17. package/dist/chunk-6NFNAKWA.js.map +1 -0
  18. package/dist/chunk-7MTEMQLL.js +47 -0
  19. package/dist/chunk-7MTEMQLL.js.map +1 -0
  20. package/dist/chunk-7V4SWTUK.js +83 -0
  21. package/dist/chunk-7V4SWTUK.js.map +1 -0
  22. package/dist/chunk-7XH7MFZ4.js +295 -0
  23. package/dist/chunk-7XH7MFZ4.js.map +1 -0
  24. package/dist/chunk-BK3MYSUS.js +61 -0
  25. package/dist/chunk-BK3MYSUS.js.map +1 -0
  26. package/dist/chunk-BLHNKR3S.js +35 -0
  27. package/dist/chunk-BLHNKR3S.js.map +1 -0
  28. package/dist/chunk-CCJ43SIX.js +342 -0
  29. package/dist/chunk-CCJ43SIX.js.map +1 -0
  30. package/dist/chunk-CNEDWATL.js +210 -0
  31. package/dist/chunk-CNEDWATL.js.map +1 -0
  32. package/dist/chunk-CYTWDJE2.js +152 -0
  33. package/dist/chunk-CYTWDJE2.js.map +1 -0
  34. package/dist/chunk-DKVOICYZ.js +45 -0
  35. package/dist/chunk-DKVOICYZ.js.map +1 -0
  36. package/dist/chunk-DPEJU4P6.js +79 -0
  37. package/dist/chunk-DPEJU4P6.js.map +1 -0
  38. package/dist/chunk-DQPABZTU.js +22 -0
  39. package/dist/chunk-DQPABZTU.js.map +1 -0
  40. package/dist/chunk-EKHEY3LI.js +46 -0
  41. package/dist/chunk-EKHEY3LI.js.map +1 -0
  42. package/dist/chunk-FIEYAKPI.js +421 -0
  43. package/dist/chunk-FIEYAKPI.js.map +1 -0
  44. package/dist/chunk-G467LFKC.js +191 -0
  45. package/dist/chunk-G467LFKC.js.map +1 -0
  46. package/dist/chunk-GLYWSVSB.js +250 -0
  47. package/dist/chunk-GLYWSVSB.js.map +1 -0
  48. package/dist/chunk-GXBIMUO7.js +135 -0
  49. package/dist/chunk-GXBIMUO7.js.map +1 -0
  50. package/dist/chunk-H6EERRCR.js +180 -0
  51. package/dist/chunk-H6EERRCR.js.map +1 -0
  52. package/dist/chunk-HDTM3LXD.js +35 -0
  53. package/dist/chunk-HDTM3LXD.js.map +1 -0
  54. package/dist/chunk-IGMX56C3.js +180 -0
  55. package/dist/chunk-IGMX56C3.js.map +1 -0
  56. package/dist/chunk-J4IRICCJ.js +74 -0
  57. package/dist/chunk-J4IRICCJ.js.map +1 -0
  58. package/dist/chunk-JXKQ7S6W.js +331 -0
  59. package/dist/chunk-JXKQ7S6W.js.map +1 -0
  60. package/dist/chunk-KY6DXS6S.js +34 -0
  61. package/dist/chunk-KY6DXS6S.js.map +1 -0
  62. package/dist/chunk-NOFGCYGS.js +22 -0
  63. package/dist/chunk-NOFGCYGS.js.map +1 -0
  64. package/dist/chunk-OSHUJRKD.js +131 -0
  65. package/dist/chunk-OSHUJRKD.js.map +1 -0
  66. package/dist/chunk-PMH7OWPF.js +40 -0
  67. package/dist/chunk-PMH7OWPF.js.map +1 -0
  68. package/dist/chunk-PRBKSX3A.js +299 -0
  69. package/dist/chunk-PRBKSX3A.js.map +1 -0
  70. package/dist/chunk-SVPUX7AM.js +48 -0
  71. package/dist/chunk-SVPUX7AM.js.map +1 -0
  72. package/dist/chunk-TR6HJ4NO.js +659 -0
  73. package/dist/chunk-TR6HJ4NO.js.map +1 -0
  74. package/dist/chunk-TV3E4YUF.js +69 -0
  75. package/dist/chunk-TV3E4YUF.js.map +1 -0
  76. package/dist/chunk-VCTN77K2.js +112 -0
  77. package/dist/chunk-VCTN77K2.js.map +1 -0
  78. package/dist/chunk-WEOPDD3P.js +120 -0
  79. package/dist/chunk-WEOPDD3P.js.map +1 -0
  80. package/dist/chunk-ZBZ7OC6I.js +228 -0
  81. package/dist/chunk-ZBZ7OC6I.js.map +1 -0
  82. package/dist/config-CJYo46X9.d.ts +19 -0
  83. package/dist/index.d.ts +517 -0
  84. package/dist/index.js +23 -0
  85. package/dist/index.js.map +1 -0
  86. package/dist/react/certificate-sign.d.ts +5 -0
  87. package/dist/react/certificate-sign.js +14 -0
  88. package/dist/react/certificate-sign.js.map +1 -0
  89. package/dist/react/compress.d.ts +5 -0
  90. package/dist/react/compress.js +8 -0
  91. package/dist/react/compress.js.map +1 -0
  92. package/dist/react/fill-form.d.ts +5 -0
  93. package/dist/react/fill-form.js +8 -0
  94. package/dist/react/fill-form.js.map +1 -0
  95. package/dist/react/images-to-pdf.d.ts +5 -0
  96. package/dist/react/images-to-pdf.js +7 -0
  97. package/dist/react/images-to-pdf.js.map +1 -0
  98. package/dist/react/merge.d.ts +5 -0
  99. package/dist/react/merge.js +8 -0
  100. package/dist/react/merge.js.map +1 -0
  101. package/dist/react/metadata.d.ts +5 -0
  102. package/dist/react/metadata.js +8 -0
  103. package/dist/react/metadata.js.map +1 -0
  104. package/dist/react/organize.d.ts +5 -0
  105. package/dist/react/organize.js +12 -0
  106. package/dist/react/organize.js.map +1 -0
  107. package/dist/react/pdf-to-images.d.ts +5 -0
  108. package/dist/react/pdf-to-images.js +13 -0
  109. package/dist/react/pdf-to-images.js.map +1 -0
  110. package/dist/react/protect.d.ts +5 -0
  111. package/dist/react/protect.js +8 -0
  112. package/dist/react/protect.js.map +1 -0
  113. package/dist/react/sign.d.ts +5 -0
  114. package/dist/react/sign.js +13 -0
  115. package/dist/react/sign.js.map +1 -0
  116. package/dist/react/split.d.ts +5 -0
  117. package/dist/react/split.js +11 -0
  118. package/dist/react/split.js.map +1 -0
  119. package/dist/react/verify-signatures.d.ts +5 -0
  120. package/dist/react/verify-signatures.js +8 -0
  121. package/dist/react/verify-signatures.js.map +1 -0
  122. package/dist/react/watermark.d.ts +5 -0
  123. package/dist/react/watermark.js +10 -0
  124. package/dist/react/watermark.js.map +1 -0
  125. package/dist/react.d.ts +40 -0
  126. package/dist/react.js +40 -0
  127. package/dist/react.js.map +1 -0
  128. package/dist/styles.css +2 -0
  129. package/dist/vite.d.ts +5 -0
  130. package/dist/vite.js +9 -0
  131. package/dist/vite.js.map +1 -0
  132. package/package.json +163 -4
@@ -0,0 +1,421 @@
1
+ import { PasswordInput } from './chunk-2K5X7L5J.js';
2
+ import { heyTokenSigner, findTokenCertificates, HeyDscError, readPkcs12, CertificatePasswordError, signPdfWithCertificate } from './chunk-4ZEZ2GLD.js';
3
+ import { openRenderDocument, renderThumbnail } from './chunk-DPEJU4P6.js';
4
+ import { describeCertificate, webCryptoSigner, verifyPdfSignatures } from './chunk-5OIMR74G.js';
5
+ import { useSinglePdf } from './chunk-BK3MYSUS.js';
6
+ import { PdfToolPage, PdfToolColumns, SideTips, SelectedPdfCard, FileDropzone, cardClass, SummaryList, secondaryButtonClass, fieldLabelClass, primaryButtonClass, inputClass, StatusBanner, BackToPdfTools } from './chunk-H6EERRCR.js';
7
+ import { downloadBlob, toBaseName, formatBytes } from './chunk-EKHEY3LI.js';
8
+ import { useRef, useState, useEffect, useCallback } from 'react';
9
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
10
+
11
+ var REASONS = [
12
+ "I am the author of this document",
13
+ "I approve this document",
14
+ "I have reviewed this document",
15
+ "I agree to the terms of this document"
16
+ ];
17
+ var BOX = { width: 0.38, height: 0.09 };
18
+ var POSITIONS = {
19
+ "bottom-right": { x: 0.98 - BOX.width, y: 0.97 - BOX.height, label: "Bottom right" },
20
+ "bottom-left": { x: 0.02, y: 0.97 - BOX.height, label: "Bottom left" },
21
+ "top-right": { x: 0.98 - BOX.width, y: 0.03, label: "Top right" },
22
+ "top-left": { x: 0.02, y: 0.03, label: "Top left" }
23
+ };
24
+ var HEY_INSTALL = {
25
+ windows: "irm https://raw.githubusercontent.com/kitsyai/hey/main/install/hey.ps1 | iex",
26
+ unix: "curl -fsSL https://raw.githubusercontent.com/kitsyai/hey/main/install/hey.sh | sh"
27
+ };
28
+ function TokenSetup() {
29
+ const code = "block overflow-x-auto rounded-lg bg-slate-900 px-3 py-2 font-mono text-[11px] text-slate-100";
30
+ return /* @__PURE__ */ jsxs("div", { className: "mt-3 rounded-xl border border-slate-200 bg-slate-50 p-3 text-xs text-slate-700 dark:border-slate-700 dark:bg-slate-900/60 dark:text-slate-300", children: [
31
+ /* @__PURE__ */ jsx("p", { className: "font-semibold text-slate-900 dark:text-slate-100", children: "Set up token signing (one time)" }),
32
+ /* @__PURE__ */ jsxs("ol", { className: "mt-2 list-decimal space-y-2 pl-4", children: [
33
+ /* @__PURE__ */ jsx("li", { children: "Install your token's driver (it comes from the token vendor, e.g. ePass2003 or ProxKey), then plug in the token." }),
34
+ /* @__PURE__ */ jsxs("li", { children: [
35
+ "Install ",
36
+ /* @__PURE__ */ jsx("a", { className: "underline", href: "https://github.com/kitsyai/hey", target: "_blank", rel: "noreferrer", children: "hey" }),
37
+ ", a small open-source helper. In PowerShell (Windows):",
38
+ /* @__PURE__ */ jsx("code", { className: `mt-1 ${code}`, children: HEY_INSTALL.windows }),
39
+ "or in a terminal (macOS / Linux):",
40
+ /* @__PURE__ */ jsx("code", { className: `mt-1 ${code}`, children: HEY_INSTALL.unix })
41
+ ] }),
42
+ /* @__PURE__ */ jsxs("li", { children: [
43
+ "Start the token signer:",
44
+ /* @__PURE__ */ jsx("code", { className: `mt-1 ${code}`, children: "hey up dsc" })
45
+ ] }),
46
+ /* @__PURE__ */ jsx("li", { children: 'Click "Find my token" again. Your browser may ask whether this site can access devices on your local network: allow it, so the page can reach hey on this computer.' })
47
+ ] }),
48
+ /* @__PURE__ */ jsx("p", { className: "mt-2", children: "Your token's private key never leaves the token. Before every signature, your computer shows its own confirmation dialog naming this site and the document." })
49
+ ] });
50
+ }
51
+ var formatDate = (date) => date.toLocaleDateString(void 0, { year: "numeric", month: "short", day: "numeric" });
52
+ function usePagePreview(bytes, pageIndex) {
53
+ const [preview, setPreview] = useState(null);
54
+ useEffect(() => {
55
+ if (!bytes) {
56
+ setPreview(null);
57
+ return void 0;
58
+ }
59
+ let cancelled = false;
60
+ let url = null;
61
+ let doc = null;
62
+ (async () => {
63
+ doc = await openRenderDocument(bytes);
64
+ const thumb = await renderThumbnail(doc, pageIndex + 1, 220);
65
+ url = thumb.url;
66
+ if (cancelled) URL.revokeObjectURL(url);
67
+ else setPreview(thumb);
68
+ })().catch(() => {
69
+ });
70
+ return () => {
71
+ cancelled = true;
72
+ if (url) URL.revokeObjectURL(url);
73
+ doc?.destroy();
74
+ };
75
+ }, [bytes, pageIndex]);
76
+ return preview;
77
+ }
78
+ function CertSignPdf() {
79
+ const certInputRef = useRef(null);
80
+ const [certFile, setCertFile] = useState(null);
81
+ const [certPassword, setCertPassword] = useState("");
82
+ const [signer, setSigner] = useState(null);
83
+ const [certificate, setCertificate] = useState(null);
84
+ const [source, setSource] = useState("file");
85
+ const [tokens, setTokens] = useState(null);
86
+ const [tokenCert, setTokenCert] = useState(null);
87
+ const [needsSetup, setNeedsSetup] = useState(false);
88
+ const [reason, setReason] = useState(REASONS[1]);
89
+ const [customReason, setCustomReason] = useState("");
90
+ const [location, setLocation] = useState("");
91
+ const [visible, setVisible] = useState(true);
92
+ const [pageIndex, setPageIndex] = useState(0);
93
+ const [position, setPosition] = useState("bottom-right");
94
+ const [isWorking, setIsWorking] = useState(false);
95
+ const [status, setStatus] = useState({
96
+ tone: "info",
97
+ text: "Add a PDF, then your certificate file (.pfx / .p12) or your USB token."
98
+ });
99
+ useEffect(() => {
100
+ document.title = "Digital Signature (Certificate) | Apps";
101
+ }, []);
102
+ const onError = useCallback((text) => setStatus({ tone: "error", text }), []);
103
+ const onLoaded = useCallback(({ file, pageCount }) => {
104
+ setPageIndex(Math.max(0, pageCount - 1));
105
+ setStatus({ tone: "success", text: `${file.name} loaded. Now open your certificate.` });
106
+ }, []);
107
+ const pdf = useSinglePdf({ onError, onLoaded });
108
+ const preview = usePagePreview(visible ? pdf.bytes : null, pageIndex);
109
+ const forgetCertificate = () => {
110
+ setSigner(null);
111
+ setCertificate(null);
112
+ setCertPassword("");
113
+ setTokenCert(null);
114
+ };
115
+ const switchSource = (next) => {
116
+ if (next === source) return;
117
+ forgetCertificate();
118
+ setSource(next);
119
+ };
120
+ const chooseToken = (token) => {
121
+ setTokenCert(token);
122
+ setCertificate(token.summary);
123
+ setSigner(heyTokenSigner(token));
124
+ };
125
+ const findTokens = async () => {
126
+ setIsWorking(true);
127
+ setNeedsSetup(false);
128
+ try {
129
+ const found = await findTokenCertificates();
130
+ setTokens(found);
131
+ if (found.length === 1) chooseToken(found[0]);
132
+ setStatus(
133
+ found.length ? { tone: "success", text: `Found ${found.length} certificate${found.length === 1 ? "" : "s"} on your token.` } : { tone: "error", text: "hey is running, but no certificate was found. Is the token plugged in and its driver installed?" }
134
+ );
135
+ } catch (error) {
136
+ const setup = error instanceof HeyDscError && ["no_hub", "not_set_up", "not_running"].includes(error.code);
137
+ setNeedsSetup(setup);
138
+ setTokens(null);
139
+ setStatus({ tone: "error", text: error?.message || "Could not reach the token." });
140
+ } finally {
141
+ setIsWorking(false);
142
+ }
143
+ };
144
+ const chooseCertificate = (file) => {
145
+ if (!file) return;
146
+ forgetCertificate();
147
+ setCertFile(file);
148
+ };
149
+ const openCertificate = async () => {
150
+ if (!certFile) return;
151
+ setIsWorking(true);
152
+ try {
153
+ const identity = readPkcs12(new Uint8Array(await certFile.arrayBuffer()), certPassword);
154
+ const summary = describeCertificate(identity.certificate);
155
+ const nextSigner = await webCryptoSigner(identity);
156
+ identity.privateKeyPkcs8.fill(0);
157
+ setSigner(nextSigner);
158
+ setCertificate(summary);
159
+ setCertPassword("");
160
+ setStatus({ tone: "success", text: `Certificate for ${summary.commonName || "(no name)"} opened.` });
161
+ } catch (error) {
162
+ setStatus({
163
+ tone: "error",
164
+ text: error instanceof CertificatePasswordError ? error.message : error?.message || "Could not open the certificate."
165
+ });
166
+ } finally {
167
+ setIsWorking(false);
168
+ }
169
+ };
170
+ const now = /* @__PURE__ */ new Date();
171
+ const expired = certificate && (now > certificate.notAfter || now < certificate.notBefore);
172
+ const finalReason = reason === "custom" ? customReason.trim() : reason;
173
+ const canSign = Boolean(pdf.bytes && signer && !pdf.signed && !expired);
174
+ const handleSign = async (event) => {
175
+ event.preventDefault();
176
+ if (!canSign) return;
177
+ setIsWorking(true);
178
+ setStatus({
179
+ tone: "info",
180
+ text: source === "token" ? "Confirm the signature in the dialog on your computer (it may ask for the token PIN)..." : "Signing your PDF..."
181
+ });
182
+ try {
183
+ const spot2 = POSITIONS[position];
184
+ const activeSigner = source === "token" && tokenCert ? heyTokenSigner(tokenCert, { document: pdf.file.name }) : signer;
185
+ const signed = await signPdfWithCertificate(pdf.bytes, activeSigner, {
186
+ reason: finalReason,
187
+ location: location.trim(),
188
+ visible: visible ? { pageIndex, x: spot2.x, y: spot2.y, ...BOX } : null
189
+ });
190
+ const [check] = await verifyPdfSignatures(signed);
191
+ if (!check?.intact || !check.coversWholeFile) {
192
+ throw new Error("The signed file did not pass verification. Nothing was downloaded.");
193
+ }
194
+ const blob = new Blob([signed], { type: "application/pdf" });
195
+ downloadBlob(blob, `${toBaseName(pdf.file.name, "document")}-signed.pdf`);
196
+ setStatus({
197
+ tone: "success",
198
+ text: `Signed and verified (${formatBytes(blob.size)}). Your download has started.`
199
+ });
200
+ } catch (error) {
201
+ setStatus({ tone: "error", text: `Signing failed: ${error?.message || "Please try again."}` });
202
+ } finally {
203
+ setIsWorking(false);
204
+ }
205
+ };
206
+ const busy = isWorking || pdf.isReading;
207
+ const spot = POSITIONS[position];
208
+ return /* @__PURE__ */ jsx(
209
+ PdfToolPage,
210
+ {
211
+ title: "Digital Signature (Certificate)",
212
+ description: "Sign a PDF with your digital signature certificate (DSC): a .pfx / .p12 file or a USB token. Viewers such as Adobe Reader show who signed it and flag any later change. Private keys never leave your browser or your token.",
213
+ stats: [
214
+ { value: "PAdES", label: "Standard" },
215
+ { value: certificate ? "Ready" : "-", label: "Certificate" }
216
+ ],
217
+ children: /* @__PURE__ */ jsx(
218
+ PdfToolColumns,
219
+ {
220
+ main: /* @__PURE__ */ jsxs("form", { onSubmit: handleSign, children: [
221
+ pdf.file ? /* @__PURE__ */ jsx(SelectedPdfCard, { file: pdf.file, pageCount: pdf.pageCount, onRemove: pdf.clear, disabled: busy }) : /* @__PURE__ */ jsx(
222
+ FileDropzone,
223
+ {
224
+ accept: ".pdf,application/pdf",
225
+ disabled: busy,
226
+ onFiles: pdf.selectFiles,
227
+ title: "Drag and drop the PDF to sign",
228
+ hint: "or click to browse from your device"
229
+ }
230
+ ),
231
+ pdf.signed ? /* @__PURE__ */ jsx("p", { className: "mt-2 text-xs text-rose-700 dark:text-rose-300", children: "Adding a second signature to an already-signed PDF is not supported yet." }) : null,
232
+ /* @__PURE__ */ jsxs("section", { className: `mt-5 ${cardClass}`, children: [
233
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [
234
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold text-slate-900 dark:text-slate-100", children: "1. Your certificate" }),
235
+ /* @__PURE__ */ jsx("div", { role: "radiogroup", "aria-label": "Certificate source", className: "inline-flex rounded-xl border border-slate-200 p-0.5 text-xs dark:border-slate-700", children: [
236
+ ["file", "fa-file-shield", "Certificate file"],
237
+ ["token", "fa-usb", "USB token"]
238
+ ].map(([value, icon, label]) => /* @__PURE__ */ jsxs(
239
+ "button",
240
+ {
241
+ type: "button",
242
+ role: "radio",
243
+ "aria-checked": source === value,
244
+ disabled: busy,
245
+ onClick: () => switchSource(value),
246
+ className: `inline-flex items-center gap-1.5 rounded-lg px-3 py-1.5 font-medium transition ${source === value ? "bg-[#1F8CFF] text-white" : "text-slate-600 hover:bg-slate-100 dark:text-slate-300 dark:hover:bg-slate-800"}`,
247
+ children: [
248
+ /* @__PURE__ */ jsx("i", { className: `fa-${value === "token" ? "brands" : "solid"} ${icon}` }),
249
+ label
250
+ ]
251
+ },
252
+ value
253
+ )) })
254
+ ] }),
255
+ /* @__PURE__ */ jsx(
256
+ "input",
257
+ {
258
+ ref: certInputRef,
259
+ type: "file",
260
+ accept: ".pfx,.p12,application/x-pkcs12",
261
+ className: "hidden",
262
+ onChange: (event) => {
263
+ chooseCertificate(event.target.files?.[0]);
264
+ event.target.value = "";
265
+ }
266
+ }
267
+ ),
268
+ certificate ? /* @__PURE__ */ jsxs("div", { className: "mt-3", children: [
269
+ /* @__PURE__ */ jsx(
270
+ SummaryList,
271
+ {
272
+ items: [
273
+ { label: "Signer", value: certificate.commonName || "(no name)" },
274
+ { label: "Organisation", value: certificate.organization || "-" },
275
+ { label: "Issued by", value: certificate.issuerCommonName || certificate.issuerOrganization || "-" },
276
+ { label: "Valid", value: `${formatDate(certificate.notBefore)} \u2013 ${formatDate(certificate.notAfter)}` },
277
+ ...source === "token" && tokenCert ? [{ label: "Key stored on", value: tokenCert.label }] : []
278
+ ]
279
+ }
280
+ ),
281
+ expired ? /* @__PURE__ */ jsx("p", { className: "mt-2 text-xs text-rose-700 dark:text-rose-300", children: "This certificate is not valid today, so it cannot be used to sign." }) : certificate.selfSigned ? /* @__PURE__ */ jsx("p", { className: "mt-2 text-xs text-amber-700 dark:text-amber-300", children: "Self-signed certificate: the signature is valid, but viewers will show the signer as untrusted." }) : null,
282
+ /* @__PURE__ */ jsxs("button", { type: "button", onClick: forgetCertificate, disabled: busy, className: `mt-3 ${secondaryButtonClass}`, children: [
283
+ /* @__PURE__ */ jsx("i", { className: "fa-solid fa-rotate" }),
284
+ "Use a different certificate"
285
+ ] })
286
+ ] }) : source === "token" ? /* @__PURE__ */ jsxs("div", { className: "mt-3", children: [
287
+ tokens && tokens.length > 1 ? /* @__PURE__ */ jsxs("fieldset", { className: "mb-3 grid gap-2", children: [
288
+ /* @__PURE__ */ jsx("legend", { className: fieldLabelClass, children: "Choose a certificate" }),
289
+ tokens.map((token) => /* @__PURE__ */ jsxs(
290
+ "label",
291
+ {
292
+ className: "flex cursor-pointer items-start gap-3 rounded-xl border border-slate-200 px-3 py-2 text-sm dark:border-slate-700",
293
+ children: [
294
+ /* @__PURE__ */ jsx("input", { type: "radio", name: "token-certificate", className: "mt-1", onChange: () => chooseToken(token), disabled: busy }),
295
+ /* @__PURE__ */ jsxs("span", { children: [
296
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-slate-900 dark:text-slate-100", children: token.summary.commonName || "(no name)" }),
297
+ /* @__PURE__ */ jsxs("span", { className: "block text-xs text-slate-500 dark:text-slate-400", children: [
298
+ token.summary.issuerCommonName || token.summary.issuerOrganization || "Unknown issuer",
299
+ " \xB7 valid until",
300
+ " ",
301
+ formatDate(token.summary.notAfter),
302
+ " \xB7 ",
303
+ token.label
304
+ ] })
305
+ ] })
306
+ ]
307
+ },
308
+ token.id
309
+ ))
310
+ ] }) : null,
311
+ /* @__PURE__ */ jsxs("button", { type: "button", onClick: findTokens, disabled: busy, className: primaryButtonClass, children: [
312
+ /* @__PURE__ */ jsx("i", { className: `fa-solid ${isWorking ? "fa-spinner fa-spin" : "fa-magnifying-glass"}` }),
313
+ "Find my token"
314
+ ] }),
315
+ needsSetup ? /* @__PURE__ */ jsx(TokenSetup, {}) : null
316
+ ] }) : /* @__PURE__ */ jsxs("div", { className: "mt-3 grid gap-3 sm:grid-cols-[auto_minmax(0,1fr)_auto] sm:items-end", children: [
317
+ /* @__PURE__ */ jsxs("button", { type: "button", onClick: () => certInputRef.current?.click(), disabled: busy, className: secondaryButtonClass, children: [
318
+ /* @__PURE__ */ jsx("i", { className: "fa-solid fa-id-card" }),
319
+ certFile ? certFile.name : "Choose .pfx / .p12"
320
+ ] }),
321
+ /* @__PURE__ */ jsx(
322
+ PasswordInput,
323
+ {
324
+ id: "certificate-password",
325
+ label: "Certificate password",
326
+ value: certPassword,
327
+ onChange: setCertPassword,
328
+ disabled: busy || !certFile,
329
+ autoComplete: "off"
330
+ }
331
+ ),
332
+ /* @__PURE__ */ jsxs("button", { type: "button", onClick: openCertificate, disabled: busy || !certFile, className: primaryButtonClass, children: [
333
+ /* @__PURE__ */ jsx("i", { className: "fa-solid fa-unlock-keyhole" }),
334
+ "Open"
335
+ ] })
336
+ ] })
337
+ ] }),
338
+ /* @__PURE__ */ jsxs("fieldset", { className: `mt-5 ${cardClass}`, disabled: busy, children: [
339
+ /* @__PURE__ */ jsx("legend", { className: "px-1 text-sm font-semibold text-slate-900 dark:text-slate-100", children: "2. Signature details" }),
340
+ /* @__PURE__ */ jsxs("div", { className: "grid gap-4 sm:grid-cols-2", children: [
341
+ /* @__PURE__ */ jsxs("label", { className: fieldLabelClass, children: [
342
+ "Reason",
343
+ /* @__PURE__ */ jsxs("select", { value: reason, onChange: (event) => setReason(event.target.value), className: `mt-2 ${inputClass}`, children: [
344
+ REASONS.map((option) => /* @__PURE__ */ jsx("option", { value: option, children: option }, option)),
345
+ /* @__PURE__ */ jsx("option", { value: "custom", children: "Other\u2026" })
346
+ ] })
347
+ ] }),
348
+ /* @__PURE__ */ jsxs("label", { className: fieldLabelClass, children: [
349
+ "Location (optional)",
350
+ /* @__PURE__ */ jsx("input", { type: "text", value: location, maxLength: 80, onChange: (event) => setLocation(event.target.value), placeholder: "City", className: `mt-2 ${inputClass}` })
351
+ ] }),
352
+ reason === "custom" ? /* @__PURE__ */ jsxs("label", { className: `sm:col-span-2 ${fieldLabelClass}`, children: [
353
+ "Your reason",
354
+ /* @__PURE__ */ jsx("input", { type: "text", value: customReason, maxLength: 120, onChange: (event) => setCustomReason(event.target.value), className: `mt-2 ${inputClass}` })
355
+ ] }) : null
356
+ ] }),
357
+ /* @__PURE__ */ jsxs("label", { className: "mt-4 flex items-center gap-3 text-sm text-slate-700 dark:text-slate-300", children: [
358
+ /* @__PURE__ */ jsx("input", { type: "checkbox", checked: visible, onChange: (event) => setVisible(event.target.checked), className: "h-4 w-4 accent-[#1F8CFF]" }),
359
+ "Show a signature box on the page"
360
+ ] }),
361
+ visible && pdf.pageCount ? /* @__PURE__ */ jsxs("div", { className: "mt-3 grid gap-4 sm:grid-cols-[minmax(0,1fr)_auto]", children: [
362
+ /* @__PURE__ */ jsxs("div", { className: "grid gap-3 sm:grid-cols-2", children: [
363
+ /* @__PURE__ */ jsxs("label", { className: fieldLabelClass, children: [
364
+ "Page",
365
+ /* @__PURE__ */ jsx("select", { value: pageIndex, onChange: (event) => setPageIndex(Number(event.target.value)), className: `mt-2 ${inputClass}`, children: Array.from({ length: pdf.pageCount }, (_, index) => /* @__PURE__ */ jsxs("option", { value: index, children: [
366
+ "Page ",
367
+ index + 1,
368
+ index === pdf.pageCount - 1 ? " (last)" : ""
369
+ ] }, index)) })
370
+ ] }),
371
+ /* @__PURE__ */ jsxs("label", { className: fieldLabelClass, children: [
372
+ "Position",
373
+ /* @__PURE__ */ jsx("select", { value: position, onChange: (event) => setPosition(event.target.value), className: `mt-2 ${inputClass}`, children: Object.entries(POSITIONS).map(([value, option]) => /* @__PURE__ */ jsx("option", { value, children: option.label }, value)) })
374
+ ] })
375
+ ] }),
376
+ preview ? /* @__PURE__ */ jsxs("div", { className: "relative mx-auto w-28 shadow-sm", style: { aspectRatio: `${preview.width} / ${preview.height}` }, children: [
377
+ /* @__PURE__ */ jsx("img", { src: preview.url, alt: `Page ${pageIndex + 1} preview`, className: "absolute inset-0 h-full w-full" }),
378
+ /* @__PURE__ */ jsx(
379
+ "div",
380
+ {
381
+ className: "absolute border-2 border-[#1F8CFF] bg-[#1F8CFF]/15",
382
+ style: { left: `${spot.x * 100}%`, top: `${spot.y * 100}%`, width: `${BOX.width * 100}%`, height: `${BOX.height * 100}%` },
383
+ "aria-hidden": "true"
384
+ }
385
+ )
386
+ ] }) : null
387
+ ] }) : null
388
+ ] }),
389
+ /* @__PURE__ */ jsx(StatusBanner, { status }),
390
+ /* @__PURE__ */ jsxs("div", { className: "mt-5 flex flex-wrap gap-3", children: [
391
+ /* @__PURE__ */ jsxs("button", { type: "submit", disabled: busy || !canSign || reason === "custom" && !finalReason, className: primaryButtonClass, children: [
392
+ /* @__PURE__ */ jsx("i", { className: `fa-solid ${isWorking ? "fa-spinner fa-spin" : "fa-file-signature"}` }),
393
+ isWorking ? "Working..." : "Sign & Download"
394
+ ] }),
395
+ /* @__PURE__ */ jsx(BackToPdfTools, {})
396
+ ] })
397
+ ] }),
398
+ side: /* @__PURE__ */ jsxs(Fragment, { children: [
399
+ /* @__PURE__ */ jsx("h2", { className: "text-base font-semibold text-slate-900 dark:text-slate-100", children: "About digital signatures" }),
400
+ /* @__PURE__ */ jsx(
401
+ SideTips,
402
+ {
403
+ tips: [
404
+ "This is a cryptographic signature (PAdES), not just an image. Any later change to the file is detected.",
405
+ "Whether viewers show it as trusted depends on who issued your certificate, not on this tool.",
406
+ "Your .pfx file, its password, and its private key stay in this browser tab and are never uploaded.",
407
+ "USB tokens (ePass2003, ProxKey, Watchdata, mToken and others) sign through hey, a small open-source helper on your computer. The key stays on the token.",
408
+ "Check any signed PDF with Verify Signatures."
409
+ ]
410
+ }
411
+ )
412
+ ] })
413
+ }
414
+ )
415
+ }
416
+ );
417
+ }
418
+
419
+ export { CertSignPdf };
420
+ //# sourceMappingURL=chunk-FIEYAKPI.js.map
421
+ //# sourceMappingURL=chunk-FIEYAKPI.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/certsign/CertSignPdf.tsx"],"names":["spot"],"mappings":"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","file":"chunk-FIEYAKPI.js","sourcesContent":["import React, { useCallback, useEffect, useRef, useState } from \"react\";\nimport { downloadBlob, formatBytes, toBaseName } from \"../shared/files\";\nimport { PasswordInput } from \"../shared/PasswordInput\";\nimport { openRenderDocument, renderThumbnail } from \"../shared/pdfRender\";\nimport {\n BackToPdfTools,\n FileDropzone,\n PdfToolColumns,\n PdfToolPage,\n SelectedPdfCard,\n SideTips,\n StatusBanner,\n SummaryList,\n cardClass,\n fieldLabelClass,\n inputClass,\n primaryButtonClass,\n secondaryButtonClass,\n} from \"../shared/ToolLayout\";\nimport { useSinglePdf } from \"../shared/useSinglePdf\";\nimport { describeCertificate, webCryptoSigner } from \"./cms\";\nimport { signPdfWithCertificate } from \"./padesCore\";\nimport { HeyDscError, findTokenCertificates, heyTokenSigner, type TokenCertificate } from \"./heySigner\";\nimport { CertificatePasswordError, readPkcs12 } from \"./pkcs12\";\nimport { verifyPdfSignatures } from \"./verifyCore\";\n\nconst REASONS = [\n \"I am the author of this document\",\n \"I approve this document\",\n \"I have reviewed this document\",\n \"I agree to the terms of this document\",\n];\n\nconst BOX = { width: 0.38, height: 0.09 };\nconst POSITIONS = {\n \"bottom-right\": { x: 0.98 - BOX.width, y: 0.97 - BOX.height, label: \"Bottom right\" },\n \"bottom-left\": { x: 0.02, y: 0.97 - BOX.height, label: \"Bottom left\" },\n \"top-right\": { x: 0.98 - BOX.width, y: 0.03, label: \"Top right\" },\n \"top-left\": { x: 0.02, y: 0.03, label: \"Top left\" },\n};\n\nconst HEY_INSTALL = {\n windows: \"irm https://raw.githubusercontent.com/kitsyai/hey/main/install/hey.ps1 | iex\",\n unix: \"curl -fsSL https://raw.githubusercontent.com/kitsyai/hey/main/install/hey.sh | sh\",\n};\n\nfunction TokenSetup() {\n const code = \"block overflow-x-auto rounded-lg bg-slate-900 px-3 py-2 font-mono text-[11px] text-slate-100\";\n return (\n <div className=\"mt-3 rounded-xl border border-slate-200 bg-slate-50 p-3 text-xs text-slate-700 dark:border-slate-700 dark:bg-slate-900/60 dark:text-slate-300\">\n <p className=\"font-semibold text-slate-900 dark:text-slate-100\">Set up token signing (one time)</p>\n <ol className=\"mt-2 list-decimal space-y-2 pl-4\">\n <li>\n Install your token's driver (it comes from the token vendor, e.g. ePass2003 or ProxKey), then plug in the token.\n </li>\n <li>\n Install <a className=\"underline\" href=\"https://github.com/kitsyai/hey\" target=\"_blank\" rel=\"noreferrer\">hey</a>, a small\n open-source helper. In PowerShell (Windows):\n <code className={`mt-1 ${code}`}>{HEY_INSTALL.windows}</code>\n or in a terminal (macOS / Linux):\n <code className={`mt-1 ${code}`}>{HEY_INSTALL.unix}</code>\n </li>\n <li>\n Start the token signer:\n <code className={`mt-1 ${code}`}>hey up dsc</code>\n </li>\n <li>\n Click \"Find my token\" again. Your browser may ask whether this site can access devices on your local network:\n allow it, so the page can reach hey on this computer.\n </li>\n </ol>\n <p className=\"mt-2\">\n Your token's private key never leaves the token. Before every signature, your computer shows its own confirmation\n dialog naming this site and the document.\n </p>\n </div>\n );\n}\n\nconst formatDate = (date: Date) =>\n date.toLocaleDateString(undefined, { year: \"numeric\", month: \"short\", day: \"numeric\" });\n\nfunction usePagePreview(bytes, pageIndex) {\n const [preview, setPreview] = useState(null);\n useEffect(() => {\n if (!bytes) {\n setPreview(null);\n return undefined;\n }\n let cancelled = false;\n let url = null;\n let doc = null;\n (async () => {\n doc = await openRenderDocument(bytes);\n const thumb = await renderThumbnail(doc, pageIndex + 1, 220);\n url = thumb.url;\n if (cancelled) URL.revokeObjectURL(url);\n else setPreview(thumb);\n })().catch(() => {});\n return () => {\n cancelled = true;\n if (url) URL.revokeObjectURL(url);\n doc?.destroy();\n };\n }, [bytes, pageIndex]);\n return preview;\n}\n\nexport default function CertSignPdf() {\n const certInputRef = useRef(null);\n const [certFile, setCertFile] = useState(null);\n const [certPassword, setCertPassword] = useState(\"\");\n const [signer, setSigner] = useState(null);\n const [certificate, setCertificate] = useState(null);\n const [source, setSource] = useState<\"file\" | \"token\">(\"file\");\n const [tokens, setTokens] = useState<TokenCertificate[] | null>(null);\n const [tokenCert, setTokenCert] = useState<TokenCertificate | null>(null);\n const [needsSetup, setNeedsSetup] = useState(false);\n const [reason, setReason] = useState(REASONS[1]);\n const [customReason, setCustomReason] = useState(\"\");\n const [location, setLocation] = useState(\"\");\n const [visible, setVisible] = useState(true);\n const [pageIndex, setPageIndex] = useState(0);\n const [position, setPosition] = useState(\"bottom-right\");\n const [isWorking, setIsWorking] = useState(false);\n const [status, setStatus] = useState({\n tone: \"info\",\n text: \"Add a PDF, then your certificate file (.pfx / .p12) or your USB token.\",\n });\n\n useEffect(() => {\n document.title = \"Digital Signature (Certificate) | Apps\";\n }, []);\n\n const onError = useCallback((text) => setStatus({ tone: \"error\", text }), []);\n const onLoaded = useCallback(({ file, pageCount }) => {\n setPageIndex(Math.max(0, pageCount - 1));\n setStatus({ tone: \"success\", text: `${file.name} loaded. Now open your certificate.` });\n }, []);\n const pdf = useSinglePdf({ onError, onLoaded });\n const preview = usePagePreview(visible ? pdf.bytes : null, pageIndex);\n\n const forgetCertificate = () => {\n setSigner(null);\n setCertificate(null);\n setCertPassword(\"\");\n setTokenCert(null);\n };\n\n const switchSource = (next: \"file\" | \"token\") => {\n if (next === source) return;\n forgetCertificate();\n setSource(next);\n };\n\n const chooseToken = (token: TokenCertificate) => {\n setTokenCert(token);\n setCertificate(token.summary);\n setSigner(heyTokenSigner(token));\n };\n\n const findTokens = async () => {\n setIsWorking(true);\n setNeedsSetup(false);\n try {\n const found = await findTokenCertificates();\n setTokens(found);\n if (found.length === 1) chooseToken(found[0]);\n setStatus(\n found.length\n ? { tone: \"success\", text: `Found ${found.length} certificate${found.length === 1 ? \"\" : \"s\"} on your token.` }\n : { tone: \"error\", text: \"hey is running, but no certificate was found. Is the token plugged in and its driver installed?\" }\n );\n } catch (error) {\n const setup = error instanceof HeyDscError && [\"no_hub\", \"not_set_up\", \"not_running\"].includes(error.code);\n setNeedsSetup(setup);\n setTokens(null);\n setStatus({ tone: \"error\", text: error?.message || \"Could not reach the token.\" });\n } finally {\n setIsWorking(false);\n }\n };\n\n const chooseCertificate = (file) => {\n if (!file) return;\n forgetCertificate();\n setCertFile(file);\n };\n\n const openCertificate = async () => {\n if (!certFile) return;\n setIsWorking(true);\n try {\n const identity = readPkcs12(new Uint8Array(await certFile.arrayBuffer()), certPassword);\n const summary = describeCertificate(identity.certificate);\n // The key is imported as non-extractable; drop our copy of its bytes.\n const nextSigner = await webCryptoSigner(identity);\n identity.privateKeyPkcs8.fill(0);\n setSigner(nextSigner);\n setCertificate(summary);\n setCertPassword(\"\");\n setStatus({ tone: \"success\", text: `Certificate for ${summary.commonName || \"(no name)\"} opened.` });\n } catch (error) {\n setStatus({\n tone: \"error\",\n text: error instanceof CertificatePasswordError ? error.message : error?.message || \"Could not open the certificate.\",\n });\n } finally {\n setIsWorking(false);\n }\n };\n\n const now = new Date();\n const expired = certificate && (now > certificate.notAfter || now < certificate.notBefore);\n const finalReason = reason === \"custom\" ? customReason.trim() : reason;\n const canSign = Boolean(pdf.bytes && signer && !pdf.signed && !expired);\n\n const handleSign = async (event) => {\n event.preventDefault();\n if (!canSign) return;\n setIsWorking(true);\n setStatus({\n tone: \"info\",\n text:\n source === \"token\"\n ? \"Confirm the signature in the dialog on your computer (it may ask for the token PIN)...\"\n : \"Signing your PDF...\",\n });\n try {\n const spot = POSITIONS[position];\n // The token signer shows the document name in its confirmation dialog.\n const activeSigner = source === \"token\" && tokenCert ? heyTokenSigner(tokenCert, { document: pdf.file.name }) : signer;\n const signed = await signPdfWithCertificate(pdf.bytes, activeSigner, {\n reason: finalReason,\n location: location.trim(),\n visible: visible ? { pageIndex, x: spot.x, y: spot.y, ...BOX } : null,\n });\n // Check our own output before handing it over.\n const [check] = await verifyPdfSignatures(signed);\n if (!check?.intact || !check.coversWholeFile) {\n throw new Error(\"The signed file did not pass verification. Nothing was downloaded.\");\n }\n const blob = new Blob([signed as BlobPart], { type: \"application/pdf\" });\n downloadBlob(blob, `${toBaseName(pdf.file.name, \"document\")}-signed.pdf`);\n setStatus({\n tone: \"success\",\n text: `Signed and verified (${formatBytes(blob.size)}). Your download has started.`,\n });\n } catch (error) {\n setStatus({ tone: \"error\", text: `Signing failed: ${error?.message || \"Please try again.\"}` });\n } finally {\n setIsWorking(false);\n }\n };\n\n const busy = isWorking || pdf.isReading;\n const spot = POSITIONS[position];\n\n return (\n <PdfToolPage\n title=\"Digital Signature (Certificate)\"\n description=\"Sign a PDF with your digital signature certificate (DSC): a .pfx / .p12 file or a USB token. Viewers such as Adobe Reader show who signed it and flag any later change. Private keys never leave your browser or your token.\"\n stats={[\n { value: \"PAdES\", label: \"Standard\" },\n { value: certificate ? \"Ready\" : \"-\", label: \"Certificate\" },\n ]}\n >\n <PdfToolColumns\n main={\n <form onSubmit={handleSign}>\n {pdf.file ? (\n <SelectedPdfCard file={pdf.file} pageCount={pdf.pageCount} onRemove={pdf.clear} disabled={busy} />\n ) : (\n <FileDropzone\n accept=\".pdf,application/pdf\"\n disabled={busy}\n onFiles={pdf.selectFiles}\n title=\"Drag and drop the PDF to sign\"\n hint=\"or click to browse from your device\"\n />\n )}\n {pdf.signed ? (\n <p className=\"mt-2 text-xs text-rose-700 dark:text-rose-300\">\n Adding a second signature to an already-signed PDF is not supported yet.\n </p>\n ) : null}\n\n <section className={`mt-5 ${cardClass}`}>\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <h3 className=\"text-sm font-semibold text-slate-900 dark:text-slate-100\">1. Your certificate</h3>\n <div role=\"radiogroup\" aria-label=\"Certificate source\" className=\"inline-flex rounded-xl border border-slate-200 p-0.5 text-xs dark:border-slate-700\">\n {[\n [\"file\", \"fa-file-shield\", \"Certificate file\"],\n [\"token\", \"fa-usb\", \"USB token\"],\n ].map(([value, icon, label]) => (\n <button\n key={value}\n type=\"button\"\n role=\"radio\"\n aria-checked={source === value}\n disabled={busy}\n onClick={() => switchSource(value as \"file\" | \"token\")}\n className={`inline-flex items-center gap-1.5 rounded-lg px-3 py-1.5 font-medium transition ${\n source === value\n ? \"bg-[#1F8CFF] text-white\"\n : \"text-slate-600 hover:bg-slate-100 dark:text-slate-300 dark:hover:bg-slate-800\"\n }`}\n >\n <i className={`fa-${value === \"token\" ? \"brands\" : \"solid\"} ${icon}`} />\n {label}\n </button>\n ))}\n </div>\n </div>\n <input\n ref={certInputRef}\n type=\"file\"\n accept=\".pfx,.p12,application/x-pkcs12\"\n className=\"hidden\"\n onChange={(event) => {\n chooseCertificate(event.target.files?.[0]);\n event.target.value = \"\";\n }}\n />\n {certificate ? (\n <div className=\"mt-3\">\n <SummaryList\n items={[\n { label: \"Signer\", value: certificate.commonName || \"(no name)\" },\n { label: \"Organisation\", value: certificate.organization || \"-\" },\n { label: \"Issued by\", value: certificate.issuerCommonName || certificate.issuerOrganization || \"-\" },\n { label: \"Valid\", value: `${formatDate(certificate.notBefore)} – ${formatDate(certificate.notAfter)}` },\n ...(source === \"token\" && tokenCert ? [{ label: \"Key stored on\", value: tokenCert.label }] : []),\n ]}\n />\n {expired ? (\n <p className=\"mt-2 text-xs text-rose-700 dark:text-rose-300\">This certificate is not valid today, so it cannot be used to sign.</p>\n ) : certificate.selfSigned ? (\n <p className=\"mt-2 text-xs text-amber-700 dark:text-amber-300\">\n Self-signed certificate: the signature is valid, but viewers will show the signer as untrusted.\n </p>\n ) : null}\n <button type=\"button\" onClick={forgetCertificate} disabled={busy} className={`mt-3 ${secondaryButtonClass}`}>\n <i className=\"fa-solid fa-rotate\" />\n Use a different certificate\n </button>\n </div>\n ) : source === \"token\" ? (\n <div className=\"mt-3\">\n {tokens && tokens.length > 1 ? (\n <fieldset className=\"mb-3 grid gap-2\">\n <legend className={fieldLabelClass}>Choose a certificate</legend>\n {tokens.map((token) => (\n <label\n key={token.id}\n className=\"flex cursor-pointer items-start gap-3 rounded-xl border border-slate-200 px-3 py-2 text-sm dark:border-slate-700\"\n >\n <input type=\"radio\" name=\"token-certificate\" className=\"mt-1\" onChange={() => chooseToken(token)} disabled={busy} />\n <span>\n <span className=\"font-medium text-slate-900 dark:text-slate-100\">{token.summary.commonName || \"(no name)\"}</span>\n <span className=\"block text-xs text-slate-500 dark:text-slate-400\">\n {token.summary.issuerCommonName || token.summary.issuerOrganization || \"Unknown issuer\"} · valid until{\" \"}\n {formatDate(token.summary.notAfter)} · {token.label}\n </span>\n </span>\n </label>\n ))}\n </fieldset>\n ) : null}\n <button type=\"button\" onClick={findTokens} disabled={busy} className={primaryButtonClass}>\n <i className={`fa-solid ${isWorking ? \"fa-spinner fa-spin\" : \"fa-magnifying-glass\"}`} />\n Find my token\n </button>\n {needsSetup ? <TokenSetup /> : null}\n </div>\n ) : (\n <div className=\"mt-3 grid gap-3 sm:grid-cols-[auto_minmax(0,1fr)_auto] sm:items-end\">\n <button type=\"button\" onClick={() => certInputRef.current?.click()} disabled={busy} className={secondaryButtonClass}>\n <i className=\"fa-solid fa-id-card\" />\n {certFile ? certFile.name : \"Choose .pfx / .p12\"}\n </button>\n <PasswordInput\n id=\"certificate-password\"\n label=\"Certificate password\"\n value={certPassword}\n onChange={setCertPassword}\n disabled={busy || !certFile}\n autoComplete=\"off\"\n />\n <button type=\"button\" onClick={openCertificate} disabled={busy || !certFile} className={primaryButtonClass}>\n <i className=\"fa-solid fa-unlock-keyhole\" />\n Open\n </button>\n </div>\n )}\n </section>\n\n <fieldset className={`mt-5 ${cardClass}`} disabled={busy}>\n <legend className=\"px-1 text-sm font-semibold text-slate-900 dark:text-slate-100\">2. Signature details</legend>\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <label className={fieldLabelClass}>\n Reason\n <select value={reason} onChange={(event) => setReason(event.target.value)} className={`mt-2 ${inputClass}`}>\n {REASONS.map((option) => (\n <option key={option} value={option}>\n {option}\n </option>\n ))}\n <option value=\"custom\">Other…</option>\n </select>\n </label>\n <label className={fieldLabelClass}>\n Location (optional)\n <input type=\"text\" value={location} maxLength={80} onChange={(event) => setLocation(event.target.value)} placeholder=\"City\" className={`mt-2 ${inputClass}`} />\n </label>\n {reason === \"custom\" ? (\n <label className={`sm:col-span-2 ${fieldLabelClass}`}>\n Your reason\n <input type=\"text\" value={customReason} maxLength={120} onChange={(event) => setCustomReason(event.target.value)} className={`mt-2 ${inputClass}`} />\n </label>\n ) : null}\n </div>\n\n <label className=\"mt-4 flex items-center gap-3 text-sm text-slate-700 dark:text-slate-300\">\n <input type=\"checkbox\" checked={visible} onChange={(event) => setVisible(event.target.checked)} className=\"h-4 w-4 accent-[#1F8CFF]\" />\n Show a signature box on the page\n </label>\n {visible && pdf.pageCount ? (\n <div className=\"mt-3 grid gap-4 sm:grid-cols-[minmax(0,1fr)_auto]\">\n <div className=\"grid gap-3 sm:grid-cols-2\">\n <label className={fieldLabelClass}>\n Page\n <select value={pageIndex} onChange={(event) => setPageIndex(Number(event.target.value))} className={`mt-2 ${inputClass}`}>\n {Array.from({ length: pdf.pageCount }, (_, index) => (\n <option key={index} value={index}>\n Page {index + 1}\n {index === pdf.pageCount - 1 ? \" (last)\" : \"\"}\n </option>\n ))}\n </select>\n </label>\n <label className={fieldLabelClass}>\n Position\n <select value={position} onChange={(event) => setPosition(event.target.value)} className={`mt-2 ${inputClass}`}>\n {Object.entries(POSITIONS).map(([value, option]) => (\n <option key={value} value={value}>\n {option.label}\n </option>\n ))}\n </select>\n </label>\n </div>\n {preview ? (\n <div className=\"relative mx-auto w-28 shadow-sm\" style={{ aspectRatio: `${preview.width} / ${preview.height}` }}>\n <img src={preview.url} alt={`Page ${pageIndex + 1} preview`} className=\"absolute inset-0 h-full w-full\" />\n <div\n className=\"absolute border-2 border-[#1F8CFF] bg-[#1F8CFF]/15\"\n style={{ left: `${spot.x * 100}%`, top: `${spot.y * 100}%`, width: `${BOX.width * 100}%`, height: `${BOX.height * 100}%` }}\n aria-hidden=\"true\"\n />\n </div>\n ) : null}\n </div>\n ) : null}\n </fieldset>\n\n <StatusBanner status={status} />\n\n <div className=\"mt-5 flex flex-wrap gap-3\">\n <button type=\"submit\" disabled={busy || !canSign || (reason === \"custom\" && !finalReason)} className={primaryButtonClass}>\n <i className={`fa-solid ${isWorking ? \"fa-spinner fa-spin\" : \"fa-file-signature\"}`} />\n {isWorking ? \"Working...\" : \"Sign & Download\"}\n </button>\n <BackToPdfTools />\n </div>\n </form>\n }\n side={\n <>\n <h2 className=\"text-base font-semibold text-slate-900 dark:text-slate-100\">About digital signatures</h2>\n <SideTips\n tips={[\n \"This is a cryptographic signature (PAdES), not just an image. Any later change to the file is detected.\",\n \"Whether viewers show it as trusted depends on who issued your certificate, not on this tool.\",\n \"Your .pfx file, its password, and its private key stay in this browser tab and are never uploaded.\",\n \"USB tokens (ePass2003, ProxKey, Watchdata, mToken and others) sign through hey, a small open-source helper on your computer. The key stays on the token.\",\n \"Check any signed PDF with Verify Signatures.\",\n ]}\n />\n </>\n }\n />\n </PdfToolPage>\n );\n}\n"]}