UNPKG

html-pdf-chrome

Version:

HTML to PDF and image converter via Chrome/Chromium

220 lines 8.05 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.create = exports.CreateResult = exports.CompletionTrigger = void 0; const chrome_launcher_1 = require("chrome-launcher"); const CDP = require("chrome-remote-interface"); const CompletionTrigger = require("./CompletionTriggers"); exports.CompletionTrigger = CompletionTrigger; const ConnectionLostError_1 = require("./ConnectionLostError"); const CreateResult_1 = require("./CreateResult"); Object.defineProperty(exports, "CreateResult", { enumerable: true, get: function () { return CreateResult_1.CreateResult; } }); const DEFAULT_CHROME_FLAGS = [ '--disable-gpu', '--headless', '--hide-scrollbars', ]; /** * Generates a PDF or screenshot from the given HTML string, launching Chrome as necessary. * * @export * @param {string} html the HTML string. * @param {Options} [options] the generation options. * @returns {Promise<CreateResult>} the generated PDF or screenshot data. */ async function create(html, options) { const myOptions = normalizeCreateOptions(options); let chrome; if (!myOptions.host && !myOptions.port) { chrome = await launchChrome(myOptions); } try { const tab = await CDP.New(myOptions); try { return await generate(html, myOptions, tab); } finally { if (!(myOptions._exitCondition instanceof ConnectionLostError_1.ConnectionLostError)) { await CDP.Close({ ...myOptions, id: tab.id }); } } } finally { if (chrome) { await chrome.kill(); } } } exports.create = create; /** * Connects to Chrome and generates a PDF of screenshot from HTML or a URL. * * @param {string} html the HTML string or URL. * @param {CreateOptions} options the generation options. * @param {CDP.Target} tab the tab to use. * @returns {Promise<CreateResult>} the generated PDF or screenshot data. */ async function generate(html, options, tab) { await throwIfExitCondition(options); const client = await CDP({ ...options, target: tab }); const connectionLostOrTimeout = new Promise((_, reject) => { client.on('disconnect', () => { const error = new ConnectionLostError_1.ConnectionLostError(); options._exitCondition = error; reject(error); }); if (options.timeout != null && options.timeout >= 0) { setTimeout(() => { const error = new Error('HtmlPdf.create() timed out.'); options._exitCondition = error; reject(error); }, options.timeout); } }); async function generateInternal() { try { await beforeNavigate(options, client); const { Page } = client; if (/^(https?|file|data):/i.test(html)) { await Promise.all([ Page.navigate({ url: html }), Page.loadEventFired(), ]); // Resolve order varies } else { const { frameTree } = await Page.getResourceTree(); await Promise.all([ Page.setDocumentContent({ html, frameId: frameTree.frame.id }), Page.loadEventFired(), ]); // Resolve order varies } await afterNavigate(options, client); let base64Result; if (options.screenshotOptions) { // https://chromedevtools.github.io/devtools-protocol/tot/Page/#method-captureScreenshot const screenshot = await Page.captureScreenshot(options.screenshotOptions); base64Result = screenshot.data; } else { // https://chromedevtools.github.io/debugger-protocol-viewer/tot/Page/#method-printToPDF const pdf = await Page.printToPDF(options.printOptions); base64Result = pdf.data; } await throwIfExitCondition(options); return new CreateResult_1.CreateResult(base64Result, options._mainRequestResponse); } finally { client.close(); } } return Promise.race([ connectionLostOrTimeout, generateInternal(), ]); } /** * Code to execute before the page navigation. * * @param {CreateOptions} options the generation options. * @param {CDP.Client} client the Chrome client. * @returns {Promise<void>} resolves if there we no errors or cancellations. */ async function beforeNavigate(options, client) { const { Emulation, Network, Page, Runtime } = client; await throwIfExitCondition(options); if (options.clearCache) { await Network.clearBrowserCache(); } // Enable events to be used here, in generate(), or in afterNavigate(). await Promise.all([ Network.enable(), Page.enable(), Runtime.enable(), ]); if (options.runtimeConsoleHandler) { Runtime.consoleAPICalled(options.runtimeConsoleHandler); } if (options.runtimeExceptionHandler) { Runtime.exceptionThrown(options.runtimeExceptionHandler); } Network.requestWillBeSent((e) => { options._mainRequestId = options._mainRequestId || e.requestId; }); Network.loadingFailed((e) => { if (e.requestId === options._mainRequestId) { options._exitCondition = new Error('HtmlPdf.create() page navigate failed.'); } }); Network.responseReceived((e) => { if (e.requestId === options._mainRequestId) { options._mainRequestResponse = e.response; } }); if (options.extraHTTPHeaders) { Network.setExtraHTTPHeaders({ headers: options.extraHTTPHeaders }); } if (options.deviceMetrics) { Emulation.setDeviceMetricsOverride(options.deviceMetrics); } const promises = [throwIfExitCondition(options)]; if (options.cookies) { promises.push(Network.setCookies({ cookies: options.cookies })); } if (options.completionTrigger) { promises.push(options.completionTrigger.init(client)); } await Promise.all(promises); } /** * Code to execute after the page navigation. * * @param {CreateOptions} options the generation options. * @param {CDP.Client} client the Chrome client. * @returns {Promise<void>} resolves if there we no errors or cancellations. */ async function afterNavigate(options, client) { if (options.completionTrigger) { await throwIfExitCondition(options); const waitResult = await options.completionTrigger.wait(client); if (waitResult && waitResult.exceptionDetails) { await throwIfExitCondition(options); throw new Error(waitResult.result.value); } } await throwIfExitCondition(options); } /** * Throws an exception if the operation has been canceled or the main page * navigation failed. * * @param {CreateOptions} options the options which track cancellation and failure. * @returns {Promise<void>} rejects if canceled or failed, resolves if not. */ async function throwIfExitCondition(options) { if (options._exitCondition) { throw options._exitCondition; } } function normalizeCreateOptions(options) { const myOptions = Object.assign({}, options); // clone // make sure these aren't set externally delete myOptions._exitCondition; delete myOptions._mainRequestId; delete myOptions._mainRequestResponse; return myOptions; } /** * Launches Chrome with the specified options. * * @param {CreateOptions} options the options for Chrome. * @returns {Promise<LaunchedChrome>} The launched Chrome instance. */ async function launchChrome(options) { const chrome = await (0, chrome_launcher_1.launch)({ port: options.port, chromePath: options.chromePath, chromeFlags: options.chromeFlags || DEFAULT_CHROME_FLAGS, }); options.port = chrome.port; return chrome; } //# sourceMappingURL=index.js.map