deepak sharma
deepak sharma

Reputation: 33

dynamically generate pdf with the html-pdf package

I am using the npm package html-pdf

I was trying to dynamically generate invoices. Eventually it creates PDF files but instead of saving values to the PDF it saves ejs code. Anyone knows what went wrong?

You can check the screenshot below of the PDF output with ejs variables:

https://i.sstatic.net/LPjN7.png

Now let me explain my node app:

I am using two ejs pages. One is home.ejs from where we post input values which need to be filled in the PDF. Second page is crm.ejs where my invoice template is. In crm.ejs I am using window.onload function so when everything is loaded to my crm.ejs template . Then it will generate PDF. But still it only save ejs variables.

Now here is the code

app.js

var express = require("express");
var app = express();
var webshot = require('webshot');
var fs = require('fs');
var pdf = require('html-pdf');
var html = fs.readFileSync('views/crm.ejs', 'utf8');
var options = { format: 'a4' };
var bodyParser = require("body-parser");
app.use(bodyParser.urlencoded({extended:true}));

app.locals.pdf = require("html-pdf");
app.locals.html = fs.readFileSync('views/crm.ejs', 'utf8');
app.locals.options =  { format: 'a4' };
app.locals.fs = require("fs");
app.use(express.static("public"));

app.get("/",function(req,res){
    res.render("home.ejs");
});

app.post("/crm",function(req,res){
    // res.render("crm.ejs");
    console.log(req.body);
    var details = req.body;
    res.render("crm.ejs",{details:details});
});

app.listen(process.env.PORT,process.env.IP,function(){
    console.log("Server started successfully!!");
}); 

home.ejs:

 <form action="/crm" method="post">
    <label>First Name</label>
    <input type="text" name="fname"><br />

    <label>Last Name</label>
    <input type="text" name="lname"><br />

    <label>Email</label>
    <input type="text" name="email"><br />

    <label>Phone number</label>
    <input type="text" name="phone"><br />

    <label>Address</label>
    <textarea name="address"></textarea><br />

    <button>Save</button>

</form>

crm.ejs:

<html>
<head>
<title>BillBook</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<style>
    #billbook{
       height:845px;
        width:595px;
        background-repeat: no-repeat;
        background-size: 595px 845px;
        border:1px solid red;
        /*margin:0px auto;*/
        margin:0px !important;
    }

    table{
       width: 92% !important;

            margin: 0px auto;
            color:#7f7f7f;
              border-radius: 0px 0px 6px 6px;
    -moz-border-radius: 6px 6px 6px 6px;
    -webkit-border-radius: 6px 6px 6px 6px;
  border-width: 0.5px !important;
  border-style: solid !important;
  border-color: #f3f3f3 !important;
    }
    tr{
        border:1px solid #f3f3f3;
    }
    td{
        border:1px solid #f3f3f3;
           padding: 5px 0px 5px 6px;
            font-size:12px;  
    }
</style>
</head>
<body bgcolor="#FFFFFF" leftmargin="0" topmargin="0" marginwidth="0" marginheight="0">
    <!--<img src="../images/final.jpg" width="595" height="842" alt="">-->  
    <section id="billbook">
        <table style=" margin-top: 175px !important;">
            <tr>
                <td width="59%" style="padding-left:7px;">Name :<span><%= details.fname + " " +details.lname; %></span></td>
                <td class="text-center">INVOICE</td>
            </tr>
            <tr>
                <td>Address :<%= details.address %></td>
                <td>GSTIN   :   <span  style="padding-left:40px;">547etbdjh787jfh</span></td>
            </tr>

            <tr>
                <td></td>
                <td>Invoice No :</td>
            </tr>

             <tr>
                <td>Phone Number :<%= details.phone%></td>
                <td>Invoice Date :</td>
            </tr>

        </table>

        <table style="margin-top:11px;">
            <tr class="text-center">
                <td width="6.5%">SNo</td>
                <td width="39.5%">Description</td>
                <td width="13%">HSN</td>
                <td width="9.5%">Qty</td>
                <td width="15.5%">Rate</td>
                <td width="16%">Amount</td>
            </tr>

            <tr style="height:242px">
                <td></td>
                <td></td>
                <td></td>
                <td></td>
                <td></td>
                <td></td>
            </tr>

            <tr>
                <td colspan="3"  rowspan="2">Rupees In Words : yrtyrtyrty ryrty rty rtyrtyrty ryrty</td>
                <td colspan="2" class="text-center">Taxable Value :</td>
                <td>&nbsp;</td>
            </tr>
            <tr>
                <td colspan="2" style="padding-left:30px;">SGST................%</td>
                <!--<td></td>-->
                <td>&nbsp;</td>
            </tr>

            <tr>
                <td colspan="3" rowspan="3">Terms and Conditions:</td>
                <td colspan="2" style="padding-left:30px;">SGST................%</td>
                <!--<td></td>-->
                <td>&nbsp;</td>
            </tr>

            <tr>
                <td colspan="2" style="padding-left:30px;">SGST................%</td>
                <!--<td></td>-->
                <td>&nbsp;</td>
            </tr>

            <tr>
                <td colspan="2" style="padding-left:30px;">Grand Total</td>
                <!--<td></td>-->
                <td>&nbsp;</td>
            </tr>
        </table>
    </section>     
</body>

<script>
     window.onload = function(){
        <%=
             pdf.create(html, options).toFile('public/pdf/businesscard.pdf', function(err, res) {
          if (err) {
               console.log(err);
          }
          else{console.log(res);}

        });         
        %>
    }
</script>
</html>

Upvotes: 3

Views: 3987

Answers (1)

mihai
mihai

Reputation: 38533

You need to do the rendering on the server, so remove the window.onload script and add this little piece of magic to your app.js

app.post("/crm",function(req,res){
  console.log(req.body);
  var details = req.body;

  res.render("crm.ejs", { details: details }, function (err, html) {
    pdf.create(html, options).toFile('./public/pdf/businesscard.pdf', function (err, res) {
      if (err) {
        console.log(err);
      }
      else { console.log(res); }
    });

    res.send(html);
  });
});

To understand the code, you need to think of your server response as the following sequence:

  • render the ejs to a html string
  • create a pdf from the html string and save it to the local drive
  • send the html string as a response to the browser

Calling res.render with a callback allows your view to be rendered to a string, without getting sent to the browser. This allows you to call pdf.create in the callback and then manually send the response with res.send

This is a screenshot of the business card I was able to create using your code (which is not bad by the way):

card

Upvotes: 3

Related Questions