p3x-html-pdf 2025.4.153 → 2025.4.155
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/README.md +162 -7
- package/package.json +1 -1
- package/src/index.js +24 -6
- package/test/test.js +21 -11
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
# 📃 Generates PDF from HTML with custom headers and footers with wkhtmltopdf v2025.4.
|
|
9
|
+
# 📃 Generates PDF from HTML with custom headers and footers with wkhtmltopdf v2025.4.155
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
|
|
@@ -37,7 +37,7 @@ v22.13.0
|
|
|
37
37
|
|
|
38
38
|
**p3x-html-pdf** is a Node.js package that generates PDFs from HTML with custom headers and footers using `wkhtmltopdf`. It is a robust tool for creating professional-grade PDFs with features like:
|
|
39
39
|
|
|
40
|
-
- 📜 **Dynamic Headers and Footers**: Add placeholders for page numbers.
|
|
40
|
+
- 📜 **Dynamic Headers and Footers**: Add placeholders for page numbers, dates, and more.
|
|
41
41
|
- 🛠️ **Customizable Layouts**: Configure margins, orientation, and paper size.
|
|
42
42
|
- ⚡ **Async/Await Support**: Modern JavaScript compatibility for efficient workflows.
|
|
43
43
|
- 🔄 **Dynamic Content**: Render data-driven tables and content dynamically.
|
|
@@ -160,16 +160,28 @@ You can use placeholders in your HTML for dynamic data (only these, but it is en
|
|
|
160
160
|
|
|
161
161
|
- `${page}`: Current page.
|
|
162
162
|
- `${pages}`: Total pages.
|
|
163
|
-
|
|
164
|
-
|
|
163
|
+
- `${frompage}`: The starting page of the current section.
|
|
164
|
+
- `${topage}`: The ending page of the current section.
|
|
165
|
+
- `${webpage}`: The URL of the web page (if applicable).
|
|
166
|
+
- `${section}`: The name of the current section.
|
|
167
|
+
- `${subsection}`: The name of the current subsection.
|
|
168
|
+
- `${date}`: The current date in a localized format.
|
|
169
|
+
- `${isodate}`: The current date in ISO 8601 format.
|
|
170
|
+
- `${time}`: The current time.
|
|
171
|
+
- `${title}`: The document title.
|
|
172
|
+
- `${doctitle}`: The title of the document as defined in metadata.
|
|
173
|
+
- `${sitepage}`: Current site page number (specific context).
|
|
174
|
+
- `${sitepages}`: Total number of site pages (specific context).
|
|
175
|
+
|
|
176
|
+
### Example
|
|
165
177
|
|
|
166
178
|
```html
|
|
167
|
-
<div id="p3x-footer" data-height="
|
|
179
|
+
<div id="p3x-footer" data-height="10mm">
|
|
168
180
|
<p>Page ${page} of ${pages}</p>
|
|
169
181
|
</div>
|
|
170
182
|
```
|
|
171
183
|
|
|
172
|
-
The `p3x-footer` and `p3x-header` should not have any styles other than `id` and `data-height`.
|
|
184
|
+
The `p3x-footer` and `p3x-header` should not have any styles other than `id` and `data-height`.
|
|
173
185
|
|
|
174
186
|
---
|
|
175
187
|
|
|
@@ -181,6 +193,149 @@ The `p3x-footer` and `p3x-header` should not have any styles other than `id` and
|
|
|
181
193
|
|
|
182
194
|
---
|
|
183
195
|
|
|
196
|
+
## 🌟 Headers and Footers in `p3x-html-pdf`
|
|
197
|
+
|
|
198
|
+
This document provides a detailed explanation of how to work with headers and footers using `p3x-html-pdf`, including first-page-specific headers and indexed headers for subsequent pages. With this approach, you can create professional-grade PDFs with precise control over header and footer content.
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
### 📖 Overview
|
|
203
|
+
|
|
204
|
+
Headers and footers in `p3x-html-pdf` are managed via HTML templates. You can:
|
|
205
|
+
- Define **default headers and footers** for all pages.
|
|
206
|
+
- Create **specific headers or footers** for certain pages using indexing (e.g., `p3x-header-1` for the first page).
|
|
207
|
+
- Dynamically calculate content for headers and footers, such as page numbers, document titles, or custom logic.
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
### 🚀 How It Works
|
|
212
|
+
|
|
213
|
+
`p3x-html-pdf` uses the `id` attribute and `data-height` to manage headers and footers. The key attributes and elements are:
|
|
214
|
+
|
|
215
|
+
- **Header IDs**:
|
|
216
|
+
- `p3x-header`: The default header for all pages.
|
|
217
|
+
- `p3x-header-<page>`: A header for a specific page (e.g., `p3x-header-1` for the first page).
|
|
218
|
+
- **Footer IDs**:
|
|
219
|
+
- `p3x-footer`: The default footer for all pages.
|
|
220
|
+
- `p3x-footer-<page>`: A footer for a specific page.
|
|
221
|
+
- **`data-height`**: Specifies the height of the header/footer (in millimeters). Ensure this matches the expected content size to prevent overlap.
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
### 🌟 Example: First Page Header and Default Header
|
|
226
|
+
|
|
227
|
+
The following example demonstrates a **custom header** for the first page and a **default header** for the rest of the document.
|
|
228
|
+
|
|
229
|
+
#### HTML Template
|
|
230
|
+
|
|
231
|
+
```html
|
|
232
|
+
<div id="p3x-header-1" data-height="40mm">
|
|
233
|
+
<div style="width: 100%; padding: 0px; display: table;">
|
|
234
|
+
<div style="display: table-cell; vertical-align: middle;">
|
|
235
|
+
<img src="http://cdn.corifeus.com/assets/png/patrikx3.png" alt="Header Logo" style="height:40mm; margin:0;"/>
|
|
236
|
+
</div>
|
|
237
|
+
<div style="display: table-cell; vertical-align: middle; text-align: right; width: 100%;">
|
|
238
|
+
<h1 style="margin: 0; font-size: 20px; color: #333;">P3X HTML Invoice - First Page</h1>
|
|
239
|
+
<p style="margin: 5px 0 0; font-size: 14px; color: #555;">Generated: ${new Date().toLocaleDateString()}</p>
|
|
240
|
+
</div>
|
|
241
|
+
</div>
|
|
242
|
+
</div>
|
|
243
|
+
|
|
244
|
+
<div id="p3x-header" data-height="40mm">
|
|
245
|
+
<div style="display: table; width: 100%; height: 40mm; text-align: right;">
|
|
246
|
+
<div style="display: table-cell; vertical-align: middle; text-align: right;">
|
|
247
|
+
<h1 style="margin: 0; font-size: 20px; color: #333;">P3X HTML Invoice</h1>
|
|
248
|
+
<p style="margin: 5px 0 0; font-size: 14px; color: #555;">Generated: ${new Date().toLocaleDateString()}</p>
|
|
249
|
+
</div>
|
|
250
|
+
</div>
|
|
251
|
+
</div>
|
|
252
|
+
|
|
253
|
+
<div id="p3x-footer" data-height="10mm">
|
|
254
|
+
<div style="text-align: right; font-size: 12px; color: #777;">
|
|
255
|
+
Page ${page} of ${pages}
|
|
256
|
+
</div>
|
|
257
|
+
</div>
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
### 🛠️ Dynamic Header and Footer Logic
|
|
263
|
+
|
|
264
|
+
- Use indexed headers or footers for specific pages.
|
|
265
|
+
- Utilize placeholders like `${page}` and `${pages}` to dynamically display the current page and total pages.
|
|
266
|
+
|
|
267
|
+
#### Example Configuration in JavaScript
|
|
268
|
+
|
|
269
|
+
```javascript
|
|
270
|
+
const options = {
|
|
271
|
+
settings: {
|
|
272
|
+
save: true,
|
|
273
|
+
template: {
|
|
274
|
+
format: 'A4',
|
|
275
|
+
marginLeft: 10,
|
|
276
|
+
marginRight: 10,
|
|
277
|
+
orientation: 'portrait',
|
|
278
|
+
},
|
|
279
|
+
html: `
|
|
280
|
+
<div id="p3x-header-1" data-height="40mm">
|
|
281
|
+
<!-- Custom header for the first page -->
|
|
282
|
+
</div>
|
|
283
|
+
<div id="p3x-header" data-height="40mm">
|
|
284
|
+
<!-- Default header for subsequent pages -->
|
|
285
|
+
</div>
|
|
286
|
+
<div id="p3x-footer" data-height="10mm">
|
|
287
|
+
<!-- Footer for all pages -->
|
|
288
|
+
</div>
|
|
289
|
+
`,
|
|
290
|
+
},
|
|
291
|
+
title: 'Dynamic Headers and Footers Example',
|
|
292
|
+
saveFile: path.resolve(__dirname, 'output.pdf'),
|
|
293
|
+
};
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
### 📏 Calculating Headers and Footers per Page
|
|
299
|
+
|
|
300
|
+
When designing headers and footers:
|
|
301
|
+
1. **Estimate Content Size:**
|
|
302
|
+
- Use `data-height` to reserve enough space for your header or footer content.
|
|
303
|
+
- Example: A header with a logo and text may need `40mm`.
|
|
304
|
+
|
|
305
|
+
2. **Adjust Margins:**
|
|
306
|
+
- Ensure the margins accommodate both the header/footer and the main content.
|
|
307
|
+
|
|
308
|
+
3. **Testing for Multi-Page Documents:**
|
|
309
|
+
- For multi-page documents, validate the alignment of headers and footers across all pages.
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
#### 📄 Headers and Footers with Indexed Customization
|
|
314
|
+
|
|
315
|
+
`p3x-html-pdf` supports indexed headers and footers, allowing unique designs for specific pages. For example, `p3x-header-1` can define a header for the first page, while `p3x-header` applies to subsequent pages. Similarly, `p3x-footer-1` can be used for a custom first-page footer.
|
|
316
|
+
|
|
317
|
+
##### Key Points:
|
|
318
|
+
1. **Indexed IDs**: Use `p3x-header-1`, `p3x-footer-1`, etc., for specific pages. Default headers (`p3x-header`) and footers (`p3x-footer`) are used when no specific index is found.
|
|
319
|
+
2. **Consistent Heights**: All headers and footers must share the same `data-height` (e.g., `40mm` for headers, `10mm` for footers) to ensure proper alignment and accurate page calculations.
|
|
320
|
+
3. **Dynamic Content**: Use placeholders like `${page}` and `${pages}` to display page-specific data dynamically.
|
|
321
|
+
|
|
322
|
+
This approach allows tailored styling for specific pages while maintaining consistent layouts throughout the document.
|
|
323
|
+
|
|
324
|
+
---
|
|
325
|
+
|
|
326
|
+
### 📊 Advanced Features
|
|
327
|
+
|
|
328
|
+
- Combine **indexed headers/footers** for specific pages with a **default** fallback.
|
|
329
|
+
- Use JavaScript in the `header-footer.html` template to dynamically adjust content.
|
|
330
|
+
- Use indexed configurations to simulate "first-page" or "last-page" behavior.
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
334
|
+
### 🖼️ Example Output
|
|
335
|
+
|
|
336
|
+
You can generate a PDF with the provided configuration to see how headers and footers are applied dynamically. For larger documents, this approach allows consistent styling with room for customization.
|
|
337
|
+
|
|
338
|
+
|
|
184
339
|
## 🌍 Architecture
|
|
185
340
|
|
|
186
341
|
`p3x-html-pdf` works seamlessly on Linux, Windows and ARM64.
|
|
@@ -354,7 +509,7 @@ All my domains, including [patrikx3.com](https://patrikx3.com), [corifeus.eu](ht
|
|
|
354
509
|
---
|
|
355
510
|
|
|
356
511
|
|
|
357
|
-
[**P3X-HTML-PDF**](https://corifeus.com/html-pdf) Build v2025.4.
|
|
512
|
+
[**P3X-HTML-PDF**](https://corifeus.com/html-pdf) Build v2025.4.155
|
|
358
513
|
|
|
359
514
|
[](https://www.npmjs.com/package/p3x-html-pdf) [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=QZVM4V6HVZJW6) [](https://www.patrikx3.com/en/front/contact) [](https://www.facebook.com/corifeus.software)
|
|
360
515
|
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -111,6 +111,18 @@ const generate = async (options) => {
|
|
|
111
111
|
const lodashTemplateHack = `
|
|
112
112
|
item = item.replace(/\\$\{page}/g, vars.page);
|
|
113
113
|
item = item.replace(/\\$\{pages}/g, vars.pages);
|
|
114
|
+
item = item.replace(/\\$\{frompage}/g, vars.frompage);
|
|
115
|
+
item = item.replace(/\\$\{topage}/g, vars.topage);
|
|
116
|
+
item = item.replace(/\\$\{webpage}/g, vars.webpage);
|
|
117
|
+
item = item.replace(/\\$\{section}/g, vars.section);
|
|
118
|
+
item = item.replace(/\\$\{subsection}/g, vars.subsection);
|
|
119
|
+
item = item.replace(/\\$\{date}/g, vars.date);
|
|
120
|
+
item = item.replace(/\\$\{isodate}/g, vars.isodate);
|
|
121
|
+
item = item.replace(/\\$\{time}/g, vars.time);
|
|
122
|
+
item = item.replace(/\\$\{title}/g, vars.title);
|
|
123
|
+
item = item.replace(/\\$\{doctitle}/g, vars.doctitle);
|
|
124
|
+
item = item.replace(/\\$\{sitepage}/g, vars.sitepage);
|
|
125
|
+
item = item.replace(/\\$\{sitepages}/g, vars.sitepages);
|
|
114
126
|
item = item.replace(/\\$\{qr}/g, qr);
|
|
115
127
|
|
|
116
128
|
`;
|
|
@@ -145,10 +157,18 @@ var qr = ${JSON.stringify(mainsSettings.settings.qr)};
|
|
|
145
157
|
// console.debug('html', html)
|
|
146
158
|
// console.debug('header', header)
|
|
147
159
|
|
|
148
|
-
|
|
160
|
+
if (save) {
|
|
161
|
+
tmpPdfPath = saveFile;
|
|
162
|
+
tmpHtmlPath = `${saveFile}.html`;
|
|
163
|
+
await utils.fs.ensureFile(tmpHtmlPath, html);
|
|
164
|
+
} else {
|
|
165
|
+
tmpHtmlPath = await utils.fs.ensureTempFile(html, 'html')
|
|
166
|
+
tmpPdfPath = await utils.fs.tempFileName('pdf');
|
|
167
|
+
}
|
|
168
|
+
|
|
149
169
|
tmpHtmlPathHeader = await utils.fs.ensureTempFile(header, 'html')
|
|
150
170
|
tmpHtmlPathFooter = await utils.fs.ensureTempFile(footer, 'html')
|
|
151
|
-
|
|
171
|
+
|
|
152
172
|
//console.debug('header', footer)
|
|
153
173
|
//console.debug('footer', footer)
|
|
154
174
|
//console.debug('html', footer)
|
|
@@ -189,10 +209,8 @@ var qr = ${JSON.stringify(mainsSettings.settings.qr)};
|
|
|
189
209
|
|
|
190
210
|
await utils.childProcess.exec(generatePdfCommand, debug);
|
|
191
211
|
|
|
192
|
-
if (save) {
|
|
193
|
-
await
|
|
194
|
-
} else {
|
|
195
|
-
return fs.readFile(tmpPdfPath);
|
|
212
|
+
if (!save) {
|
|
213
|
+
return await fs.readFile(tmpPdfPath);
|
|
196
214
|
}
|
|
197
215
|
} finally {
|
|
198
216
|
if (tmpHtmlPath !== undefined) {
|
package/test/test.js
CHANGED
|
@@ -34,19 +34,29 @@ const fs = require('fs');
|
|
|
34
34
|
orientation: 'portrait',
|
|
35
35
|
},
|
|
36
36
|
html: `
|
|
37
|
-
<div id="p3x-header" data-height="40mm"
|
|
38
|
-
<div
|
|
37
|
+
<div id="p3x-header-1" data-height="40mm">
|
|
38
|
+
<div style="width: 100%; padding: 0px; display: table;">
|
|
39
39
|
<div style="display: table-cell; vertical-align: middle;">
|
|
40
40
|
<img src="http://cdn.corifeus.com/assets/png/patrikx3.png" alt="Header Logo" style="height:40mm; margin:0;"/>
|
|
41
41
|
</div>
|
|
42
42
|
<div style="display: table-cell; vertical-align: middle; text-align: right; width: 100%;">
|
|
43
|
-
<h1 style="margin: 0; font-size: 20px; color: #333;">P3X HTML Invoice</h1>
|
|
43
|
+
<h1 style="margin: 0; font-size: 20px; color: #333;">P3X HTML Invoice - First Page</h1>
|
|
44
44
|
<p style="margin: 5px 0 0; font-size: 14px; color: #555;">Generated: ${new Date().toLocaleDateString()}</p>
|
|
45
45
|
</div>
|
|
46
46
|
</div>
|
|
47
47
|
</div>
|
|
48
|
-
<div id="p3x-
|
|
49
|
-
<div style="
|
|
48
|
+
<div id="p3x-header" data-height="40mm" >
|
|
49
|
+
<div style="display: table; width: 100%; height: 40mm; text-align: right;">
|
|
50
|
+
<div style="display: table-cell; vertical-align: middle; text-align: right;">
|
|
51
|
+
<h1 style="margin: 0; font-size: 20px; color: #333;">P3X HTML Invoice </h1>
|
|
52
|
+
<p style="margin: 5px 0 0; font-size: 14px; color: #555;">Generated: ${new Date().toLocaleDateString()}</p>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
<div id="p3x-footer" data-height="10mm">
|
|
57
|
+
<div style="text-align: right; font-size: 12px; color: #777;">
|
|
58
|
+
Page \${page} of \${pages}
|
|
59
|
+
</div>
|
|
50
60
|
</div>
|
|
51
61
|
<div>
|
|
52
62
|
<h2 style="color: #222;">Invoice Content</h2>
|
|
@@ -59,16 +69,16 @@ const fs = require('fs');
|
|
|
59
69
|
<th style="border: 1px solid #ddd; padding: 8px; background-color: #f4f4f4;">Total</th>
|
|
60
70
|
</tr>
|
|
61
71
|
${Array.from({ length: 26 }).map((_, i) => {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
72
|
+
const price = (i + 1) * 10;
|
|
73
|
+
const quantity = (i % 5) + 1;
|
|
74
|
+
const total = price * quantity;
|
|
75
|
+
return `<tr>
|
|
66
76
|
<td style="border: 1px solid #ddd; padding: 8px;">Product ${String.fromCharCode(65 + (i % 26))}</td>
|
|
67
77
|
<td style="border: 1px solid #ddd; padding: 8px; text-align: right;">${quantity}</td>
|
|
68
78
|
<td style="border: 1px solid #ddd; padding: 8px; text-align: right;">$${price}.00</td>
|
|
69
79
|
<td style="border: 1px solid #ddd; padding: 8px; text-align: right;">$${total}.00</td>
|
|
70
80
|
</tr>`;
|
|
71
|
-
|
|
81
|
+
}).join('')}
|
|
72
82
|
<tr>
|
|
73
83
|
<td colspan="3" style="border: 1px solid #ddd; padding: 8px; text-align: right; font-weight: bold;">Subtotal</td>
|
|
74
84
|
<td style="border: 1px solid #ddd; padding: 8px; text-align: right; font-weight: bold;">$${Array.from({ length: 15 }).reduce((acc, _, i) => acc + ((i + 1) * 10 * ((i % 5) + 1)), 0)}.00</td>
|
|
@@ -102,7 +112,7 @@ const fs = require('fs');
|
|
|
102
112
|
`,
|
|
103
113
|
},
|
|
104
114
|
title: 'P3X-HTML-PDF Detailed Invoice',
|
|
105
|
-
debug:
|
|
115
|
+
debug: true,
|
|
106
116
|
saveFile: outputPath,
|
|
107
117
|
};
|
|
108
118
|
|