Creating PDFs using Puppeteer in the presence of `link` tags

On my website, students have the ability to create their own notes using a RichText editor where the content can be quite complex. I want to provide them with an option to download these notes as a PDF file. To achieve this, I am planning to use the "puppeteer" tool. When I tested it by navigating to a specific page on my site, the outcome looked promising. However, for the note-printing functionality, I don't want to navigate to a separate page; instead, I just want to pass an HTML snippet of a div that contains the notes. The issue I'm facing is that when I do this, the styles are not preserved and the final output appears very poorly formatted. Since I am using NextJS with Tailwind CSS, with many styles being added through link tags, I suspect that puppeteer may not recognize them resulting in the style discrepancies. What could be a potential solution to ensure that the styles are applied correctly? Here is my current implementation:



  async generatePdfFromHtml(user: UserEntity, data: InputGetPdfDto) {
    const browser = await puppeteer.launch({ executablePath: puppeteer.executablePath('chrome') });
    const page = await browser.newPage();
    const html = data.html;
    await page.setContent(html, { waitUntil: 'networkidle2' });

    console.log(html);

    const fileName = `element-${Date.now()}.pdf`;
    const filePath = path.join('output', fileName);

    await page.pdf({
      path: filePath,
      format: 'A4',
      printBackground: true,
      timeout: 0,
    });

    await browser.close();

    return filePath;
  }

MORE DETAILS

My website structure resembles the following:

<html lang="en" class="dark" style="color-scheme: dark;">
   <head>
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <link rel="stylesheet" href="/_next/static/css/app/layout.css?v=1717793996045" data-precedence="next_static/css/app/layout.css">

// content 

</html>

I aim to include a button labeled "Download as PDF", which will convert a div with the ID notes into a PDF file. My goal is to retain all the original styles used on the website that are linked via link tags. Currently, the html being used consists simply of the outerHTML of the div#notes. Naturally, when I utilize page.setContent(html), the result appears messy due to the absence of styles. How can I rectify this issue?

Answer №1

Without knowing all the specifics of your situation, it's worth mentioning that when you use setContent in JavaScript, it causes the browser to interpret an HTML string. This string may contain requests for external assets, which the browser will try to fetch. However, trying to access these assets directly from a local file won't work; they need to be served by a server.

To illustrate this issue and offer a solution, consider the following example:

make-pdf.js:

const puppeteer = require("puppeteer"); // ^22.10.0

let browser;
(async () => {
  browser = await puppeteer.launch();
  const [page] = await browser.pages();
  const html = await fs.readFile("test.html", "utf-8");
  await page.setContent(html, {waitUntil: "networkidle2"});
  await page.pdf({path: "test.pdf"});
})()
  .catch(err => console.error(err))
  .finally(() => browser?.close());

test.html:

<!DOCTYPE HTML>
<html>
<head>
<link rel="stylesheet" href="test.css"></link>
</head>
<body>
<h1>Hello World</h1>
</body></html>

test.css:

body {
  color: red;
}

If you run node make-pdf and open the resulting test.pdf, you'll notice that the red text is missing because the CSS asset failed to load. This behavior is expected as accessing local files without a proper server setup is restricted. Checking the network log in the browser's developer tools while opening the test.html file will confirm this loading error.

To resolve this issue, you can set up a web server in your current working directory (such as using python http.server) and update the asset link to point to the hosted version:

<link rel="stylesheet" href="http://localhost:8000/test.css"></link>

Rerun the node make-pdf command, and now you should see the PDF displaying with the intended red background.

In a production environment, connect to your static file server (typically same-origin) without requiring a prefix. Adjust your Puppeteer code to insert a flag into the HTML or perform string replacement based on the environment to determine the appropriate URL.

...

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Issues with the disappearance of elements when using the Toggle() function

I've created a toggle feature for displaying a sub menu (.li-one) when clicking on the main menu (.ul-one). The issue arises when you click on another menu item, as the previous one does not disappear unless you click outside of the .main-nav div. Is ...

Spinning an SVG circle using a group element

I'm interested in rotating an SVG circle without affecting the rotation of other elements. https://i.sstatic.net/Be501.png My attempt to rotate the white circle using rotateZ(15deg) resulted in this: https://i.sstatic.net/GMp96.png This is the prog ...

Enhance Your Images with Hovering Icons

Is there a way to display an icon checkmark over the main image when a user hovers over it? The icon should appear in the top right corner of the image. <div> <img class="result-image" src="{{$property->photo}}"> <! ...

The 'next-session' function is not functioning properly when used in conjunction with 'connect-pg-simple'

The explanation and demonstration of 'next-session' integration with Postgres lacked depth and detail. Trying to follow the compatibility example on npm proved unsuccessful. const session = require("express-session"); const RedisStore ...

I'm having trouble getting my Retina image code to work perfectly. The high resolution image isn't loading at its full

After learning how to display my Retina optimized logo from this tutorial (link), I encountered an issue when testing on my iPad and Samsung Galaxy Note II (using Safari, Firefox, Dolphin apps). The Retina image is showing up at its full size of 1200px x 5 ...

Using JavaScript in PHP files to create a box shadow effect while scrolling may not produce the desired result

Issue at hand : My JavaScript is not functioning properly in my .php files CSS not applying while scrolling *CSS Files are named "var.css" #kepala { padding: 10px; top: 0px; left: 0px; right: 0px; position: fixed; background - c ...

What is the best way to display a responsive website design exclusively for certain devices?

My website currently lacks responsiveness. Typically, we can check if a website is responsive by resizing the browser to see if it adjusts using CSS3 media queries. However, I am looking to create a better user experience by ensuring that visitors using a ...

Contrasts in Bootstrap 5.3 color schemes between development and live environments

Currently in the process of building a website using Astro [^2.2.1], Bootstrap [5.3.0-alpha3], and Svelte [^3.58.0]. Most components are functioning correctly, except for some elements like buttons where the foreground and background colors seem off. I&apo ...

issue with next auth not properly transferring all user data to the client

I'm currently attempting to assign a role for the user during the session Here is the output I receive from session.user on the client side : { "email": "test value" } My goal is to obtain : { "email": "test value", "role": "user" } Strangely, ...

Tips for customizing the cursor to avoid it reverting to the conventional scroll cursor when using the middle click

When you wish to navigate by scrolling with the middle mouse button instead of using the scroll wheel, simply middle-click and your cursor will change allowing for easy scrolling on web pages. Even though I am utilizing style="cursor: pointer" and @click. ...

When the web browser page is zoomed out (Ctrl -), elements do not appear to float

Can you test resizing the window to the minimum (CTRL-)? Here's the link: http://jsfiddle.net/Zty9k/13/ This is the HTML code: <ul> <li><img src="http://i.imgur.com/PBuDAFH.gif"></li> <li><img src="http://i.i ...

Google Map with rounded corners that have a transparent design

Trying to create a circular Google map using CSS3 border-radius, but having issues with browsers other than Firefox. Here's the code snippet: <div class="map mapCircle" style="position: relative; background-color: transparent; overflow: hidden;"&g ...

Make the inner div within the td stretch to cover the entire height of the td

Inside the first tds, there is dynamic text content that automatically expands the trs and tds. However, the second tds have a div with a background image but no text inside. I want these divs with background images to expand or collapse along with the tab ...

Should I use a single CSS file or separate CSS files for each page?

As I construct my website pages, I often ponder whether it's best to utilize separate stylesheets for each page or to have one comprehensive stylesheet for the entire site. In terms of loading efficiency, wouldn't having individual files be more ...

What is the best way to make a flex box smoothly appear on the

Is there a way to make a flex box hidden until it fades in without losing its flexibility? I used to use 'display: 0;' and then apply jQuery .fadeIn(). However, setting display to 0 causes the flex properties of the box to disappear when fading i ...

Determine the height of a Bootstrap 4 row based on a specific column, rather than the tallest

I am facing an issue with a row that contains two columns. The first column has content that should be visible without overflowing, while the second column includes a long list that needs to be scrollable within the row. The challenge with responsive desi ...

The size of table cells automatically adjusts when zooming in the browser

While trying to display my table in Chrome, I noticed that the cell sizes of the table change when zooming in. Additionally, the table is rendered incorrectly in other browsers like IE and Firefox. I attempted adjusting the sizes to percentage values but i ...

Implement the anti-flickering script for Google Optimize in a NextJS/ReactJS environment

While working on a NextJS/ReactJS project, I am experimenting with setting up Google Optimize for some tests. One issue I have encountered is the flickering effect that occurs when Optimize changes visual elements during experiments. To address this probl ...

reason behind text styling: thick 12 pixels height with line spacing of 25 pixels using "Arial" font

Question about CSS Size: Understanding font sizes in CSS {font: 14px/24px Arial, Helvetica, sans-serif} I've noticed some CSS code like this on the website .selector {font: bold 12px/25px "Arial";} I know that 'bold' refers to font w ...

Incorporate new functions through the css class name "javafx"

On my dashboard, I have 10 labels that share the same CSS class for mouse over events. Now, I want to change the button style after it is pressed and remove the mouse over effect. How can I achieve this? ...