puppeteer: footerTemplate and headerTemplate don't use body styles

Tell us about your environment:

Puppeteer version: 1.0.0 Platform / OS version: Linux Mint 18.3 Node.js version: 8.9.4

What steps will reproduce the problem?

I have a piece of code i use to generate pdf reports from html documents:

  const page = await browser.newPage();
  await page.goto(file, { waitUntil: "networkidle0", timeout: 100000 });
  const default_options = {
    format: "A4",
    printBackground: true
  };
  const filename = `file-${uuid()}.pdf`;
  const final_options = Object.assign({}, default_options, { path: filename }, options);
  console.log(final_options);
  await page.pdf(final_options);
  await page.close();

file is a link to a local html file. In this file head, I load specific fonts that I need for the document. Inside of the options , I pass the templates for headers and footers:

options: {
    headerTemplate: "<p></p>", 
    footerTemplate: "<div class=\"footer\" style=\"font-size: 10px;color: #999; margin: 15px 0;clear:both; position: relative; top: 20px;font-family:my-font\"><p>Footer text</p></div>",
    displayHeaderFooter: true, 
    margin: { 
      top: "100px", 
      bottom: "200px"
    }
  }

What is the expected result?

I would expect the footer to use the font loaded in the html body.

What happens instead?

The footer is looking into my system for the first font that could correspond the name provided and put this one instead. If the font is not installed on the system, it is using a default one.

About this issue

  • Original URL
  • State: open
  • Created 6 years ago
  • Reactions: 89
  • Comments: 49

Most upvoted comments

I also noticed that if you stick a style tag in your header/footer templates, the CSS doesn’t all work right. Font sizes are different, custom font family is not changeable it seems, etc, it’s weird.

This issue is not resolved.

Is it possible to use custom font family in footer template? I’ve tried adding <style> tags in the string and classes with styles in a styles.css file but nothing seems to work.

In-lining was the solution for me to get this to work. Thank to @kyriakos

Some initial failure notes to share:

The header and footer sections do not have any style at all. Everything must be defined.

  • Left margin is zero even if you set it in the margin param for page.pdf, so you need to specify that independently in the header/footer css.
  • Note that in my example, the page number fields are enclosed in outer H1. If you do not do this, and independently style .pageNumber and .totalPage, then if you want Page N to M to show, you will also need to set the font size for Page and OF parts. Just easier to enclose the whole thing in H1 to have the same font size.
var cssb = [];
cssb.push('<style>');
cssb.push('h1 { font-size:10px; margin-left:30px;}');
cssb.push('</style>');
var css = cssb.join('');

And

await page.pdf({
        path: outputPath, format: 'Letter', 
        displayHeaderFooter: true,
        headerTemplate: css+'<h1>'+'My PDF Report Header'+'</h1>',
        footerTemplate: css+'<h1>Page <span class="pageNumber"></span> of <span class="totalPages"></span></h1>',
        margin: { 
          top: "100px", 
          bottom: "200px",
          right: "30px",
          left: "30px",
        }
      });

Inlining CSS in footer / header works correctly. Its a workaround to do the trick for now till this issue is fixed.

We’re marking this issue as unconfirmed because it has not had recent activity and we weren’t able to confirm it yet. It will be closed if no further activity occurs within the next 30 days.

I have the exact same issue as described by @WalterWeidner

@adelriosantiago I tried your solution for custom fonts and it doesn’t appear to work. I also tried using base64 encoded fonts but they crash the page. Any ideas?

  • Inline fonts in base64 in header make crash the page :

(node:13416) UnhandledPromiseRejectionWarning: Error: Page crashed! at Page._onTargetCrashed (…\node_modules\puppeteer\lib\Page.js:176:24) at CDPSession.Page.client.on.event (…\node_modules\puppeteer\lib\Page.js:138:56) at CDPSession.emit (events.js:182:13) at CDPSession.EventEmitter.emit (domain.js:442:20) at CDPSession._onMessage (…\node_modules\puppeteer\lib\Connection.js:233:12) at Connection._onMessage (…\node_modules\puppeteer\lib\Connection.js:119:19) at WebSocket.emit (events.js:182:13) at WebSocket.EventEmitter.emit (domain.js:442:20) at Receiver.receiverOnMessage (…\node_modules\ws\lib\websocket.js:720:20) at Receiver.emit (events.js:182:13)

NB: same css rule works well in the body of the pdf

  • inline fonts by using a path to local file, for example url(/mina/Mina-Regular.woff2), doesn’t work. It silently fails, there is no font loaded. And it’s not a problem with the path, I tested the exact same css rule in the body and it works well

Not being able to use custom font-family in the header and footer have significant impact: the generated pdf looks clumsy as different fonts are displayed depending of the zone (header, body, footer)

I’m using node.js 10.1.0 and Puppeteer 1.8.0

For now until the puppeteer lets us customize the header/footer I create this. It uses puppeteer and let fully customize header, footer, and pagination

https://github.com/PejmanNik/puppeteer-report#readme

@HosseinAgha I am using that. That’s not the issue. The actual document prints background just fine, but the header and footer part didn’t work the same way. It turned out to be a webkit issue. see https://github.com/GoogleChrome/puppeteer/issues/2182

TL;DR: add -webkit-print-color-adjust: exact to the header and footer styles

There are related issues regarding footerTemplate visibility on all pages before the last page. The Footer is only visible on last page but invisible all pages before the last.

See here with some explaining images: https://stackoverflow.com/questions/49985955/puppeteer-footer-only-display-on-last-page/49990697#49990697

Here’s the code to reproduce:

const puppeteer = require('puppeteer');
(async () => {
    const browser = await puppeteer.launch();
    const page = await browser.newPage();
    await page.goto('http:/faz.net/aktuell');

    var cssb = [];
    cssb.push('<style>');
    cssb.push('h1 { font-size:30px;position:absolute;background-color:#f00; z-index:1000;color:#000; margin-left:30px;}');
    cssb.push('</style>');

    const css = cssb.join('');

    await page.pdf({
        path: 'example.pdf',
        format: 'A4',
        displayHeaderFooter: true,
        footerTemplate: css + '<h1>Page <span class="pageNumber"></span> of <span class="totalPages"></span></h1>',

    });
    await browser.close();
})();

I am having the same issue.

@TylerJAllen yes, it is possible to do that. Your <style> tag is very likely working but it is referencing a font-family that is outside of its scope. Take a look at this snippet:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.setViewport({ width: 960, height: 780 });
  await page.goto('http://localhost:3000/results', {waitUntil: 'networkidle2'});
  await page.pdf({path: 'hn' + Date.now() + '.pdf', format: 'Letter',
      headerTemplate: '<img style="margin-top:-15px;" src="data:image/png;base64,[image data in BASE 64 here]"></img>',
      footerTemplate: '<style>@font-face{font-family:Mina;src:url(/mina/Mina-Regular.woff2) format("woff2"),url(/mina/Mina-Regular.woff) format("woff"),url(/mina/Mina-Regular.ttf) format("truetype");font-weight:400;font-style:normal}@font-face{font-family:Mina;src:url(/mina/Mina-Bold.woff2) format("woff2"),url(/mina/Mina-Bold.woff) format("woff"),url(/mina/Mina-Bold.ttf) format("truetype");font-weight:700;font-style:normal}h1,h2,h3,h4,h5,h6,p,span{font-family:Mina,sans-serif}</style><h1 style="margin: 10px;font-size: 20px;">footer</h1><div style="font-size:10px!important;color:grey!important;padding-left:400px;" class="pdfheader"><span>Page: </span><span class="pageNumber"></span>/<span class="totalPages"></span></div>',
      displayHeaderFooter: true,
      margin: {
        top: "100px",
        bottom: "100px"
      }
    });

  await browser.close();
})();

In this example:

  • A custom font is loaded from the local folder “/mina” and it is shown in the footer.
  • Page numbers are printed in the footer.
  • An image is added to the header. (Note that the image must be in BASE64 so I erased it because it takes a lot of space.)

@sellerin I found a weird fix for the issue you are experiencing (outlined below) I too could not render the fonts using a full qualified path. The solution was to download the fonts then base64 encode them, put them inline and wrap another style sheet around it also base64 encoding that too. Fix: First download the .woff2 fonts that you require. Then base64 encode them. Then create an inlined style sheet like this: @font-face { font-family: ‘Libre Barcode 39 Extended’; font-style: normal; font-weight: 400; src: local(‘Libre Barcode 39 Extended’), local(‘LibreBarcode39Extended-Regular’), url(data:application/x-font-woff;charset=utf-8;base64,d09GMgABAAAAAA…AAA=) format(‘woff2’); unicode-range: U+0000-00FF, …; }

Then base64 encode this style sheet too, like this:

<style type="text/css"> @import url("data:text/css;base64,DQpAZm9udC1mYWNlIHs........"); </style>

Insert this style sheet as inlined in the template. Now you can use the this style sheet in the header, body and footer e.g. .someClass { font-family: ‘Libre Barcode 39 Extended’; font-style: normal; font-weight: 400; font-size: 12pt; }

One other issue to mention. If I tried to use the above method in the header only it still causes the hang issue. I had to also use the same inlined style sheet in the body and I also had to use the same font too but setting the font size to 0 so it was not displayed.

Example here: https://github.com/Trejay/CssEmbeddedFonts

works pretty well for me.

footer.html

<html>
  <head>
    <style type="text/css">
      #footer {
        padding: 0;
      }
      .content-footer {
        width: 100%;
        font-family:Arial, Helvetica, sans-serif;
        background-color: white;
        color: black;
        padding: 5px;
        -webkit-print-color-adjust: exact;
        vertical-align: middle;
        font-size: 12px;
        margin-top: 0;
        display: inline-block;
        text-align: center;
        border-top: 1px solid lightgray;
      }
    </style>
  </head>
  <body>
    <div class="content-footer">
      Page <span class="pageNumber"></span> of <span class="totalPages"></span>
    </div>
  </body>
</html>

code

      const templateFooter = fs.readFileSync(__dirname+'/footer.html', 'utf-8')
.
.
.
      const pdf = await page.pdf({
        format: 'A4',
        displayHeaderFooter: true,
        printBackground: true,
        headerTemplate: templateHeader,
        footerTemplate: templateFooter,
        margin: {
          bottom: 70, // minimum required for footer msg to display
          left: 20,
          right: 25,
          top: 60,
        }
      });


👍 same, setting footerTemplate and no footer shows up… 😦

This might help some, was trying to use Playwright with Chromium on OSX for PDF generation, sans-serif seemed to be ignored and gave me a serif default which was frustrating.

body {
    font-family: sans-serif;
}
Screenshot 2023-01-08 at 12 07 40 PM

Changing “sans-serif” to “system-ui” gave me a sans-serif font i can live with, without having to load any specific fonts.

body {
    font-family: system-ui;
}
Screenshot 2023-01-08 at 12 09 00 PM

Maybe this will help, I was running my Puppeteer script as basic node script from the CLI (node index.js). The directory structure looked like this:

myPackage
├── index.js
├── node_modules/
├── 312267_3_0.eot
├── 312267_3_0.ttf
├── 312267_3_0.woff
├── 312267_3_0.woff2

Are you saying that the files need to be on the same domain as the page I am navigating to and then accessed relatively from there? For example if I navigated to https://example.com/about and my font was at https://example.com/fonts/font.woff I would put url(/fonts/font.woff)?

Normally, that would make sense to me but these template sections don’t seem to play by normal rules.

I assumed it would be relative to the system I was running the script on since it looked like remote resources don’t seem to work (I tried https://fonts.gstatic.com/s/sourcesanspro/v11/6xK3dSBYKcSV-LCoeQqfX1RYOo3qNa7lujVj9_mf.woff2, for example, and it didn’t work).

freegroup are you sure the font of your footer is 12px? I have noticed that the header and footer are scaleed like 1.4 times (all elements are bigger than defined - margins, padding, fonts etc.)

the solution is to insert the font as a base64. it’s working then. i tested with woff and woff2 formats, boths are working

  footerTemplate: `
   <style>
    @font-face {
       font-family: 'yourfontname';
       src: url(data:application/font-woff2;charset=utf-8;base64,${base64Woff2}) format('woff2'),
              url(data:application/font-woff;charset=utf-8;base64,${base64Woff}) format('woff');
        font-weight: normal;
        font-style: normal;
    }
   </style>
   <div style="font-family: yourfontname">I am custom font</div>`,`

Im using chrome-aws-lambda and the following works for me

(1) Simply pass a URL to your custom font face before launching Chromium await chromium.font('https://url-to-fonts.com/tahoma.ttf');

(2) Use the font in the header/footer template ... footerTemplate: "<style>body{font-family: tahoma;}</style>" ...

Nice! I’ll have to try testing this out in our solution. Much better than base64 encoding the entire font.

For now until the puppeteer lets us customize the header/footer I create this. It uses puppeteer and let fully customize header, footer, and pagination

https://github.com/PejmanNik/puppeteer-report#readme

Wow, this is a lifesaver. Works really well.

@Trejay your method helped me get the font to appear (although I had to use the TTF; WOFF/WOFF2 were ignored).

However, if I try to use ANY other font anywhere in the document, Page crashes. This happens whether in a CSS class or an inline style. Also, it seems that the custom font must be declared in the body selector, or Page will crash.

How exactly did you configure the body to use an additional font?

Yeah, still no dice @adelriosantiago. I added my test repo if you want to look: https://github.com/WalterWeidner/headless . The best I could do is install the font on the system and have Chrome pick it up from there.

Installing the google font as a system font as @WalterWeidner mentioned is the only way I was able to get custom fonts to work.

@seyfer puppeteer also crashes with me when I try to use font-face

On the other hand, I am using latest puppeteer 10.4.0 and just having any <style> tag in footerTemplate makes it crash. So I tried Node js inline libraries, like Juice and inline-css. It works for all styles, except @font-face, cause font-face has to be in <style> tag in footerTemplate and it leads to crash.

In-lining was the solution for me to get this to work. Thank to @kyriakos

Some initial failure notes to share:

The header and footer sections do not have any style at all. Everything must be defined.

  • Left margin is zero even if you set it in the margin param for page.pdf, so you need to specify that independently in the header/footer css.
  • Note that in my example, the page number fields are enclosed in outer H1. If you do not do this, and independently style .pageNumber and .totalPage, then if you want Page N to M to show, you will also need to set the font size for Page and OF parts. Just easier to enclose the whole thing in H1 to have the same font size.
var cssb = [];
cssb.push('<style>');
cssb.push('h1 { font-size:10px; margin-left:30px;}');
cssb.push('</style>');
var css = cssb.join('');

And

await page.pdf({
        path: outputPath, format: 'Letter', 
        displayHeaderFooter: true,
        headerTemplate: css+'<h1>'+'My PDF Report Header'+'</h1>',
        footerTemplate: css+'<h1>Page <span class="pageNumber"></span> of <span class="totalPages"></span></h1>',
        margin: { 
          top: "100px", 
          bottom: "200px",
          right: "30px",
          left: "30px",
        }
      });

Thank you!

@Trejay I actually followed your tip and it worked! Although, I started doing some trial and error by removing certain things and it turns out that you only need the font src as base64 and to have the font included in your main HTML as well as the header/footer. You don’t actually need to base64 encode and @import. At least not in my case. Thank you for your tip!

I still faced same problem. I used puppeteer 1.19.0. Node v12.6.0. If I set the footer like this:

await page.pdf({ format: 'A4', printBackground: true, landscape: true, displayHeaderFooter: true, headerTemplate: '<div style="display: none"></div>', footerTemplate: ' <html> <head> <link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css?family=Tangerine"> <style> body { font-family: 'Tangerine', serif; font-size: 48px; } </style> </head> <body> <div style="width: 100%; text-align: right; -webkit-print-color-adjust: exact; margin-right: 20px;" > Page <span class='pageNumber'></span> of <span class='totalPages'></span> </div> </body> </html> ', margin: { top: 0, bottom: 60 }, });

it will crash. But, if I use this:

`await page.pdf({ format: ‘A4’, printBackground: true, landscape: true, displayHeaderFooter: true, headerTemplate: ‘<div style="display: none"></div>’, footerTemplate: ’ <style type="text/css" > @font-face { font-family: ‘Tinos’, serif; src: url(“https://fonts.googleapis.com/css?family=Tinos&display=swap”); }

      .Footer__title {
        font-family: 'Tinos' !important;
        font-size: 12px;
        color: rgb(255, 0, 0);
      }
    </style>
    
    <div
      style="width: 100%; text-align: right; -webkit-print-color-adjust: exact; margin-right: 20px;"
      class="Footer__title"
    >
      Page
      <span class='pageNumber'></span> 
      of 
      <span class='totalPages'></span>
    </div>
    ',
    margin: { top: 0, bottom: 60 },
  });`

the font style not working. Only font size and color that works

Just saying, but using web fonts will/can make the PDF somewhat 10 times larger since each character is rendered as a single SVG. You really should try to find the corresponding system font and install it. At least on Linux this works very well after some try and error. Also web fonts text can not be copy-pasted from the created PDF. Generally chrome has many problems of current/old phantomJS since they both are based on Webkit, so many phantomJS workarounds are working for chrome too.

@adelriosantiago I tried your solution for custom fonts and it doesn’t appear to work. I also tried using base64 encoded fonts but they crash the page. Any ideas?

@cyrus-za use printBackground: true option

It seems like as @imanabu said inline styles only work for element types!
so you can’t write a new class and add it to your element you should use div, h1, etc css rules.
I also found out that the font-family only works using the <style> tag. here is my workaround for changing font-family:

  <style>
    div {
      font-family: 'Helvetica';
      direction: 'rtl'
    }
  </style>

I have built a template to solve the following problems:

  • JavaScript in header and footer (similar possibilities as with phantomjs)
  • Same CSS as in the body
  • Display of images by base64

@ovheurdrive theoretically you just have to pull the style tags out of the body and assign them to header, footer

Code

Maybe it helps, or is an drive for your own thoughts.