html-pdf-chrome
Version:
HTML to PDF and image converter via Chrome/Chromium
290 lines (220 loc) • 9 kB
Markdown
//badge.fury.io/js/html-pdf-chrome.svg)](https://www.npmjs.com/package/html-pdf-chrome)
[](https://github.com/westy92/html-pdf-chrome/actions/workflows/github-actions.yml?query=branch%3Amaster)
[](https://codeclimate.com/github/westy92/html-pdf-chrome/maintainability)
[](https://codecov.io/gh/westy92/html-pdf-chrome)
[](https://snyk.io/test/github/westy92/html-pdf-chrome)
[](https://github.com/sponsors/westy92)
HTML to PDF or image (jpeg, png, webp) converter via Chrome/Chromium.
## Prerequisites
* Latest Chrome/Chromium
* Windows, macOS, or Linux
* A [currently supported version of Node.js](https://nodejs.org/en/about/releases/)
## Installation
```bash
npm install --save html-pdf-chrome
```
## Security
This library is **_NOT_** meant to accept untrusted user input. Doing so may have serious security risks such as Server-Side Request Forgery (SSRF).
### CORS
If you run into CORS issues, try using the `--disable-web-security` Chrome flag, either when you start Chrome externally, or in `options.chromeFlags`. This option should only be used if you fully trust the code you are executing during a print job!
## Usage
__Note:__ It is _strongly_ recommended that you keep Chrome running side-by-side with Node.js. There is significant overhead starting up Chrome for each PDF generation which can be easily avoided.
It's suggested to use [pm2](http://pm2.keymetrics.io/) to ensure Chrome continues to run. If it crashes, it will restart automatically.
As of this writing, headless Chrome uses about 65mb of RAM while idle.
```bash
# install pm2 globally
npm install -g pm2
# start Chrome and be sure to specify a port to use in the html-pdf-chrome options.
pm2 start google-chrome \
--interpreter none \
-- \
--headless \
--disable-gpu \
--disable-translate \
--disable-extensions \
--disable-background-networking \
--safebrowsing-disable-auto-update \
--disable-sync \
--metrics-recording-only \
--disable-default-apps \
--no-first-run \
--mute-audio \
--hide-scrollbars \
--remote-debugging-port=<port goes here>
```
TypeScript:
```js
import * as htmlPdf from 'html-pdf-chrome';
const html = '<p>Hello, world!</p>';
const options: htmlPdf.CreateOptions = {
port: 9222, // port Chrome is listening on
};
// async
const pdf = await htmlPdf.create(html, options);
await pdf.toFile('test.pdf');
const base64 = pdf.toBase64();
const buffer = pdf.toBuffer();
const stream = pdf.toStream();
// Promise
htmlPdf.create(html, options).then((pdf) => pdf.toFile('test.pdf'));
htmlPdf.create(html, options).then((pdf) => pdf.toBase64());
htmlPdf.create(html, options).then((pdf) => pdf.toBuffer());
htmlPdf.create(html, options).then((pdf) => pdf.toStream());
```
JavaScript:
```js
const htmlPdf = require('html-pdf-chrome');
const html = '<p>Hello, world!</p>';
const options = {
port: 9222, // port Chrome is listening on
};
htmlPdf.create(html, options).then((pdf) => pdf.toFile('test.pdf'));
htmlPdf.create(html, options).then((pdf) => pdf.toBase64());
htmlPdf.create(html, options).then((pdf) => pdf.toBuffer());
htmlPdf.create(html, options).then((pdf) => pdf.toStream());
```
View the full documentation in the source code.
By default, pages are saved as a PDF. To save as a screenshot instead, supply `screenshotOptions`.
All supported options can be viewed [here](https://chromedevtools.github.io/devtools-protocol/tot/Page/#method-captureScreenshot).
```js
const htmlPdf = require('html-pdf-chrome');
const html = '<p>Hello, world!</p>';
const options = {
port: 9222, // port Chrome is listening on
screenshotOptions: {
format: 'png', // png, jpeg, or webp. Optional, defaults to png.
// quality: 100, // Optional, quality percent (jpeg only)
// optional, defaults to entire window
clip: {
x: 0,
y: 0,
width: 100,
height: 200,
scale: 1,
},
},
// Optional. Options here: https://chromedevtools.github.io/devtools-protocol/tot/Emulation/#method-setDeviceMetricsOverride
deviceMetrics: {
width: 1000,
height: 1000,
deviceScaleFactor: 0,
mobile: false,
},
};
htmlPdf.create(html, options).then((pdf) => pdf.toFile('test.png'));
```
```js
import * as htmlPdf from 'html-pdf-chrome';
const options: htmlPdf.CreateOptions = {
port: 9222, // port Chrome is listening on
};
const url = 'https://github.com/westy92/html-pdf-chrome';
const pdf = await htmlPdf.create(url, options);
```
```js
import * as htmlPdf from 'html-pdf-chrome';
import * as marked from 'marked';
const options: htmlPdf.CreateOptions = {
port: 9222, // port Chrome is listening on
};
const html = marked('# Hello [World](https://www.google.com/)!');
const pdf = await htmlPdf.create(html, options);
```
Pug (formerly known as Jade)
```js
import * as htmlPdf from 'html-pdf-chrome';
import * as pug from 'pug';
const template = pug.compile('p Hello, #{noun}!');
const templateData = {
noun: 'world',
};
const options: htmlPdf.CreateOptions = {
port: 9222, // port Chrome is listening on
};
const html = template(templateData);
const pdf = await htmlPdf.create(html, options);
```
Specify additional headers you wish to send with your request via `CreateOptions.extraHTTPHeaders`.
```js
const options: HtmlPdf.CreateOptions = {
port: 9222, // port Chrome is listening on
extraHTTPHeaders: {
'Authorization': 'Bearer 123',
'X-Custom-Test-Header': 'This is great!',
},
};
const pdf = await HtmlPdf.create('https://httpbin.org/headers', options);
```
_Note: Requires Chrome 65 or later._
You can optionally provide an HTML template for a custom header and/or footer.
A few classes can be used to inject printing values:
* `date` - formatted print date
* `title` - document title
* `url` - document location
* `pageNumber` - current page number
* `totalPages` - total pages in the document
You can tweak the margins with the `printOptions` of `marginTop`, `marginBottom`, `marginLeft`, and `marginRight`.
At this time, you must inline any images using [base64 encoding](http://www.bigfastblog.com/embed-base64-encoded-images-inline-in-html).
You can view how Chrome lays out the templates [here](https://cs.chromium.org/chromium/src/components/printing/resources/print_preview_page.html).
```js
const pdf = await htmlPdf.create(html, {
port,
printOptions: {
displayHeaderFooter: true,
headerTemplate: `
<div class="text center">
Page <span class="pageNumber"></span> of <span class="totalPages"></span>
</div>
`,
footerTemplate: '<div class="text center">Custom footer!</div>',
},
});
```
There are a few `CompletionTrigger` types that wait for something to occur before triggering PDF printing.
* Callback - waits for a callback to be called
* Element - waits for an element to be injected into the DOM
* Event - waits for an Event to fire
* Timer - waits a specified amount of time
* LifecycleEvent - waits for a Chrome page lifecycle event
* Variable - waits for a variable to be set to `true`
* Custom - extend `htmlPdf.CompletionTrigger.CompletionTrigger`
```js
const options: htmlPdf.CreateOptions = {
port: 9222, // port Chrome is listening on
completionTrigger: new htmlPdf.CompletionTrigger.Timer(5000), // milliseconds
};
// Alternative completionTrigger options:
new htmlPdf.CompletionTrigger.Callback(
'cbName', // optional, name of the callback to define for the browser to call when finished rendering. Defaults to 'htmlPdfCb'.
5000 // optional, timeout (milliseconds)
),
new htmlPdf.CompletionTrigger.Element(
'div#myElement', // name of the DOM element to wait for
5000 // optional, timeout (milliseconds)
),
new htmlPdf.CompletionTrigger.Event(
'myEvent', // name of the event to listen for
'#myElement', // optional DOM element CSS selector to listen on, defaults to body
5000 // optional timeout (milliseconds)
),
new htmlPdf.CompletionTrigger.LifecycleEvent(
'networkIdle', // name of the Chrome lifecycle event to listen for. Defaults to 'firstMeaningfulPaint'.
5000 // optional timeout (milliseconds)
),
new htmlPdf.CompletionTrigger.Variable(
'myVarName', // optional, name of the variable to wait for. Defaults to 'htmlPdfDone'
5000 // optional, timeout (milliseconds)
),
```
html-pdf-chrome is released under the MIT License.
[![npm version](https: