I would like to load fonts from local files while rendering PDF using the puppeteer node module.
It worked for me to use web fonts but this does not satisfy requirements.
My environment:
Here is the code that I tried so far: loading font's using @font-face, changing values of networkidle, set up timeout, set up event listeners for font update. Event I set up this callback for getting fonts properties that returns "Open Sans" but rendered PDF file is not in Open Sans font.
const puppeteer = require('puppeteer');
const chromiumExecutablePath = '/bin/chromium-browser';
let document = `
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
@font-face {
font-family: "Open Sans";
font-style: normal;
font-weight: 400;
src: local("Open Sans") url("/opt/www/webapp/resources/packed/OpenSans-Regular.eot");
src: local("Open Sans") url("/opt/www/webapp/resources/packed/OpenSans-Regular.woof") format("woff"),
local("Open Sans") url("/opt/www/webapp/resources/packed/OpenSans-Regular.ttf") format("truetype");
}
html, body {
font-family: 'Open Sans', sans-serif;
}
</style>
<sript>
</script>
<link rel="stylesheet" type="text/css" href="font.css">
</head>
<body>
<div class="content">
<h1 id="hello-world">Hello world long report</h1>
</div>
</body>
</html>
`
const browser = await puppeteer.launch({
args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-web-security', '--font-render-hinting=none'],
headless: true,
executablePath: chromiumExecutablePath
});
const page = await browser.newPage()
const session = await page.target().createCDPSession();
await session.send('DOM.enable');
await session.send('CSS.enable');
await new Promise(resolve => setTimeout(resolve, 500));
session.on('CSS.fontsUpdated', event => {
console.log(event);
// event will be received when browser updates fonts on the page due to webfont loading.
});
await page.goto("data:text/html;charset=UTF-8,"+document, { waitUntil: 'networkidle0' }).catch(e => console.error(e));
await page.waitFor(2000);
await page.evaluateHandle('document.fonts.ready');
const selector = 'h1';
const getFontProperty = async (page) => {
const font = await page.evaluate((selector) => {
const title = document.querySelector(selector);
return getComputedStyle(title).font;
}, selector);
return font;
}
console.log(await getFontProperty(page)); // output: 700 32px "Open Sans", sans-serif
await page.emulateMedia("print");
await page.pdf({
displayHeaderFooter: false,
path: outputPath,
width: 400+'px',
height: 400+'px',
printBackground: true,
margin: {
top: '20px',
bottom: '40px'
}
}); // output is valid PDF file but without Open Sans font
Any ideas on how this can be resolved? There is a similar question on how to render images from local files but I cannot resolve from the answer how can it be done with the fonts.
I understand, but don't solve it that way.
It might be that PDF is importing fonts correctly if you install those fonts on systems correctly. However, the PDF consumers (especially some cheap printers) will have hard issues printing a PDF. I have seen very weird errors in those cases, such as printer based smearings. However, those are kind of assumed.
Instead embed the whole font as base64. The result PDF will always transport those fonts. base64 myfont.woff > fontbase64.txt
const yourOpenSans = require('fs').readFileSync('./fontbase64.txt')
function genFont () {
return `
@font-face {
font-family: 'YOUR Open Sans';
src: url(data:font/truetype;charset=utf-8;base64,${yourOpenSans}) format('truetype');
font-weight: normal;
font-style: normal;
}
`
}
Use that font-face
accordingly in your CSS.
Note: those files are sensitive to newlines.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With