@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.
- package/CHANGELOG.md +32 -0
- package/LICENSE +201 -0
- package/NOTICE +16 -0
- package/README.md +256 -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-4ZEZ2GLD.js +353 -0
- package/dist/chunk-4ZEZ2GLD.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-FIEYAKPI.js +421 -0
- package/dist/chunk-FIEYAKPI.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-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 +517 -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,47 @@
|
|
|
1
|
+
import { PDFDict, PDFArray, PDFRef, PDFName, PDFString, PDFHexString } from 'pdf-lib';
|
|
2
|
+
|
|
3
|
+
// src/shared/signatureFields.ts
|
|
4
|
+
var N = (name) => PDFName.of(name);
|
|
5
|
+
function text(value) {
|
|
6
|
+
return value instanceof PDFString || value instanceof PDFHexString ? value.decodeText() : "";
|
|
7
|
+
}
|
|
8
|
+
function signatureFields(doc) {
|
|
9
|
+
const acroForm = doc.catalog.lookup(N("AcroForm"));
|
|
10
|
+
const fields = acroForm instanceof PDFDict ? acroForm.lookup(N("Fields")) : null;
|
|
11
|
+
if (!(fields instanceof PDFArray)) return [];
|
|
12
|
+
const found = [];
|
|
13
|
+
const seen = /* @__PURE__ */ new Set();
|
|
14
|
+
const walk = (ref, parentName, inheritedType) => {
|
|
15
|
+
if (ref instanceof PDFRef) {
|
|
16
|
+
if (seen.has(ref.toString())) return;
|
|
17
|
+
seen.add(ref.toString());
|
|
18
|
+
}
|
|
19
|
+
const field = ref instanceof PDFRef ? doc.context.lookup(ref) : ref;
|
|
20
|
+
if (!(field instanceof PDFDict)) return;
|
|
21
|
+
const partial = text(field.lookup(N("T")));
|
|
22
|
+
const name = [parentName, partial].filter(Boolean).join(".");
|
|
23
|
+
const type = field.lookup(N("FT")) ?? inheritedType;
|
|
24
|
+
const kids = field.lookup(N("Kids"));
|
|
25
|
+
const kidFields = kids instanceof PDFArray ? kids.asArray().filter((kid) => {
|
|
26
|
+
const dict = kid instanceof PDFRef ? doc.context.lookup(kid) : kid;
|
|
27
|
+
return dict instanceof PDFDict && dict.has(N("T"));
|
|
28
|
+
}) : [];
|
|
29
|
+
if (kidFields.length) {
|
|
30
|
+
kidFields.forEach((kid) => walk(kid, name, type));
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (type === N("Sig")) {
|
|
34
|
+
const value = field.lookup(N("V"));
|
|
35
|
+
found.push({ name, field, value: value instanceof PDFDict ? value : null });
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
fields.asArray().forEach((ref) => walk(ref, "", void 0));
|
|
39
|
+
return found;
|
|
40
|
+
}
|
|
41
|
+
function hasDigitalSignatures(doc) {
|
|
42
|
+
return signatureFields(doc).some((field) => field.value);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export { hasDigitalSignatures, signatureFields };
|
|
46
|
+
//# sourceMappingURL=chunk-7MTEMQLL.js.map
|
|
47
|
+
//# sourceMappingURL=chunk-7MTEMQLL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/shared/signatureFields.ts"],"names":[],"mappings":"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","file":"chunk-7MTEMQLL.js","sourcesContent":["import { PDFArray, PDFDict, PDFDocument, PDFName, PDFRef, PDFString, PDFHexString } from \"pdf-lib\";\n\nconst N = (name: string) => PDFName.of(name);\n\nexport type SignatureField = {\n /** Fully qualified field name, e.g. \"Signature1\". */\n name: string;\n field: PDFDict;\n /** The signature dictionary (/V), when the field has been signed. */\n value: PDFDict | null;\n};\n\nfunction text(value: unknown) {\n return value instanceof PDFString || value instanceof PDFHexString ? value.decodeText() : \"\";\n}\n\n/** Every signature field in the AcroForm tree (FT is inheritable). */\nexport function signatureFields(doc: PDFDocument): SignatureField[] {\n const acroForm = doc.catalog.lookup(N(\"AcroForm\"));\n const fields = acroForm instanceof PDFDict ? acroForm.lookup(N(\"Fields\")) : null;\n if (!(fields instanceof PDFArray)) return [];\n\n const found: SignatureField[] = [];\n const seen = new Set<string>();\n const walk = (ref: unknown, parentName: string, inheritedType: unknown) => {\n if (ref instanceof PDFRef) {\n if (seen.has(ref.toString())) return;\n seen.add(ref.toString());\n }\n const field = ref instanceof PDFRef ? doc.context.lookup(ref) : ref;\n if (!(field instanceof PDFDict)) return;\n const partial = text(field.lookup(N(\"T\")));\n const name = [parentName, partial].filter(Boolean).join(\".\");\n const type = field.lookup(N(\"FT\")) ?? inheritedType;\n const kids = field.lookup(N(\"Kids\"));\n const kidFields = kids instanceof PDFArray\n ? kids.asArray().filter((kid) => {\n const dict = kid instanceof PDFRef ? doc.context.lookup(kid) : kid;\n return dict instanceof PDFDict && dict.has(N(\"T\"));\n })\n : [];\n if (kidFields.length) {\n kidFields.forEach((kid) => walk(kid, name, type));\n return;\n }\n if (type === N(\"Sig\")) {\n const value = field.lookup(N(\"V\"));\n found.push({ name, field, value: value instanceof PDFDict ? value : null });\n }\n };\n fields.asArray().forEach((ref) => walk(ref, \"\", undefined));\n return found;\n}\n\n/** True when the PDF carries at least one applied digital signature. */\nexport function hasDigitalSignatures(doc: PDFDocument) {\n return signatureFields(doc).some((field) => field.value);\n}\n"]}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// src/shared/zip.ts
|
|
2
|
+
var CRC_TABLE = (() => {
|
|
3
|
+
const table = new Uint32Array(256);
|
|
4
|
+
for (let n = 0; n < 256; n += 1) {
|
|
5
|
+
let c = n;
|
|
6
|
+
for (let k = 0; k < 8; k += 1) {
|
|
7
|
+
c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
|
|
8
|
+
}
|
|
9
|
+
table[n] = c >>> 0;
|
|
10
|
+
}
|
|
11
|
+
return table;
|
|
12
|
+
})();
|
|
13
|
+
function crc32(bytes) {
|
|
14
|
+
let crc = 4294967295;
|
|
15
|
+
for (let i = 0; i < bytes.length; i += 1) {
|
|
16
|
+
crc = CRC_TABLE[(crc ^ bytes[i]) & 255] ^ crc >>> 8;
|
|
17
|
+
}
|
|
18
|
+
return (crc ^ 4294967295) >>> 0;
|
|
19
|
+
}
|
|
20
|
+
function dosDateTime(date) {
|
|
21
|
+
const time = date.getHours() << 11 | date.getMinutes() << 5 | Math.floor(date.getSeconds() / 2);
|
|
22
|
+
const day = Math.max(date.getFullYear(), 1980) - 1980 << 9 | date.getMonth() + 1 << 5 | date.getDate();
|
|
23
|
+
return { time, day };
|
|
24
|
+
}
|
|
25
|
+
function createZip(entries, date = /* @__PURE__ */ new Date()) {
|
|
26
|
+
const encoder = new TextEncoder();
|
|
27
|
+
const { time, day } = dosDateTime(date);
|
|
28
|
+
const localParts = [];
|
|
29
|
+
const centralParts = [];
|
|
30
|
+
let offset = 0;
|
|
31
|
+
for (const entry of entries) {
|
|
32
|
+
const name = encoder.encode(entry.name);
|
|
33
|
+
const crc = crc32(entry.bytes);
|
|
34
|
+
const size = entry.bytes.length;
|
|
35
|
+
const local = new DataView(new ArrayBuffer(30));
|
|
36
|
+
local.setUint32(0, 67324752, true);
|
|
37
|
+
local.setUint16(4, 20, true);
|
|
38
|
+
local.setUint16(6, 2048, true);
|
|
39
|
+
local.setUint16(8, 0, true);
|
|
40
|
+
local.setUint16(10, time, true);
|
|
41
|
+
local.setUint16(12, day, true);
|
|
42
|
+
local.setUint32(14, crc, true);
|
|
43
|
+
local.setUint32(18, size, true);
|
|
44
|
+
local.setUint32(22, size, true);
|
|
45
|
+
local.setUint16(26, name.length, true);
|
|
46
|
+
localParts.push(new Uint8Array(local.buffer), name, entry.bytes);
|
|
47
|
+
const central = new DataView(new ArrayBuffer(46));
|
|
48
|
+
central.setUint32(0, 33639248, true);
|
|
49
|
+
central.setUint16(4, 20, true);
|
|
50
|
+
central.setUint16(6, 20, true);
|
|
51
|
+
central.setUint16(8, 2048, true);
|
|
52
|
+
central.setUint16(10, 0, true);
|
|
53
|
+
central.setUint16(12, time, true);
|
|
54
|
+
central.setUint16(14, day, true);
|
|
55
|
+
central.setUint32(16, crc, true);
|
|
56
|
+
central.setUint32(20, size, true);
|
|
57
|
+
central.setUint32(24, size, true);
|
|
58
|
+
central.setUint16(28, name.length, true);
|
|
59
|
+
central.setUint32(42, offset, true);
|
|
60
|
+
centralParts.push(new Uint8Array(central.buffer), name);
|
|
61
|
+
offset += 30 + name.length + size;
|
|
62
|
+
}
|
|
63
|
+
const centralSize = centralParts.reduce((sum, part) => sum + part.length, 0);
|
|
64
|
+
const end = new DataView(new ArrayBuffer(22));
|
|
65
|
+
end.setUint32(0, 101010256, true);
|
|
66
|
+
end.setUint16(8, entries.length, true);
|
|
67
|
+
end.setUint16(10, entries.length, true);
|
|
68
|
+
end.setUint32(12, centralSize, true);
|
|
69
|
+
end.setUint32(16, offset, true);
|
|
70
|
+
const parts = [...localParts, ...centralParts, new Uint8Array(end.buffer)];
|
|
71
|
+
const total = parts.reduce((sum, part) => sum + part.length, 0);
|
|
72
|
+
const out = new Uint8Array(total);
|
|
73
|
+
let cursor = 0;
|
|
74
|
+
for (const part of parts) {
|
|
75
|
+
out.set(part, cursor);
|
|
76
|
+
cursor += part.length;
|
|
77
|
+
}
|
|
78
|
+
return out;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export { crc32, createZip };
|
|
82
|
+
//# sourceMappingURL=chunk-7V4SWTUK.js.map
|
|
83
|
+
//# sourceMappingURL=chunk-7V4SWTUK.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/shared/zip.ts"],"names":[],"mappings":"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","file":"chunk-7V4SWTUK.js","sourcesContent":["// Minimal ZIP writer (STORE, no compression). PDFs are already compressed, so\n// this keeps multi-file downloads dependency-free.\n\nconst CRC_TABLE = (() => {\n const table = new Uint32Array(256);\n for (let n = 0; n < 256; n += 1) {\n let c = n;\n for (let k = 0; k < 8; k += 1) {\n c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;\n }\n table[n] = c >>> 0;\n }\n return table;\n})();\n\nexport function crc32(bytes: Uint8Array) {\n let crc = 0xffffffff;\n for (let i = 0; i < bytes.length; i += 1) {\n crc = CRC_TABLE[(crc ^ bytes[i]) & 0xff] ^ (crc >>> 8);\n }\n return (crc ^ 0xffffffff) >>> 0;\n}\n\nfunction dosDateTime(date: Date) {\n const time =\n (date.getHours() << 11) | (date.getMinutes() << 5) | Math.floor(date.getSeconds() / 2);\n const day =\n ((Math.max(date.getFullYear(), 1980) - 1980) << 9) |\n ((date.getMonth() + 1) << 5) |\n date.getDate();\n return { time, day };\n}\n\nexport type ZipEntry = { name: string; bytes: Uint8Array };\n\nexport function createZip(entries: ZipEntry[], date = new Date()) {\n const encoder = new TextEncoder();\n const { time, day } = dosDateTime(date);\n const localParts: Uint8Array[] = [];\n const centralParts: Uint8Array[] = [];\n let offset = 0;\n\n for (const entry of entries) {\n const name = encoder.encode(entry.name);\n const crc = crc32(entry.bytes);\n const size = entry.bytes.length;\n\n const local = new DataView(new ArrayBuffer(30));\n local.setUint32(0, 0x04034b50, true);\n local.setUint16(4, 20, true);\n local.setUint16(6, 0x0800, true); // UTF-8 names\n local.setUint16(8, 0, true); // STORE\n local.setUint16(10, time, true);\n local.setUint16(12, day, true);\n local.setUint32(14, crc, true);\n local.setUint32(18, size, true);\n local.setUint32(22, size, true);\n local.setUint16(26, name.length, true);\n localParts.push(new Uint8Array(local.buffer), name, entry.bytes);\n\n const central = new DataView(new ArrayBuffer(46));\n central.setUint32(0, 0x02014b50, true);\n central.setUint16(4, 20, true);\n central.setUint16(6, 20, true);\n central.setUint16(8, 0x0800, true);\n central.setUint16(10, 0, true);\n central.setUint16(12, time, true);\n central.setUint16(14, day, true);\n central.setUint32(16, crc, true);\n central.setUint32(20, size, true);\n central.setUint32(24, size, true);\n central.setUint16(28, name.length, true);\n central.setUint32(42, offset, true);\n centralParts.push(new Uint8Array(central.buffer), name);\n\n offset += 30 + name.length + size;\n }\n\n const centralSize = centralParts.reduce((sum, part) => sum + part.length, 0);\n const end = new DataView(new ArrayBuffer(22));\n end.setUint32(0, 0x06054b50, true);\n end.setUint16(8, entries.length, true);\n end.setUint16(10, entries.length, true);\n end.setUint32(12, centralSize, true);\n end.setUint32(16, offset, true);\n\n const parts = [...localParts, ...centralParts, new Uint8Array(end.buffer)];\n const total = parts.reduce((sum, part) => sum + part.length, 0);\n const out = new Uint8Array(total);\n let cursor = 0;\n for (const part of parts) {\n out.set(part, cursor);\n cursor += part.length;\n }\n return out;\n}\n"]}
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import { readFormFields, fillForm } from './chunk-CYTWDJE2.js';
|
|
2
|
+
import { useSinglePdf } from './chunk-BK3MYSUS.js';
|
|
3
|
+
import { PdfToolPage, PdfToolColumns, SummaryList, SideTips, SelectedPdfCard, FileDropzone, cardClass, StatusBanner, primaryButtonClass, secondaryButtonClass, BackToPdfTools, fieldLabelClass, inputClass } from './chunk-H6EERRCR.js';
|
|
4
|
+
import { downloadBlob, toBaseName, formatBytes } from './chunk-EKHEY3LI.js';
|
|
5
|
+
import { useState, useEffect, useCallback, useMemo } from 'react';
|
|
6
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
7
|
+
|
|
8
|
+
function friendlyName(name) {
|
|
9
|
+
const last = name.split(".").pop() || name;
|
|
10
|
+
return last.replace(/\[\d+\]$/, "").replace(/[_-]+/g, " ").trim() || name;
|
|
11
|
+
}
|
|
12
|
+
function initialValues(fields) {
|
|
13
|
+
return Object.fromEntries(fields.map((field) => [field.name, field.value]));
|
|
14
|
+
}
|
|
15
|
+
function FieldInput({ field, value, onChange, disabled }) {
|
|
16
|
+
const id = `field-${field.name}`;
|
|
17
|
+
const locked = disabled || field.readOnly;
|
|
18
|
+
if (field.kind === "checkbox") {
|
|
19
|
+
return /* @__PURE__ */ jsxs("label", { htmlFor: id, className: "flex items-center gap-3 text-sm text-slate-800 dark:text-slate-200", children: [
|
|
20
|
+
/* @__PURE__ */ jsx(
|
|
21
|
+
"input",
|
|
22
|
+
{
|
|
23
|
+
id,
|
|
24
|
+
type: "checkbox",
|
|
25
|
+
checked: Boolean(value),
|
|
26
|
+
disabled: locked,
|
|
27
|
+
onChange: (event) => onChange(event.target.checked),
|
|
28
|
+
className: "h-4 w-4 accent-[#1F8CFF]"
|
|
29
|
+
}
|
|
30
|
+
),
|
|
31
|
+
friendlyName(field.name),
|
|
32
|
+
field.required ? /* @__PURE__ */ jsx("span", { className: "text-rose-600", children: "*" }) : null
|
|
33
|
+
] });
|
|
34
|
+
}
|
|
35
|
+
const label = /* @__PURE__ */ jsxs("span", { className: fieldLabelClass, title: field.name, children: [
|
|
36
|
+
friendlyName(field.name),
|
|
37
|
+
field.required ? /* @__PURE__ */ jsx("span", { className: "ml-1 text-rose-600", children: "*" }) : null,
|
|
38
|
+
field.readOnly ? /* @__PURE__ */ jsx("span", { className: "ml-2 normal-case tracking-normal text-slate-400", children: "(read-only)" }) : null
|
|
39
|
+
] });
|
|
40
|
+
if (field.kind === "radio") {
|
|
41
|
+
return /* @__PURE__ */ jsxs("fieldset", { disabled: locked, children: [
|
|
42
|
+
/* @__PURE__ */ jsx("legend", { children: label }),
|
|
43
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-2 flex flex-wrap gap-3", children: [
|
|
44
|
+
field.options.map((option) => /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2 text-sm text-slate-800 dark:text-slate-200", children: [
|
|
45
|
+
/* @__PURE__ */ jsx(
|
|
46
|
+
"input",
|
|
47
|
+
{
|
|
48
|
+
type: "radio",
|
|
49
|
+
name: id,
|
|
50
|
+
checked: value === option,
|
|
51
|
+
onChange: () => onChange(option),
|
|
52
|
+
className: "h-4 w-4 accent-[#1F8CFF]"
|
|
53
|
+
}
|
|
54
|
+
),
|
|
55
|
+
option
|
|
56
|
+
] }, option)),
|
|
57
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => onChange(""), className: "text-xs text-slate-500 underline", children: "Clear" })
|
|
58
|
+
] })
|
|
59
|
+
] });
|
|
60
|
+
}
|
|
61
|
+
if (field.kind === "dropdown") {
|
|
62
|
+
return /* @__PURE__ */ jsxs("label", { htmlFor: id, className: "block", children: [
|
|
63
|
+
label,
|
|
64
|
+
/* @__PURE__ */ jsxs("select", { id, value, disabled: locked, onChange: (event) => onChange(event.target.value), className: `mt-2 ${inputClass}`, children: [
|
|
65
|
+
/* @__PURE__ */ jsx("option", { value: "", children: "\u2014" }),
|
|
66
|
+
field.options.map((option) => /* @__PURE__ */ jsx("option", { value: option, children: option }, option))
|
|
67
|
+
] })
|
|
68
|
+
] });
|
|
69
|
+
}
|
|
70
|
+
if (field.kind === "list") {
|
|
71
|
+
return /* @__PURE__ */ jsxs("label", { htmlFor: id, className: "block", children: [
|
|
72
|
+
label,
|
|
73
|
+
/* @__PURE__ */ jsxs(
|
|
74
|
+
"select",
|
|
75
|
+
{
|
|
76
|
+
id,
|
|
77
|
+
multiple: field.multiSelect,
|
|
78
|
+
value: field.multiSelect ? value : value[0] ?? "",
|
|
79
|
+
disabled: locked,
|
|
80
|
+
onChange: (event) => onChange(Array.from(event.target.selectedOptions, (option) => option.value).filter(Boolean)),
|
|
81
|
+
className: `mt-2 ${inputClass}`,
|
|
82
|
+
children: [
|
|
83
|
+
field.multiSelect ? null : /* @__PURE__ */ jsx("option", { value: "", children: "\u2014" }),
|
|
84
|
+
field.options.map((option) => /* @__PURE__ */ jsx("option", { value: option, children: option }, option))
|
|
85
|
+
]
|
|
86
|
+
}
|
|
87
|
+
)
|
|
88
|
+
] });
|
|
89
|
+
}
|
|
90
|
+
if (field.kind === "text") {
|
|
91
|
+
const props = {
|
|
92
|
+
id,
|
|
93
|
+
value,
|
|
94
|
+
disabled: locked,
|
|
95
|
+
maxLength: field.maxLength,
|
|
96
|
+
onChange: (event) => onChange(event.target.value),
|
|
97
|
+
className: `mt-2 ${inputClass}`
|
|
98
|
+
};
|
|
99
|
+
return /* @__PURE__ */ jsxs("label", { htmlFor: id, className: "block", children: [
|
|
100
|
+
label,
|
|
101
|
+
field.multiline ? /* @__PURE__ */ jsx("textarea", { rows: 3, ...props }) : /* @__PURE__ */ jsx("input", { type: "text", ...props }),
|
|
102
|
+
field.maxLength ? /* @__PURE__ */ jsxs("span", { className: "mt-1 block text-right text-[11px] text-slate-400", children: [
|
|
103
|
+
String(value).length,
|
|
104
|
+
"/",
|
|
105
|
+
field.maxLength
|
|
106
|
+
] }) : null
|
|
107
|
+
] });
|
|
108
|
+
}
|
|
109
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
110
|
+
label,
|
|
111
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-slate-500 dark:text-slate-400", children: field.kind === "signature" ? "Signature field: use Sign PDF to add a visible signature." : "Button: nothing to fill." })
|
|
112
|
+
] });
|
|
113
|
+
}
|
|
114
|
+
function FillPdfForm() {
|
|
115
|
+
const [fields, setFields] = useState([]);
|
|
116
|
+
const [values, setValues] = useState({});
|
|
117
|
+
const [hasXfa, setHasXfa] = useState(false);
|
|
118
|
+
const [flatten, setFlatten] = useState(false);
|
|
119
|
+
const [isSaving, setIsSaving] = useState(false);
|
|
120
|
+
const [status, setStatus] = useState({
|
|
121
|
+
tone: "info",
|
|
122
|
+
text: "Add a PDF form to fill in its fields."
|
|
123
|
+
});
|
|
124
|
+
useEffect(() => {
|
|
125
|
+
document.title = "Fill PDF Forms | Apps";
|
|
126
|
+
}, []);
|
|
127
|
+
const onError = useCallback((text) => setStatus({ tone: "error", text }), []);
|
|
128
|
+
const onLoaded = useCallback(async ({ file, bytes }) => {
|
|
129
|
+
try {
|
|
130
|
+
const result = await readFormFields(bytes);
|
|
131
|
+
setFields(result.fields);
|
|
132
|
+
setValues(initialValues(result.fields));
|
|
133
|
+
setHasXfa(result.hasXfa);
|
|
134
|
+
setStatus(
|
|
135
|
+
result.fields.length ? { tone: "success", text: `${file.name} has ${result.fields.length} field${result.fields.length === 1 ? "" : "s"}. Fill them in and download.` } : { tone: "info", text: `${file.name} has no fillable form fields. Use Sign PDF or Watermark to add text instead.` }
|
|
136
|
+
);
|
|
137
|
+
} catch (error) {
|
|
138
|
+
setStatus({ tone: "error", text: error?.message || "Could not read this PDF." });
|
|
139
|
+
}
|
|
140
|
+
}, []);
|
|
141
|
+
const pdf = useSinglePdf({ onError, onLoaded });
|
|
142
|
+
const groups = useMemo(() => {
|
|
143
|
+
const byPage = /* @__PURE__ */ new Map();
|
|
144
|
+
for (const field of fields) {
|
|
145
|
+
const key = field.page ?? 0;
|
|
146
|
+
if (!byPage.has(key)) byPage.set(key, []);
|
|
147
|
+
byPage.get(key).push(field);
|
|
148
|
+
}
|
|
149
|
+
return [...byPage.entries()].sort(([a], [b]) => (a || Infinity) - (b || Infinity));
|
|
150
|
+
}, [fields]);
|
|
151
|
+
const editable = fields.filter((field) => !field.readOnly && field.kind !== "button" && field.kind !== "signature");
|
|
152
|
+
const missingRequired = editable.filter((field) => {
|
|
153
|
+
const value = values[field.name];
|
|
154
|
+
return field.required && (Array.isArray(value) ? !value.length : !value);
|
|
155
|
+
});
|
|
156
|
+
const clearFile = () => {
|
|
157
|
+
pdf.clear();
|
|
158
|
+
setFields([]);
|
|
159
|
+
setValues({});
|
|
160
|
+
setHasXfa(false);
|
|
161
|
+
};
|
|
162
|
+
const handleSave = async (event) => {
|
|
163
|
+
event.preventDefault();
|
|
164
|
+
if (!pdf.bytes || !editable.length) return;
|
|
165
|
+
setIsSaving(true);
|
|
166
|
+
setStatus({ tone: "info", text: "Filling your PDF..." });
|
|
167
|
+
try {
|
|
168
|
+
const bytes = await fillForm(pdf.bytes, values, { flatten });
|
|
169
|
+
const blob = new Blob([bytes], { type: "application/pdf" });
|
|
170
|
+
downloadBlob(blob, `${toBaseName(pdf.file.name, "form")}-filled.pdf`);
|
|
171
|
+
setStatus({
|
|
172
|
+
tone: "success",
|
|
173
|
+
text: `Saved (${formatBytes(blob.size)})${flatten ? " with answers locked in" : ""}. Your download has started.${missingRequired.length ? ` Note: ${missingRequired.length} required field${missingRequired.length === 1 ? " is" : "s are"} still empty.` : ""}`
|
|
174
|
+
});
|
|
175
|
+
} catch (error) {
|
|
176
|
+
setStatus({ tone: "error", text: `Could not fill the form: ${error?.message || "Please try again."}` });
|
|
177
|
+
} finally {
|
|
178
|
+
setIsSaving(false);
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
const busy = isSaving || pdf.isReading;
|
|
182
|
+
return /* @__PURE__ */ jsx(
|
|
183
|
+
PdfToolPage,
|
|
184
|
+
{
|
|
185
|
+
title: "Fill PDF Forms",
|
|
186
|
+
description: "Fill in the text boxes, checkboxes, and choices of a PDF form, then download it, optionally locked so answers can't be changed. Runs in your browser; your answers never leave your device.",
|
|
187
|
+
stats: [
|
|
188
|
+
{ value: fields.length || "-", label: "Fields" },
|
|
189
|
+
{ value: pdf.pageCount || "-", label: "Pages" }
|
|
190
|
+
],
|
|
191
|
+
children: /* @__PURE__ */ jsx(
|
|
192
|
+
PdfToolColumns,
|
|
193
|
+
{
|
|
194
|
+
main: /* @__PURE__ */ jsxs("form", { onSubmit: handleSave, children: [
|
|
195
|
+
pdf.file ? /* @__PURE__ */ jsx(
|
|
196
|
+
SelectedPdfCard,
|
|
197
|
+
{
|
|
198
|
+
signed: pdf.signed,
|
|
199
|
+
file: pdf.file,
|
|
200
|
+
pageCount: pdf.pageCount,
|
|
201
|
+
onRemove: clearFile,
|
|
202
|
+
disabled: isSaving
|
|
203
|
+
}
|
|
204
|
+
) : /* @__PURE__ */ jsx(
|
|
205
|
+
FileDropzone,
|
|
206
|
+
{
|
|
207
|
+
accept: ".pdf,application/pdf",
|
|
208
|
+
disabled: busy,
|
|
209
|
+
onFiles: pdf.selectFiles,
|
|
210
|
+
title: "Drag and drop a PDF form here",
|
|
211
|
+
hint: "or click to browse from your device"
|
|
212
|
+
}
|
|
213
|
+
),
|
|
214
|
+
hasXfa ? /* @__PURE__ */ jsx("p", { className: "mt-4 rounded-xl border border-amber-200 bg-amber-50 px-4 py-3 text-xs text-amber-800 dark:border-amber-500/30 dark:bg-amber-500/10 dark:text-amber-200", children: "This form also contains an XFA (Adobe LiveCycle) version. It is removed when you save so every viewer shows the answers you enter here." }) : null,
|
|
215
|
+
groups.map(([page, pageFields]) => /* @__PURE__ */ jsxs("section", { className: `mt-5 ${cardClass}`, children: [
|
|
216
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold text-slate-900 dark:text-slate-100", children: page ? `Page ${page}` : "Not on a page" }),
|
|
217
|
+
/* @__PURE__ */ jsx("div", { className: "mt-3 grid gap-4 sm:grid-cols-2", children: pageFields.map((field) => /* @__PURE__ */ jsx("div", { className: field.multiline || field.kind === "radio" ? "sm:col-span-2" : "", children: /* @__PURE__ */ jsx(
|
|
218
|
+
FieldInput,
|
|
219
|
+
{
|
|
220
|
+
field,
|
|
221
|
+
value: values[field.name],
|
|
222
|
+
disabled: busy,
|
|
223
|
+
onChange: (value) => setValues((current) => ({ ...current, [field.name]: value }))
|
|
224
|
+
}
|
|
225
|
+
) }, field.name)) })
|
|
226
|
+
] }, page)),
|
|
227
|
+
fields.length ? /* @__PURE__ */ jsxs("label", { className: "mt-5 flex items-start gap-3 text-sm text-slate-700 dark:text-slate-300", children: [
|
|
228
|
+
/* @__PURE__ */ jsx(
|
|
229
|
+
"input",
|
|
230
|
+
{
|
|
231
|
+
type: "checkbox",
|
|
232
|
+
checked: flatten,
|
|
233
|
+
onChange: (event) => setFlatten(event.target.checked),
|
|
234
|
+
disabled: busy,
|
|
235
|
+
className: "mt-0.5 h-4 w-4 accent-[#1F8CFF]"
|
|
236
|
+
}
|
|
237
|
+
),
|
|
238
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
239
|
+
"Lock answers (flatten)",
|
|
240
|
+
/* @__PURE__ */ jsx("span", { className: "block text-xs text-slate-500 dark:text-slate-400", children: "Draws your answers into the page and removes the form, so they can't be edited." })
|
|
241
|
+
] })
|
|
242
|
+
] }) : null,
|
|
243
|
+
/* @__PURE__ */ jsx(StatusBanner, { status }),
|
|
244
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-5 flex flex-wrap gap-3", children: [
|
|
245
|
+
/* @__PURE__ */ jsxs("button", { type: "submit", disabled: busy || !editable.length, className: primaryButtonClass, children: [
|
|
246
|
+
/* @__PURE__ */ jsx("i", { className: `fa-solid ${isSaving ? "fa-spinner fa-spin" : "fa-pen-to-square"}` }),
|
|
247
|
+
isSaving ? "Saving..." : "Fill & Download"
|
|
248
|
+
] }),
|
|
249
|
+
/* @__PURE__ */ jsxs(
|
|
250
|
+
"button",
|
|
251
|
+
{
|
|
252
|
+
type: "button",
|
|
253
|
+
onClick: () => setValues(initialValues(fields)),
|
|
254
|
+
disabled: busy || !fields.length,
|
|
255
|
+
className: secondaryButtonClass,
|
|
256
|
+
children: [
|
|
257
|
+
/* @__PURE__ */ jsx("i", { className: "fa-solid fa-arrow-rotate-left" }),
|
|
258
|
+
"Reset"
|
|
259
|
+
]
|
|
260
|
+
}
|
|
261
|
+
),
|
|
262
|
+
/* @__PURE__ */ jsx(BackToPdfTools, {})
|
|
263
|
+
] })
|
|
264
|
+
] }),
|
|
265
|
+
side: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
266
|
+
/* @__PURE__ */ jsx("h2", { className: "text-base font-semibold text-slate-900 dark:text-slate-100", children: "Summary" }),
|
|
267
|
+
/* @__PURE__ */ jsx(
|
|
268
|
+
SummaryList,
|
|
269
|
+
{
|
|
270
|
+
items: [
|
|
271
|
+
{ label: "Fillable fields", value: pdf.file ? editable.length : "-" },
|
|
272
|
+
{ label: "Required, still empty", value: pdf.file ? missingRequired.length : "-" }
|
|
273
|
+
]
|
|
274
|
+
}
|
|
275
|
+
),
|
|
276
|
+
/* @__PURE__ */ jsx(
|
|
277
|
+
SideTips,
|
|
278
|
+
{
|
|
279
|
+
tips: [
|
|
280
|
+
"Hover a field label to see its full internal name.",
|
|
281
|
+
"Answers use Helvetica, so basic Latin characters only.",
|
|
282
|
+
"Scanned forms without real fields can be filled with Sign PDF \u2192 Type."
|
|
283
|
+
]
|
|
284
|
+
}
|
|
285
|
+
)
|
|
286
|
+
] })
|
|
287
|
+
}
|
|
288
|
+
)
|
|
289
|
+
}
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
export { FillPdfForm };
|
|
294
|
+
//# sourceMappingURL=chunk-7XH7MFZ4.js.map
|
|
295
|
+
//# sourceMappingURL=chunk-7XH7MFZ4.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/forms/FillPdfForm.tsx"],"names":[],"mappings":"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","file":"chunk-7XH7MFZ4.js","sourcesContent":["import React, { useCallback, useEffect, useMemo, useState } from \"react\";\nimport { downloadBlob, formatBytes, toBaseName } from \"../shared/files\";\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 { fillForm, readFormFields } from \"./fillFormCore\";\n\n/** \"topmostSubform[0].Page1[0].FirstName[0]\" -> \"FirstName\". */\nfunction friendlyName(name) {\n const last = name.split(\".\").pop() || name;\n return last.replace(/\\[\\d+\\]$/, \"\").replace(/[_-]+/g, \" \").trim() || name;\n}\n\nfunction initialValues(fields) {\n return Object.fromEntries(fields.map((field) => [field.name, field.value]));\n}\n\nfunction FieldInput({ field, value, onChange, disabled }) {\n const id = `field-${field.name}`;\n const locked = disabled || field.readOnly;\n\n if (field.kind === \"checkbox\") {\n return (\n <label htmlFor={id} className=\"flex items-center gap-3 text-sm text-slate-800 dark:text-slate-200\">\n <input\n id={id}\n type=\"checkbox\"\n checked={Boolean(value)}\n disabled={locked}\n onChange={(event) => onChange(event.target.checked)}\n className=\"h-4 w-4 accent-[#1F8CFF]\"\n />\n {friendlyName(field.name)}\n {field.required ? <span className=\"text-rose-600\">*</span> : null}\n </label>\n );\n }\n\n const label = (\n <span className={fieldLabelClass} title={field.name}>\n {friendlyName(field.name)}\n {field.required ? <span className=\"ml-1 text-rose-600\">*</span> : null}\n {field.readOnly ? <span className=\"ml-2 normal-case tracking-normal text-slate-400\">(read-only)</span> : null}\n </span>\n );\n\n if (field.kind === \"radio\") {\n return (\n <fieldset disabled={locked}>\n <legend>{label}</legend>\n <div className=\"mt-2 flex flex-wrap gap-3\">\n {field.options.map((option) => (\n <label key={option} className=\"flex items-center gap-2 text-sm text-slate-800 dark:text-slate-200\">\n <input\n type=\"radio\"\n name={id}\n checked={value === option}\n onChange={() => onChange(option)}\n className=\"h-4 w-4 accent-[#1F8CFF]\"\n />\n {option}\n </label>\n ))}\n <button type=\"button\" onClick={() => onChange(\"\")} className=\"text-xs text-slate-500 underline\">\n Clear\n </button>\n </div>\n </fieldset>\n );\n }\n\n if (field.kind === \"dropdown\") {\n return (\n <label htmlFor={id} className=\"block\">\n {label}\n <select id={id} value={value} disabled={locked} onChange={(event) => onChange(event.target.value)} className={`mt-2 ${inputClass}`}>\n <option value=\"\">—</option>\n {field.options.map((option) => (\n <option key={option} value={option}>\n {option}\n </option>\n ))}\n </select>\n </label>\n );\n }\n\n if (field.kind === \"list\") {\n return (\n <label htmlFor={id} className=\"block\">\n {label}\n <select\n id={id}\n multiple={field.multiSelect}\n value={field.multiSelect ? value : value[0] ?? \"\"}\n disabled={locked}\n onChange={(event) =>\n onChange(Array.from(event.target.selectedOptions, (option) => option.value).filter(Boolean))\n }\n className={`mt-2 ${inputClass}`}\n >\n {field.multiSelect ? null : <option value=\"\">—</option>}\n {field.options.map((option) => (\n <option key={option} value={option}>\n {option}\n </option>\n ))}\n </select>\n </label>\n );\n }\n\n if (field.kind === \"text\") {\n const props = {\n id,\n value,\n disabled: locked,\n maxLength: field.maxLength,\n onChange: (event) => onChange(event.target.value),\n className: `mt-2 ${inputClass}`,\n };\n return (\n <label htmlFor={id} className=\"block\">\n {label}\n {field.multiline ? <textarea rows={3} {...props} /> : <input type=\"text\" {...props} />}\n {field.maxLength ? (\n <span className=\"mt-1 block text-right text-[11px] text-slate-400\">\n {String(value).length}/{field.maxLength}\n </span>\n ) : null}\n </label>\n );\n }\n\n return (\n <div>\n {label}\n <p className=\"mt-1 text-xs text-slate-500 dark:text-slate-400\">\n {field.kind === \"signature\"\n ? \"Signature field: use Sign PDF to add a visible signature.\"\n : \"Button: nothing to fill.\"}\n </p>\n </div>\n );\n}\n\nexport default function FillPdfForm() {\n const [fields, setFields] = useState([]);\n const [values, setValues] = useState({});\n const [hasXfa, setHasXfa] = useState(false);\n const [flatten, setFlatten] = useState(false);\n const [isSaving, setIsSaving] = useState(false);\n const [status, setStatus] = useState({\n tone: \"info\",\n text: \"Add a PDF form to fill in its fields.\",\n });\n\n useEffect(() => {\n document.title = \"Fill PDF Forms | Apps\";\n }, []);\n\n const onError = useCallback((text) => setStatus({ tone: \"error\", text }), []);\n const onLoaded = useCallback(async ({ file, bytes }) => {\n try {\n const result = await readFormFields(bytes);\n setFields(result.fields);\n setValues(initialValues(result.fields));\n setHasXfa(result.hasXfa);\n setStatus(\n result.fields.length\n ? { tone: \"success\", text: `${file.name} has ${result.fields.length} field${result.fields.length === 1 ? \"\" : \"s\"}. Fill them in and download.` }\n : { tone: \"info\", text: `${file.name} has no fillable form fields. Use Sign PDF or Watermark to add text instead.` }\n );\n } catch (error) {\n setStatus({ tone: \"error\", text: error?.message || \"Could not read this PDF.\" });\n }\n }, []);\n const pdf = useSinglePdf({ onError, onLoaded });\n\n const groups = useMemo(() => {\n const byPage = new Map();\n for (const field of fields) {\n const key = field.page ?? 0;\n if (!byPage.has(key)) byPage.set(key, []);\n byPage.get(key).push(field);\n }\n return [...byPage.entries()].sort(([a], [b]) => (a || Infinity) - (b || Infinity));\n }, [fields]);\n\n const editable = fields.filter((field) => !field.readOnly && field.kind !== \"button\" && field.kind !== \"signature\");\n const missingRequired = editable.filter((field) => {\n const value = values[field.name];\n return field.required && (Array.isArray(value) ? !value.length : !value);\n });\n\n const clearFile = () => {\n pdf.clear();\n setFields([]);\n setValues({});\n setHasXfa(false);\n };\n\n const handleSave = async (event) => {\n event.preventDefault();\n if (!pdf.bytes || !editable.length) return;\n setIsSaving(true);\n setStatus({ tone: \"info\", text: \"Filling your PDF...\" });\n try {\n const bytes = await fillForm(pdf.bytes, values, { flatten });\n const blob = new Blob([bytes as BlobPart], { type: \"application/pdf\" });\n downloadBlob(blob, `${toBaseName(pdf.file.name, \"form\")}-filled.pdf`);\n setStatus({\n tone: \"success\",\n text: `Saved (${formatBytes(blob.size)})${flatten ? \" with answers locked in\" : \"\"}. Your download has started.${\n missingRequired.length ? ` Note: ${missingRequired.length} required field${missingRequired.length === 1 ? \" is\" : \"s are\"} still empty.` : \"\"\n }`,\n });\n } catch (error) {\n setStatus({ tone: \"error\", text: `Could not fill the form: ${error?.message || \"Please try again.\"}` });\n } finally {\n setIsSaving(false);\n }\n };\n\n const busy = isSaving || pdf.isReading;\n\n return (\n <PdfToolPage\n title=\"Fill PDF Forms\"\n description=\"Fill in the text boxes, checkboxes, and choices of a PDF form, then download it, optionally locked so answers can't be changed. Runs in your browser; your answers never leave your device.\"\n stats={[\n { value: fields.length || \"-\", label: \"Fields\" },\n { value: pdf.pageCount || \"-\", label: \"Pages\" },\n ]}\n >\n <PdfToolColumns\n main={\n <form onSubmit={handleSave}>\n {pdf.file ? (\n <SelectedPdfCard\n signed={pdf.signed} file={pdf.file} pageCount={pdf.pageCount} onRemove={clearFile} disabled={isSaving} />\n ) : (\n <FileDropzone\n accept=\".pdf,application/pdf\"\n disabled={busy}\n onFiles={pdf.selectFiles}\n title=\"Drag and drop a PDF form here\"\n hint=\"or click to browse from your device\"\n />\n )}\n\n {hasXfa ? (\n <p className=\"mt-4 rounded-xl border border-amber-200 bg-amber-50 px-4 py-3 text-xs text-amber-800 dark:border-amber-500/30 dark:bg-amber-500/10 dark:text-amber-200\">\n This form also contains an XFA (Adobe LiveCycle) version. It is removed when you save so every viewer shows\n the answers you enter here.\n </p>\n ) : null}\n\n {groups.map(([page, pageFields]) => (\n <section key={page} className={`mt-5 ${cardClass}`}>\n <h3 className=\"text-sm font-semibold text-slate-900 dark:text-slate-100\">\n {page ? `Page ${page}` : \"Not on a page\"}\n </h3>\n <div className=\"mt-3 grid gap-4 sm:grid-cols-2\">\n {pageFields.map((field) => (\n <div key={field.name} className={field.multiline || field.kind === \"radio\" ? \"sm:col-span-2\" : \"\"}>\n <FieldInput\n field={field}\n value={values[field.name]}\n disabled={busy}\n onChange={(value) => setValues((current) => ({ ...current, [field.name]: value }))}\n />\n </div>\n ))}\n </div>\n </section>\n ))}\n\n {fields.length ? (\n <label className=\"mt-5 flex items-start gap-3 text-sm text-slate-700 dark:text-slate-300\">\n <input\n type=\"checkbox\"\n checked={flatten}\n onChange={(event) => setFlatten(event.target.checked)}\n disabled={busy}\n className=\"mt-0.5 h-4 w-4 accent-[#1F8CFF]\"\n />\n <span>\n Lock answers (flatten)\n <span className=\"block text-xs text-slate-500 dark:text-slate-400\">\n Draws your answers into the page and removes the form, so they can't be edited.\n </span>\n </span>\n </label>\n ) : null}\n\n <StatusBanner status={status} />\n\n <div className=\"mt-5 flex flex-wrap gap-3\">\n <button type=\"submit\" disabled={busy || !editable.length} className={primaryButtonClass}>\n <i className={`fa-solid ${isSaving ? \"fa-spinner fa-spin\" : \"fa-pen-to-square\"}`} />\n {isSaving ? \"Saving...\" : \"Fill & Download\"}\n </button>\n <button\n type=\"button\"\n onClick={() => setValues(initialValues(fields))}\n disabled={busy || !fields.length}\n className={secondaryButtonClass}\n >\n <i className=\"fa-solid fa-arrow-rotate-left\" />\n Reset\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\">Summary</h2>\n <SummaryList\n items={[\n { label: \"Fillable fields\", value: pdf.file ? editable.length : \"-\" },\n { label: \"Required, still empty\", value: pdf.file ? missingRequired.length : \"-\" },\n ]}\n />\n <SideTips\n tips={[\n \"Hover a field label to see its full internal name.\",\n \"Answers use Helvetica, so basic Latin characters only.\",\n \"Scanned forms without real fields can be filled with Sign PDF → Type.\",\n ]}\n />\n </>\n }\n />\n </PdfToolPage>\n );\n}\n"]}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { hasDigitalSignatures } from './chunk-7MTEMQLL.js';
|
|
2
|
+
import { isPdfFile, loadPdf } from './chunk-EKHEY3LI.js';
|
|
3
|
+
import { useState, useRef, useCallback } from 'react';
|
|
4
|
+
|
|
5
|
+
var MAX_SINGLE_PDF_BYTES = 200 * 1024 * 1024;
|
|
6
|
+
function useSinglePdf({ onError, onLoaded } = {}) {
|
|
7
|
+
const [file, setFile] = useState(null);
|
|
8
|
+
const [bytes, setBytes] = useState(null);
|
|
9
|
+
const [pageCount, setPageCount] = useState(0);
|
|
10
|
+
const [isReading, setIsReading] = useState(false);
|
|
11
|
+
const [signed, setSigned] = useState(false);
|
|
12
|
+
const requestRef = useRef(0);
|
|
13
|
+
const clear = useCallback(() => {
|
|
14
|
+
requestRef.current += 1;
|
|
15
|
+
setFile(null);
|
|
16
|
+
setBytes(null);
|
|
17
|
+
setPageCount(0);
|
|
18
|
+
setSigned(false);
|
|
19
|
+
setIsReading(false);
|
|
20
|
+
}, []);
|
|
21
|
+
const selectFiles = useCallback(
|
|
22
|
+
async (files) => {
|
|
23
|
+
const next = (files || []).find(isPdfFile);
|
|
24
|
+
if (!next) {
|
|
25
|
+
if (files?.length) onError?.("Only PDF files are supported.");
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (next.size > MAX_SINGLE_PDF_BYTES) {
|
|
29
|
+
onError?.("This PDF is larger than 200 MB.");
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const request = ++requestRef.current;
|
|
33
|
+
setFile(next);
|
|
34
|
+
setBytes(null);
|
|
35
|
+
setPageCount(0);
|
|
36
|
+
setSigned(false);
|
|
37
|
+
setIsReading(true);
|
|
38
|
+
try {
|
|
39
|
+
const data = await next.arrayBuffer();
|
|
40
|
+
const doc = await loadPdf(data);
|
|
41
|
+
if (request !== requestRef.current) return;
|
|
42
|
+
setBytes(data);
|
|
43
|
+
setPageCount(doc.getPageCount());
|
|
44
|
+
setSigned(hasDigitalSignatures(doc));
|
|
45
|
+
onLoaded?.({ file: next, bytes: data, pageCount: doc.getPageCount() });
|
|
46
|
+
} catch (error) {
|
|
47
|
+
if (request !== requestRef.current) return;
|
|
48
|
+
setFile(null);
|
|
49
|
+
onError?.(error?.message || "Could not read this PDF.");
|
|
50
|
+
} finally {
|
|
51
|
+
if (request === requestRef.current) setIsReading(false);
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
[onError, onLoaded]
|
|
55
|
+
);
|
|
56
|
+
return { file, bytes, pageCount, signed, isReading, selectFiles, clear };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export { MAX_SINGLE_PDF_BYTES, useSinglePdf };
|
|
60
|
+
//# sourceMappingURL=chunk-BK3MYSUS.js.map
|
|
61
|
+
//# sourceMappingURL=chunk-BK3MYSUS.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/shared/useSinglePdf.ts"],"names":[],"mappings":";;;;AAIO,IAAM,oBAAA,GAAuB,MAAM,IAAA,GAAO;AAW1C,SAAS,aAAa,EAAE,OAAA,EAAS,QAAA,EAAS,GAAsB,EAAC,EAAG;AACzE,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAI,SAAS,IAAI,CAAA;AACrC,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAI,SAAS,IAAI,CAAA;AACvC,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAI,SAAS,CAAC,CAAA;AAC5C,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAI,SAAS,KAAK,CAAA;AAChD,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAI,SAAS,KAAK,CAAA;AAC1C,EAAA,MAAM,UAAA,GAAa,OAAO,CAAC,CAAA;AAE3B,EAAA,MAAM,KAAA,GAAQ,YAAY,MAAM;AAC9B,IAAA,UAAA,CAAW,OAAA,IAAW,CAAA;AACtB,IAAA,OAAA,CAAQ,IAAI,CAAA;AACZ,IAAA,QAAA,CAAS,IAAI,CAAA;AACb,IAAA,YAAA,CAAa,CAAC,CAAA;AACd,IAAA,SAAA,CAAU,KAAK,CAAA;AACf,IAAA,YAAA,CAAa,KAAK,CAAA;AAAA,EACpB,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,MAAM,WAAA,GAAc,WAAA;AAAA,IAClB,OAAO,KAAA,KAAU;AACf,MAAA,MAAM,IAAA,GAAA,CAAQ,KAAA,IAAS,EAAC,EAAG,KAAK,SAAS,CAAA;AACzC,MAAA,IAAI,CAAC,IAAA,EAAM;AACT,QAAA,IAAI,KAAA,EAAO,MAAA,EAAQ,OAAA,GAAU,+BAA+B,CAAA;AAC5D,QAAA;AAAA,MACF;AACA,MAAA,IAAI,IAAA,CAAK,OAAO,oBAAA,EAAsB;AACpC,QAAA,OAAA,GAAU,iCAAiC,CAAA;AAC3C,QAAA;AAAA,MACF;AAEA,MAAA,MAAM,OAAA,GAAU,EAAE,UAAA,CAAW,OAAA;AAC7B,MAAA,OAAA,CAAQ,IAAI,CAAA;AACZ,MAAA,QAAA,CAAS,IAAI,CAAA;AACb,MAAA,YAAA,CAAa,CAAC,CAAA;AACd,MAAA,SAAA,CAAU,KAAK,CAAA;AACf,MAAA,YAAA,CAAa,IAAI,CAAA;AAEjB,MAAA,IAAI;AACF,QAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,WAAA,EAAY;AACpC,QAAA,MAAM,GAAA,GAAM,MAAM,OAAA,CAAQ,IAAI,CAAA;AAC9B,QAAA,IAAI,OAAA,KAAY,WAAW,OAAA,EAAS;AACpC,QAAA,QAAA,CAAS,IAAI,CAAA;AACb,QAAA,YAAA,CAAa,GAAA,CAAI,cAAc,CAAA;AAC/B,QAAA,SAAA,CAAU,oBAAA,CAAqB,GAAG,CAAC,CAAA;AACnC,QAAA,QAAA,GAAW,EAAE,MAAM,IAAA,EAAM,KAAA,EAAO,MAAM,SAAA,EAAW,GAAA,CAAI,YAAA,EAAa,EAAG,CAAA;AAAA,MACvE,SAAS,KAAA,EAAO;AACd,QAAA,IAAI,OAAA,KAAY,WAAW,OAAA,EAAS;AACpC,QAAA,OAAA,CAAQ,IAAI,CAAA;AACZ,QAAA,OAAA,GAAU,KAAA,EAAO,WAAW,0BAA0B,CAAA;AAAA,MACxD,CAAA,SAAE;AACA,QAAA,IAAI,OAAA,KAAY,UAAA,CAAW,OAAA,EAAS,YAAA,CAAa,KAAK,CAAA;AAAA,MACxD;AAAA,IACF,CAAA;AAAA,IACA,CAAC,SAAS,QAAQ;AAAA,GACpB;AAEA,EAAA,OAAO,EAAE,IAAA,EAAM,KAAA,EAAO,WAAW,MAAA,EAAQ,SAAA,EAAW,aAAa,KAAA,EAAM;AACzE","file":"chunk-BK3MYSUS.js","sourcesContent":["import { useCallback, useRef, useState } from \"react\";\nimport { isPdfFile, loadPdf } from \"./files\";\nimport { hasDigitalSignatures } from \"./signatureFields\";\n\nexport const MAX_SINGLE_PDF_BYTES = 200 * 1024 * 1024;\n\n/**\n * Holds one selected PDF: the File, its bytes and page count once read, or a\n * read error. A newer selection always wins over a slower earlier read.\n */\ntype SinglePdfOptions = {\n onError?: (message: string) => void;\n onLoaded?: (loaded: { file: File; bytes: ArrayBuffer; pageCount: number }) => void;\n};\n\nexport function useSinglePdf({ onError, onLoaded }: SinglePdfOptions = {}) {\n const [file, setFile] = useState(null);\n const [bytes, setBytes] = useState(null);\n const [pageCount, setPageCount] = useState(0);\n const [isReading, setIsReading] = useState(false);\n const [signed, setSigned] = useState(false);\n const requestRef = useRef(0);\n\n const clear = useCallback(() => {\n requestRef.current += 1;\n setFile(null);\n setBytes(null);\n setPageCount(0);\n setSigned(false);\n setIsReading(false);\n }, []);\n\n const selectFiles = useCallback(\n async (files) => {\n const next = (files || []).find(isPdfFile);\n if (!next) {\n if (files?.length) onError?.(\"Only PDF files are supported.\");\n return;\n }\n if (next.size > MAX_SINGLE_PDF_BYTES) {\n onError?.(\"This PDF is larger than 200 MB.\");\n return;\n }\n\n const request = ++requestRef.current;\n setFile(next);\n setBytes(null);\n setPageCount(0);\n setSigned(false);\n setIsReading(true);\n\n try {\n const data = await next.arrayBuffer();\n const doc = await loadPdf(data);\n if (request !== requestRef.current) return;\n setBytes(data);\n setPageCount(doc.getPageCount());\n setSigned(hasDigitalSignatures(doc));\n onLoaded?.({ file: next, bytes: data, pageCount: doc.getPageCount() });\n } catch (error) {\n if (request !== requestRef.current) return;\n setFile(null);\n onError?.(error?.message || \"Could not read this PDF.\");\n } finally {\n if (request === requestRef.current) setIsReading(false);\n }\n },\n [onError, onLoaded]\n );\n\n return { file, bytes, pageCount, signed, isReading, selectFiles, clear };\n}\n"]}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// src/shared/pageRanges.ts
|
|
2
|
+
function parseRangePart(part, pageCount) {
|
|
3
|
+
const match = part.match(/^(\d*)\s*(?:(-)\s*(\d*))?$/);
|
|
4
|
+
if (!match || !match[1] && !match[3]) {
|
|
5
|
+
throw new Error(`Invalid page range "${part}".`);
|
|
6
|
+
}
|
|
7
|
+
const start = match[1] ? Number(match[1]) : 1;
|
|
8
|
+
const end = match[2] ? match[3] ? Number(match[3]) : pageCount : start;
|
|
9
|
+
if (start < 1 || end < 1 || start > pageCount || end > pageCount) {
|
|
10
|
+
throw new Error(`Page range "${part}" is outside 1-${pageCount}.`);
|
|
11
|
+
}
|
|
12
|
+
const indices = [];
|
|
13
|
+
const step = start <= end ? 1 : -1;
|
|
14
|
+
for (let page = start; step > 0 ? page <= end : page >= end; page += step) {
|
|
15
|
+
indices.push(page - 1);
|
|
16
|
+
}
|
|
17
|
+
return indices;
|
|
18
|
+
}
|
|
19
|
+
function parsePageGroups(selection, pageCount) {
|
|
20
|
+
const groups = (selection || "").split(",").map((part) => part.trim()).filter(Boolean).map((part) => parseRangePart(part, pageCount));
|
|
21
|
+
if (!groups.length) {
|
|
22
|
+
throw new Error("Page selection is empty.");
|
|
23
|
+
}
|
|
24
|
+
return groups;
|
|
25
|
+
}
|
|
26
|
+
function parsePageSelection(selection, pageCount) {
|
|
27
|
+
if (!(selection || "").trim()) {
|
|
28
|
+
return Array.from({ length: pageCount }, (_, index) => index);
|
|
29
|
+
}
|
|
30
|
+
return parsePageGroups(selection, pageCount).flat();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export { parsePageGroups, parsePageSelection };
|
|
34
|
+
//# sourceMappingURL=chunk-BLHNKR3S.js.map
|
|
35
|
+
//# sourceMappingURL=chunk-BLHNKR3S.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/shared/pageRanges.ts"],"names":[],"mappings":";AAAA,SAAS,cAAA,CAAe,MAAc,SAAA,EAAmB;AACvD,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,KAAA,CAAM,4BAA4B,CAAA;AACrD,EAAA,IAAI,CAAC,SAAU,CAAC,KAAA,CAAM,CAAC,CAAA,IAAK,CAAC,KAAA,CAAM,CAAC,CAAA,EAAI;AACtC,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,oBAAA,EAAuB,IAAI,CAAA,EAAA,CAAI,CAAA;AAAA,EACjD;AAEA,EAAA,MAAM,KAAA,GAAQ,MAAM,CAAC,CAAA,GAAI,OAAO,KAAA,CAAM,CAAC,CAAC,CAAA,GAAI,CAAA;AAC5C,EAAA,MAAM,GAAA,GAAM,KAAA,CAAM,CAAC,CAAA,GAAK,KAAA,CAAM,CAAC,CAAA,GAAI,MAAA,CAAO,KAAA,CAAM,CAAC,CAAC,CAAA,GAAI,SAAA,GAAa,KAAA;AAEnE,EAAA,IAAI,QAAQ,CAAA,IAAK,GAAA,GAAM,KAAK,KAAA,GAAQ,SAAA,IAAa,MAAM,SAAA,EAAW;AAChE,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,YAAA,EAAe,IAAI,CAAA,eAAA,EAAkB,SAAS,CAAA,CAAA,CAAG,CAAA;AAAA,EACnE;AAEA,EAAA,MAAM,UAAoB,EAAC;AAC3B,EAAA,MAAM,IAAA,GAAO,KAAA,IAAS,GAAA,GAAM,CAAA,GAAI,EAAA;AAChC,EAAA,KAAA,IAAS,IAAA,GAAO,OAAO,IAAA,GAAO,CAAA,GAAI,QAAQ,GAAA,GAAM,IAAA,IAAQ,GAAA,EAAK,IAAA,IAAQ,IAAA,EAAM;AACzE,IAAA,OAAA,CAAQ,IAAA,CAAK,OAAO,CAAC,CAAA;AAAA,EACvB;AACA,EAAA,OAAO,OAAA;AACT;AAMO,SAAS,eAAA,CAAgB,WAA+B,SAAA,EAAmB;AAChF,EAAA,MAAM,MAAA,GAAA,CAAU,aAAa,EAAA,EAC1B,KAAA,CAAM,GAAG,CAAA,CACT,GAAA,CAAI,CAAC,IAAA,KAAS,IAAA,CAAK,IAAA,EAAM,CAAA,CACzB,MAAA,CAAO,OAAO,CAAA,CACd,GAAA,CAAI,CAAC,IAAA,KAAS,cAAA,CAAe,IAAA,EAAM,SAAS,CAAC,CAAA;AAEhD,EAAA,IAAI,CAAC,OAAO,MAAA,EAAQ;AAClB,IAAA,MAAM,IAAI,MAAM,0BAA0B,CAAA;AAAA,EAC5C;AACA,EAAA,OAAO,MAAA;AACT;AAOO,SAAS,kBAAA,CAAmB,WAA+B,SAAA,EAAmB;AACnF,EAAA,IAAI,CAAA,CAAE,SAAA,IAAa,EAAA,EAAI,IAAA,EAAK,EAAG;AAC7B,IAAA,OAAO,KAAA,CAAM,KAAK,EAAE,MAAA,EAAQ,WAAU,EAAG,CAAC,CAAA,EAAG,KAAA,KAAU,KAAK,CAAA;AAAA,EAC9D;AACA,EAAA,OAAO,eAAA,CAAgB,SAAA,EAAW,SAAS,CAAA,CAAE,IAAA,EAAK;AACpD","file":"chunk-BLHNKR3S.js","sourcesContent":["function parseRangePart(part: string, pageCount: number) {\n const match = part.match(/^(\\d*)\\s*(?:(-)\\s*(\\d*))?$/);\n if (!match || (!match[1] && !match[3])) {\n throw new Error(`Invalid page range \"${part}\".`);\n }\n\n const start = match[1] ? Number(match[1]) : 1;\n const end = match[2] ? (match[3] ? Number(match[3]) : pageCount) : start;\n\n if (start < 1 || end < 1 || start > pageCount || end > pageCount) {\n throw new Error(`Page range \"${part}\" is outside 1-${pageCount}.`);\n }\n\n const indices: number[] = [];\n const step = start <= end ? 1 : -1;\n for (let page = start; step > 0 ? page <= end : page >= end; page += step) {\n indices.push(page - 1);\n }\n return indices;\n}\n\n/**\n * Parse a 1-based page selection (\"1-3, 5, 8-\") into groups of 0-based page\n * indices, one group per comma-separated part, in the order typed.\n */\nexport function parsePageGroups(selection: string | undefined, pageCount: number) {\n const groups = (selection || \"\")\n .split(\",\")\n .map((part) => part.trim())\n .filter(Boolean)\n .map((part) => parseRangePart(part, pageCount));\n\n if (!groups.length) {\n throw new Error(\"Page selection is empty.\");\n }\n return groups;\n}\n\n/**\n * Parse a 1-based page selection (\"1-3, 5, 8-\") into 0-based page indices,\n * preserving the order the user typed. Empty means every page. Throws on\n * out-of-range or malformed input.\n */\nexport function parsePageSelection(selection: string | undefined, pageCount: number) {\n if (!(selection || \"\").trim()) {\n return Array.from({ length: pageCount }, (_, index) => index);\n }\n return parsePageGroups(selection, pageCount).flat();\n}\n"]}
|