UNPKG

@nzz/q-server

Version:

__Q__ is a system that lets journalists create visual elements for stories. It is developed by [NZZ Storytelling](https://www.nzz.ch/storytelling) and used in the [NZZ](https://www.nzz.ch) newsroom.

94 lines (77 loc) 2.62 kB
const puppeteer = require('puppeteer'); const fetch = require('node-fetch'); // start a chromium process here const browserPromise = puppeteer.launch({ args: ['--no-sandbox'] }); let browserWSEndpoint; browserPromise .then(browser => { browserWSEndpoint = browser.wsEndpoint(); }) // fetches assets and returnes a concatenated string containing everything fetched async function getConcatenatedAssets(assets, userAgent) { let result = ''; for (let asset of assets) { if (asset.content) { result = result + asset.content; } if (asset.url) { const response = await fetch(asset.url, { headers: { 'User-Agent': userAgent } }); if (response.ok) { result = result + await response.text(); } } } return result; } async function getScreenshot(emptyPageUrl, markup, scripts, stylesheets, config) { await browserPromise; if (!browserWSEndpoint) { throw new Error('Browser not ready yet'); } const browser = await puppeteer.connect({browserWSEndpoint: browserWSEndpoint}); const page = await browser.newPage(); // the height of 16384 is just a wild guess that no graphic will ever exceed this await page.setViewport({ width: config.width, height: 16384, deviceScaleFactor: config.dpr }); await page.goto(emptyPageUrl); let bodyStyle = 'margin: 0;'; if (config.background) { bodyStyle += `background: ${config.background}`; } page.setContent(`<body style="${bodyStyle}"><div id="q-screenshot-service-container" style="padding: ${config.padding};">${markup}</div></body>`); const userAgent = await page.evaluate(() => { return navigator.userAgent; }) const scriptContent = await getConcatenatedAssets(scripts, userAgent); await page.mainFrame().addScriptTag({ content: scriptContent }); const styleContent = await getConcatenatedAssets(stylesheets); await page.mainFrame().addStyleTag({ content: styleContent }); // wait for the next animation frame (style is applied then) await page.evaluate(() => { return new Promise((resolve, reject) => { requestAnimationFrame(resolve); }); }); const graphicElement = await page.$('#q-screenshot-service-container'); const imageBuffer = await graphicElement.screenshot({ omitBackground: !config.background }); // we should use disconnect once this is released in puppeteer. it's merged to master, so anything after 0.12.0 should have it // await browser.disconnect(); await browser.close(); return imageBuffer; } module.exports = { getScreenshot: getScreenshot }