Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Puppeteer npm how to set up fonts from local file

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:

  • Node: v10.18.1,
  • Puppeteer: "puppeteer": "^2.0.0",
  • Chromium: Chromium 79.0.3945.88 Fedora Project,
  • OS: CentOS Linux release 8.0.1905 (Core)

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.

like image 276
Hadzo Avatar asked Mar 05 '20 09:03

Hadzo


1 Answers

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.

like image 154
eljefedelrodeodeljefe Avatar answered Nov 11 '22 01:11

eljefedelrodeodeljefe