Skip to content

Instantly share code, notes, and snippets.

@tjvr
Last active August 23, 2017 22:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tjvr/702190b7eaee17a6b2246bb9ad454af2 to your computer and use it in GitHub Desktop.
Save tjvr/702190b7eaee17a6b2246bb9ad454af2 to your computer and use it in GitHub Desktop.
metalsmith-pdf-chrome

Don't use this, use wkhtmltopdf. Don't waste your life like I did.


Render PDFs using Chrome.

const m = Metalsmith(__dirname)
  .use(markdown({
      smartypants: true,
      gfm: true,
  }))
  .use(layouts({
    engine: 'handlebars',
    default: 'default.html',
  }))
  .use(pdf({
    format: 'A4',
    printBackground: true,
  }))
  .build(function(err) {
      if (err) throw err
  })
const path = require('path')
const chromeLauncher = require('chrome-launcher')
const CDP = require('chrome-remote-interface')
async function launchChrome() {
return await chromeLauncher.launch({
chromeFlags: ['--headless', '--disable-gpu'],
})
}
async function closeChrome(chrome) {
const protocol = await CDP({port: chrome.port})
await protocol.close()
await chrome.kill()
}
async function newPage(chrome, url) {
const target = await CDP.New({port: chrome.port})
const client = await CDP({target})
const {Page} = client
await Page.enable()
await Page.navigate({url, referrer: ''})
await Page.loadEventFired()
return {chrome, Page, client}
}
async function renderPDF({chrome, Page, client}, options) {
const {data} = await Page.printToPDF(Object.assign({
format: 'A4',
}, options))
return Buffer.from(data, 'base64')
}
async function closePage({chrome, Page, client}) {
await CDP.Close({port: chrome.port, id: client.target.id})
}
async function renderPdfs(files, metalsmith) {
// find html files
const htmlPages = []
for (let name of Object.keys(files)) {
if (/x?html?/i.test(path.extname(name))) {
htmlPages.push(name)
}
}
// launch Chrome
const chrome = await launchChrome()
// open a tab to render each PDF
const promises = htmlPages.map(async (name, index) => {
const html = files[name].contents
const url = 'data:text/html;base64,' + html.toString('base64')
let tab, buffer
try {
tab = await newPage(chrome, url)
buffer = await renderPDF(tab)
closePage(tab)
} catch (err) {
console.error(`Error rendering '${name}' to PDF:\n${err}`)
throw err
}
files['pdfs/' + name.replace(/\.x?html?$/i, '.pdf')] = {
contents: buffer,
}
})
await Promise.all(promises)
// close Chrome gracefully
await closeChrome(chrome)
}
module.exports = options => function pdf(files, metalsmith, done) {
renderPdfs(files, metalsmith, options).then(() => {
done()
})
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment