@cityssm/pdf-puppeteer
Version:
A simple NPM package to convert html to pdf for Node applications by using Puppeteer
158 lines (157 loc) • 5.93 kB
JavaScript
import { getPaperSize } from '@cityssm/paper-sizes';
import launchPuppeteer from '@cityssm/puppeteer-launch';
import Debug from 'debug';
import exitHook from 'exit-hook';
import { DEBUG_NAMESPACE } from './debug.config.js';
import { defaultPdfOptions, defaultPdfPuppeteerOptions, defaultPuppeteerOptions, htmlNavigationTimeoutMillis, urlNavigationTimeoutMillis } from './defaultOptions.js';
const debug = Debug(`${DEBUG_NAMESPACE}:index`);
let cachedBrowser;
/**
* Converts HTML or a webpage into HTML using Puppeteer.
* @param html - An HTML string, or a URL.
* @param instancePdfOptions - PDF options for Puppeteer.
* @param instancePdfPuppeteerOptions - pdf-puppeteer options.
* @returns A Buffer of PDF data.
*/
// eslint-disable-next-line complexity
export async function convertHTMLToPDF(html, instancePdfOptions = {}, instancePdfPuppeteerOptions = {}) {
if (typeof html !== 'string') {
throw new TypeError('Invalid Argument: HTML expected as type of string and received a value of a different type. Check your request body and request headers.');
}
const pdfPuppeteerOptions = {
...defaultPdfPuppeteerOptions,
...instancePdfPuppeteerOptions
};
/*
* Initialize browser
*/
const puppeteerOptions = { ...defaultPuppeteerOptions };
puppeteerOptions.browser = pdfPuppeteerOptions.browser ?? 'chrome';
if (pdfPuppeteerOptions.disableSandbox) {
puppeteerOptions.args = ['--no-sandbox', '--disable-setuid-sandbox'];
}
let browser;
let doCloseBrowser = false;
let isRunningPdfGeneration = false;
try {
let puppeteerLaunchFunction = launchPuppeteer;
if (pdfPuppeteerOptions.usePackagePuppeteer) {
const puppeteerPackage = await import('puppeteer');
puppeteerLaunchFunction = puppeteerPackage.launch;
}
if (pdfPuppeteerOptions.cacheBrowser) {
cachedBrowser ??= await puppeteerLaunchFunction(puppeteerOptions);
browser = cachedBrowser;
}
else {
doCloseBrowser = true;
browser = await puppeteerLaunchFunction(puppeteerOptions);
}
const browserVersion = await browser.version();
debug(`Browser: ${browserVersion}`);
const browserIsFirefox = browserVersion.toLowerCase().includes('firefox');
/*
* Initialize page
*/
const page = await browser.newPage();
const remoteContent = pdfPuppeteerOptions.remoteContent;
if (pdfPuppeteerOptions.htmlIsUrl) {
debug('Loading URL...');
await page.goto(html, {
waitUntil: browserIsFirefox ? 'domcontentloaded' : 'networkidle0',
timeout: urlNavigationTimeoutMillis
});
}
else if (remoteContent) {
debug('Loading HTML with remote content...');
await page.goto(`data:text/html;base64,${Buffer.from(html).toString('base64')}`, {
waitUntil: browserIsFirefox ? 'domcontentloaded' : 'networkidle0',
timeout: urlNavigationTimeoutMillis
});
}
else {
debug('Loading HTML...');
await page.setContent(html, {
timeout: htmlNavigationTimeoutMillis
});
}
debug('Content loaded.');
const pdfOptions = { ...defaultPdfOptions, ...instancePdfOptions };
// Fix "format" issue
if (pdfOptions.format !== undefined) {
const size = getPaperSize(pdfOptions.format);
// eslint-disable-next-line sonarjs/different-types-comparison, @typescript-eslint/no-unnecessary-condition
if (size !== undefined) {
delete pdfOptions.format;
pdfOptions.width = `${size.width}${size.unit}`;
pdfOptions.height = `${size.height}${size.unit}`;
}
}
debug('Converting to PDF...');
// eslint-disable-next-line sonarjs/no-dead-store
isRunningPdfGeneration = true;
const pdfBuffer = await page.pdf(pdfOptions);
// eslint-disable-next-line sonarjs/no-dead-store
isRunningPdfGeneration = false;
debug('PDF conversion done.');
await page.close();
if (!pdfPuppeteerOptions.cacheBrowser || cachedBrowser !== browser) {
await browser.close();
}
return pdfBuffer;
}
catch (error) {
if (isRunningPdfGeneration &&
defaultPuppeteerOptions.browser === 'chrome') {
if (!doCloseBrowser) {
await closeCachedBrowser();
}
defaultPuppeteerOptions.browser = 'firefox';
debug('Trying again with Firefox.');
return await convertHTMLToPDF(html, instancePdfOptions, instancePdfPuppeteerOptions);
}
else {
// eslint-disable-next-line @typescript-eslint/only-throw-error
throw error;
}
}
finally {
try {
if (doCloseBrowser && browser !== undefined) {
debug('Closing browser...');
await browser.close();
debug('Browser closed.');
}
}
catch {
// ignore
}
}
}
export default convertHTMLToPDF;
/**
* Closes the cached browser instance.
*/
export async function closeCachedBrowser() {
if (cachedBrowser !== undefined) {
try {
await cachedBrowser.close();
}
catch {
// ignore
}
cachedBrowser = undefined;
}
}
/**
* Checks for any cached browser instance.
* @returns True is a cached browser instance exists.
*/
export function hasCachedBrowser() {
return cachedBrowser !== undefined;
}
export { defaultPdfOptions, defaultPdfPuppeteerOptions } from './defaultOptions.js';
exitHook(() => {
debug('Running exit hook.');
void closeCachedBrowser();
});