pdf-creator-node 2.5.0 → 4.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/dist/createPdf.d.ts +3 -3
- package/dist/createPdf.d.ts.map +1 -1
- package/dist/createPdf.js +19 -30
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/pdfChrome.d.ts +4 -4
- package/dist/pdfChrome.d.ts.map +1 -1
- package/dist/pdfChrome.js +1 -1
- package/dist/pdfPuppeteer.d.ts +11 -0
- package/dist/pdfPuppeteer.d.ts.map +1 -0
- package/dist/pdfPuppeteer.js +154 -0
- package/dist/pdfRenderOptions.d.ts +65 -0
- package/dist/pdfRenderOptions.d.ts.map +1 -0
- package/dist/pdfRenderOptions.js +2 -0
- package/dist/types.d.ts +7 -7
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -4
- package/readme.md +28 -11
package/dist/createPdf.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { FileInfo } from "html-pdf";
|
|
2
1
|
import type { PdfCreateOptions, PdfDocument } from "./types";
|
|
2
|
+
import type { PdfFileInfo } from "./pdfRenderOptions";
|
|
3
3
|
/**
|
|
4
|
-
* Renders the Handlebars template and generates a PDF via
|
|
4
|
+
* Renders the Handlebars template and generates a PDF via Puppeteer (headless Chromium).
|
|
5
5
|
*/
|
|
6
|
-
export declare function create(document: PdfDocument, options: PdfCreateOptions): Promise<Buffer | NodeJS.ReadableStream |
|
|
6
|
+
export declare function create(document: PdfDocument, options: PdfCreateOptions): Promise<Buffer | NodeJS.ReadableStream | PdfFileInfo>;
|
|
7
7
|
//# sourceMappingURL=createPdf.d.ts.map
|
package/dist/createPdf.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createPdf.d.ts","sourceRoot":"","sources":["../src/createPdf.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"createPdf.d.ts","sourceRoot":"","sources":["../src/createPdf.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAC7D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAmBtD;;GAEG;AACH,wBAAgB,MAAM,CACpB,QAAQ,EAAE,WAAW,EACrB,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC,cAAc,GAAG,WAAW,CAAC,CA2BvD"}
|
package/dist/createPdf.js
CHANGED
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
2
11
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
12
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
13
|
};
|
|
5
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
15
|
exports.create = create;
|
|
7
16
|
const handlebars_1 = __importDefault(require("handlebars"));
|
|
8
|
-
const
|
|
17
|
+
const pdfPuppeteer_1 = require("./pdfPuppeteer");
|
|
9
18
|
const pdfChrome_1 = require("./pdfChrome");
|
|
10
19
|
const registerHandlebarsHelpers_1 = require("./registerHandlebarsHelpers");
|
|
11
20
|
const validation_1 = require("./validation");
|
|
@@ -26,52 +35,32 @@ function renderHtml(document, options) {
|
|
|
26
35
|
}
|
|
27
36
|
}
|
|
28
37
|
/**
|
|
29
|
-
* Renders the Handlebars template and generates a PDF via
|
|
38
|
+
* Renders the Handlebars template and generates a PDF via Puppeteer (headless Chromium).
|
|
30
39
|
*/
|
|
31
40
|
function create(document, options) {
|
|
32
|
-
return
|
|
41
|
+
return (() => __awaiter(this, void 0, void 0, function* () {
|
|
33
42
|
try {
|
|
34
43
|
(0, validation_1.validatePdfDocument)(document);
|
|
35
44
|
}
|
|
36
45
|
catch (e) {
|
|
37
|
-
|
|
38
|
-
return;
|
|
46
|
+
throw e;
|
|
39
47
|
}
|
|
40
48
|
let html;
|
|
41
49
|
try {
|
|
42
50
|
html = renderHtml(document, options);
|
|
43
51
|
}
|
|
44
52
|
catch (e) {
|
|
45
|
-
|
|
46
|
-
return;
|
|
53
|
+
throw e;
|
|
47
54
|
}
|
|
48
55
|
const pdfOptions = (0, pdfChrome_1.mergePdfCreateOptions)(options);
|
|
49
|
-
const
|
|
56
|
+
const buffer = yield (0, pdfPuppeteer_1.renderPdfToBuffer)(html, pdfOptions);
|
|
50
57
|
switch (document.type) {
|
|
51
58
|
case "buffer":
|
|
52
|
-
|
|
53
|
-
if (err)
|
|
54
|
-
reject(err);
|
|
55
|
-
else
|
|
56
|
-
resolve(res);
|
|
57
|
-
});
|
|
58
|
-
break;
|
|
59
|
+
return buffer;
|
|
59
60
|
case "stream":
|
|
60
|
-
|
|
61
|
-
if (err)
|
|
62
|
-
reject(err);
|
|
63
|
-
else
|
|
64
|
-
resolve(res);
|
|
65
|
-
});
|
|
66
|
-
break;
|
|
61
|
+
return (0, pdfPuppeteer_1.bufferToStream)(buffer);
|
|
67
62
|
default:
|
|
68
|
-
|
|
69
|
-
if (err)
|
|
70
|
-
reject(err);
|
|
71
|
-
else
|
|
72
|
-
resolve(res);
|
|
73
|
-
});
|
|
74
|
-
break;
|
|
63
|
+
return (0, pdfPuppeteer_1.writePdfToFile)(document.path, buffer);
|
|
75
64
|
}
|
|
76
|
-
});
|
|
65
|
+
}))();
|
|
77
66
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -4,7 +4,10 @@
|
|
|
4
4
|
import { create } from "./createPdf";
|
|
5
5
|
export { create };
|
|
6
6
|
export { buildPdfChrome } from "./pdfChrome";
|
|
7
|
+
export { htmlPdfTokensToPuppeteer } from "./pdfPuppeteer";
|
|
7
8
|
export type { Document, IfCondOptions, PdfChromeOptions, PdfCreateOptions, PdfDocument, PdfDocumentBuffer, PdfDocumentFile, PdfDocumentStream, PdfFooterConfig, PdfHeaderConfig, PdfLayout, } from "./types";
|
|
9
|
+
export type { PdfFileInfo, PdfRenderOptions } from "./pdfRenderOptions";
|
|
10
|
+
export type { PdfFileInfo as FileInfo } from "./pdfRenderOptions";
|
|
8
11
|
declare const api: {
|
|
9
12
|
create: typeof create;
|
|
10
13
|
};
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAIrC,OAAO,EAAE,MAAM,EAAE,CAAC;AAClB,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,YAAY,EACV,QAAQ,EACR,aAAa,EACb,gBAAgB,EAChB,gBAAgB,EAChB,WAAW,EACX,iBAAiB,EACjB,eAAe,EACf,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,SAAS,GACV,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAIrC,OAAO,EAAE,MAAM,EAAE,CAAC;AAClB,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAC;AAC1D,YAAY,EACV,QAAQ,EACR,aAAa,EACb,gBAAgB,EAChB,gBAAgB,EAChB,WAAW,EACX,iBAAiB,EACjB,eAAe,EACf,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,SAAS,GACV,MAAM,SAAS,CAAC;AACjB,YAAY,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACxE,YAAY,EAAE,WAAW,IAAI,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAElE,QAAA,MAAM,GAAG;;CAAa,CAAC;AACvB,eAAe,GAAG,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -3,12 +3,14 @@
|
|
|
3
3
|
* @author Shyam Hajare <hajareshyam@gmail.com>
|
|
4
4
|
*/
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.buildPdfChrome = exports.create = void 0;
|
|
6
|
+
exports.htmlPdfTokensToPuppeteer = exports.buildPdfChrome = exports.create = void 0;
|
|
7
7
|
const registerHandlebarsHelpers_1 = require("./registerHandlebarsHelpers");
|
|
8
8
|
const createPdf_1 = require("./createPdf");
|
|
9
9
|
Object.defineProperty(exports, "create", { enumerable: true, get: function () { return createPdf_1.create; } });
|
|
10
10
|
(0, registerHandlebarsHelpers_1.registerHandlebarsHelpers)();
|
|
11
11
|
var pdfChrome_1 = require("./pdfChrome");
|
|
12
12
|
Object.defineProperty(exports, "buildPdfChrome", { enumerable: true, get: function () { return pdfChrome_1.buildPdfChrome; } });
|
|
13
|
+
var pdfPuppeteer_1 = require("./pdfPuppeteer");
|
|
14
|
+
Object.defineProperty(exports, "htmlPdfTokensToPuppeteer", { enumerable: true, get: function () { return pdfPuppeteer_1.htmlPdfTokensToPuppeteer; } });
|
|
13
15
|
const api = { create: createPdf_1.create };
|
|
14
16
|
exports.default = api;
|
package/dist/pdfChrome.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import type { CreateOptions } from "html-pdf";
|
|
2
1
|
import type { PdfChromeOptions, PdfCreateOptions } from "./types";
|
|
2
|
+
import type { PdfRenderOptions } from "./pdfRenderOptions";
|
|
3
3
|
/**
|
|
4
4
|
* Builds partial html-pdf options from layout / header / footer / copyright.
|
|
5
5
|
* Use {@link mergePdfCreateOptions} to merge with explicit `format`, `header`, `footer`.
|
|
6
6
|
*/
|
|
7
|
-
export declare function buildPdfChrome(chrome?: PdfChromeOptions): Partial<
|
|
7
|
+
export declare function buildPdfChrome(chrome?: PdfChromeOptions): Partial<PdfRenderOptions>;
|
|
8
8
|
/**
|
|
9
|
-
* Merges `pdfChrome` with explicit
|
|
9
|
+
* Merges `pdfChrome` with explicit PDF options. Explicit `header` / `footer` / `format` win.
|
|
10
10
|
*/
|
|
11
|
-
export declare function mergePdfCreateOptions(options: PdfCreateOptions):
|
|
11
|
+
export declare function mergePdfCreateOptions(options: PdfCreateOptions): PdfRenderOptions;
|
|
12
12
|
//# sourceMappingURL=pdfChrome.d.ts.map
|
package/dist/pdfChrome.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pdfChrome.d.ts","sourceRoot":"","sources":["../src/pdfChrome.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"pdfChrome.d.ts","sourceRoot":"","sources":["../src/pdfChrome.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAClE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AA+C3D;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAuDnF;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,gBAAgB,GAAG,gBAAgB,CASjF"}
|
package/dist/pdfChrome.js
CHANGED
|
@@ -107,7 +107,7 @@ function buildPdfChrome(chrome) {
|
|
|
107
107
|
return out;
|
|
108
108
|
}
|
|
109
109
|
/**
|
|
110
|
-
* Merges `pdfChrome` with explicit
|
|
110
|
+
* Merges `pdfChrome` with explicit PDF options. Explicit `header` / `footer` / `format` win.
|
|
111
111
|
*/
|
|
112
112
|
function mergePdfCreateOptions(options) {
|
|
113
113
|
var _a, _b;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Readable } from "stream";
|
|
2
|
+
import type { PdfFileInfo, PdfRenderOptions } from "./pdfRenderOptions";
|
|
3
|
+
/** Maps html-pdf `{{page}}` / `{{pages}}` to Puppeteer header/footer template tokens. */
|
|
4
|
+
export declare function htmlPdfTokensToPuppeteer(html: string): string;
|
|
5
|
+
/**
|
|
6
|
+
* Renders HTML to PDF bytes using headless Chromium (Puppeteer).
|
|
7
|
+
*/
|
|
8
|
+
export declare function renderPdfToBuffer(html: string, options: PdfRenderOptions): Promise<Buffer>;
|
|
9
|
+
export declare function writePdfToFile(path: string, buffer: Buffer): Promise<PdfFileInfo>;
|
|
10
|
+
export declare function bufferToStream(buffer: Buffer): Readable;
|
|
11
|
+
//# sourceMappingURL=pdfPuppeteer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pdfPuppeteer.d.ts","sourceRoot":"","sources":["../src/pdfPuppeteer.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAClC,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAExE,yFAAyF;AACzF,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAI7D;AA8FD;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,MAAM,CAAC,CA4BjB;AAED,wBAAsB,cAAc,CAClC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,WAAW,CAAC,CAItB;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,CAEvD"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.htmlPdfTokensToPuppeteer = htmlPdfTokensToPuppeteer;
|
|
16
|
+
exports.renderPdfToBuffer = renderPdfToBuffer;
|
|
17
|
+
exports.writePdfToFile = writePdfToFile;
|
|
18
|
+
exports.bufferToStream = bufferToStream;
|
|
19
|
+
const promises_1 = require("fs/promises");
|
|
20
|
+
const path_1 = require("path");
|
|
21
|
+
const puppeteer_1 = __importDefault(require("puppeteer"));
|
|
22
|
+
const stream_1 = require("stream");
|
|
23
|
+
/** Maps html-pdf `{{page}}` / `{{pages}}` to Puppeteer header/footer template tokens. */
|
|
24
|
+
function htmlPdfTokensToPuppeteer(html) {
|
|
25
|
+
return html
|
|
26
|
+
.replace(/\{\{\s*page\s*\}\}/gi, '<span class="pageNumber"></span>')
|
|
27
|
+
.replace(/\{\{\s*pages\s*\}\}/gi, '<span class="totalPages"></span>');
|
|
28
|
+
}
|
|
29
|
+
function wrapPrintTemplate(html) {
|
|
30
|
+
return `<div style="font-size:10px;width:100%;box-sizing:border-box;padding:4px 12px;">${htmlPdfTokensToPuppeteer(html)}</div>`;
|
|
31
|
+
}
|
|
32
|
+
function borderToMargin(border) {
|
|
33
|
+
var _a, _b, _c, _d;
|
|
34
|
+
if (border == null)
|
|
35
|
+
return undefined;
|
|
36
|
+
if (typeof border === "string") {
|
|
37
|
+
return { top: border, right: border, bottom: border, left: border };
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
top: (_a = border.top) !== null && _a !== void 0 ? _a : "0",
|
|
41
|
+
right: (_b = border.right) !== null && _b !== void 0 ? _b : "0",
|
|
42
|
+
bottom: (_c = border.bottom) !== null && _c !== void 0 ? _c : "0",
|
|
43
|
+
left: (_d = border.left) !== null && _d !== void 0 ? _d : "0",
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function ensureFullHtmlDocument(html) {
|
|
47
|
+
const trimmed = html.trim();
|
|
48
|
+
if (/^<!DOCTYPE/i.test(trimmed) || /<html[\s>]/i.test(trimmed)) {
|
|
49
|
+
return html;
|
|
50
|
+
}
|
|
51
|
+
return `<!DOCTYPE html><html><head><meta charset="utf-8"></head><body>${html}</body></html>`;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* v4+: Puppeteer only supports one footer template — we use `default`, else `first`, else `last`.
|
|
55
|
+
* Per-page numeric keys are ignored.
|
|
56
|
+
*/
|
|
57
|
+
function pickFooterHtml(footer) {
|
|
58
|
+
const c = footer === null || footer === void 0 ? void 0 : footer.contents;
|
|
59
|
+
if (!c)
|
|
60
|
+
return undefined;
|
|
61
|
+
if (c.default != null && String(c.default).trim() !== "")
|
|
62
|
+
return String(c.default);
|
|
63
|
+
if (c.first != null && String(c.first).trim() !== "")
|
|
64
|
+
return String(c.first);
|
|
65
|
+
if (c.last != null && String(c.last).trim() !== "")
|
|
66
|
+
return String(c.last);
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
function withBrowser(fn) {
|
|
70
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
71
|
+
const browser = yield puppeteer_1.default.launch({
|
|
72
|
+
headless: true,
|
|
73
|
+
args: ["--no-sandbox", "--disable-setuid-sandbox", "--disable-dev-shm-usage"],
|
|
74
|
+
});
|
|
75
|
+
try {
|
|
76
|
+
return yield fn(browser);
|
|
77
|
+
}
|
|
78
|
+
finally {
|
|
79
|
+
yield browser.close();
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
function buildPdfOptions(opts) {
|
|
84
|
+
var _a, _b;
|
|
85
|
+
const margin = borderToMargin(opts.border);
|
|
86
|
+
const headerHtml = (_a = opts.header) === null || _a === void 0 ? void 0 : _a.contents;
|
|
87
|
+
const footerHtml = pickFooterHtml(opts.footer);
|
|
88
|
+
const hasHeader = Boolean(headerHtml && headerHtml.trim());
|
|
89
|
+
const hasFooter = Boolean(footerHtml && footerHtml.trim());
|
|
90
|
+
const format = opts.width && opts.height ? undefined : (_b = opts.format) !== null && _b !== void 0 ? _b : "A4";
|
|
91
|
+
const landscape = opts.orientation === "landscape";
|
|
92
|
+
const pdfOpts = {
|
|
93
|
+
printBackground: true,
|
|
94
|
+
landscape,
|
|
95
|
+
};
|
|
96
|
+
if (hasHeader || hasFooter) {
|
|
97
|
+
pdfOpts.displayHeaderFooter = true;
|
|
98
|
+
pdfOpts.headerTemplate = hasHeader
|
|
99
|
+
? wrapPrintTemplate(headerHtml)
|
|
100
|
+
: "<span></span>";
|
|
101
|
+
pdfOpts.footerTemplate = hasFooter
|
|
102
|
+
? wrapPrintTemplate(footerHtml)
|
|
103
|
+
: "<span></span>";
|
|
104
|
+
}
|
|
105
|
+
if (margin) {
|
|
106
|
+
pdfOpts.margin = margin;
|
|
107
|
+
}
|
|
108
|
+
if (opts.width && opts.height) {
|
|
109
|
+
pdfOpts.width = opts.width;
|
|
110
|
+
pdfOpts.height = opts.height;
|
|
111
|
+
}
|
|
112
|
+
else if (format) {
|
|
113
|
+
pdfOpts.format = format;
|
|
114
|
+
}
|
|
115
|
+
return pdfOpts;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Renders HTML to PDF bytes using headless Chromium (Puppeteer).
|
|
119
|
+
*/
|
|
120
|
+
function renderPdfToBuffer(html, options) {
|
|
121
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
122
|
+
var _a;
|
|
123
|
+
const fullHtml = ensureFullHtmlDocument(html);
|
|
124
|
+
const pdfOpts = buildPdfOptions(options);
|
|
125
|
+
const timeout = (_a = options.timeout) !== null && _a !== void 0 ? _a : 30000;
|
|
126
|
+
return withBrowser((browser) => __awaiter(this, void 0, void 0, function* () {
|
|
127
|
+
var _a;
|
|
128
|
+
const page = yield browser.newPage();
|
|
129
|
+
page.setDefaultTimeout(timeout);
|
|
130
|
+
if (options.httpHeaders && Object.keys(options.httpHeaders).length > 0) {
|
|
131
|
+
yield page.setExtraHTTPHeaders(options.httpHeaders);
|
|
132
|
+
}
|
|
133
|
+
if ((_a = options.httpCookies) === null || _a === void 0 ? void 0 : _a.length) {
|
|
134
|
+
yield page.setCookie(...options.httpCookies);
|
|
135
|
+
}
|
|
136
|
+
yield page.setContent(fullHtml, Object.assign({ waitUntil: "load", timeout }, (options.base ? { baseURL: options.base } : {})));
|
|
137
|
+
if (typeof options.renderDelay === "number" && options.renderDelay > 0) {
|
|
138
|
+
yield new Promise((r) => setTimeout(r, options.renderDelay));
|
|
139
|
+
}
|
|
140
|
+
const buf = yield page.pdf(pdfOpts);
|
|
141
|
+
return Buffer.from(buf);
|
|
142
|
+
}));
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
function writePdfToFile(path, buffer) {
|
|
146
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
147
|
+
yield (0, promises_1.mkdir)((0, path_1.dirname)(path), { recursive: true });
|
|
148
|
+
yield (0, promises_1.writeFile)(path, buffer);
|
|
149
|
+
return { filename: path };
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
function bufferToStream(buffer) {
|
|
153
|
+
return stream_1.Readable.from(buffer);
|
|
154
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PDF engine options (formerly html-pdf / PhantomJS; now mapped to Puppeteer).
|
|
3
|
+
* Kept compatible with the previous option shape where possible.
|
|
4
|
+
*/
|
|
5
|
+
export interface PdfRenderOptions {
|
|
6
|
+
directory?: string;
|
|
7
|
+
height?: string;
|
|
8
|
+
width?: string;
|
|
9
|
+
format?: "A3" | "A4" | "A5" | "Legal" | "Letter" | "Tabloid";
|
|
10
|
+
orientation?: "portrait" | "landscape";
|
|
11
|
+
border?: string | {
|
|
12
|
+
top?: string;
|
|
13
|
+
right?: string;
|
|
14
|
+
bottom?: string;
|
|
15
|
+
left?: string;
|
|
16
|
+
};
|
|
17
|
+
paginationOffset?: number;
|
|
18
|
+
header?: {
|
|
19
|
+
height?: string;
|
|
20
|
+
contents?: string;
|
|
21
|
+
};
|
|
22
|
+
footer?: {
|
|
23
|
+
height?: string;
|
|
24
|
+
contents?: {
|
|
25
|
+
first?: string;
|
|
26
|
+
[page: number]: string | undefined;
|
|
27
|
+
default?: string;
|
|
28
|
+
last?: string;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
base?: string;
|
|
32
|
+
zoomFactor?: string;
|
|
33
|
+
type?: "png" | "jpeg" | "pdf";
|
|
34
|
+
quality?: string;
|
|
35
|
+
/** @deprecated No effect in v4 (Puppeteer). */
|
|
36
|
+
phantomPath?: string;
|
|
37
|
+
/** @deprecated No effect in v4 (Puppeteer). */
|
|
38
|
+
phantomArgs?: string[];
|
|
39
|
+
/** @deprecated No effect in v4 (Puppeteer). */
|
|
40
|
+
localUrlAccess?: boolean;
|
|
41
|
+
/** @deprecated No effect in v4 (Puppeteer). */
|
|
42
|
+
script?: string;
|
|
43
|
+
timeout?: number;
|
|
44
|
+
renderDelay?: "manual" | number;
|
|
45
|
+
httpHeaders?: {
|
|
46
|
+
[header: string]: string;
|
|
47
|
+
};
|
|
48
|
+
childProcessOptions?: {
|
|
49
|
+
detached?: boolean;
|
|
50
|
+
};
|
|
51
|
+
httpCookies?: Array<{
|
|
52
|
+
name: string;
|
|
53
|
+
value: string;
|
|
54
|
+
domain?: string;
|
|
55
|
+
path: string;
|
|
56
|
+
httponly?: boolean;
|
|
57
|
+
secure?: boolean;
|
|
58
|
+
expires?: number;
|
|
59
|
+
}>;
|
|
60
|
+
}
|
|
61
|
+
/** Result shape when writing to disk (same as previous html-pdf FileInfo). */
|
|
62
|
+
export interface PdfFileInfo {
|
|
63
|
+
filename: string;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=pdfRenderOptions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pdfRenderOptions.d.ts","sourceRoot":"","sources":["../src/pdfRenderOptions.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,CAAC;IAC7D,WAAW,CAAC,EAAE,UAAU,GAAG,WAAW,CAAC;IACvC,MAAM,CAAC,EACH,MAAM,GACN;QACE,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;IACN,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,MAAM,CAAC,EAAE;QACP,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,MAAM,CAAC,EAAE;QACP,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,QAAQ,CAAC,EAAE;YACT,KAAK,CAAC,EAAE,MAAM,CAAC;YACf,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;YACnC,OAAO,CAAC,EAAE,MAAM,CAAC;YACjB,IAAI,CAAC,EAAE,MAAM,CAAC;SACf,CAAC;KACH,CAAC;IACF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+CAA+C;IAC/C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+CAA+C;IAC/C,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,+CAA+C;IAC/C,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,+CAA+C;IAC/C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;IAChC,WAAW,CAAC,EAAE;QACZ,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;KAC1B,CAAC;IACF,mBAAmB,CAAC,EAAE;QACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;KACpB,CAAC;IACF,WAAW,CAAC,EAAE,KAAK,CAAC;QAClB,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC,CAAC;CACJ;AAED,8EAA8E;AAC9E,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;CAClB"}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { CreateOptions } from "html-pdf";
|
|
2
1
|
import type { HelperDelegate } from "handlebars";
|
|
2
|
+
import type { PdfRenderOptions } from "./pdfRenderOptions";
|
|
3
3
|
/** Options passed to Handlebars `ifCond` helper inverse/fn blocks */
|
|
4
4
|
export interface IfCondOptions {
|
|
5
5
|
fn: (context: any) => string;
|
|
@@ -29,13 +29,13 @@ export interface PdfDocumentStream {
|
|
|
29
29
|
export type PdfDocument = PdfDocumentFile | PdfDocumentBuffer | PdfDocumentStream;
|
|
30
30
|
/** @deprecated Use `PdfDocument` — kept for compatibility with earlier typings */
|
|
31
31
|
export type Document = PdfDocument;
|
|
32
|
-
/** Page size / margins — maps to
|
|
32
|
+
/** Page size / margins — maps to PDF engine paper options */
|
|
33
33
|
export interface PdfLayout {
|
|
34
34
|
format?: "A3" | "A4" | "A5" | "Legal" | "Letter" | "Tabloid";
|
|
35
35
|
orientation?: "portrait" | "landscape";
|
|
36
36
|
width?: string;
|
|
37
37
|
height?: string;
|
|
38
|
-
border?:
|
|
38
|
+
border?: PdfRenderOptions["border"];
|
|
39
39
|
}
|
|
40
40
|
/** Repeating header — use `html` or a plain `title` (escaped) */
|
|
41
41
|
export interface PdfHeaderConfig {
|
|
@@ -59,7 +59,7 @@ export interface PdfFooterConfig {
|
|
|
59
59
|
showPageNumbers?: boolean;
|
|
60
60
|
}
|
|
61
61
|
/**
|
|
62
|
-
* High-level layout + header/footer/copyright. Merged before
|
|
62
|
+
* High-level layout + header/footer/copyright. Merged before PDF render; any
|
|
63
63
|
* explicit `format`, `header`, `footer` on the same options object overrides these.
|
|
64
64
|
*/
|
|
65
65
|
export interface PdfChromeOptions {
|
|
@@ -68,12 +68,12 @@ export interface PdfChromeOptions {
|
|
|
68
68
|
footer?: PdfFooterConfig;
|
|
69
69
|
}
|
|
70
70
|
/**
|
|
71
|
-
*
|
|
71
|
+
* PDF engine options (Puppeteer / Chromium), plus optional Handlebars helpers for this render only
|
|
72
72
|
* (rendering uses an isolated Handlebars instance).
|
|
73
73
|
*/
|
|
74
|
-
export type PdfCreateOptions =
|
|
74
|
+
export type PdfCreateOptions = PdfRenderOptions & {
|
|
75
75
|
handlebarsHelpers?: Record<string, HelperDelegate>;
|
|
76
|
-
/** Convenience presets merged into
|
|
76
|
+
/** Convenience presets merged into PDF options (explicit keys override) */
|
|
77
77
|
pdfChrome?: PdfChromeOptions;
|
|
78
78
|
};
|
|
79
79
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE3D,qEAAqE;AACrE,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,MAAM,CAAC;IAC7B,OAAO,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,MAAM,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,GAAG,CAAC;IACV,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,GAAG,EAAE,CAAC;CACpB;AAED,0BAA0B;AAC1B,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,GAAG,CAAC;IACV,IAAI,EAAE,QAAQ,CAAC;CAChB;AAED,+BAA+B;AAC/B,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,GAAG,CAAC;IACV,IAAI,EAAE,QAAQ,CAAC;CAChB;AAED,MAAM,MAAM,WAAW,GACnB,eAAe,GACf,iBAAiB,GACjB,iBAAiB,CAAC;AAEtB,kFAAkF;AAClF,MAAM,MAAM,QAAQ,GAAG,WAAW,CAAC;AAEnC,6DAA6D;AAC7D,MAAM,WAAW,SAAS;IACxB,MAAM,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,CAAC;IAC7D,WAAW,CAAC,EAAE,UAAU,GAAG,WAAW,CAAC;IACvC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,gBAAgB,CAAC,QAAQ,CAAC,CAAC;CACrC;AAED,iEAAiE;AACjE,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kCAAkC;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mEAAmE;IACnE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wEAAwE;AACxE,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2CAA2C;IAC3C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0CAA0C;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,MAAM,CAAC,EAAE,eAAe,CAAC;CAC1B;AAED;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GAAG,gBAAgB,GAAG;IAChD,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACnD,2EAA2E;IAC3E,SAAS,CAAC,EAAE,gBAAgB,CAAC;CAC9B,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pdf-creator-node",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "node pdf creator",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -30,7 +30,8 @@
|
|
|
30
30
|
"html2pdf",
|
|
31
31
|
"html-to-pdf",
|
|
32
32
|
"handlebars",
|
|
33
|
-
"
|
|
33
|
+
"puppeteer",
|
|
34
|
+
"chromium",
|
|
34
35
|
"nodejs",
|
|
35
36
|
"express",
|
|
36
37
|
"report",
|
|
@@ -40,10 +41,9 @@
|
|
|
40
41
|
"license": "MIT",
|
|
41
42
|
"dependencies": {
|
|
42
43
|
"handlebars": "^4.7.7",
|
|
43
|
-
"
|
|
44
|
+
"puppeteer": "^24.0.0"
|
|
44
45
|
},
|
|
45
46
|
"devDependencies": {
|
|
46
|
-
"@types/html-pdf": "^3.0.3",
|
|
47
47
|
"@types/node": "^22.13.0",
|
|
48
48
|
"tsx": "^4.19.2",
|
|
49
49
|
"typescript": "^5.7.3"
|
package/readme.md
CHANGED
|
@@ -1,9 +1,28 @@
|
|
|
1
1
|
# pdf-creator-node
|
|
2
2
|
|
|
3
|
-
Convert **HTML + Handlebars** templates to **PDF** in Node.js
|
|
3
|
+
Convert **HTML + Handlebars** templates to **PDF** in Node.js using **[Puppeteer](https://pptr.dev/)** (headless **Chromium**). PhantomJS / `html-pdf` are no longer used as of **v4**.
|
|
4
4
|
|
|
5
5
|
**Requirements:** [Node.js](https://nodejs.org/) 18 or newer.
|
|
6
6
|
|
|
7
|
+
**Install size:** `puppeteer` downloads a compatible Chromium build on `npm install` (hundreds of MB). In CI you can cache the browser directory or set [`PUPPETEER_CACHE_DIR`](https://pptr.dev/guides/configuration) / `PUPPETEER_SKIP_DOWNLOAD` as needed.
|
|
8
|
+
|
|
9
|
+
### Strengths and trade-offs
|
|
10
|
+
|
|
11
|
+
| | |
|
|
12
|
+
| --- | --- |
|
|
13
|
+
| **Strengths** | **HTML + Handlebars** — quick to build **invoices, reports, and letters** without drawing coordinates. **v4+** uses **Chromium**, so you get **modern CSS** (flexbox, grid, web fonts) and a **maintained** rendering stack — not deprecated PhantomJS. **`pdfChrome`** helps with layout, headers, footers, and copyright in one place. |
|
|
14
|
+
| **Trade-offs** | **Footprint:** Chromium adds install size and **RAM** per browser instance. **Throughput:** launching a browser is heavier than a pure-JS library like **PDFKit** for tiny one-off jobs; for **high volume**, reuse browsers ([Puppeteer patterns](https://pptr.dev/)), queue work, or run workers. **Model:** this package is **HTML → PDF**; if you need a **drawing API** (paths, precise vector control) without HTML, use PDFKit / pdf-lib instead. **Print pipeline:** PDFs use Chrome’s **print** path — edge cases can differ from on-screen CSS (true for any headless-Chrome PDF approach). |
|
|
15
|
+
|
|
16
|
+
**Practical verdict:** strong fit for **small and medium** projects and **production** workloads where you control **memory** and **concurrency**; for **very large scale** (always-on millions of PDFs/day), plan **infrastructure** (pooling, autoscaling, or a dedicated rendering service) like any Chromium-based pipeline.
|
|
17
|
+
|
|
18
|
+
### Upgrading from v2.x (Phantom / html-pdf)
|
|
19
|
+
|
|
20
|
+
- **v4** is a **major** release: PDFs are rendered with Chromium, so layout can differ slightly from Phantom.
|
|
21
|
+
- Options are still the same shape for most fields (`format`, `orientation`, `border`, `header`, `footer`, `pdfChrome`).
|
|
22
|
+
- **Footer `contents`:** only one template is applied. We use `default`, else `first`, else `last`. Per-page keys (e.g. page `2`) are **not** supported in v4.
|
|
23
|
+
- **`phantomPath`**, **`phantomArgs`**, etc. are **ignored** (see `PdfRenderOptions` in types).
|
|
24
|
+
- `{{page}}` / `{{pages}}` in header/footer HTML are converted to Puppeteer’s print tokens automatically.
|
|
25
|
+
|
|
7
26
|
## Install
|
|
8
27
|
|
|
9
28
|
```bash
|
|
@@ -65,9 +84,9 @@ await pdf.create(document, options);
|
|
|
65
84
|
|
|
66
85
|
### Layout, header, footer, and copyright (`pdfChrome`)
|
|
67
86
|
|
|
68
|
-
Use **`pdfChrome`** on the second argument for common paper layout and repeating header/footer
|
|
87
|
+
Use **`pdfChrome`** on the second argument for common paper layout and repeating header/footer. Plain `title` / `copyright` strings are HTML-escaped.
|
|
69
88
|
|
|
70
|
-
- **`layout`:** `format`, `orientation`, `width`, `height`, `border` (
|
|
89
|
+
- **`layout`:** `format`, `orientation`, `width`, `height`, `border` (mapped to Puppeteer’s PDF / margin options).
|
|
71
90
|
- **`header`:** `html` (raw HTML per page) **or** `title` (centered text). Default height `45mm` when content is set.
|
|
72
91
|
- **`footer`:** `html` **or** combine **`copyright`** with optional **`showPageNumbers`** (`{{page}}` / `{{pages}}`). With `copyright` only, page numbers default **on** unless you set `showPageNumbers: false`. For **page numbers only**, set `showPageNumbers: true` and omit `copyright`. Default footer height `28mm` when content is set.
|
|
73
92
|
|
|
@@ -83,11 +102,11 @@ pdf.create(document, {
|
|
|
83
102
|
});
|
|
84
103
|
```
|
|
85
104
|
|
|
86
|
-
Advanced: `buildPdfChrome(pdfChrome)` returns partial
|
|
105
|
+
Advanced: `buildPdfChrome(pdfChrome)` returns partial `PdfRenderOptions` if you want to compose manually (also exported).
|
|
87
106
|
|
|
88
107
|
### Optional Handlebars helpers
|
|
89
108
|
|
|
90
|
-
Pass `handlebarsHelpers` on the **second** argument (alongside
|
|
109
|
+
Pass `handlebarsHelpers` on the **second** argument (alongside PDF options). Helpers apply only to that render (isolated Handlebars instance). Built-in `ifCond` is always registered.
|
|
91
110
|
|
|
92
111
|
```javascript
|
|
93
112
|
pdf.create(
|
|
@@ -135,12 +154,13 @@ You may see explicit errors such as:
|
|
|
135
154
|
</html>
|
|
136
155
|
```
|
|
137
156
|
|
|
138
|
-
## PDF options (
|
|
157
|
+
## PDF options (Puppeteer)
|
|
139
158
|
|
|
140
159
|
Examples:
|
|
141
160
|
|
|
142
|
-
- **Size:** `"height": "10.5in"`, `"width": "8in"` (units:
|
|
143
|
-
-
|
|
161
|
+
- **Size:** `"height": "10.5in"`, `"width": "8in"` (CSS units), **or** `"format": "Letter"` (A3, A4, A5, Legal, Letter, Tabloid) with `"orientation"`.
|
|
162
|
+
- **`border`:** margin around the page content (string or per-side object).
|
|
163
|
+
- **`header` / `footer`:** `contents` is HTML. In the footer, prefer **`contents.default`** for the repeating footer; `{{page}}` and `{{pages}}` work as with older versions (mapped for Chromium print).
|
|
144
164
|
|
|
145
165
|
```javascript
|
|
146
166
|
const options = {
|
|
@@ -154,11 +174,8 @@ const options = {
|
|
|
154
174
|
footer: {
|
|
155
175
|
height: "28mm",
|
|
156
176
|
contents: {
|
|
157
|
-
first: "Cover page",
|
|
158
|
-
2: "Second page",
|
|
159
177
|
default:
|
|
160
178
|
'<span style="color: #444;">{{page}}</span>/<span>{{pages}}</span>',
|
|
161
|
-
last: "Last Page",
|
|
162
179
|
},
|
|
163
180
|
},
|
|
164
181
|
};
|