puppeteer / puppeteer

Node.js API for Chrome

Home Page:https://pptr.dev

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

footerTemplate and headerTemplate don't use body styles

ovheurdrive opened this issue · comments

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.

commented

I am having the same issue.

👍 same, setting footerTemplate and no footer shows up... :(

Anyone get traction on this? I'm also having the same issue

Same issue here! Any idea or workaround for this?

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.

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.

Right... I have just noticed. The CSS in the footer template overwrites / adds to the header template! I think in the background a DOM is built for both. font-size "rem" does not work either.

So my template has somehow become useless...

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

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",
        }
      });

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();
})();

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.

@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.)

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 am having a similar issue. Using inline styles gets the header and footer to show, but the background color and so forth don't go with it.

@cyrus-za use printBackground: true option

@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 #2182

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

@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?

@WalterWeidner It is very likely related to where the fonts are located. Are you sure the fonts are reachable? Show me the code to see if I can spot a mistake.

With CSS rule like: url(/mina/Mina-Regular.woff2)
The font file would be in: "/public/mina/Mina-Regular.woff2", (assuming "public" is your public folder)

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).

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.

That is the issue @WalterWeidner, those files are not in a "public" folder. You should use a framework like Express.JS to serve static files.

All this is assuming that you want to print a page you created in a Node project. Are you trying to print an external website? If so, you can try using an url directly from a CDN, try this: url('https://fonts.googleapis.com/css?family=Maven+Pro:400,500,700,900'), note the quotes inside, it should load the Maven Pro font. This will probably work for the first case too, so you may want to try it.

Thanks. @adelriosantiago. We are loading our own website so I can access its public files. It still seems to use whatever the system default is when printing to PDF when loading relative to my website.

For some reason I can't load from other resources though. We have a our own static CDN we load resources from e.g., https://resources.mydomain.com/fonts/proximanova. If I try to load from an external resource the page crashes.

(node:18747) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: Page crashed!

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.

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

@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

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: '

',
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

@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?

@GridTrekkor Yes I too experienced a crash.
My use case was to install the font in the header of the doc.
I had to declare the font in the body too or else it would crash.
I'm not sure why the WOFF font does not work for you.

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

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.

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>" ...

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.

@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!

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",
        }
      });

This one here really solved the problems.

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 @gavin310 thank you two for the suggestions.
I have found this wonderful service for any google fonts to base64 transformation and used it for Barlow
https://amio.github.io/embedded-google-fonts/

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.

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

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

commented

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.

This issue is not resolved.

How I can add it center of footer ?

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.

const created_pdf = await page.pdf({
printBackground: true,
format: "A4" ,
headerTemplate: "<div class="header" style="font-size: 10px;color: #999; margin: 15px 0;clear:both; position: relative; top: 20px;font-family:my-font">

header text

",
footerTemplate: "<div class="footer" style="font-size: 10px;color: #999;background-color:blue ; margin: 15px 0;clear:both; position: relative; top: 20px;font-family:my-font">
/
<div class="footer_body"> <div class=" sub_footer pb-3"> <div class="client"> Private and Confidential
<div class="Mobilesite">www.xiarch.com <div class="page_no">Page No. ${page_no} <div class="footer_text">The information in this document has been classified as 'Confidential'. This classification applies to the most sensitive business information. Its unauthorized disclosure could seriously and adversely impact the owner, its stakeholders, its business partners, and/or its bolders leading to legal and financial repercussions and adverse public opinion ",
displayHeaderFooter: true,
margin: {
top: "100px",
bottom: "200px"
}

    }) 

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>`,`

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

If I load fonts via CDN in the html like this:

<link href="https://fonts.googleapis.com/css2?family=family=Roboto:wght@300;400;500&display=swap" rel="stylesheet" />

How can I use the same font in footerTemplate?

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,
        }
      });

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.)

About the font problem not applying to the header and footer you guys can use base64 it works for me and I don't need to follow inline stuff

  1. Convert your font from woff2 or ttf... to base64 then save it to a text file
    https://stackoverflow.com/a/59116157/12353877

  2. Read the file and include it into your header (it will affect the footer as well)

format('truetype') this depend on your font convert to base64 above so remember to change it in my case it is ttf

  const fontContent = fs.readFileSync("./Inter-Regular.txt", "utf8");
  await page.pdf({
       printBackground: true,
       displayHeaderFooter: true,
       headerTemplate: `
       <style>@font-face {font-family: 'Inter';src: url(${fontContent}) format('truetype');}</style>
       <div style="font-family: 'Inter', sans-serif;">Example text</div>`,
   });

But just remember that you will include base64 text to the header for each page so the PDF size will increase dramatically
I'm end up generating my header using pdf-lib and @pdf-lib/fontkit for my purpose