12

This is my code for creating the pdf. Everything works great except the footer and header doesn't work. They are there (i think) but not visible. I have tried with displayHeaderFooter: true but all that makes is a date stamp in the header and some broken html code in the footer (as last picture).

async function createListenPdf(html, header, footer) {
try {
    var jobId = uuidv4();

    this.browser = await puppeteer.launch();
    const page = await browser.newPage();
    var viewport = {
        width:1165,
        height:1200
    }
    page.setViewport(viewport);
    page.on("console", msg => {
        for (let i = 0; i < msg.args.length; ++i) {
            console.log(`${jobId} - From page. Arg ${i}: ${msg.args[i]}`);
        }
    });

    await page.goto(`data:text/html,${html}`, { waitUntil: 'networkidle0' });   

    await page.emulateMedia('screen');

    console.log("Header footer");
    var buffer = await page.pdf({
         printBackground: true,         
         footerTemplate: "<div style='width: 200px; background-color: #4286f4; position: relative; position: absolute; top:0;'>Hej</div>",
         headerTemplate: "<div style='width: 200px; background-color: #4286f4; position: relative; position: absolute; bottom: 0;'>Footer</div>",
         //displayHeaderFooter: true, 
         margin:{
             top: "100px",
             bottom: "100px"
         }
    });

    console.log(`${jobId} - Done. Will return the stream`);
    return buffer;
}
finally {
    if (this.browser) {
        console.log(`${jobId} - Closing browser`);
        this.browser.close();
    }
}
 }

enter image description here

As you can see i somehow got a footer with some grey area (i don't know why its grey). When i enable displayHeaderFooter: true in the options it looks like this:

enter image description here

Has anyone managed to create a pdf with puppeteer using html with header and footer? Here in their API description its seems pretty obvious but it really doesn't work.

https://github.com/GoogleChrome/puppeteer/blob/v1.3.0/docs/api.md

Daniel Gustafsson
  • 1,725
  • 7
  • 37
  • 78

6 Answers6

19

Please try the following templates:

headerTemplate: '<span style="font-size: 30px; width: 200px; height: 200px; background-color: black; color: white; margin: 20px;">Header 1</span>',
footerTemplate: '<span style="font-size: 30px; width: 50px; height: 50px; background-color: red; color:black; margin: 20px;">Footer</span>'

Not all styling properties are supported, you should avoid using position, top, bottom.

Also make sure you are on the latest version of puppeteer.

nachtjasmin
  • 386
  • 4
  • 13
Tarun Lalwani
  • 142,312
  • 9
  • 204
  • 265
  • It prints, but the footer and header on next page goes behind the page content. Any workaround for that? (z-index didn't work) – Srk95 Jul 05 '23 at 05:46
13

Just bumped into the issue.

My observations about headerTemplate/footerTemplate are the following:

  1. At least the font-size has to be specified (it's set to 1px by default, which is very small):
headerTemplate: '<span style="font-size: 10px"> <span class="pageNumber"></span>/<span class="totalPages"></span></span>',
  1. top/bottom margin must be defined to set space for the header/footer

  2. Some CSS properties indeed don't work. I didn't have any success with background-color, but maybe it's because the background isn't painted for these sections.

  3. No asynchronous code (that would require an HTTP request) is allowed, such as calling a CSS stylesheet, loading an image or a font.
    Consequently, CSS have to be inlined. To add an image, it can be base-64 encoded, as shown below. Concerning the fonts, we would need to rely on what is expected to be installed on the Puppeteer server I guess. Maybe there's a way to also embed it in base-64, I didn't try.

const printPDF = async () => {
    const footer = `<footer style="margin: auto; width: 40%">
            <img style="float: left; marginRight: 8px; marginLeft: 36px; width: 25%" src="data:image/jpeg;base64,/9j/4AAQSkZJRgAB...09Yv//Z" alt="Pivohub" />
            <p style="font-family: arial; float: right; width: 55%; color: red; margin-top: 24px; font-size: 10px">
                <b>My footer</b>
            </p>
        </footer>`

    const content = `<!DOCTYPE html>
        <html>
            <head>
                <meta charSet="utf-8"/>
                <style type="text/css">
                    body { backgroundColor: "red" }
                </style>
            </head>
            <body>
                <h1>Hello</h1>
            </body>
        </html>`

    const browser = await puppeteer.launch({ headless: true })
    const page = await browser.newPage()
    await page.setContent(content, { waitUntil: "networkidle0" })
    const buffer = await page.pdf({
        format: "A4",
        displayHeaderFooter: true,
        headerTemplate: '<span style="font-size: 10px"> <span class="pageNumber"></span> of <span class="totalPages"></span></span>',
        footerTemplate: footer,
        margin: { top: "100px", bottom: "200px" },
        printBackground: true,
    })
}

In this sample, the base 64 image was truncated. An image can be converted thanks to an online base-64 converter such as https://www.base64-image.de.

arvymetal
  • 2,787
  • 1
  • 30
  • 39
  • 3
    for background, I had to add this style along with the header/footer content: html,body,div{-webkit-print-color-adjust: exact} – Kishor Tiwari Jan 25 '21 at 22:35
  • 1
    any idea how to have header/footer without margin? – Kishor Tiwari Jan 25 '21 at 23:08
  • @KishorTiwari Not sure about which margins you're speaking about – arvymetal Mar 12 '21 at 04:56
  • It prints, but the footer and header on next page goes behind the page content. Any workaround for that? (z-index didn't work) – Srk95 Jul 05 '23 at 05:46
  • @Srk95 Not sure, I always managed to have high enough top/bottom margins to prevent the header/footer from mixing up with the content. That lib is a lot of tinkering if you ask me... – arvymetal Jul 06 '23 at 11:47
  • so that's why my tailwind styling is not being applied. – lance2k Aug 11 '23 at 13:09
  • Have you tried multiple logo? I can only use one base64 logo in browsershot, after that my postman returns ``` Symfony\Component\Process\Exception\ProcessFailedException Error Output: in file /home/lance/pledgebox/pledgebox_be/vendor/spatie/browsershot/src/Browsershot.php on line 953 ``` – lance2k Aug 14 '23 at 11:49
5
  // or a .pdf file
  await page.pdf({
    printBackground: true,
    width: `595px`, // ? 3508 x 2480 (print) || 595 pixels x 842 pixels (screen)
    height: `842px`, // ? Here subraction for making alignment looks cool
    margin: {
      top: '25px',
      bottom: '60px',
      left: '25px',
      right: '25px'
    },
    path: path.join(ROOT_PATH, 'pdf', 'report.pdf'),
    displayHeaderFooter: true,
    footerTemplate: `
    <p style="margin: auto;font-size: 13px;">
      <span class="pageNumber"></span>
        /
      <span class="totalPages"></span>
    </p>
    `
  });

Hopefully, this one resolved someone's issue with the same problem. The footer was not shown before properly and finally done about a little bit search so comes with the above solution.

NOTE:

  1. Don't forget to mentioned margin-bottom: 60px(atleast).
  2. Same for displayHeaderFooter: true.
  3. Must needs to specify the font-size: 10px(atleast bcze default by font-size: 0).

For more info refer to this issue: https://github.com/puppeteer/puppeteer/issues/1822

Mohamed Jakkariya
  • 1,257
  • 1
  • 13
  • 16
0

if the above code didn't work for you. please try to use marginTop and marginBottom instead of margin object.

var buffer = await page.pdf({
         printBackground: true,         
         footerTemplate: "<div style='width: 200px; background-color: #4286f4; position: relative; position: absolute; top:0;'>Hej</div>",
         headerTemplate: "<div style='width: 200px; background-color: #4286f4; position: relative; position: absolute; bottom: 0;'>Footer</div>",
         //displayHeaderFooter: true, 
         marginTop:"100px",
         marginBottom: "100px"
    });

notes: it works for me in nodejs. I use jsreport with recipe "chrome-pdf". The chrome-pdf module using puppeteer.

0

You need to use:

-webkit-print-color-adjust: exact;

Here is an example of a function that can add a background:

const getHeaderFooter = () => {
   return `<html>
             <style>html {-webkit-print-color-adjust: exact;} </style>
             <span style="margin: -5em 0 0 0; height: 3mm; z-index: 1000; 
                    background: blue; width: 100%;"/>
          </html>`
}

And calling the function:

 headerTemplate: getHeaderFooter(),
ShadyAmoeba
  • 527
  • 1
  • 4
  • 15
0

2023 Implementation

  • Header: 2 columns
  • Footer: 1 column with centred text
// create a new page  
const page = await browser.newPage();

// set your html as the pages content
await page.setContent(html, {
    waitUntil: 'networkidle2' // waits until no more than 2 requests happen within 500ms
});
await page.evaluateHandle('document.fonts.ready');
await page.screenshot();

// create a PDF buffer
await page.pdf(
  {
    format: 'a4',
    displayHeaderFooter: true,
    headerTemplate: headerTemplate,
    footerTemplate: footerTemplate,
    margin: { top: 100, bottom: 60 }
  }
);
const headerTemplate = `<div style="display: inline-block; width: 75%; margin: 0 2cm">
                            <img style="height: 50px;" src="${LogoBase64}" />  
                            <div style="float: right; font-size: 7pt; text-align: right;">${AddressInfo}</div>
                        </div>`
const footerTemplate = `<div style="margin: 0 2cm; width: 75%; font-size: 7px; text-align: center;">${Disclaimer}</div>`;

FINDINGS

  • Only use inline styles
  • To get all elements to display on the same line use display: inline-block
  • Position using float: position
  • Apply font-size since the default is 0px
  • RECOMMENDED: minimum margin size is 60
  • RECOMMENDED: page format set to a4
  • Margin size determines the amount of displayable space for the header and footer
    • NOTE: This does not position the elements
  • NOTE: Margin is double the content size
    • image = 50px
    • margin = 100px
    • Not sure how this scales
  • Don't use async data
    • image is encoded as base54 instead of the CDN link
  • Use margin tag to apply additional margins for positioning