@xenterprises/fastify-xpdf 1.0.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/.env.example +11 -0
- package/CHANGELOG.md +106 -0
- package/LICENSE +15 -0
- package/QUICK_START.md +462 -0
- package/README.md +580 -0
- package/SECURITY.md +417 -0
- package/package.json +57 -0
- package/server/app.js +151 -0
- package/src/index.js +7 -0
- package/src/services/forms.js +163 -0
- package/src/services/generator.js +147 -0
- package/src/services/merger.js +115 -0
- package/src/utils/helpers.js +220 -0
- package/src/xPDF.js +126 -0
- package/test/xPDF.test.js +903 -0
package/README.md
ADDED
|
@@ -0,0 +1,580 @@
|
|
|
1
|
+
# xPDF
|
|
2
|
+
|
|
3
|
+
> Fastify v5 plugin for PDF generation and manipulation with a simple, intuitive library of methods.
|
|
4
|
+
|
|
5
|
+
Generate PDFs from HTML and Markdown, fill PDF forms, and merge PDFs. Works with any Fastify application and optionally integrates with [xStorage](https://github.com/x-enterprises/fastify-plugins/tree/main/fastify-xstorage) for automatic cloud storage.
|
|
6
|
+
|
|
7
|
+
## Requirements
|
|
8
|
+
|
|
9
|
+
- **Fastify v5.0.0+**
|
|
10
|
+
- **Node.js v20+**
|
|
11
|
+
|
|
12
|
+
## Features
|
|
13
|
+
|
|
14
|
+
- 📄 **HTML to PDF** - Convert HTML to PDF using Puppeteer
|
|
15
|
+
- 📝 **Markdown to PDF** - Convert Markdown to PDF with styled formatting
|
|
16
|
+
- 📋 **Form Filling** - Fill PDF form fields with values
|
|
17
|
+
- ✏️ **Form Flattening** - Make form fields non-editable
|
|
18
|
+
- 🔗 **PDF Merging** - Combine multiple PDFs into one
|
|
19
|
+
- 💾 **Optional Storage** - Save PDFs to S3-compatible storage via xStorage
|
|
20
|
+
- 🔌 **Simple API** - Methods decorated on Fastify instance
|
|
21
|
+
- 🎯 **Library Pattern** - Use as a service, not as HTTP routes
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install @xenterprises/fastify-xpdf puppeteer marked pdf-lib fastify@5
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
For optional storage integration:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm install @xenterprises/fastify-xstorage
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Quick Start
|
|
36
|
+
|
|
37
|
+
```javascript
|
|
38
|
+
import Fastify from "fastify";
|
|
39
|
+
import xPDF from "@xenterprises/fastify-xpdf";
|
|
40
|
+
|
|
41
|
+
const fastify = Fastify({ logger: true });
|
|
42
|
+
|
|
43
|
+
// Register xPDF
|
|
44
|
+
await fastify.register(xPDF, {
|
|
45
|
+
headless: true,
|
|
46
|
+
useStorage: false,
|
|
47
|
+
defaultFolder: "pdfs",
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Generate PDF from HTML
|
|
51
|
+
const result = await fastify.xPDF.generateFromHtml("<h1>Hello World</h1>", {
|
|
52
|
+
filename: "hello.pdf",
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
console.log(result);
|
|
56
|
+
// {
|
|
57
|
+
// buffer: Buffer,
|
|
58
|
+
// filename: "hello.pdf",
|
|
59
|
+
// size: 12345,
|
|
60
|
+
// storageKey?: "pdfs/hello.pdf", // Only if saveToStorage: true
|
|
61
|
+
// url?: "https://..." // Only if saveToStorage: true
|
|
62
|
+
// }
|
|
63
|
+
|
|
64
|
+
await fastify.listen({ port: 3000 });
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Configuration
|
|
68
|
+
|
|
69
|
+
### Plugin Options
|
|
70
|
+
|
|
71
|
+
```javascript
|
|
72
|
+
await fastify.register(xPDF, {
|
|
73
|
+
// Puppeteer options
|
|
74
|
+
headless: true, // Default: true
|
|
75
|
+
args: ["--no-sandbox"], // Chrome launch args
|
|
76
|
+
|
|
77
|
+
// xStorage integration (optional)
|
|
78
|
+
useStorage: false, // Default: false
|
|
79
|
+
defaultFolder: "pdfs", // Default storage folder
|
|
80
|
+
|
|
81
|
+
// PDF generation defaults
|
|
82
|
+
format: "A4", // Default page format
|
|
83
|
+
printBackground: true, // Include background in PDFs
|
|
84
|
+
margin: { // Default margins
|
|
85
|
+
top: "1cm",
|
|
86
|
+
right: "1cm",
|
|
87
|
+
bottom: "1cm",
|
|
88
|
+
left: "1cm",
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Core API
|
|
94
|
+
|
|
95
|
+
All methods are available on the `fastify.xPDF` namespace.
|
|
96
|
+
|
|
97
|
+
### `fastify.xPDF.generateFromHtml(html, options)`
|
|
98
|
+
|
|
99
|
+
Convert HTML to PDF.
|
|
100
|
+
|
|
101
|
+
```javascript
|
|
102
|
+
const result = await fastify.xPDF.generateFromHtml("<h1>Invoice</h1>", {
|
|
103
|
+
filename: "invoice.pdf",
|
|
104
|
+
format: "A4",
|
|
105
|
+
landscape: false,
|
|
106
|
+
margin: { top: "1cm", right: "1cm", bottom: "1cm", left: "1cm" },
|
|
107
|
+
printBackground: true,
|
|
108
|
+
saveToStorage: false,
|
|
109
|
+
folder: "pdfs",
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
console.log(result);
|
|
113
|
+
// {
|
|
114
|
+
// buffer: Buffer, // PDF content as Buffer
|
|
115
|
+
// filename: "invoice.pdf", // Output filename
|
|
116
|
+
// size: 245678, // File size in bytes
|
|
117
|
+
// storageKey?: "pdfs/...", // Only if saveToStorage: true
|
|
118
|
+
// url?: "https://..." // Only if saveToStorage: true
|
|
119
|
+
// }
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
**Options:**
|
|
123
|
+
- `filename` - Output filename (default: `generated-{timestamp}.pdf`)
|
|
124
|
+
- `format` - Page format: A4, Letter, Legal, A3, A5, etc. (default: A4)
|
|
125
|
+
- `landscape` - Landscape orientation (default: false)
|
|
126
|
+
- `margin` - Page margins (default: 1cm all sides)
|
|
127
|
+
- `printBackground` - Include background graphics (default: true)
|
|
128
|
+
- `saveToStorage` - Save to xStorage (default: false)
|
|
129
|
+
- `folder` - Storage folder (default: 'pdfs')
|
|
130
|
+
|
|
131
|
+
### `fastify.xPDF.generateFromMarkdown(markdown, options)`
|
|
132
|
+
|
|
133
|
+
Convert Markdown to PDF.
|
|
134
|
+
|
|
135
|
+
```javascript
|
|
136
|
+
const markdown = `
|
|
137
|
+
# Invoice
|
|
138
|
+
|
|
139
|
+
**Date:** January 1, 2024
|
|
140
|
+
|
|
141
|
+
## Items
|
|
142
|
+
|
|
143
|
+
1. Item 1: $100
|
|
144
|
+
2. Item 2: $200
|
|
145
|
+
|
|
146
|
+
**Total:** $300
|
|
147
|
+
`;
|
|
148
|
+
|
|
149
|
+
const result = await fastify.xPDF.generateFromMarkdown(markdown, {
|
|
150
|
+
filename: "invoice.pdf",
|
|
151
|
+
saveToStorage: true,
|
|
152
|
+
folder: "invoices",
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
console.log(result);
|
|
156
|
+
// {
|
|
157
|
+
// buffer: Buffer,
|
|
158
|
+
// filename: "invoice.pdf",
|
|
159
|
+
// size: 234567,
|
|
160
|
+
// storageKey: "invoices/invoice-1234567890.pdf",
|
|
161
|
+
// url: "https://storage.example.com/invoices/invoice-1234567890.pdf"
|
|
162
|
+
// }
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
**Supports:**
|
|
166
|
+
- Headings (H1-H6)
|
|
167
|
+
- Paragraphs and line breaks
|
|
168
|
+
- Lists (ordered and unordered)
|
|
169
|
+
- Code blocks with syntax highlighting
|
|
170
|
+
- Tables
|
|
171
|
+
- Blockquotes
|
|
172
|
+
- Links
|
|
173
|
+
- Emphasis (bold, italic)
|
|
174
|
+
|
|
175
|
+
### `fastify.xPDF.fillForm(pdfBuffer, fieldValues, options)`
|
|
176
|
+
|
|
177
|
+
Fill PDF form fields and optionally flatten.
|
|
178
|
+
|
|
179
|
+
```javascript
|
|
180
|
+
// Download PDF from storage
|
|
181
|
+
const pdfBuffer = await fastify.xStorage.download("forms/application.pdf");
|
|
182
|
+
|
|
183
|
+
// Fill form
|
|
184
|
+
const result = await fastify.xPDF.fillForm(
|
|
185
|
+
pdfBuffer,
|
|
186
|
+
{
|
|
187
|
+
firstName: "John",
|
|
188
|
+
lastName: "Doe",
|
|
189
|
+
email: "john@example.com",
|
|
190
|
+
agreeToTerms: true,
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
flatten: true, // Make fields non-editable
|
|
194
|
+
filename: "application-filled.pdf",
|
|
195
|
+
saveToStorage: true,
|
|
196
|
+
folder: "submitted-forms",
|
|
197
|
+
}
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
console.log(result);
|
|
201
|
+
// {
|
|
202
|
+
// buffer: Buffer,
|
|
203
|
+
// filename: "application-filled.pdf",
|
|
204
|
+
// size: 256789,
|
|
205
|
+
// storageKey: "submitted-forms/...",
|
|
206
|
+
// url: "https://..."
|
|
207
|
+
// }
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
**Options:**
|
|
211
|
+
- `flatten` - Make form fields non-editable (default: true)
|
|
212
|
+
- `filename` - Output filename
|
|
213
|
+
- `saveToStorage` - Save to xStorage (default: false)
|
|
214
|
+
- `folder` - Storage folder (default: 'pdfs')
|
|
215
|
+
|
|
216
|
+
**Supported Field Types:**
|
|
217
|
+
- Text fields: `field.setText(value)`
|
|
218
|
+
- Checkboxes: `field.check()` or `field.uncheck()`
|
|
219
|
+
- Radio buttons: `field.select(value)`
|
|
220
|
+
- Dropdowns: `field.select(value)`
|
|
221
|
+
|
|
222
|
+
### `fastify.xPDF.listFormFields(pdfBuffer)`
|
|
223
|
+
|
|
224
|
+
List all form fields in a PDF.
|
|
225
|
+
|
|
226
|
+
```javascript
|
|
227
|
+
const pdfBuffer = await fastify.xStorage.download("forms/application.pdf");
|
|
228
|
+
|
|
229
|
+
const fields = await fastify.xPDF.listFormFields(pdfBuffer);
|
|
230
|
+
|
|
231
|
+
console.log(fields);
|
|
232
|
+
// [
|
|
233
|
+
// { name: "firstName", type: "text", value: null },
|
|
234
|
+
// { name: "lastName", type: "text", value: null },
|
|
235
|
+
// { name: "email", type: "text", value: null },
|
|
236
|
+
// { name: "agreeToTerms", type: "checkbox", value: false }
|
|
237
|
+
// ]
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
**Returns:**
|
|
241
|
+
- `name` - Field name
|
|
242
|
+
- `type` - Field type (text, checkbox, radio, dropdown, option)
|
|
243
|
+
- `value` - Current field value
|
|
244
|
+
|
|
245
|
+
### `fastify.xPDF.mergePDFs(pdfBuffers, options)`
|
|
246
|
+
|
|
247
|
+
Merge multiple PDFs into one.
|
|
248
|
+
|
|
249
|
+
```javascript
|
|
250
|
+
// Download PDFs from storage
|
|
251
|
+
const page1 = await fastify.xStorage.download("documents/page1.pdf");
|
|
252
|
+
const page2 = await fastify.xStorage.download("documents/page2.pdf");
|
|
253
|
+
const page3 = await fastify.xStorage.download("documents/page3.pdf");
|
|
254
|
+
|
|
255
|
+
const result = await fastify.xPDF.mergePDFs([page1, page2, page3], {
|
|
256
|
+
filename: "complete-document.pdf",
|
|
257
|
+
saveToStorage: true,
|
|
258
|
+
folder: "merged",
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
console.log(result);
|
|
262
|
+
// {
|
|
263
|
+
// buffer: Buffer,
|
|
264
|
+
// filename: "complete-document.pdf",
|
|
265
|
+
// size: 512345,
|
|
266
|
+
// pageCount: 15, // Total pages from all 3 PDFs
|
|
267
|
+
// storageKey: "merged/complete-document-1234567890.pdf",
|
|
268
|
+
// url: "https://..."
|
|
269
|
+
// }
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
**Options:**
|
|
273
|
+
- `filename` - Output filename (default: `merged-{timestamp}.pdf`)
|
|
274
|
+
- `saveToStorage` - Save to xStorage (default: false)
|
|
275
|
+
- `folder` - Storage folder (default: 'pdfs')
|
|
276
|
+
|
|
277
|
+
**Returns:**
|
|
278
|
+
- `buffer` - Merged PDF buffer
|
|
279
|
+
- `filename` - Output filename
|
|
280
|
+
- `size` - File size in bytes
|
|
281
|
+
- `pageCount` - Total number of pages
|
|
282
|
+
- `storageKey` - Storage key (if saved)
|
|
283
|
+
- `url` - Public URL (if saved)
|
|
284
|
+
|
|
285
|
+
## xStorage Integration
|
|
286
|
+
|
|
287
|
+
xPDF works seamlessly with xStorage for automatic cloud storage of generated PDFs.
|
|
288
|
+
|
|
289
|
+
### Basic Integration
|
|
290
|
+
|
|
291
|
+
```javascript
|
|
292
|
+
import Fastify from "fastify";
|
|
293
|
+
import xStorage from "@xenterprises/fastify-xstorage";
|
|
294
|
+
import xPDF from "@xenterprises/fastify-xpdf";
|
|
295
|
+
|
|
296
|
+
const fastify = Fastify();
|
|
297
|
+
|
|
298
|
+
// Register xStorage first
|
|
299
|
+
await fastify.register(xStorage, {
|
|
300
|
+
endpoint: "https://nyc3.digitaloceanspaces.com",
|
|
301
|
+
region: "nyc3",
|
|
302
|
+
accessKeyId: process.env.DO_SPACES_KEY,
|
|
303
|
+
secretAccessKey: process.env.DO_SPACES_SECRET,
|
|
304
|
+
bucket: "my-bucket",
|
|
305
|
+
publicUrl: "https://my-bucket.nyc3.digitaloceanspaces.com",
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
// Register xPDF (will auto-detect xStorage)
|
|
309
|
+
await fastify.register(xPDF, {
|
|
310
|
+
useStorage: true,
|
|
311
|
+
defaultFolder: "pdfs",
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
// Now all PDFs can be saved to storage
|
|
315
|
+
const result = await fastify.xPDF.generateFromHtml("<h1>Report</h1>", {
|
|
316
|
+
saveToStorage: true,
|
|
317
|
+
folder: "reports",
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
console.log(result.url); // https://my-bucket.nyc3.digitaloceanspaces.com/reports/...
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### Advanced: Retrieve and Process Stored PDFs
|
|
324
|
+
|
|
325
|
+
```javascript
|
|
326
|
+
// Get PDF from storage
|
|
327
|
+
const storedUrl = "https://my-bucket.nyc3.digitaloceanspaces.com/forms/template.pdf";
|
|
328
|
+
const key = storedUrl.replace(process.env.STORAGE_PUBLIC_URL + "/", "");
|
|
329
|
+
const pdfBuffer = await fastify.xStorage.download(key);
|
|
330
|
+
|
|
331
|
+
// Fill form and save result
|
|
332
|
+
const result = await fastify.xPDF.fillForm(pdfBuffer, { name: "John" }, {
|
|
333
|
+
saveToStorage: true,
|
|
334
|
+
folder: "submitted",
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
console.log(result.url); // Direct link to filled form
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
## Helper Utilities
|
|
341
|
+
|
|
342
|
+
```javascript
|
|
343
|
+
import { helpers } from "@xenterprises/fastify-xpdf";
|
|
344
|
+
|
|
345
|
+
// Generate unique PDF filename
|
|
346
|
+
helpers.generatePdfFilename("invoice");
|
|
347
|
+
// "invoice-1704067200000.pdf"
|
|
348
|
+
|
|
349
|
+
// Validate PDF buffer
|
|
350
|
+
helpers.isValidPdfBuffer(buffer);
|
|
351
|
+
// true/false
|
|
352
|
+
|
|
353
|
+
// Get PDF metadata
|
|
354
|
+
helpers.getPdfMetadata(buffer);
|
|
355
|
+
// { size: 12345 }
|
|
356
|
+
|
|
357
|
+
// Sanitize filename
|
|
358
|
+
helpers.sanitizeFilename("My File (2024).pdf");
|
|
359
|
+
// "my_file_2024.pdf"
|
|
360
|
+
|
|
361
|
+
// Get page format dimensions
|
|
362
|
+
helpers.getPageFormat("A4");
|
|
363
|
+
// { width: 8.27, height: 11.7 }
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
## Usage Examples
|
|
367
|
+
|
|
368
|
+
### Document Generation
|
|
369
|
+
|
|
370
|
+
```javascript
|
|
371
|
+
fastify.post("/invoices", async (request, reply) => {
|
|
372
|
+
const { items, total, date } = request.body;
|
|
373
|
+
|
|
374
|
+
const html = `
|
|
375
|
+
<h1>Invoice</h1>
|
|
376
|
+
<p>Date: ${date}</p>
|
|
377
|
+
<ul>
|
|
378
|
+
${items.map((item) => `<li>${item.name}: $${item.price}</li>`).join("")}
|
|
379
|
+
</ul>
|
|
380
|
+
<h2>Total: $${total}</h2>
|
|
381
|
+
`;
|
|
382
|
+
|
|
383
|
+
const result = await fastify.xPDF.generateFromHtml(html, {
|
|
384
|
+
filename: "invoice.pdf",
|
|
385
|
+
saveToStorage: true,
|
|
386
|
+
folder: "invoices",
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
return { success: true, url: result.url };
|
|
390
|
+
});
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
### Form Processing
|
|
394
|
+
|
|
395
|
+
```javascript
|
|
396
|
+
fastify.post("/applications/:id/submit", async (request, reply) => {
|
|
397
|
+
const { id } = request.params;
|
|
398
|
+
const formData = request.body;
|
|
399
|
+
|
|
400
|
+
// Get form template
|
|
401
|
+
const template = await fastify.xStorage.download("forms/application-template.pdf");
|
|
402
|
+
|
|
403
|
+
// Fill with submitted data
|
|
404
|
+
const result = await fastify.xPDF.fillForm(template, formData, {
|
|
405
|
+
flatten: true,
|
|
406
|
+
filename: `application-${id}.pdf`,
|
|
407
|
+
saveToStorage: true,
|
|
408
|
+
folder: "applications",
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
// Save record in database
|
|
412
|
+
await fastify.db.application.create({
|
|
413
|
+
id,
|
|
414
|
+
pdfKey: result.storageKey,
|
|
415
|
+
pdfUrl: result.url,
|
|
416
|
+
submittedAt: new Date(),
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
return { success: true, pdfUrl: result.url };
|
|
420
|
+
});
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
### Report Compilation
|
|
424
|
+
|
|
425
|
+
```javascript
|
|
426
|
+
fastify.post("/reports/compile", async (request, reply) => {
|
|
427
|
+
const { reportIds } = request.body;
|
|
428
|
+
|
|
429
|
+
// Get all report PDFs
|
|
430
|
+
const pdfBuffers = await Promise.all(
|
|
431
|
+
reportIds.map((id) =>
|
|
432
|
+
fastify.xStorage.download(`reports/${id}.pdf`)
|
|
433
|
+
)
|
|
434
|
+
);
|
|
435
|
+
|
|
436
|
+
// Merge into single document
|
|
437
|
+
const result = await fastify.xPDF.mergePDFs(pdfBuffers, {
|
|
438
|
+
filename: "compiled-report.pdf",
|
|
439
|
+
saveToStorage: true,
|
|
440
|
+
folder: "compiled",
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
return {
|
|
444
|
+
success: true,
|
|
445
|
+
filename: result.filename,
|
|
446
|
+
pages: result.pageCount,
|
|
447
|
+
url: result.url,
|
|
448
|
+
};
|
|
449
|
+
});
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
## Best Practices
|
|
453
|
+
|
|
454
|
+
1. **Use xStorage for Production** - Automatically manage PDF storage and access
|
|
455
|
+
2. **Validate Input** - Sanitize HTML/Markdown to prevent XSS in PDF generation
|
|
456
|
+
3. **Handle Large PDFs** - Set reasonable timeouts for Puppeteer operations
|
|
457
|
+
4. **Organize with Folders** - Use meaningful folder structures (e.g., "invoices/2024")
|
|
458
|
+
5. **Store Metadata** - Keep records of storage keys in your database
|
|
459
|
+
6. **Error Handling** - Always wrap PDF operations in try-catch blocks
|
|
460
|
+
7. **Memory Management** - PDFs are loaded in memory; handle size carefully
|
|
461
|
+
8. **Reuse Browser** - Plugin maintains single browser instance for efficiency
|
|
462
|
+
|
|
463
|
+
## Configuration Profiles
|
|
464
|
+
|
|
465
|
+
### Development
|
|
466
|
+
|
|
467
|
+
```javascript
|
|
468
|
+
await fastify.register(xPDF, {
|
|
469
|
+
headless: true,
|
|
470
|
+
useStorage: false, // Save to disk instead
|
|
471
|
+
format: "A4",
|
|
472
|
+
});
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
### Production with Storage
|
|
476
|
+
|
|
477
|
+
```javascript
|
|
478
|
+
await fastify.register(xPDF, {
|
|
479
|
+
headless: true,
|
|
480
|
+
useStorage: true,
|
|
481
|
+
defaultFolder: "pdfs",
|
|
482
|
+
args: ["--no-sandbox", "--disable-setuid-sandbox"],
|
|
483
|
+
});
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
### High Performance
|
|
487
|
+
|
|
488
|
+
```javascript
|
|
489
|
+
await fastify.register(xPDF, {
|
|
490
|
+
headless: true,
|
|
491
|
+
args: [
|
|
492
|
+
"--no-sandbox",
|
|
493
|
+
"--disable-setuid-sandbox",
|
|
494
|
+
"--disable-gpu",
|
|
495
|
+
"--disable-dev-shm-usage",
|
|
496
|
+
],
|
|
497
|
+
useStorage: true,
|
|
498
|
+
});
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
## Troubleshooting
|
|
502
|
+
|
|
503
|
+
### Puppeteer Launch Fails
|
|
504
|
+
|
|
505
|
+
**Error:** `Timeout waiting for browser to start`
|
|
506
|
+
|
|
507
|
+
**Solution:** Increase available memory and ensure Chrome dependencies are installed
|
|
508
|
+
```bash
|
|
509
|
+
# macOS
|
|
510
|
+
brew install chromium
|
|
511
|
+
|
|
512
|
+
# Linux
|
|
513
|
+
apt-get install -y chromium-browser
|
|
514
|
+
|
|
515
|
+
# Or use args to help with sandboxing
|
|
516
|
+
headless: true,
|
|
517
|
+
args: ["--no-sandbox", "--disable-setuid-sandbox", "--disable-gpu"]
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
### Form Fields Not Filling
|
|
521
|
+
|
|
522
|
+
**Error:** `Form field not found: fieldName`
|
|
523
|
+
|
|
524
|
+
**Solution:** List form fields first to verify correct names
|
|
525
|
+
```javascript
|
|
526
|
+
const fields = await fastify.xPDF.listFormFields(pdfBuffer);
|
|
527
|
+
console.log(fields); // Check exact field names
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
### PDF Generation Times Out
|
|
531
|
+
|
|
532
|
+
**Error:** `Timeout waiting for page to load`
|
|
533
|
+
|
|
534
|
+
**Solution:** Simplify HTML, remove external resources, or optimize content
|
|
535
|
+
```javascript
|
|
536
|
+
// ❌ Avoid external resources
|
|
537
|
+
const html = '<img src="https://example.com/image.jpg">';
|
|
538
|
+
|
|
539
|
+
// ✅ Use inline or relative paths
|
|
540
|
+
const html = '<img src="/images/logo.png">';
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
### Storage Integration Not Working
|
|
544
|
+
|
|
545
|
+
**Error:** `xStorage plugin not registered`
|
|
546
|
+
|
|
547
|
+
**Solution:** Register xStorage before xPDF
|
|
548
|
+
```javascript
|
|
549
|
+
// Register xStorage FIRST
|
|
550
|
+
await fastify.register(xStorage, {...});
|
|
551
|
+
|
|
552
|
+
// Then register xPDF
|
|
553
|
+
await fastify.register(xPDF, {
|
|
554
|
+
useStorage: true,
|
|
555
|
+
});
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
## Performance Tips
|
|
559
|
+
|
|
560
|
+
- Use `headless: true` (already default)
|
|
561
|
+
- Enable `printBackground: false` for simpler documents
|
|
562
|
+
- Avoid large images or external resources in HTML
|
|
563
|
+
- Use Markdown for simple, styled documents
|
|
564
|
+
- Batch operations when merging many PDFs
|
|
565
|
+
|
|
566
|
+
## Testing
|
|
567
|
+
|
|
568
|
+
```bash
|
|
569
|
+
npm test
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
See [TESTING.md](./TESTING.md) for comprehensive testing guide.
|
|
573
|
+
|
|
574
|
+
## Examples
|
|
575
|
+
|
|
576
|
+
See [EXAMPLES.md](./EXAMPLES.md) for complete real-world examples.
|
|
577
|
+
|
|
578
|
+
## License
|
|
579
|
+
|
|
580
|
+
ISC
|