@kitsy/pdf 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/LICENSE +201 -0
  3. package/NOTICE +16 -0
  4. package/README.md +229 -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-4USCGC24.js +304 -0
  13. package/dist/chunk-4USCGC24.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-G467LFKC.js +191 -0
  43. package/dist/chunk-G467LFKC.js.map +1 -0
  44. package/dist/chunk-GLYWSVSB.js +250 -0
  45. package/dist/chunk-GLYWSVSB.js.map +1 -0
  46. package/dist/chunk-GXBIMUO7.js +135 -0
  47. package/dist/chunk-GXBIMUO7.js.map +1 -0
  48. package/dist/chunk-H6EERRCR.js +180 -0
  49. package/dist/chunk-H6EERRCR.js.map +1 -0
  50. package/dist/chunk-HDTM3LXD.js +35 -0
  51. package/dist/chunk-HDTM3LXD.js.map +1 -0
  52. package/dist/chunk-IGMX56C3.js +180 -0
  53. package/dist/chunk-IGMX56C3.js.map +1 -0
  54. package/dist/chunk-J4IRICCJ.js +74 -0
  55. package/dist/chunk-J4IRICCJ.js.map +1 -0
  56. package/dist/chunk-JXKQ7S6W.js +331 -0
  57. package/dist/chunk-JXKQ7S6W.js.map +1 -0
  58. package/dist/chunk-KY6DXS6S.js +34 -0
  59. package/dist/chunk-KY6DXS6S.js.map +1 -0
  60. package/dist/chunk-NOFGCYGS.js +22 -0
  61. package/dist/chunk-NOFGCYGS.js.map +1 -0
  62. package/dist/chunk-OSHUJRKD.js +131 -0
  63. package/dist/chunk-OSHUJRKD.js.map +1 -0
  64. package/dist/chunk-PMH7OWPF.js +40 -0
  65. package/dist/chunk-PMH7OWPF.js.map +1 -0
  66. package/dist/chunk-PRBKSX3A.js +299 -0
  67. package/dist/chunk-PRBKSX3A.js.map +1 -0
  68. package/dist/chunk-SVPUX7AM.js +48 -0
  69. package/dist/chunk-SVPUX7AM.js.map +1 -0
  70. package/dist/chunk-TR6HJ4NO.js +659 -0
  71. package/dist/chunk-TR6HJ4NO.js.map +1 -0
  72. package/dist/chunk-TV3E4YUF.js +69 -0
  73. package/dist/chunk-TV3E4YUF.js.map +1 -0
  74. package/dist/chunk-U2ROTOPF.js +256 -0
  75. package/dist/chunk-U2ROTOPF.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 +487 -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,362 @@
1
+ import { formatPageNumber, stampPdf } from './chunk-VCTN77K2.js';
2
+ import { useSinglePdf } from './chunk-BK3MYSUS.js';
3
+ import { PdfToolPage, PdfToolColumns, SideTips, SelectedPdfCard, FileDropzone, cardClass, fieldLabelClass, inputClass, StatusBanner, primaryButtonClass, BackToPdfTools } from './chunk-H6EERRCR.js';
4
+ import { toBaseName, downloadBlob, formatBytes } from './chunk-EKHEY3LI.js';
5
+ import { useState, useEffect, useCallback } from 'react';
6
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
7
+
8
+ var NUMBER_FORMATS = ["{n}", "Page {n}", "Page {n} of {total}", "{n} / {total}"];
9
+ var POSITIONS = [
10
+ { value: "bottom-center", label: "Bottom center" },
11
+ { value: "bottom-right", label: "Bottom right" },
12
+ { value: "bottom-left", label: "Bottom left" },
13
+ { value: "top-center", label: "Top center" },
14
+ { value: "top-right", label: "Top right" },
15
+ { value: "top-left", label: "Top left" }
16
+ ];
17
+ function Toggle({ checked, onChange, label, disabled = false }) {
18
+ return /* @__PURE__ */ jsxs("label", { className: "flex cursor-pointer items-center gap-3 text-sm font-semibold text-slate-900 dark:text-slate-100", children: [
19
+ /* @__PURE__ */ jsx(
20
+ "input",
21
+ {
22
+ type: "checkbox",
23
+ checked,
24
+ onChange: (event) => onChange(event.target.checked),
25
+ disabled,
26
+ className: "h-4 w-4 accent-[#1F8CFF]"
27
+ }
28
+ ),
29
+ label
30
+ ] });
31
+ }
32
+ function StampPreview({ watermark, pageNumbers }) {
33
+ const [vertical, horizontal] = (pageNumbers?.position || "bottom-center").split("-");
34
+ const justify = { left: "justify-start", center: "justify-center", right: "justify-end" }[horizontal];
35
+ return /* @__PURE__ */ jsxs("div", { className: "mx-auto mt-4 flex aspect-[1/1.414] w-40 flex-col overflow-hidden rounded-md border border-slate-300 bg-white p-2 shadow-sm", children: [
36
+ pageNumbers && vertical === "top" ? /* @__PURE__ */ jsx("div", { className: `flex ${justify} text-[7px] text-slate-600`, children: formatPageNumber(pageNumbers.format, 1, 12) }) : null,
37
+ /* @__PURE__ */ jsxs("div", { className: "relative flex flex-1 items-center justify-center", children: [
38
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-x-1 top-2 space-y-1", children: Array.from({ length: 9 }, (_, i) => /* @__PURE__ */ jsx("div", { className: "h-1 rounded bg-slate-200", style: { width: `${70 + i * 17 % 30}%` } }, i)) }),
39
+ watermark ? /* @__PURE__ */ jsx(
40
+ "span",
41
+ {
42
+ className: "relative whitespace-nowrap font-bold",
43
+ style: {
44
+ color: watermark.color,
45
+ opacity: watermark.opacity,
46
+ fontSize: `${Math.max(6, watermark.fontSize / 4)}px`,
47
+ transform: `rotate(${-watermark.rotation}deg)`
48
+ },
49
+ children: watermark.text
50
+ }
51
+ ) : null
52
+ ] }),
53
+ pageNumbers && vertical === "bottom" ? /* @__PURE__ */ jsx("div", { className: `flex ${justify} text-[7px] text-slate-600`, children: formatPageNumber(pageNumbers.format, 1, 12) }) : null
54
+ ] });
55
+ }
56
+ function WatermarkPdf() {
57
+ const [useWatermark, setUseWatermark] = useState(true);
58
+ const [watermarkText, setWatermarkText] = useState("CONFIDENTIAL");
59
+ const [watermarkSize, setWatermarkSize] = useState(60);
60
+ const [watermarkOpacity, setWatermarkOpacity] = useState(0.2);
61
+ const [watermarkRotation, setWatermarkRotation] = useState(45);
62
+ const [watermarkColor, setWatermarkColor] = useState("#e11d48");
63
+ const [useNumbers, setUseNumbers] = useState(false);
64
+ const [numberFormat, setNumberFormat] = useState("Page {n} of {total}");
65
+ const [numberPosition, setNumberPosition] = useState("bottom-center");
66
+ const [numberSize, setNumberSize] = useState(11);
67
+ const [startAt, setStartAt] = useState(1);
68
+ const [pages, setPages] = useState("");
69
+ const [outputName, setOutputName] = useState("");
70
+ const [isStamping, setIsStamping] = useState(false);
71
+ const [status, setStatus] = useState({
72
+ tone: "info",
73
+ text: "Add a PDF, then stamp a text watermark, page numbers, or both."
74
+ });
75
+ useEffect(() => {
76
+ document.title = "Watermark & Page Numbers | Apps";
77
+ }, []);
78
+ const onError = useCallback((text) => setStatus({ tone: "error", text }), []);
79
+ const onLoaded = useCallback(({ file, pageCount }) => {
80
+ setOutputName(`${toBaseName(file.name, "document")}-stamped`);
81
+ setStatus({
82
+ tone: "success",
83
+ text: `${file.name} loaded with ${pageCount} page${pageCount === 1 ? "" : "s"}.`
84
+ });
85
+ }, []);
86
+ const pdf = useSinglePdf({ onError, onLoaded });
87
+ const watermark = useWatermark ? {
88
+ text: watermarkText,
89
+ fontSize: Number(watermarkSize),
90
+ opacity: Number(watermarkOpacity),
91
+ rotation: Number(watermarkRotation),
92
+ color: watermarkColor
93
+ } : null;
94
+ const pageNumbers = useNumbers ? {
95
+ format: numberFormat,
96
+ position: numberPosition,
97
+ fontSize: Number(numberSize),
98
+ startAt: Number(startAt)
99
+ } : null;
100
+ const hasStamp = Boolean(watermark && watermarkText.trim() || pageNumbers);
101
+ const handleStamp = async (event) => {
102
+ event.preventDefault();
103
+ if (!pdf.bytes || !hasStamp) return;
104
+ setIsStamping(true);
105
+ setStatus({ tone: "info", text: "Stamping your PDF..." });
106
+ try {
107
+ const bytes = await stampPdf(pdf.bytes, { pages, watermark, pageNumbers });
108
+ const blob = new Blob([bytes], { type: "application/pdf" });
109
+ downloadBlob(blob, `${toBaseName(outputName, "stamped")}.pdf`);
110
+ setStatus({
111
+ tone: "success",
112
+ text: `Done (${formatBytes(blob.size)}). Your download has started.`
113
+ });
114
+ } catch (error) {
115
+ setStatus({ tone: "error", text: `Stamping failed: ${error?.message || "Please try again."}` });
116
+ } finally {
117
+ setIsStamping(false);
118
+ }
119
+ };
120
+ const busy = isStamping || pdf.isReading;
121
+ return /* @__PURE__ */ jsx(
122
+ PdfToolPage,
123
+ {
124
+ title: "Watermark & Page Numbers",
125
+ description: "Stamp a text watermark such as DRAFT or CONFIDENTIAL, and add page numbers, on every page or just the ones you pick. Runs in your browser.",
126
+ stats: [
127
+ { value: pdf.pageCount || "-", label: "Pages" },
128
+ { value: "PDF", label: "Output" }
129
+ ],
130
+ children: /* @__PURE__ */ jsx(
131
+ PdfToolColumns,
132
+ {
133
+ main: /* @__PURE__ */ jsxs("form", { onSubmit: handleStamp, children: [
134
+ pdf.file ? /* @__PURE__ */ jsx(
135
+ SelectedPdfCard,
136
+ {
137
+ signed: pdf.signed,
138
+ file: pdf.file,
139
+ pageCount: pdf.pageCount,
140
+ onRemove: pdf.clear,
141
+ disabled: isStamping
142
+ }
143
+ ) : /* @__PURE__ */ jsx(
144
+ FileDropzone,
145
+ {
146
+ accept: ".pdf,application/pdf",
147
+ disabled: busy,
148
+ onFiles: pdf.selectFiles,
149
+ title: "Drag and drop a PDF here",
150
+ hint: "or click to browse from your device"
151
+ }
152
+ ),
153
+ /* @__PURE__ */ jsxs("fieldset", { className: "mt-5 space-y-4", disabled: busy, children: [
154
+ /* @__PURE__ */ jsxs("div", { className: cardClass, children: [
155
+ /* @__PURE__ */ jsx(Toggle, { checked: useWatermark, onChange: setUseWatermark, label: "Text watermark" }),
156
+ useWatermark ? /* @__PURE__ */ jsxs("div", { className: "mt-4 grid gap-4 sm:grid-cols-2", children: [
157
+ /* @__PURE__ */ jsxs("label", { className: `sm:col-span-2 ${fieldLabelClass}`, children: [
158
+ "Text",
159
+ /* @__PURE__ */ jsx(
160
+ "input",
161
+ {
162
+ type: "text",
163
+ value: watermarkText,
164
+ onChange: (event) => setWatermarkText(event.target.value),
165
+ maxLength: 80,
166
+ className: `mt-2 ${inputClass}`
167
+ }
168
+ )
169
+ ] }),
170
+ /* @__PURE__ */ jsxs("label", { className: fieldLabelClass, children: [
171
+ "Size: ",
172
+ watermarkSize,
173
+ "pt",
174
+ /* @__PURE__ */ jsx(
175
+ "input",
176
+ {
177
+ type: "range",
178
+ min: 12,
179
+ max: 160,
180
+ value: watermarkSize,
181
+ onChange: (event) => setWatermarkSize(Number(event.target.value)),
182
+ className: "mt-2 w-full accent-[#1F8CFF]"
183
+ }
184
+ )
185
+ ] }),
186
+ /* @__PURE__ */ jsxs("label", { className: fieldLabelClass, children: [
187
+ "Opacity: ",
188
+ Math.round(watermarkOpacity * 100),
189
+ "%",
190
+ /* @__PURE__ */ jsx(
191
+ "input",
192
+ {
193
+ type: "range",
194
+ min: 0.05,
195
+ max: 1,
196
+ step: 0.05,
197
+ value: watermarkOpacity,
198
+ onChange: (event) => setWatermarkOpacity(Number(event.target.value)),
199
+ className: "mt-2 w-full accent-[#1F8CFF]"
200
+ }
201
+ )
202
+ ] }),
203
+ /* @__PURE__ */ jsxs("label", { className: fieldLabelClass, children: [
204
+ "Angle",
205
+ /* @__PURE__ */ jsxs(
206
+ "select",
207
+ {
208
+ value: watermarkRotation,
209
+ onChange: (event) => setWatermarkRotation(Number(event.target.value)),
210
+ className: `mt-2 ${inputClass}`,
211
+ children: [
212
+ /* @__PURE__ */ jsx("option", { value: 0, children: "Horizontal" }),
213
+ /* @__PURE__ */ jsx("option", { value: 45, children: "Diagonal (45\xB0)" }),
214
+ /* @__PURE__ */ jsx("option", { value: -45, children: "Diagonal (-45\xB0)" }),
215
+ /* @__PURE__ */ jsx("option", { value: 90, children: "Vertical" })
216
+ ]
217
+ }
218
+ )
219
+ ] }),
220
+ /* @__PURE__ */ jsxs("label", { className: fieldLabelClass, children: [
221
+ "Colour",
222
+ /* @__PURE__ */ jsx(
223
+ "input",
224
+ {
225
+ type: "color",
226
+ value: watermarkColor,
227
+ onChange: (event) => setWatermarkColor(event.target.value),
228
+ className: "mt-2 h-9 w-full cursor-pointer rounded-lg border border-slate-300 bg-white dark:border-slate-700 dark:bg-slate-950"
229
+ }
230
+ )
231
+ ] })
232
+ ] }) : null
233
+ ] }),
234
+ /* @__PURE__ */ jsxs("div", { className: cardClass, children: [
235
+ /* @__PURE__ */ jsx(Toggle, { checked: useNumbers, onChange: setUseNumbers, label: "Page numbers" }),
236
+ useNumbers ? /* @__PURE__ */ jsxs("div", { className: "mt-4 grid gap-4 sm:grid-cols-2", children: [
237
+ /* @__PURE__ */ jsxs("label", { className: fieldLabelClass, children: [
238
+ "Format",
239
+ /* @__PURE__ */ jsx(
240
+ "select",
241
+ {
242
+ value: numberFormat,
243
+ onChange: (event) => setNumberFormat(event.target.value),
244
+ className: `mt-2 ${inputClass}`,
245
+ children: NUMBER_FORMATS.map((format) => /* @__PURE__ */ jsx("option", { value: format, children: formatPageNumber(format, 1, 12) }, format))
246
+ }
247
+ )
248
+ ] }),
249
+ /* @__PURE__ */ jsxs("label", { className: fieldLabelClass, children: [
250
+ "Position",
251
+ /* @__PURE__ */ jsx(
252
+ "select",
253
+ {
254
+ value: numberPosition,
255
+ onChange: (event) => setNumberPosition(event.target.value),
256
+ className: `mt-2 ${inputClass}`,
257
+ children: POSITIONS.map((position) => /* @__PURE__ */ jsx("option", { value: position.value, children: position.label }, position.value))
258
+ }
259
+ )
260
+ ] }),
261
+ /* @__PURE__ */ jsxs("label", { className: fieldLabelClass, children: [
262
+ "Font size",
263
+ /* @__PURE__ */ jsx(
264
+ "input",
265
+ {
266
+ type: "number",
267
+ min: 6,
268
+ max: 36,
269
+ value: numberSize,
270
+ onChange: (event) => setNumberSize(Number(event.target.value)),
271
+ className: `mt-2 ${inputClass}`
272
+ }
273
+ )
274
+ ] }),
275
+ /* @__PURE__ */ jsxs("label", { className: fieldLabelClass, children: [
276
+ "Start numbering at",
277
+ /* @__PURE__ */ jsx(
278
+ "input",
279
+ {
280
+ type: "number",
281
+ min: 1,
282
+ value: startAt,
283
+ onChange: (event) => setStartAt(Number(event.target.value)),
284
+ className: `mt-2 ${inputClass}`
285
+ }
286
+ )
287
+ ] })
288
+ ] }) : null
289
+ ] }),
290
+ /* @__PURE__ */ jsxs("div", { className: "grid gap-4 sm:grid-cols-2", children: [
291
+ /* @__PURE__ */ jsxs("label", { className: fieldLabelClass, children: [
292
+ "Apply to pages",
293
+ /* @__PURE__ */ jsx(
294
+ "input",
295
+ {
296
+ type: "text",
297
+ value: pages,
298
+ onChange: (event) => setPages(event.target.value),
299
+ placeholder: "All pages (e.g. 2-)",
300
+ className: `mt-2 ${inputClass}`
301
+ }
302
+ )
303
+ ] }),
304
+ /* @__PURE__ */ jsxs("label", { className: fieldLabelClass, children: [
305
+ "Output file name",
306
+ /* @__PURE__ */ jsx(
307
+ "input",
308
+ {
309
+ type: "text",
310
+ value: outputName,
311
+ onChange: (event) => setOutputName(event.target.value),
312
+ placeholder: "stamped",
313
+ className: `mt-2 ${inputClass}`
314
+ }
315
+ )
316
+ ] })
317
+ ] })
318
+ ] }),
319
+ /* @__PURE__ */ jsx(StatusBanner, { status }),
320
+ /* @__PURE__ */ jsxs("div", { className: "mt-5 flex flex-wrap gap-3", children: [
321
+ /* @__PURE__ */ jsxs(
322
+ "button",
323
+ {
324
+ type: "submit",
325
+ disabled: busy || !pdf.bytes || !hasStamp,
326
+ className: primaryButtonClass,
327
+ children: [
328
+ /* @__PURE__ */ jsx("i", { className: `fa-solid ${isStamping ? "fa-spinner fa-spin" : "fa-stamp"}` }),
329
+ isStamping ? "Stamping..." : "Apply & Download"
330
+ ]
331
+ }
332
+ ),
333
+ /* @__PURE__ */ jsx(BackToPdfTools, {})
334
+ ] })
335
+ ] }),
336
+ side: /* @__PURE__ */ jsxs(Fragment, { children: [
337
+ /* @__PURE__ */ jsx("h2", { className: "text-base font-semibold text-slate-900 dark:text-slate-100", children: "Preview" }),
338
+ /* @__PURE__ */ jsx(StampPreview, { watermark, pageNumbers }),
339
+ /* @__PURE__ */ jsx(
340
+ SideTips,
341
+ {
342
+ tips: [
343
+ "The preview is a rough guide to placement, not an exact render.",
344
+ /* @__PURE__ */ jsxs(Fragment, { children: [
345
+ "Skip a cover page with ",
346
+ /* @__PURE__ */ jsx("code", { children: "2-" }),
347
+ " in Apply to pages. Numbering counts only stamped pages."
348
+ ] }),
349
+ "Text uses Helvetica, so it supports basic Latin characters only."
350
+ ]
351
+ }
352
+ )
353
+ ] })
354
+ }
355
+ )
356
+ }
357
+ );
358
+ }
359
+
360
+ export { WatermarkPdf };
361
+ //# sourceMappingURL=chunk-4EAYBJ6G.js.map
362
+ //# sourceMappingURL=chunk-4EAYBJ6G.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/stamp/WatermarkPdf.tsx"],"names":[],"mappings":"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","file":"chunk-4EAYBJ6G.js","sourcesContent":["import React, { useCallback, useEffect, 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 cardClass,\n fieldLabelClass,\n inputClass,\n primaryButtonClass,\n} from \"../shared/ToolLayout\";\nimport { useSinglePdf } from \"../shared/useSinglePdf\";\nimport { formatPageNumber, stampPdf, type NumberPosition } from \"./stampPdfCore\";\n\nconst NUMBER_FORMATS = [\"{n}\", \"Page {n}\", \"Page {n} of {total}\", \"{n} / {total}\"];\n\nconst POSITIONS = [\n { value: \"bottom-center\", label: \"Bottom center\" },\n { value: \"bottom-right\", label: \"Bottom right\" },\n { value: \"bottom-left\", label: \"Bottom left\" },\n { value: \"top-center\", label: \"Top center\" },\n { value: \"top-right\", label: \"Top right\" },\n { value: \"top-left\", label: \"Top left\" },\n];\n\nfunction Toggle({ checked, onChange, label, disabled = false }) {\n return (\n <label className=\"flex cursor-pointer items-center gap-3 text-sm font-semibold text-slate-900 dark:text-slate-100\">\n <input\n type=\"checkbox\"\n checked={checked}\n onChange={(event) => onChange(event.target.checked)}\n disabled={disabled}\n className=\"h-4 w-4 accent-[#1F8CFF]\"\n />\n {label}\n </label>\n );\n}\n\nfunction StampPreview({ watermark, pageNumbers }) {\n const [vertical, horizontal] = (pageNumbers?.position || \"bottom-center\").split(\"-\");\n const justify = { left: \"justify-start\", center: \"justify-center\", right: \"justify-end\" }[horizontal];\n\n return (\n <div className=\"mx-auto mt-4 flex aspect-[1/1.414] w-40 flex-col overflow-hidden rounded-md border border-slate-300 bg-white p-2 shadow-sm\">\n {pageNumbers && vertical === \"top\" ? (\n <div className={`flex ${justify} text-[7px] text-slate-600`}>\n {formatPageNumber(pageNumbers.format, 1, 12)}\n </div>\n ) : null}\n <div className=\"relative flex flex-1 items-center justify-center\">\n <div className=\"absolute inset-x-1 top-2 space-y-1\">\n {Array.from({ length: 9 }, (_, i) => (\n <div key={i} className=\"h-1 rounded bg-slate-200\" style={{ width: `${70 + ((i * 17) % 30)}%` }} />\n ))}\n </div>\n {watermark ? (\n <span\n className=\"relative whitespace-nowrap font-bold\"\n style={{\n color: watermark.color,\n opacity: watermark.opacity,\n fontSize: `${Math.max(6, watermark.fontSize / 4)}px`,\n transform: `rotate(${-watermark.rotation}deg)`,\n }}\n >\n {watermark.text}\n </span>\n ) : null}\n </div>\n {pageNumbers && vertical === \"bottom\" ? (\n <div className={`flex ${justify} text-[7px] text-slate-600`}>\n {formatPageNumber(pageNumbers.format, 1, 12)}\n </div>\n ) : null}\n </div>\n );\n}\n\nexport default function WatermarkPdf() {\n const [useWatermark, setUseWatermark] = useState(true);\n const [watermarkText, setWatermarkText] = useState(\"CONFIDENTIAL\");\n const [watermarkSize, setWatermarkSize] = useState(60);\n const [watermarkOpacity, setWatermarkOpacity] = useState(0.2);\n const [watermarkRotation, setWatermarkRotation] = useState(45);\n const [watermarkColor, setWatermarkColor] = useState(\"#e11d48\");\n const [useNumbers, setUseNumbers] = useState(false);\n const [numberFormat, setNumberFormat] = useState(\"Page {n} of {total}\");\n const [numberPosition, setNumberPosition] = useState<NumberPosition>(\"bottom-center\");\n const [numberSize, setNumberSize] = useState(11);\n const [startAt, setStartAt] = useState(1);\n const [pages, setPages] = useState(\"\");\n const [outputName, setOutputName] = useState(\"\");\n const [isStamping, setIsStamping] = useState(false);\n const [status, setStatus] = useState({\n tone: \"info\",\n text: \"Add a PDF, then stamp a text watermark, page numbers, or both.\",\n });\n\n useEffect(() => {\n document.title = \"Watermark & Page Numbers | Apps\";\n }, []);\n\n const onError = useCallback((text) => setStatus({ tone: \"error\", text }), []);\n const onLoaded = useCallback(({ file, pageCount }) => {\n setOutputName(`${toBaseName(file.name, \"document\")}-stamped`);\n setStatus({\n tone: \"success\",\n text: `${file.name} loaded with ${pageCount} page${pageCount === 1 ? \"\" : \"s\"}.`,\n });\n }, []);\n const pdf = useSinglePdf({ onError, onLoaded });\n\n const watermark = useWatermark\n ? {\n text: watermarkText,\n fontSize: Number(watermarkSize),\n opacity: Number(watermarkOpacity),\n rotation: Number(watermarkRotation),\n color: watermarkColor,\n }\n : null;\n const pageNumbers = useNumbers\n ? {\n format: numberFormat,\n position: numberPosition,\n fontSize: Number(numberSize),\n startAt: Number(startAt),\n }\n : null;\n const hasStamp = Boolean((watermark && watermarkText.trim()) || pageNumbers);\n\n const handleStamp = async (event) => {\n event.preventDefault();\n if (!pdf.bytes || !hasStamp) return;\n\n setIsStamping(true);\n setStatus({ tone: \"info\", text: \"Stamping your PDF...\" });\n try {\n const bytes = await stampPdf(pdf.bytes, { pages, watermark, pageNumbers });\n const blob = new Blob([bytes as BlobPart], { type: \"application/pdf\" });\n downloadBlob(blob, `${toBaseName(outputName, \"stamped\")}.pdf`);\n setStatus({\n tone: \"success\",\n text: `Done (${formatBytes(blob.size)}). Your download has started.`,\n });\n } catch (error) {\n setStatus({ tone: \"error\", text: `Stamping failed: ${error?.message || \"Please try again.\"}` });\n } finally {\n setIsStamping(false);\n }\n };\n\n const busy = isStamping || pdf.isReading;\n\n return (\n <PdfToolPage\n title=\"Watermark & Page Numbers\"\n description=\"Stamp a text watermark such as DRAFT or CONFIDENTIAL, and add page numbers, on every page or just the ones you pick. Runs in your browser.\"\n stats={[\n { value: pdf.pageCount || \"-\", label: \"Pages\" },\n { value: \"PDF\", label: \"Output\" },\n ]}\n >\n <PdfToolColumns\n main={\n <form onSubmit={handleStamp}>\n {pdf.file ? (\n <SelectedPdfCard\n signed={pdf.signed}\n file={pdf.file}\n pageCount={pdf.pageCount}\n onRemove={pdf.clear}\n disabled={isStamping}\n />\n ) : (\n <FileDropzone\n accept=\".pdf,application/pdf\"\n disabled={busy}\n onFiles={pdf.selectFiles}\n title=\"Drag and drop a PDF here\"\n hint=\"or click to browse from your device\"\n />\n )}\n\n <fieldset className=\"mt-5 space-y-4\" disabled={busy}>\n <div className={cardClass}>\n <Toggle checked={useWatermark} onChange={setUseWatermark} label=\"Text watermark\" />\n {useWatermark ? (\n <div className=\"mt-4 grid gap-4 sm:grid-cols-2\">\n <label className={`sm:col-span-2 ${fieldLabelClass}`}>\n Text\n <input\n type=\"text\"\n value={watermarkText}\n onChange={(event) => setWatermarkText(event.target.value)}\n maxLength={80}\n className={`mt-2 ${inputClass}`}\n />\n </label>\n <label className={fieldLabelClass}>\n Size: {watermarkSize}pt\n <input\n type=\"range\"\n min={12}\n max={160}\n value={watermarkSize}\n onChange={(event) => setWatermarkSize(Number(event.target.value))}\n className=\"mt-2 w-full accent-[#1F8CFF]\"\n />\n </label>\n <label className={fieldLabelClass}>\n Opacity: {Math.round(watermarkOpacity * 100)}%\n <input\n type=\"range\"\n min={0.05}\n max={1}\n step={0.05}\n value={watermarkOpacity}\n onChange={(event) => setWatermarkOpacity(Number(event.target.value))}\n className=\"mt-2 w-full accent-[#1F8CFF]\"\n />\n </label>\n <label className={fieldLabelClass}>\n Angle\n <select\n value={watermarkRotation}\n onChange={(event) => setWatermarkRotation(Number(event.target.value))}\n className={`mt-2 ${inputClass}`}\n >\n <option value={0}>Horizontal</option>\n <option value={45}>Diagonal (45°)</option>\n <option value={-45}>Diagonal (-45°)</option>\n <option value={90}>Vertical</option>\n </select>\n </label>\n <label className={fieldLabelClass}>\n Colour\n <input\n type=\"color\"\n value={watermarkColor}\n onChange={(event) => setWatermarkColor(event.target.value)}\n className=\"mt-2 h-9 w-full cursor-pointer rounded-lg border border-slate-300 bg-white dark:border-slate-700 dark:bg-slate-950\"\n />\n </label>\n </div>\n ) : null}\n </div>\n\n <div className={cardClass}>\n <Toggle checked={useNumbers} onChange={setUseNumbers} label=\"Page numbers\" />\n {useNumbers ? (\n <div className=\"mt-4 grid gap-4 sm:grid-cols-2\">\n <label className={fieldLabelClass}>\n Format\n <select\n value={numberFormat}\n onChange={(event) => setNumberFormat(event.target.value)}\n className={`mt-2 ${inputClass}`}\n >\n {NUMBER_FORMATS.map((format) => (\n <option key={format} value={format}>\n {formatPageNumber(format, 1, 12)}\n </option>\n ))}\n </select>\n </label>\n <label className={fieldLabelClass}>\n Position\n <select\n value={numberPosition}\n onChange={(event) => setNumberPosition(event.target.value as NumberPosition)}\n className={`mt-2 ${inputClass}`}\n >\n {POSITIONS.map((position) => (\n <option key={position.value} value={position.value}>\n {position.label}\n </option>\n ))}\n </select>\n </label>\n <label className={fieldLabelClass}>\n Font size\n <input\n type=\"number\"\n min={6}\n max={36}\n value={numberSize}\n onChange={(event) => setNumberSize(Number(event.target.value))}\n className={`mt-2 ${inputClass}`}\n />\n </label>\n <label className={fieldLabelClass}>\n Start numbering at\n <input\n type=\"number\"\n min={1}\n value={startAt}\n onChange={(event) => setStartAt(Number(event.target.value))}\n className={`mt-2 ${inputClass}`}\n />\n </label>\n </div>\n ) : null}\n </div>\n\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <label className={fieldLabelClass}>\n Apply to pages\n <input\n type=\"text\"\n value={pages}\n onChange={(event) => setPages(event.target.value)}\n placeholder=\"All pages (e.g. 2-)\"\n className={`mt-2 ${inputClass}`}\n />\n </label>\n <label className={fieldLabelClass}>\n Output file name\n <input\n type=\"text\"\n value={outputName}\n onChange={(event) => setOutputName(event.target.value)}\n placeholder=\"stamped\"\n className={`mt-2 ${inputClass}`}\n />\n </label>\n </div>\n </fieldset>\n\n <StatusBanner status={status} />\n\n <div className=\"mt-5 flex flex-wrap gap-3\">\n <button\n type=\"submit\"\n disabled={busy || !pdf.bytes || !hasStamp}\n className={primaryButtonClass}\n >\n <i className={`fa-solid ${isStamping ? \"fa-spinner fa-spin\" : \"fa-stamp\"}`} />\n {isStamping ? \"Stamping...\" : \"Apply & 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\">Preview</h2>\n <StampPreview watermark={watermark} pageNumbers={pageNumbers} />\n <SideTips\n tips={[\n \"The preview is a rough guide to placement, not an exact render.\",\n <>\n Skip a cover page with <code>2-</code> in Apply to pages. Numbering counts only\n stamped pages.\n </>,\n \"Text uses Helvetica, so it supports basic Latin characters only.\",\n ]}\n />\n </>\n }\n />\n </PdfToolPage>\n );\n}\n"]}
@@ -0,0 +1,165 @@
1
+ import { COMPRESSION_LEVELS, compressPdf, browserCodec } from './chunk-ZBZ7OC6I.js';
2
+ import { useSinglePdf } from './chunk-BK3MYSUS.js';
3
+ import { PdfToolPage, PdfToolColumns, SummaryList, SideTips, SelectedPdfCard, FileDropzone, fieldLabelClass, StatusBanner, primaryButtonClass, BackToPdfTools } from './chunk-H6EERRCR.js';
4
+ import { formatBytes, downloadBlob, toBaseName } from './chunk-EKHEY3LI.js';
5
+ import { useState, useEffect, useCallback } from 'react';
6
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
7
+
8
+ var LEVEL_HINTS = {
9
+ light: "Barely visible change. Best for print.",
10
+ balanced: "Good for email and sharing.",
11
+ strong: "Smallest file. Photos look softer."
12
+ };
13
+ function CompressPdf() {
14
+ const [level, setLevel] = useState("balanced");
15
+ const [result, setResult] = useState(null);
16
+ const [isCompressing, setIsCompressing] = useState(false);
17
+ const [status, setStatus] = useState({
18
+ tone: "info",
19
+ text: "Add a PDF to shrink it by re-compressing its images."
20
+ });
21
+ useEffect(() => {
22
+ document.title = "Compress PDF | Apps";
23
+ }, []);
24
+ const onError = useCallback((text) => setStatus({ tone: "error", text }), []);
25
+ const onLoaded = useCallback(({ file }) => {
26
+ setResult(null);
27
+ setStatus({ tone: "success", text: `${file.name} loaded (${formatBytes(file.size)}). Choose a level and compress.` });
28
+ }, []);
29
+ const pdf = useSinglePdf({ onError, onLoaded });
30
+ const clearFile = () => {
31
+ pdf.clear();
32
+ setResult(null);
33
+ };
34
+ const handleCompress = async (event) => {
35
+ event.preventDefault();
36
+ if (!pdf.bytes) return;
37
+ setIsCompressing(true);
38
+ setResult(null);
39
+ try {
40
+ const { bytes, stats } = await compressPdf(
41
+ pdf.bytes,
42
+ COMPRESSION_LEVELS[level],
43
+ browserCodec,
44
+ (done, total) => setStatus({ tone: "info", text: total ? `Compressing images (${done} of ${total})...` : "Optimizing..." })
45
+ );
46
+ setResult(stats);
47
+ if (stats.newSize < stats.originalSize * 0.98) {
48
+ const blob = new Blob([bytes], { type: "application/pdf" });
49
+ downloadBlob(blob, `${toBaseName(pdf.file.name, "document")}-compressed.pdf`);
50
+ const saved = Math.round((1 - stats.newSize / stats.originalSize) * 100);
51
+ setStatus({
52
+ tone: "success",
53
+ text: `${formatBytes(stats.originalSize)} \u2192 ${formatBytes(stats.newSize)} (${saved}% smaller). Your download has started.`
54
+ });
55
+ } else {
56
+ setStatus({
57
+ tone: "info",
58
+ text: stats.images ? "This PDF is already well optimized; a smaller version could not be made at this level. Try Strong, or keep your original." : "This PDF has no images to compress, so it cannot be made noticeably smaller. Text and vector content is already compact."
59
+ });
60
+ }
61
+ } catch (error) {
62
+ setStatus({ tone: "error", text: `Compression failed: ${error?.message || "Please try again."}` });
63
+ } finally {
64
+ setIsCompressing(false);
65
+ }
66
+ };
67
+ const busy = isCompressing || pdf.isReading;
68
+ return /* @__PURE__ */ jsx(
69
+ PdfToolPage,
70
+ {
71
+ title: "Compress PDF",
72
+ description: "Shrink PDFs that are heavy with photos or scans by re-compressing their images. Runs in your browser; files never leave your device.",
73
+ stats: [
74
+ { value: pdf.file ? formatBytes(pdf.file.size) : "-", label: "Original" },
75
+ { value: result ? formatBytes(result.newSize) : "-", label: "Result" }
76
+ ],
77
+ children: /* @__PURE__ */ jsx(
78
+ PdfToolColumns,
79
+ {
80
+ main: /* @__PURE__ */ jsxs("form", { onSubmit: handleCompress, children: [
81
+ pdf.file ? /* @__PURE__ */ jsx(
82
+ SelectedPdfCard,
83
+ {
84
+ signed: pdf.signed,
85
+ file: pdf.file,
86
+ pageCount: pdf.pageCount,
87
+ onRemove: clearFile,
88
+ disabled: isCompressing
89
+ }
90
+ ) : /* @__PURE__ */ jsx(
91
+ FileDropzone,
92
+ {
93
+ accept: ".pdf,application/pdf",
94
+ disabled: busy,
95
+ onFiles: pdf.selectFiles,
96
+ title: "Drag and drop a PDF here",
97
+ hint: "or click to browse from your device"
98
+ }
99
+ ),
100
+ /* @__PURE__ */ jsxs("fieldset", { className: "mt-5", disabled: busy || !pdf.bytes, children: [
101
+ /* @__PURE__ */ jsx("legend", { className: fieldLabelClass, children: "Compression level" }),
102
+ /* @__PURE__ */ jsx("div", { className: "mt-2 grid gap-2 sm:grid-cols-3", children: Object.entries(COMPRESSION_LEVELS).map(([id, option]) => /* @__PURE__ */ jsxs(
103
+ "label",
104
+ {
105
+ className: `cursor-pointer rounded-xl border p-3 text-sm transition ${level === id ? "border-[#1F8CFF] bg-blue-50 dark:bg-blue-500/10" : "border-slate-200 bg-white hover:border-slate-300 dark:border-slate-700 dark:bg-slate-900"}`,
106
+ children: [
107
+ /* @__PURE__ */ jsx(
108
+ "input",
109
+ {
110
+ type: "radio",
111
+ name: "compression-level",
112
+ value: id,
113
+ checked: level === id,
114
+ onChange: () => setLevel(id),
115
+ className: "sr-only"
116
+ }
117
+ ),
118
+ /* @__PURE__ */ jsx("span", { className: "block font-semibold text-slate-900 dark:text-slate-100", children: option.label }),
119
+ /* @__PURE__ */ jsx("span", { className: "mt-1 block text-xs text-slate-600 dark:text-slate-300", children: LEVEL_HINTS[id] })
120
+ ]
121
+ },
122
+ id
123
+ )) })
124
+ ] }),
125
+ /* @__PURE__ */ jsx(StatusBanner, { status }),
126
+ /* @__PURE__ */ jsxs("div", { className: "mt-5 flex flex-wrap gap-3", children: [
127
+ /* @__PURE__ */ jsxs("button", { type: "submit", disabled: busy || !pdf.bytes, className: primaryButtonClass, children: [
128
+ /* @__PURE__ */ jsx("i", { className: `fa-solid ${isCompressing ? "fa-spinner fa-spin" : "fa-file-zipper"}` }),
129
+ isCompressing ? "Compressing..." : "Compress PDF"
130
+ ] }),
131
+ /* @__PURE__ */ jsx(BackToPdfTools, {})
132
+ ] })
133
+ ] }),
134
+ side: /* @__PURE__ */ jsxs(Fragment, { children: [
135
+ /* @__PURE__ */ jsx("h2", { className: "text-base font-semibold text-slate-900 dark:text-slate-100", children: "Result" }),
136
+ /* @__PURE__ */ jsx(
137
+ SummaryList,
138
+ {
139
+ items: [
140
+ { label: "Images found", value: result ? result.images : "-" },
141
+ { label: "Images compressed", value: result ? result.compressed : "-" },
142
+ { label: "New size", value: result ? formatBytes(result.newSize) : "-" }
143
+ ]
144
+ }
145
+ ),
146
+ /* @__PURE__ */ jsx(
147
+ SideTips,
148
+ {
149
+ tips: [
150
+ "Works best on scans and photo-heavy PDFs.",
151
+ "Text stays sharp: only images are re-compressed.",
152
+ "Masks and CMYK images are left as they are."
153
+ ]
154
+ }
155
+ )
156
+ ] })
157
+ }
158
+ )
159
+ }
160
+ );
161
+ }
162
+
163
+ export { CompressPdf };
164
+ //# sourceMappingURL=chunk-4LBJTDV5.js.map
165
+ //# sourceMappingURL=chunk-4LBJTDV5.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/compress/CompressPdf.tsx"],"names":[],"mappings":"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","file":"chunk-4LBJTDV5.js","sourcesContent":["import React, { useCallback, useEffect, 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 fieldLabelClass,\n primaryButtonClass,\n} from \"../shared/ToolLayout\";\nimport { useSinglePdf } from \"../shared/useSinglePdf\";\nimport { browserCodec } from \"./browserCodec\";\nimport { COMPRESSION_LEVELS, compressPdf } from \"./compressPdfCore\";\n\nconst LEVEL_HINTS = {\n light: \"Barely visible change. Best for print.\",\n balanced: \"Good for email and sharing.\",\n strong: \"Smallest file. Photos look softer.\",\n};\n\nexport default function CompressPdf() {\n const [level, setLevel] = useState(\"balanced\");\n const [result, setResult] = useState(null);\n const [isCompressing, setIsCompressing] = useState(false);\n const [status, setStatus] = useState({\n tone: \"info\",\n text: \"Add a PDF to shrink it by re-compressing its images.\",\n });\n\n useEffect(() => {\n document.title = \"Compress PDF | Apps\";\n }, []);\n\n const onError = useCallback((text) => setStatus({ tone: \"error\", text }), []);\n const onLoaded = useCallback(({ file }) => {\n setResult(null);\n setStatus({ tone: \"success\", text: `${file.name} loaded (${formatBytes(file.size)}). Choose a level and compress.` });\n }, []);\n const pdf = useSinglePdf({ onError, onLoaded });\n\n const clearFile = () => {\n pdf.clear();\n setResult(null);\n };\n\n const handleCompress = async (event) => {\n event.preventDefault();\n if (!pdf.bytes) return;\n\n setIsCompressing(true);\n setResult(null);\n try {\n const { bytes, stats } = await compressPdf(pdf.bytes, COMPRESSION_LEVELS[level], browserCodec, (done, total) =>\n setStatus({ tone: \"info\", text: total ? `Compressing images (${done} of ${total})...` : \"Optimizing...\" })\n );\n setResult(stats);\n\n if (stats.newSize < stats.originalSize * 0.98) {\n const blob = new Blob([bytes as BlobPart], { type: \"application/pdf\" });\n downloadBlob(blob, `${toBaseName(pdf.file.name, \"document\")}-compressed.pdf`);\n const saved = Math.round((1 - stats.newSize / stats.originalSize) * 100);\n setStatus({\n tone: \"success\",\n text: `${formatBytes(stats.originalSize)} → ${formatBytes(stats.newSize)} (${saved}% smaller). Your download has started.`,\n });\n } else {\n setStatus({\n tone: \"info\",\n text: stats.images\n ? \"This PDF is already well optimized; a smaller version could not be made at this level. Try Strong, or keep your original.\"\n : \"This PDF has no images to compress, so it cannot be made noticeably smaller. Text and vector content is already compact.\",\n });\n }\n } catch (error) {\n setStatus({ tone: \"error\", text: `Compression failed: ${error?.message || \"Please try again.\"}` });\n } finally {\n setIsCompressing(false);\n }\n };\n\n const busy = isCompressing || pdf.isReading;\n\n return (\n <PdfToolPage\n title=\"Compress PDF\"\n description=\"Shrink PDFs that are heavy with photos or scans by re-compressing their images. Runs in your browser; files never leave your device.\"\n stats={[\n { value: pdf.file ? formatBytes(pdf.file.size) : \"-\", label: \"Original\" },\n { value: result ? formatBytes(result.newSize) : \"-\", label: \"Result\" },\n ]}\n >\n <PdfToolColumns\n main={\n <form onSubmit={handleCompress}>\n {pdf.file ? (\n <SelectedPdfCard\n signed={pdf.signed} file={pdf.file} pageCount={pdf.pageCount} onRemove={clearFile} disabled={isCompressing} />\n ) : (\n <FileDropzone\n accept=\".pdf,application/pdf\"\n disabled={busy}\n onFiles={pdf.selectFiles}\n title=\"Drag and drop a PDF here\"\n hint=\"or click to browse from your device\"\n />\n )}\n\n <fieldset className=\"mt-5\" disabled={busy || !pdf.bytes}>\n <legend className={fieldLabelClass}>Compression level</legend>\n <div className=\"mt-2 grid gap-2 sm:grid-cols-3\">\n {Object.entries(COMPRESSION_LEVELS).map(([id, option]) => (\n <label\n key={id}\n className={`cursor-pointer rounded-xl border p-3 text-sm transition ${\n level === id\n ? \"border-[#1F8CFF] bg-blue-50 dark:bg-blue-500/10\"\n : \"border-slate-200 bg-white hover:border-slate-300 dark:border-slate-700 dark:bg-slate-900\"\n }`}\n >\n <input\n type=\"radio\"\n name=\"compression-level\"\n value={id}\n checked={level === id}\n onChange={() => setLevel(id)}\n className=\"sr-only\"\n />\n <span className=\"block font-semibold text-slate-900 dark:text-slate-100\">{option.label}</span>\n <span className=\"mt-1 block text-xs text-slate-600 dark:text-slate-300\">{LEVEL_HINTS[id]}</span>\n </label>\n ))}\n </div>\n </fieldset>\n\n <StatusBanner status={status} />\n\n <div className=\"mt-5 flex flex-wrap gap-3\">\n <button type=\"submit\" disabled={busy || !pdf.bytes} className={primaryButtonClass}>\n <i className={`fa-solid ${isCompressing ? \"fa-spinner fa-spin\" : \"fa-file-zipper\"}`} />\n {isCompressing ? \"Compressing...\" : \"Compress PDF\"}\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\">Result</h2>\n <SummaryList\n items={[\n { label: \"Images found\", value: result ? result.images : \"-\" },\n { label: \"Images compressed\", value: result ? result.compressed : \"-\" },\n { label: \"New size\", value: result ? formatBytes(result.newSize) : \"-\" },\n ]}\n />\n <SideTips\n tips={[\n \"Works best on scans and photo-heavy PDFs.\",\n \"Text stays sharp: only images are re-compressed.\",\n \"Masks and CMYK images are left as they are.\",\n ]}\n />\n </>\n }\n />\n </PdfToolPage>\n );\n}\n"]}