Search code examples
node.jsexpressexport-to-pdfnode-html-pdf

How to generate PDF from html with chart and google map in NodeJS


I want to export the HTML into PDF in Nodejs. My HTML file having a chart, graphs, images, and table data. I've created HTML with handlebar parsing as well as creating the one public URL for HTML of pdf. I've tried to export HTML to pdf using HTML-PDF NPM package but it can not load the charts, maps, etc just load simple HTML with table data. Here is an example of how I tried to export with an HTML page link:

const requestify = require('requestify');
const path = require("path");
const pdf = require('html-pdf');
const externalURL = `myurl.com/pdf-html`;
requestify.get(externalURL).then(function(response) {
    // Get the raw HTML response body
    const html = response.body;
    const config = { format: 'A4',"base": `file:///${path.resolve("./public")}`};
    // Create the PDF
    pdf.create(html, config).toFile('./public/generated.pdf', function(err, response) {
        if (err) {
            console.log("error");
            return console.log(err);
        }
        res.attachment('report.pdf');
        return res.status(200).send(response);
      });
 });

Here is my HTML file that I'm trying to export in PDF.

<html>

<head>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <style>
        #map-canvas {
            width: 500px;
            height: 350px;
        }
    </style>
</head>

<body>
    <h1>HTML to PDF</h1>
    <div id="chart"></div>
    <div id="map-canvas"></div>
    <table>
        <thead>
            <th>Site</th>
            <th>Site Name</th>
            <th>Location</th>
            <th>Status</th>
        </thead>
        <tbody>
            {{#if records}}
                {{#each records}}
                    <tr>
                        <td><img src="{{this.site}}" style="width: 25px;height: 35px;"></img></td>
                        <td>{{this.siteName}}</td>
                        <td>{{this.location}}</td>
                        <td>{{this.status}}</td>
                    </tr>
                {{/each}}
            {{/if}}
        </tbody>
    </table>
    <script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>        

<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?libraries=places,geometry"></script>

    <script>
        $(document).ready(function() {
            var options = {
                chart: { type: 'line' },
                series: [{
                    name: 'sales',
                    data: [30, 40, 35, 50, 49, 60, 70, 91, 125]
                }],
                xaxis: {
                    categories: [1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999]
                }
            }
            var chart = new ApexCharts(document.querySelector("#chart"), options);
            chart.render();

            function initialize() {
                var myLatLng = new google.maps.LatLng(45.4375, 12.3358),
                    myOptions = {
                        zoom: 5,
                        center: myLatLng,
                        mapTypeId: google.maps.MapTypeId.ROADMAP
                    },
                    map = new google.maps.Map(document.getElementById('map-canvas'), myOptions),
                    marker = new google.maps.Marker({
                        position: myLatLng,
                        map: map
                    });

                marker.setMap(map);
                moveMarker(map, marker);
            }

            function moveMarker(map, marker) {
                //delayed so you can see it move
                setTimeout(function() {
                    marker.setPosition(new google.maps.LatLng(45.4375, 12.3358));
                    map.panTo(new google.maps.LatLng(45.4375, 12.3358));
                }, 1500);
            };

            initialize();
        });
    </script>
</body>

</html>

Also, I'm getting the following error:

message=html-pdf: Received the exit code '1'
html-pdf: Evaluation - ReferenceError: Can't find variable: ApexCharts
Stack: at file:////path/to/project/public
, stack=Error: html-pdf: Received the exit code '1'
html-pdf: Evaluation - ReferenceError: Can't find variable: ApexCharts
Stack: at file:////path/to/project/public

at ChildProcess.respond (path/to/project/node_modules/html-pdf/lib/pdf.js:121:31)
at ChildProcess.emit (events.js:315:20)
at Process.ChildProcess._handle.onexit (internal/child_process.js:277:12)
enter code here

I've also tried with the puppeteer NPM package but the same issue with this package too unable to load the chart and maps. Can not get the completely loaded HTML in PDF.

So anyone has an idea that how to export HTML which has maps, charts, images, tables, etc. Your help is really appreciated.


Solution

  • I did it using a Puppeteer. I've created one route in NodeJS which renders the HTML about how I want in my PDF. This route is accessible without a login so it does not require login. In my HTML file I've added the dynamic charts, google maps, images, and table listing.

    I've created the PDF by writing the following in my export PDF route:

    const path = require("path");
    const puppeteer = require('puppeteer');
    
    // launch a new chrome instance
    const browser = await puppeteer.launch({
         headless: true
    });
    
    // create a new page
    const page = await browser.newPage()
    
    // Go to URL of HTML that I want to export in PDF
    await page.goto(`domain.com/my-pdf-html`, {
           waitUntil: "networkidle0"
    });
    
    // Create pdf file of opened page
    const pdf = await page.pdf({
        printBackground: true,
        format: 'A4',
        path: `${path.resolve("./public/my-report.pdf")}`
    });
    
    // close the browser    
    await browser.close();
    
    // Return generated pdf in response
    res.contentType("application/pdf");
    return res.status(200).send(pdf);
    

    I hope this will help someone too.