Reputation: 989
So I am trying to generate a PDF using Puppeteer and Handlebars. I am doing it by setting the content of a new page and then using Puppeteer to generate the PDF. However I am struggling to get the CSS to link. I have tried using the following code in a simplified project to get it to work using express:
const puppeteer = require('puppeteer');
const path = require("path");
const fs = require("fs");
const handlebars = require("handlebars");
var express = require("express");
var hbs = require("express-handlebars");
var app = express();
app.engine('hbs', hbs({ extname: 'hbs' }));
app.set('view engine', 'hbs');
app.use(express.static(path.join(__dirname, 'public')));
(async () => {
let browser = null;
const file = fs.readFileSync('./templates/template.hbs', 'utf8');
const template = handlebars.compile(file);
const html = template({});
browser = await puppeteer.launch({
headless: false,
devtools: true
});
const page = await browser.newPage();
await page.setContent(html);
})();
This is how my project structure looks:
However I can not seem to get my CSS to appear when setting the page content. I have followed a lot of tutorials but I am also not sure if they are applicable to what I am trying to do?
Upvotes: 1
Views: 1999
Reputation: 1
I had this problem while using Puppeteer in a Google Cloud Function. I solved the problem by saving the compiled HTML file to a temporary file in the file system and use page.goto()
to open the file. This allowed the external images and stylesheet to be loaded. This way, your HTML template not so tightly coupled to your puppeteer script.
const htmlFile = bucket.file('invoices/template/invoice_template.html');
const html = (await htmlFile.download()).toString();
// replace template tags with invoice data
var template = handlebars.compile(html);
const htmlWithData = template({});
// save the html to the temp folder
const tempFilePath = '/tmp/invoice.html';
fs.writeFileSync(tempFilePath, htmlWithData);
// set the html of the page to the invoice template
await page.goto('file://' + tempFilePath, { waitUntil: 'load' });
// to use page breakpoints
await page.emulateMediaType('print');
// generate the pdf
const pdf = await page.pdf({
format: "letter",
margin: {
top: '50px',
right: '50px',
bottom: '50px',
left: '50px',
}
});
Upvotes: 0
Reputation: 989
What ended up working for me was using Puppeteers
built in function to set styles for the page:
await page.addStyleTag({ path: './public/css/style.css'});
This means I could get rid of all the express code as well. So my final working code looked like so:
const puppeteer = require('puppeteer');
const fs = require("fs");
const handlebars = require("handlebars");
(async () => {
let browser = null;
const file = fs.readFileSync('./templates/template.hbs', 'utf8');
const template = handlebars.compile(file);
const html = template({});
browser = await puppeteer.launch({
headless: false,
devtools: true
});
const page = await browser.newPage();
await page.setContent(html);
await page.addStyleTag({ path: './public/css/style.css'});
})();
You may need to make use of path
however like I needed to in my main project to get the correct path to the CSS. Like so:
await page.addStyleTag({ path: path.join(__dirname, '/public/css/style.css') });
Upvotes: 2