pdf-creator-node 2.5.0 → 4.0.1
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/create/index.d.ts +7 -0
- package/dist/create/index.d.ts.map +1 -0
- package/dist/create/index.js +66 -0
- package/dist/handlebars/registerHandlebarsHelpers.d.ts.map +1 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -7
- package/dist/pdf/chrome.d.ts +12 -0
- package/dist/pdf/chrome.d.ts.map +1 -0
- package/dist/{pdfChrome.js → pdf/chrome.js} +1 -1
- package/dist/pdf/puppeteer.d.ts +11 -0
- package/dist/pdf/puppeteer.d.ts.map +1 -0
- package/dist/pdf/puppeteer.js +154 -0
- package/dist/pdf/renderOptions.d.ts +65 -0
- package/dist/pdf/renderOptions.d.ts.map +1 -0
- package/dist/{types.d.ts → types/index.d.ts} +8 -8
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/{validation.d.ts → validation/index.d.ts} +2 -2
- package/dist/validation/index.d.ts.map +1 -0
- package/package.json +4 -4
- package/readme.md +76 -11
- package/dist/createPdf.d.ts +0 -7
- package/dist/createPdf.d.ts.map +0 -1
- package/dist/createPdf.js +0 -77
- package/dist/pdfChrome.d.ts +0 -12
- package/dist/pdfChrome.d.ts.map +0 -1
- package/dist/registerHandlebarsHelpers.d.ts.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/validation.d.ts.map +0 -1
- /package/dist/{registerHandlebarsHelpers.d.ts → handlebars/registerHandlebarsHelpers.d.ts} +0 -0
- /package/dist/{registerHandlebarsHelpers.js → handlebars/registerHandlebarsHelpers.js} +0 -0
- /package/dist/{types.js → pdf/renderOptions.js} +0 -0
- /package/dist/{validation.js → validation/index.js} +0 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { PdfCreateOptions, PdfDocument } from "../types";
|
|
2
|
+
import type { PdfFileInfo } from "../pdf/renderOptions";
|
|
3
|
+
/**
|
|
4
|
+
* Renders the Handlebars template and generates a PDF via Puppeteer (headless Chromium).
|
|
5
|
+
*/
|
|
6
|
+
export declare function create(document: PdfDocument, options: PdfCreateOptions): Promise<Buffer | NodeJS.ReadableStream | PdfFileInfo>;
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/create/index.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAmBxD;;GAEG;AACH,wBAAgB,MAAM,CACpB,QAAQ,EAAE,WAAW,EACrB,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC,cAAc,GAAG,WAAW,CAAC,CA2BvD"}
|
|
@@ -0,0 +1,66 @@
|
|
|
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.create = create;
|
|
16
|
+
const handlebars_1 = __importDefault(require("handlebars"));
|
|
17
|
+
const puppeteer_1 = require("../pdf/puppeteer");
|
|
18
|
+
const chrome_1 = require("../pdf/chrome");
|
|
19
|
+
const registerHandlebarsHelpers_1 = require("../handlebars/registerHandlebarsHelpers");
|
|
20
|
+
const validation_1 = require("../validation");
|
|
21
|
+
function renderHtml(document, options) {
|
|
22
|
+
const hb = handlebars_1.default.create();
|
|
23
|
+
(0, registerHandlebarsHelpers_1.registerHandlebarsHelpers)(hb);
|
|
24
|
+
if (options.handlebarsHelpers) {
|
|
25
|
+
for (const [name, fn] of Object.entries(options.handlebarsHelpers)) {
|
|
26
|
+
hb.registerHelper(name, fn);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
return hb.compile(document.html)(document.data);
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
34
|
+
throw new Error(`pdf-creator-node: template rendering failed: ${msg}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Renders the Handlebars template and generates a PDF via Puppeteer (headless Chromium).
|
|
39
|
+
*/
|
|
40
|
+
function create(document, options) {
|
|
41
|
+
return (() => __awaiter(this, void 0, void 0, function* () {
|
|
42
|
+
try {
|
|
43
|
+
(0, validation_1.validatePdfDocument)(document);
|
|
44
|
+
}
|
|
45
|
+
catch (e) {
|
|
46
|
+
throw e;
|
|
47
|
+
}
|
|
48
|
+
let html;
|
|
49
|
+
try {
|
|
50
|
+
html = renderHtml(document, options);
|
|
51
|
+
}
|
|
52
|
+
catch (e) {
|
|
53
|
+
throw e;
|
|
54
|
+
}
|
|
55
|
+
const pdfOptions = (0, chrome_1.mergePdfCreateOptions)(options);
|
|
56
|
+
const buffer = yield (0, puppeteer_1.renderPdfToBuffer)(html, pdfOptions);
|
|
57
|
+
switch (document.type) {
|
|
58
|
+
case "buffer":
|
|
59
|
+
return buffer;
|
|
60
|
+
case "stream":
|
|
61
|
+
return (0, puppeteer_1.bufferToStream)(buffer);
|
|
62
|
+
default:
|
|
63
|
+
return (0, puppeteer_1.writePdfToFile)(document.path, buffer);
|
|
64
|
+
}
|
|
65
|
+
}))();
|
|
66
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registerHandlebarsHelpers.d.ts","sourceRoot":"","sources":["../../src/handlebars/registerHandlebarsHelpers.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,YAAY,CAAC;AAGpC;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,UAAU,GAAE,OAAO,UAAuB,GACzC,IAAI,CAoCN"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @author Shyam Hajare <hajareshyam@gmail.com>
|
|
3
3
|
*/
|
|
4
|
-
import { create } from "./
|
|
4
|
+
import { create } from "./create";
|
|
5
5
|
export { create };
|
|
6
|
-
export { buildPdfChrome } from "./
|
|
6
|
+
export { buildPdfChrome } from "./pdf/chrome";
|
|
7
|
+
export { htmlPdfTokensToPuppeteer } from "./pdf/puppeteer";
|
|
7
8
|
export type { Document, IfCondOptions, PdfChromeOptions, PdfCreateOptions, PdfDocument, PdfDocumentBuffer, PdfDocumentFile, PdfDocumentStream, PdfFooterConfig, PdfHeaderConfig, PdfLayout, } from "./types";
|
|
9
|
+
export type { PdfFileInfo, PdfRenderOptions } from "./pdf/renderOptions";
|
|
10
|
+
export type { PdfFileInfo as FileInfo } from "./pdf/renderOptions";
|
|
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,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAIlC,OAAO,EAAE,MAAM,EAAE,CAAC;AAClB,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AAC3D,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,qBAAqB,CAAC;AACzE,YAAY,EAAE,WAAW,IAAI,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAEnE,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;
|
|
7
|
-
const registerHandlebarsHelpers_1 = require("./registerHandlebarsHelpers");
|
|
8
|
-
const
|
|
9
|
-
Object.defineProperty(exports, "create", { enumerable: true, get: function () { return
|
|
6
|
+
exports.htmlPdfTokensToPuppeteer = exports.buildPdfChrome = exports.create = void 0;
|
|
7
|
+
const registerHandlebarsHelpers_1 = require("./handlebars/registerHandlebarsHelpers");
|
|
8
|
+
const create_1 = require("./create");
|
|
9
|
+
Object.defineProperty(exports, "create", { enumerable: true, get: function () { return create_1.create; } });
|
|
10
10
|
(0, registerHandlebarsHelpers_1.registerHandlebarsHelpers)();
|
|
11
|
-
var
|
|
12
|
-
Object.defineProperty(exports, "buildPdfChrome", { enumerable: true, get: function () { return
|
|
13
|
-
|
|
11
|
+
var chrome_1 = require("./pdf/chrome");
|
|
12
|
+
Object.defineProperty(exports, "buildPdfChrome", { enumerable: true, get: function () { return chrome_1.buildPdfChrome; } });
|
|
13
|
+
var puppeteer_1 = require("./pdf/puppeteer");
|
|
14
|
+
Object.defineProperty(exports, "htmlPdfTokensToPuppeteer", { enumerable: true, get: function () { return puppeteer_1.htmlPdfTokensToPuppeteer; } });
|
|
15
|
+
const api = { create: create_1.create };
|
|
14
16
|
exports.default = api;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { PdfChromeOptions, PdfCreateOptions } from "../types";
|
|
2
|
+
import type { PdfRenderOptions } from "./renderOptions";
|
|
3
|
+
/**
|
|
4
|
+
* Builds partial html-pdf options from layout / header / footer / copyright.
|
|
5
|
+
* Use {@link mergePdfCreateOptions} to merge with explicit `format`, `header`, `footer`.
|
|
6
|
+
*/
|
|
7
|
+
export declare function buildPdfChrome(chrome?: PdfChromeOptions): Partial<PdfRenderOptions>;
|
|
8
|
+
/**
|
|
9
|
+
* Merges `pdfChrome` with explicit PDF options. Explicit `header` / `footer` / `format` win.
|
|
10
|
+
*/
|
|
11
|
+
export declare function mergePdfCreateOptions(options: PdfCreateOptions): PdfRenderOptions;
|
|
12
|
+
//# sourceMappingURL=chrome.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chrome.d.ts","sourceRoot":"","sources":["../../src/pdf/chrome.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AACnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AA+CxD;;;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"}
|
|
@@ -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 "./renderOptions";
|
|
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=puppeteer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"puppeteer.d.ts","sourceRoot":"","sources":["../../src/pdf/puppeteer.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAClC,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAErE,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=renderOptions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renderOptions.d.ts","sourceRoot":"","sources":["../../src/pdf/renderOptions.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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { CreateOptions } from "html-pdf";
|
|
2
1
|
import type { HelperDelegate } from "handlebars";
|
|
2
|
+
import type { PdfRenderOptions } from "../pdf/renderOptions";
|
|
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
|
-
//# sourceMappingURL=
|
|
79
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAE7D,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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { PdfDocument } from "
|
|
1
|
+
import type { PdfDocument } from "../types";
|
|
2
2
|
/**
|
|
3
3
|
* Validates `document` before PDF generation. Throws with a clear message if invalid.
|
|
4
4
|
*/
|
|
5
5
|
export declare function validatePdfDocument(document: PdfDocument): void;
|
|
6
|
-
//# sourceMappingURL=
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/validation/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAE5C;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,WAAW,GAAG,IAAI,CAgC/D"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pdf-creator-node",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.1",
|
|
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,16 +174,61 @@ 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
|
};
|
|
165
182
|
```
|
|
166
183
|
|
|
184
|
+
### Margins
|
|
185
|
+
|
|
186
|
+
**Page margins** are the **`border`** option (string or per-side object). That maps to Chromium’s PDF margins—the content area is inset by this amount.
|
|
187
|
+
|
|
188
|
+
### Custom fonts
|
|
189
|
+
|
|
190
|
+
v4 renders with **Chromium**, so fonts work like in a normal web page: **`@font-face`**, **`font-family`**, and hosted fonts.
|
|
191
|
+
|
|
192
|
+
**Hosted fonts (Google Fonts, jsDelivr, etc.)** — add a `<link>` in `<head>` and set `font-family` in your CSS. The process must be able to reach the font URL.
|
|
193
|
+
|
|
194
|
+
**Local files** — use `@font-face` with a relative `url('fonts/MyFont.woff2')` and set **`base`** on the options object to the folder that contains those paths (it becomes Puppeteer’s `baseURL`). Use a **trailing slash** when `base` is a directory:
|
|
195
|
+
|
|
196
|
+
```javascript
|
|
197
|
+
const path = require("path");
|
|
198
|
+
|
|
199
|
+
const options = {
|
|
200
|
+
format: "A4",
|
|
201
|
+
base: path.join(__dirname, "assets") + path.sep,
|
|
202
|
+
};
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
**Header / footer HTML** is rendered in Chromium’s print header/footer UI. It does not automatically inherit styles from the main template. To use the same font there, repeat a `<link>` / `@font-face` in the `header.contents` / `footer.contents` string, or rely on **system fonts** for those snippets.
|
|
206
|
+
|
|
207
|
+
### Images
|
|
208
|
+
|
|
209
|
+
Remote URLs, local files, base64 data URLs, and WebP:
|
|
210
|
+
|
|
211
|
+
- **Remote:** `src="https://..."` works when the environment can fetch the URL.
|
|
212
|
+
- **Local files:** set **`base`** and use a path relative to that root (same as for fonts).
|
|
213
|
+
- **Data URLs:** `data:image/png;base64,...` (use the correct MIME type). **WebP** is generally supported in Chromium; if something fails, try **PNG** or **JPEG**.
|
|
214
|
+
- **Images inside `header` / `footer`:** prefer **absolute** `https://` URLs, or paths that resolve correctly with your **`base`**; relative paths are easy to get wrong in the print template.
|
|
215
|
+
|
|
216
|
+
Wide tables or “horizontal scroll” in a browser do not carry over as scroll in a PDF—use **`orientation: "landscape"`**, smaller type, or table/CSS layout that fits the page width. For keeping blocks together on one page, try CSS **`break-inside: avoid`** / **`page-break-inside: avoid`** (behavior follows Chromium’s print rules).
|
|
217
|
+
|
|
218
|
+
### AWS Lambda and serverless
|
|
219
|
+
|
|
220
|
+
**It can work, but not with a default “zip only” deploy in most cases.** This package runs **Puppeteer** with **headless Chromium**, which is large and memory-hungry.
|
|
221
|
+
|
|
222
|
+
| Topic | Guidance |
|
|
223
|
+
| --- | --- |
|
|
224
|
+
| **Deployment size** | Default `puppeteer` installs a full Chromium build (hundreds of MB). AWS Lambda **zip** deployments have a **250 MB unzipped** limit, so the stock layout often **does not fit**. |
|
|
225
|
+
| **What usually works** | **Lambda container images** (Docker) with Chromium + Node, or a **trimmed Chromium** packaged for Lambda (community layers / [`@sparticuz/chromium`](https://github.com/Sparticuz/chromium)–style setups) with **`puppeteer-core`**. |
|
|
226
|
+
| **This library today** | `puppeteer.launch()` is called with **`--no-sandbox`**, **`--disable-setuid-sandbox`**, and **`--disable-dev-shm-usage`** (typical for containers and many serverless images). There is **no public option** to set **`executablePath`** or merge extra launch flags—using a custom Chromium binary may require a **fork**, **patch**, or a **wrapper** that replaces how the browser is launched until such options exist. |
|
|
227
|
+
| **Memory & timeout** | Plan for **~1.5–3 GB** RAM and a **timeout** that covers cold start + browser launch + PDF generation. |
|
|
228
|
+
| **Alternatives** | Run PDF generation on **ECS/Fargate**, **EC2**, or a **dedicated microservice**; keep Lambda for orchestration only. |
|
|
229
|
+
|
|
230
|
+
For CI and non-Lambda servers, you can tune install size with [`PUPPETEER_SKIP_DOWNLOAD`](https://pptr.dev/guides/installation#download-chromium-during-installation) / [`PUPPETEER_CACHE_DIR`](https://pptr.dev/guides/configuration) when you supply your own browser.
|
|
231
|
+
|
|
167
232
|
## `ifCond` helper
|
|
168
233
|
|
|
169
234
|
Compare two values in the template:
|
package/dist/createPdf.d.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type { FileInfo } from "html-pdf";
|
|
2
|
-
import type { PdfCreateOptions, PdfDocument } from "./types";
|
|
3
|
-
/**
|
|
4
|
-
* Renders the Handlebars template and generates a PDF via html-pdf.
|
|
5
|
-
*/
|
|
6
|
-
export declare function create(document: PdfDocument, options: PdfCreateOptions): Promise<Buffer | NodeJS.ReadableStream | FileInfo>;
|
|
7
|
-
//# sourceMappingURL=createPdf.d.ts.map
|
package/dist/createPdf.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"createPdf.d.ts","sourceRoot":"","sources":["../src/createPdf.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAGzC,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAmB7D;;GAEG;AACH,wBAAgB,MAAM,CACpB,QAAQ,EAAE,WAAW,EACrB,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC,cAAc,GAAG,QAAQ,CAAC,CA2CpD"}
|
package/dist/createPdf.js
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.create = create;
|
|
7
|
-
const handlebars_1 = __importDefault(require("handlebars"));
|
|
8
|
-
const html_pdf_1 = __importDefault(require("html-pdf"));
|
|
9
|
-
const pdfChrome_1 = require("./pdfChrome");
|
|
10
|
-
const registerHandlebarsHelpers_1 = require("./registerHandlebarsHelpers");
|
|
11
|
-
const validation_1 = require("./validation");
|
|
12
|
-
function renderHtml(document, options) {
|
|
13
|
-
const hb = handlebars_1.default.create();
|
|
14
|
-
(0, registerHandlebarsHelpers_1.registerHandlebarsHelpers)(hb);
|
|
15
|
-
if (options.handlebarsHelpers) {
|
|
16
|
-
for (const [name, fn] of Object.entries(options.handlebarsHelpers)) {
|
|
17
|
-
hb.registerHelper(name, fn);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
try {
|
|
21
|
-
return hb.compile(document.html)(document.data);
|
|
22
|
-
}
|
|
23
|
-
catch (err) {
|
|
24
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
25
|
-
throw new Error(`pdf-creator-node: template rendering failed: ${msg}`);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* Renders the Handlebars template and generates a PDF via html-pdf.
|
|
30
|
-
*/
|
|
31
|
-
function create(document, options) {
|
|
32
|
-
return new Promise((resolve, reject) => {
|
|
33
|
-
try {
|
|
34
|
-
(0, validation_1.validatePdfDocument)(document);
|
|
35
|
-
}
|
|
36
|
-
catch (e) {
|
|
37
|
-
reject(e);
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
let html;
|
|
41
|
-
try {
|
|
42
|
-
html = renderHtml(document, options);
|
|
43
|
-
}
|
|
44
|
-
catch (e) {
|
|
45
|
-
reject(e);
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
const pdfOptions = (0, pdfChrome_1.mergePdfCreateOptions)(options);
|
|
49
|
-
const pdfPromise = html_pdf_1.default.create(html, pdfOptions);
|
|
50
|
-
switch (document.type) {
|
|
51
|
-
case "buffer":
|
|
52
|
-
pdfPromise.toBuffer((err, res) => {
|
|
53
|
-
if (err)
|
|
54
|
-
reject(err);
|
|
55
|
-
else
|
|
56
|
-
resolve(res);
|
|
57
|
-
});
|
|
58
|
-
break;
|
|
59
|
-
case "stream":
|
|
60
|
-
pdfPromise.toStream((err, res) => {
|
|
61
|
-
if (err)
|
|
62
|
-
reject(err);
|
|
63
|
-
else
|
|
64
|
-
resolve(res);
|
|
65
|
-
});
|
|
66
|
-
break;
|
|
67
|
-
default:
|
|
68
|
-
pdfPromise.toFile(document.path, (err, res) => {
|
|
69
|
-
if (err)
|
|
70
|
-
reject(err);
|
|
71
|
-
else
|
|
72
|
-
resolve(res);
|
|
73
|
-
});
|
|
74
|
-
break;
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
}
|
package/dist/pdfChrome.d.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import type { CreateOptions } from "html-pdf";
|
|
2
|
-
import type { PdfChromeOptions, PdfCreateOptions } from "./types";
|
|
3
|
-
/**
|
|
4
|
-
* Builds partial html-pdf options from layout / header / footer / copyright.
|
|
5
|
-
* Use {@link mergePdfCreateOptions} to merge with explicit `format`, `header`, `footer`.
|
|
6
|
-
*/
|
|
7
|
-
export declare function buildPdfChrome(chrome?: PdfChromeOptions): Partial<CreateOptions>;
|
|
8
|
-
/**
|
|
9
|
-
* Merges `pdfChrome` with explicit html-pdf fields. Explicit `header` / `footer` / `format` win.
|
|
10
|
-
*/
|
|
11
|
-
export declare function mergePdfCreateOptions(options: PdfCreateOptions): CreateOptions;
|
|
12
|
-
//# sourceMappingURL=pdfChrome.d.ts.map
|
package/dist/pdfChrome.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"pdfChrome.d.ts","sourceRoot":"","sources":["../src/pdfChrome.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AA+ClE;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,aAAa,CAAC,CAuDhF;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,gBAAgB,GAAG,aAAa,CAS9E"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"registerHandlebarsHelpers.d.ts","sourceRoot":"","sources":["../src/registerHandlebarsHelpers.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,YAAY,CAAC;AAGpC;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,UAAU,GAAE,OAAO,UAAuB,GACzC,IAAI,CAoCN"}
|
package/dist/types.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,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,2DAA2D;AAC3D,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,aAAa,CAAC,QAAQ,CAAC,CAAC;CAClC;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,aAAa,GAAG;IAC7C,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACnD,gFAAgF;IAChF,SAAS,CAAC,EAAE,gBAAgB,CAAC;CAC9B,CAAC"}
|
package/dist/validation.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE3C;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,WAAW,GAAG,IAAI,CAgC/D"}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|