Programmatic PDF generation on worker

Hi everyone, I've been looking for some way to generate PDFs within a worker script. Currently I use the browser rendering api to create PDFs of a page but according to the dashboard a single invocation takes 6 seconds... There must be a better and faster way right? I looked into the javascript PDF libraries (pdfkit, jspdf, pdf-lib, pdfme, etc.) but these are very limited in their features (I cannot even use a bold font?). I have no experience with Rust but I'm considering this more and more as it seems the only solution? But before I plunge myself into the deep with learning a bit of Rust, I'm curious if there are any other solutions? Will I run into the same problems as javascript (limited features)? Can I improve the speed of puppeteer rendering the PDF? This function renders the pdf on the worker:
export async function generateFromPage(browser: Browser, ticketId: string, lang: string) {
const page = await browser.newPage();
await page.setCookie({ name: 'locale', value: lang, domain: '<redacted>' });
page.on('error', err => console.log(`Browser DO: Error: ${JSON.stringify(err)}`));
page.on('pageerror', err => console.log(`Browser DO: Page Error: ${JSON.stringify(err)}`));

// create a pdf of the ticket page
await page.emulateMediaType('screen');
await page.emulateTimezone('Europe/Amsterdam');
await page.setViewport({ width: 1920, height: 1080 });
console.log(`Creating PDF of ticket page: https://<redacted>/ticket/${ticketId}`);
await page.goto(`https://<redacted>/ticket/${ticketId}`, { waitUntil: ['domcontentloaded', 'load', 'networkidle0'] });
const pdf = await page.pdf({ format: 'A4' });

// Close tab when there is no more work to be done on the page
await page.close();

return {
name: `ticket-${ticketId}.pdf`,
data: pdf,
};
}
export async function generateFromPage(browser: Browser, ticketId: string, lang: string) {
const page = await browser.newPage();
await page.setCookie({ name: 'locale', value: lang, domain: '<redacted>' });
page.on('error', err => console.log(`Browser DO: Error: ${JSON.stringify(err)}`));
page.on('pageerror', err => console.log(`Browser DO: Page Error: ${JSON.stringify(err)}`));

// create a pdf of the ticket page
await page.emulateMediaType('screen');
await page.emulateTimezone('Europe/Amsterdam');
await page.setViewport({ width: 1920, height: 1080 });
console.log(`Creating PDF of ticket page: https://<redacted>/ticket/${ticketId}`);
await page.goto(`https://<redacted>/ticket/${ticketId}`, { waitUntil: ['domcontentloaded', 'load', 'networkidle0'] });
const pdf = await page.pdf({ format: 'A4' });

// Close tab when there is no more work to be done on the page
await page.close();

return {
name: `ticket-${ticketId}.pdf`,
data: pdf,
};
}
3 Replies
Gerbuuun
GerbuuunOP8mo ago
I'm mostly researching this problem because the compute time is really high The next thing I'll try is just using an html string template and using
await page.setContent(template)
await page.setContent(template)
That would at least remove the request to the external web page.
Beny
Beny8mo ago
Have you tried it with a durable object? Using DOs improves performance by eliminating the time that it takes to spin up a new browser session.
Gerbuuun
GerbuuunOP8mo ago
Yes I'm using a DO Does that count to the compute time? maybe that might be the reason
Want results from more Discord servers?
Add your server