Controlling margins when making a PDF using Playwright

2.4k Views Asked by At

When making a PDF from a headless chrome Playwright session (code below) I get the PDF on the left, whereas if I make it through the save to pdf in chrome, I get the second output.

enter image description here

I have set the margins explicitly and used preferCSSPageSize: true and both give the same (left) outcome.

How would I get Playwright to give me the same output as chrome does from the print dialog?

An example of the files being printed is here. (In real life, they're all slightly different to account for the spine width.)

const fs = require("fs");
const path = require("path");
const { chromium } = require("playwright");

const directoryPath = "outside";

(async () => {
  const filesToPrint = getFilesToPrintList(directoryPath);
  filesToPrint.forEach((f, i) => console.log(i, f));
  const browser = await chromium.launch({ headless: true });
  const context = await browser.newContext();

  for (let i = 0; i < filesToPrint.length; i++) {
    const htmlFilename = path.join(directoryPath, filesToPrint[i]);
    const pdfFilename = makePDFfilename(htmlFilename);
    console.log("[html file]", htmlFilename);
    console.log("[pdf file]", pdfFilename);
    const page = await context.newPage();
    await page.goto(
      "file:///" + path.resolve(htmlFilename),
      (waitUntil = "networkidle")
    );
    const options = {
      path: pdfFilename,
      pageRanges: "1",
      preferCSSPageSize: true,
    };
    console.log(
      `\n\nAbout to print:\n  ${pdfFilename}\n`,
      `with:\n${JSON.stringify(options, null, 2)}`
    );
    await page.pdf(options);
  }
  await browser.close();
  console.log("done");
})();

function makePDFfilename(htmlFilename) {
  const parts = htmlFilename.split(/[\\\._]/);
  const pdfFilename = path.join(
    directoryPath,
    `Experimental_${parts[1]}_${parts[2]}.pdf`
  );
  return pdfFilename;
}

function getFilesToPrintList(directoryPath) {
  let theFiles = fs
    .readdirSync(directoryPath)
    .filter((f) => f.includes("fp_") && f.includes(".html"));
  return theFiles;
}
1

There are 1 best solutions below

5
Azeem On

I tried to reproduce your issue:

I used a different background image URL (online) with your HTML page, see test.html gist.

Apparently, a number of options are the defaults. See page.pdf().

Also, added browser.newContext() and browser.close().

Here's the minimal working NodeJS code:

generate-pdf.js

const { chromium } = require("playwright");

(async () => {
    const htmlFilename = "file:///<file-path>/test.html";
    console.log("[webpage]", htmlFilename);

    const browser = await chromium.launch();
    const context = await browser.newContext();
    const page = await context.newPage();
    await page.goto(htmlFilename);

    const pdfFilename = "./test.pdf";
    console.log("[pdf file]", pdfFilename);

    const options = {
      path: pdfFilename,
      pageRanges: "1",
      preferCSSPageSize: true
    };

    console.log(
      `\n\nAbout to print:\n  ${pdfFilename}\n`,
      `with:\n${JSON.stringify(options, null, 2)}`
    );

    await page.pdf(options);
    await browser.close();

    console.log("done");
})();

Console Output:

$ node generate-pdf.js
[webpage] file:///<file-path>/test.html
[pdf file] ./test.pdf


About to print:
  ./test.pdf
 with:
{
  "path": "./test.pdf",
  "pageRanges": "1",
  "preferCSSPageSize": true
}
done

Snapshot of PDF (test.pdf):

test.pdf

You might want to test this separately and see if it works for your use-case.