Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Puppeteer does not load images stored locally when generate PDF

I'm trying to generate a PDF using Puppeteer and Handlebars inside a Electron app. But the images are not showing in the .PDF file, I tried to debug using headless mode, but did not find nothing usefull.

My folder structure is:

+-- pdf_template
|   +-- generatePDF.js
|   +-- template.html
|   +-- template.css
|   +-- logo.png
|   +-- bg.png

generatePDF.js :

try {
    (async () => {
        var dataBinding = {
            total: 123456,
        };

        var templateHtml = fs.readFileSync(
            path.join(
                process.cwd(),
                '/app/pages/pdf_template/template.html'
            ),
            'utf-8'
        );

        var template = handlebars.compile(templateHtml);
        var finalHtml = template(dataBinding);
        var options = {
            path: 'report.pdf',
            printBackground: true,
            format: 'A4',
        };

        const browser = await puppeteer.launch({
            args: ['--no-sandbox'],
            headless: true,
        });

        const page = await browser.newPage();
        await page.goto(
            `data:text/html;charset=UTF-8,${encodeURIComponent(finalHtml)}`,
            {
                waitUntil: ['domcontentloaded', 'networkidle0', 'load', 'networkidle2'],
            }
        );

        await page.addStyleTag({
            path: __dirname + '/node_modules/bootstrap/dist/css/bootstrap.min.css',
        });
        await page.addStyleTag({
            path: __dirname + '/app/pages/pdf_template/template.css',
        });

        await page.emulateMedia('screen');

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

        console.log('Done: PDF is created!');
    })();
} catch (err) {
    console.log('ERROR:', err);
}

template.html:

<!DOCTYPE html>
<html>
    <head>
        <mate charest="utf-8" />
        <title>Report</title>

    </head>
    <body>
        <div class="row" id="header">
            <div class="col" id="logo-box">
                <img
                    src="logo.png"
                    id="logo"
                />
            </div>
            <div class="col-8">
                ...
        </div>

        ...
    </body>
</html>

template.css

body {
    background-image: url('bg.png');
    background-size: cover;
    width: 21cm;
    height: 29.7cm;
    padding: 15mm;
    margin: 0;
}

Both images from HTML and CSS are not showing. Am I missing something?

like image 719
Gabriel Henrique Avatar asked Dec 17 '25 04:12

Gabriel Henrique


2 Answers

An answer can be found here : How to point Puppeteer to local images/fonts?

It seems as the generated page URL is about:blank chromium does not allow local resources to be loaded for safety reasons. I managed to bypass this problem by including the binaries of the images in my HTML template as :

//In CSS
background-image: url("data:image/png;base64,BINARY_CHUNKS");
//In HTML
<img src="data:image/png;base64,BINARY_CHUNKS" class="logo">

As for getting a local file in binary I would suggest fs:

fs.readFileSync(`${process.cwd()}\\your_image_path.png`).toString('base64')

Hope someone found that usefull !

like image 105
Chalibou Avatar answered Dec 19 '25 19:12

Chalibou


I found @Chalibou 's solution very helpful and found it to be working well for me: If I embed the entire image base64 encoded into the html source, Puppeteer can easily generate PDFs including images, even if the source is a local html file.

However, I intend to create PDF files with a number of pictures; I like being able to preview the html page in the browser during development, and want it to look the same as the pdf generated from it, without having to manually convert and replace image paths to/with base64 image content.

Therefore, I amended my nodejs puppeteer pdf generation script to automatically replace all as src="<image-path>.<extension>" to src="data:<MIME type>;base64,<base64 encoded image>.

I share this code here, just in case it is helpful to anyone.

const fs = require('fs')
const puppeteer = require('puppeteer')
const cheerio = require('cheerio')

const filetypemap = {
    "png": "image/png",
    "jpg": "image/jpg",
    "svg": "image/svg+xml"
}


function generatePdf() {
    const $ = cheerio.load(fs.readFileSync('data/test.html'))
    $("img").each(function () {
        var original_src = $(this).attr("src")
        var file_extension = original_src.split('.').slice(-1)[0] // extract file extension
        if (!filetypemap[file_extension]) {
            console.log("There is no mapping for file extension '" + file_extension + "'.")
            return
        }
        var local_filename = original_src // example for local path equalling src path
        // var local_filename = original_src.substring(3) // example for removing "../" from the beginning of the path
        if (!fs.existsSync(local_filename)) {
            console.log("File does not exist: " + local_filename)
            return
        }
        var local_src = "data:" + filetypemap[file_extension] + ";base64," + fs.readFileSync(local_filename).toString('base64')
        $(this).attr("src", local_src)
    });

        (async () => {
            const browser = await puppeteer.launch()
            const page = await browser.newPage()
            await page.emulateMediaType('print')
            await page.setContent($.html())
            await page.pdf({ path: 'example.pdf', format: 'A4' })
            await browser.close()
            console.log('PDF generated as example.pdf.')
        })()
}
like image 38
hey Avatar answered Dec 19 '25 18:12

hey