app/lib/pdf.js

/**
 * @module pdf
 */
const config = require( '../models/config-model' ).server;
const timeout = config.headless.timeout;
const puppeteer = require( 'puppeteer' );
const { URL } = require( 'url' );

/**
 * @typedef PdfGetOptions
 * @property { string } [format]
 * @property { string } [margin]
 * @property { string } [landscape]
 * @property { string } [scale]
 */

/**
 * default values for {@link module:pdf~PdfGetOptions|PdfGetOptions}
 *
 * @default
 */
const DEFAULTS = {
    FORMAT: 'A4',
    MARGIN: '0.5in',
    LANDSCAPE: false,
    SCALE: 1
};

/**
 * Asynchronously gets pdf from url using Puppeteer.
 *
 * @static
 * @param { string } url - URL to load
 * @param {PdfGetOptions} [options] - PDF options
 * @return { Promise } a promise that returns the PDF
 */
async function get( url, options = {} ) {
    if ( !url ) {
        throw new Error( 'No url provided' );
    }

    options.format = options.format || DEFAULTS.FORMAT;
    options.margin = options.margin || DEFAULTS.MARGIN;
    options.landscape = options.landscape || DEFAULTS.LANDSCAPE;
    options.scale = options.scale || DEFAULTS.SCALE;

    const urlObj = new URL( url );
    urlObj.searchParams.append( 'format', options.format );
    urlObj.searchParams.append( 'margin', options.margin );
    urlObj.searchParams.append( 'landscape', options.landscape );
    urlObj.searchParams.append( 'scale', options.scale );

    const browser = await puppeteer.launch( { headless: true } );
    const page = await browser.newPage();

    let pdf;

    try {
        await page.goto( urlObj.href, { waitUntil: 'networkidle0', timeout } )
            .catch( e => {
                e.status = /timeout/i.test( e.message ) ? 408 : 400;
                throw e;
            } );

        pdf = await page.pdf( {
            landscape: options.landscape,
            format: options.format,
            margin: {
                top: options.margin,
                left: options.margin,
                right: options.margin,
                bottom: options.margin
            },
            scale: options.scale,
            printBackground: true
        } );
    } catch ( e ) {
        e.status = e.status || 400;
        await page.close();
        throw e;
    }

    await page.close();
    await browser.close();

    return pdf;
}

module.exports = { get };