How To Convert HTML To PDF With CSS And SVG(Chart) Using jQuery

Introduction

 
We will be using jspdf.debug.js, html2canvas.min.js, and canvg.min.js in this article to build a perfect PDF of my HTML page with CSS and SVG, as we see in the charts that give an SVG object.
 
Step 1 - Create a HTML page
 
Create a new HTML page. Define the HTML body to contain two division tags, displaying different information on the screen.
  1. <html>    
  2. <head>    
  3. <style>    
  4. #customers {    
  5.   font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;    
  6.   border-collapse: collapse;    
  7.   width: 100%;    
  8. }    
  9.    
  10. #customers td, #customers th {    
  11.   border: 1px solid #ddd;    
  12.   padding: 8px;    
  13. }    
  14.    
  15. #customers tr:nth-child(even){background-color: #f2f2f2;}    
  16.    
  17. #customers tr:hover {background-color: #ddd;}    
  18.    
  19. #customers th {    
  20.   padding-top: 12px;    
  21.   padding-bottom: 12px;    
  22.   text-align: left;    
  23.   background-color: #4CAF50;    
  24.   color: white;    
  25. }    
  26. </style>    
  27. </head>    
  28.     
  29. <body>    
  30.   <input type="button" id="btnDownload" value="download" /> Please click on this button to get chart with html content on pdf    
  31.   <div class="white-back row" style="padding: 1.5rem;" id="tvgMainCnt">    
  32.     <div id="top-content">    
  33.       <div>    
  34.         <div>    
  35.           <img src="https://miro.medium.com/max/1198/1*n-Q2XBz6CFC1MgMpdab4xQ.jpeg" style="height: 100px;" />    
  36.         </div>    
  37.         </div>    
  38.       <div style="margin-top: 1rem;">    
  39.         <p style="color:green">    
  40.           Dear members,    
  41.         </p>    
  42.       </div>    
  43.       <div>    
  44.         Added some fake text: Please ignore this is test text, Please ignore this is test text, Please ignore this is test text.    
  45.       </div>    
  46.       <br />    
  47.        <table id="customers" class="table table-striped" width="100%">    
  48.            
  49.         <thead width="100%" width="100%">    
  50.             <tr class='warning' >    
  51.            <th>No.</th>    
  52.                 <th>Country</th>    
  53.                 <th>Population</th>    
  54.                 <th>Date</th>    
  55.                 <th>Age</th>    
  56.             </tr>    
  57.         </thead>    
  58.         <tbody width="100%">    
  59.             <tr>    
  60.                 <td>1</td>    
  61.                 <td>Chinna</td>    
  62.                 <td>1,363,480,000 000 000</td>    
  63.                 <td>March 24, 2014</td>    
  64.                 <td>19.1</td>    
  65.             </tr>    
  66.             <tr>    
  67.                 <td>2</td>    
  68.                 <td>India</td>    
  69.                 <td>1,241,900,000</td>    
  70.                 <td>March 24, 2014</td>    
  71.                 <td>17.4</td>    
  72.             </tr>    
  73.             <tr>    
  74.                 <td>3</td>    
  75.                 <td>United States</td>    
  76.                 <td>317,746,000</td>    
  77.                 <td>March 24, 2014</td>    
  78.                 <td>4.44</td>    
  79.             </tr>    
  80.             <tr>    
  81.                 <td>4</td>    
  82.                 <td>Indonesia</td>    
  83.                 <td>249,866,000</td>    
  84.                 <td>July 1, 2013</td>    
  85.                 <td>3.49</td>    
  86.             </tr>    
  87.             <tr>    
  88.                 <td>5</td>    
  89.                 <td>Brazil</td>    
  90.                 <td>201,032,714</td>    
  91.                 <td>July 1, 2013</td>    
  92.                 <td>2.81</td>    
  93.             </tr>    
  94.         </tbody>    
  95.     </table>    
  96.       <div style="margin-top: 1rem;">    
  97.         <p>    
  98.           Download of chart with html content should work on all browser but not working in Microsoft edge.    
  99.         </p>    
  100.       </div>    
  101.     </div>    
  102.     
  103.     <div id="testchart"></div>    
  104.      </div>    
  105.     <div id="bottom-content">    
  106.       <p>Thanks for looking into this issue, struggling to fix this for last 2 days.    
  107.       UPDATE: Finally able to fix it. Cheers!!</p>    
  108.       <p>this demo develop by Bhavdip Talaviya</p>    
  109.     </div>    
  110.   </div>    
  111. </body>    
  112. </html>    

Step 2 - Copy all CDN links into your HTML page

Now, you need to copy all the CDN (JS) links into your HTML page. Here, in the sample code, we have used the CDN URLs.
  1. <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>    
  2.     
  3. <!-- Required to convert named colors to RGB -->    
  4. <script src="https://cdnjs.cloudflare.com/ajax/libs/canvg/1.4/rgbcolor.min.js"></script>    
  5.     
  6. <!-- Optional if you want blur -->    
  7. <script src="https://cdn.jsdelivr.net/npm/stackblur-canvas@^1/dist/stackblur.min.js"></script>    
  8.     
  9. <script src="https://cdn.jsdelivr.net/npm/canvg/dist/browser/canvg.min.js"></script>    
  10. <script src="https://cdnjs.cloudflare.com/ajax/libs/highcharts/5.0.7/highcharts.js"></script>    
  11. <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.2.61/jspdf.debug.js"></script>    
  12. <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/0.4.1/html2canvas.min.js"></script>    
Paste the above code below the closing of body tag (</body>).
 
Step 3 - Write jQuery to generate the PDF file
 
Now you need to create a jQuery logic to generate the PDF file so you can add logic for the Download button and make jQuery in this same page in the script tag.
  1. <script type="text/javascript">    
  2. $(document).ready(function() {    
  3.     Highcharts.chart('testchart', {    
  4.     
  5.         title: {    
  6.             text: 'Solar Employment Growth by Sector, 2010-2016'    
  7.         },    
  8.     
  9.         subtitle: {    
  10.             text: 'Source: thesolarfoundation.com'    
  11.         },    
  12.     
  13.         yAxis: {    
  14.             title: {    
  15.                 text: 'Number of Employees'    
  16.             }    
  17.         },    
  18.         legend: {    
  19.             layout: 'vertical',    
  20.             align: 'right',    
  21.             verticalAlign: 'middle'    
  22.         },    
  23.     
  24.         plotOptions: {    
  25.             series: {    
  26.                 label: {    
  27.                     connectorAllowed: false    
  28.                 },    
  29.                 pointStart: 2010    
  30.             }    
  31.         },    
  32.     
  33.         series: [{    
  34.             name: 'Installation',    
  35.             data: [43934, 52503, 57177, 69658, 97031, 119931, 137133, 154175]    
  36.         }, {    
  37.             name: 'Manufacturing',    
  38.             data: [24916, 24064, 29742, 29851, 32490, 30282, 38121, 40434]    
  39.         }, {    
  40.             name: 'Sales & Distribution',    
  41.             data: [11744, 17722, 16005, 19771, 20185, 24377, 32147, 39387]    
  42.         }, {    
  43.             name: 'Project Development',    
  44.             data: [nullnull, 7988, 12169, 15112, 22452, 34400, 34227]    
  45.         }, {    
  46.             name: 'Other',    
  47.             data: [12908, 5948, 8105, 11248, 8989, 11816, 18274, 18111]    
  48.         }],    
  49.     
  50.         responsive: {    
  51.             rules: [{    
  52.                 condition: {    
  53.                     maxWidth: 500    
  54.                 },    
  55.                 chartOptions: {    
  56.                     legend: {    
  57.                         layout: 'horizontal',    
  58.                         align: 'center',    
  59.                         verticalAlign: 'bottom'    
  60.                     }    
  61.                 }    
  62.             }]    
  63.         }    
  64.     
  65.     });    
  66. });    
  67. $("#btnDownload").click(function(){    
  68.   var doc = new jsPDF('portrait''pt''a4'true);    
  69.     var elementHandler = {    
  70.         '#ignorePDF'function(element, renderer) {    
  71.             return true;    
  72.         }    
  73.     };    
  74.     
  75.     var source = document.getElementById("top-content");    
  76.     doc.fromHTML(source, 15, 15, {    
  77.         'width': 560,    
  78.         'elementHandlers': elementHandler    
  79.     });    
  80.     
  81.     var svg = document.querySelector('svg');    
  82.     var canvas = document.createElement('canvas');    
  83.     var canvasIE = document.createElement('canvas');    
  84.     var context = canvas.getContext('2d');    
  85.     
  86.     
  87.     
  88.     
  89.     var data = (new XMLSerializer()).serializeToString(svg);    
  90.     canvg(canvas, data);    
  91.     var svgBlob = new Blob([data], {    
  92.         type: 'image/svg+xml;charset=utf-8'    
  93.     });    
  94.     
  95.     var url = canvas.toDataURL(svgBlob);//DOMURL.createObjectURL(svgBlob);    
  96.     
  97.     var img = new Image();    
  98.     img.onload = function() {    
  99.         context.canvas.width = $('#testchart').find('svg').width();;    
  100.         context.canvas.height = $('#testchart').find('svg').height();;    
  101.         context.drawImage(img, 0, 0);    
  102.         // freeing up the memory as image is drawn to canvas    
  103.         //DOMURL.revokeObjectURL(url);    
  104.     
  105.         var dataUrl;    
  106.         if (isIEBrowser()) { // Check of IE browser     
  107.             var svg = $('#testchart').highcharts().container.innerHTML;    
  108.             canvg(canvasIE, svg);    
  109.             dataUrl = canvasIE.toDataURL('image/JPEG');    
  110.         } else {    
  111.             dataUrl = canvas.toDataURL('image/jpeg');    
  112.         }    
  113.         doc.addImage(dataUrl, 'JPEG', 20, 365, 560, 350); // 365 is top     
  114.     
  115.         var bottomContent = document.getElementById("bottom-content");    
  116.         doc.fromHTML(bottomContent, 15, 750, {   //700 is bottom content top  if you increate this then you should increase above 365    
  117.             'width': 560,    
  118.             'elementHandlers': elementHandler    
  119.         });    
  120.     
  121.         setTimeout(function() {    
  122.             doc.save('HTML-To-PDF-Dvlpby-Bhavdip.pdf');    
  123.         }, 2000);    
  124.     };    
  125.     img.src = url;    
  126. });    
  127. function isIEBrowser() {    
  128.     var ieBrowser;    
  129.     var ua = window.navigator.userAgent;    
  130.     var msie = ua.indexOf("MSIE ");    
  131.     
  132.     if (msie > 0 || !!navigator.userAgent.match(/Trident.*rv\:11\./)) // Internet Explorer    
  133.     {    
  134.         ieBrowser = true;    
  135.     } else //Other browser    
  136.     {    
  137.         console.log('Other Browser');    
  138.         ieBrowser = false;    
  139.     }    
  140.     
  141.     return ieBrowser;    
  142. };    
  143. </script>   
You can set the height and width based on your HTML page, so now I put height and width based on my HTML page.
 
Full page code is mentioned here.
  1. <html>    
  2. <head>    
  3. <style>    
  4. #customers {    
  5.   font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;    
  6.   border-collapse: collapse;    
  7.   width: 100%;    
  8. }    
  9.    
  10. #customers td, #customers th {    
  11.   border: 1px solid #ddd;    
  12.   padding: 8px;    
  13. }    
  14.    
  15. #customers tr:nth-child(even){background-color: #f2f2f2;}    
  16.    
  17. #customers tr:hover {background-color: #ddd;}    
  18.    
  19. #customers th {    
  20.   padding-top: 12px;    
  21.   padding-bottom: 12px;    
  22.   text-align: left;    
  23.   background-color: #4CAF50;    
  24.   color: white;    
  25. }    
  26. </style>    
  27. </head>    
  28.     
  29. <body>    
  30.   <input type="button" id="btnDownload" value="download" /> Please click on this button to get chart with html content on pdf    
  31.   <div class="white-back row" style="padding: 1.5rem;" id="tvgMainCnt">    
  32.     <div id="top-content">    
  33.       <div>    
  34.         <div>    
  35.           <img src="https://miro.medium.com/max/1198/1*n-Q2XBz6CFC1MgMpdab4xQ.jpeg" style="height: 100px;" />    
  36.         </div>    
  37.         </div>    
  38.       <div style="margin-top: 1rem;">    
  39.         <p style="color:green">    
  40.           Dear members,    
  41.         </p>    
  42.       </div>    
  43.       <div>    
  44.         Added some fake text: Please ignore this is test text, Please ignore this is test text, Please ignore this is test text.    
  45.       </div>    
  46.       <br />    
  47.        <table id="customers" class="table table-striped" width="100%">    
  48.            
  49.         <thead width="100%" width="100%">    
  50.             <tr class='warning' >    
  51.                  <th>No.</th>    
  52.                 <th>Country</th>    
  53.                 <th>Population</th>    
  54.                 <th>Date</th>    
  55.                 <th>Age</th>    
  56.             </tr>    
  57.         </thead>    
  58.         <tbody width="100%">    
  59.             <tr>    
  60.                 <td>1</td>    
  61.                 <td>Chinna</td>    
  62.                 <td>1,363,480,000 000 000</td>    
  63.                 <td>March 24, 2014</td>    
  64.                 <td>19.1</td>    
  65.             </tr>    
  66.             <tr>    
  67.                 <td>2</td>    
  68.                 <td>India</td>    
  69.                 <td>1,241,900,000</td>    
  70.                 <td>March 24, 2014</td>    
  71.                 <td>17.4</td>    
  72.             </tr>    
  73.             <tr>    
  74.                 <td>3</td>    
  75.                 <td>United States</td>    
  76.                 <td>317,746,000</td>    
  77.                 <td>March 24, 2014</td>    
  78.                 <td>4.44</td>    
  79.             </tr>    
  80.             <tr>    
  81.                 <td>4</td>    
  82.                 <td>Indonesia</td>    
  83.                 <td>249,866,000</td>    
  84.                 <td>July 1, 2013</td>    
  85.                 <td>3.49</td>    
  86.             </tr>    
  87.             <tr>    
  88.                 <td>5</td>    
  89.                 <td>Brazil</td>    
  90.                 <td>201,032,714</td>    
  91.                 <td>July 1, 2013</td>    
  92.                 <td>2.81</td>    
  93.             </tr>    
  94.         </tbody>    
  95.     </table>    
  96.       <div style="margin-top: 1rem;">    
  97.         <p>    
  98.           Download of chart with html content should work on all browser but not working in Microsoft edge.    
  99.         </p>    
  100.       </div>    
  101.     </div>    
  102.     
  103.     <div id="testchart"></div>    
  104.      </div>    
  105.     <div id="bottom-content">    
  106.       <p>Thanks for looking into this issue, struggling to fix this for last 2 days..    
  107.       UPDATE: Finally able to fix it. Cheers!!</p>    
  108.     </div>    
  109.   </div>    
  110. </body>    
  111. <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>    
  112.     
  113. <!-- Required to convert named colors to RGB -->    
  114. <script src="https://cdnjs.cloudflare.com/ajax/libs/canvg/1.4/rgbcolor.min.js"></script>    
  115.     
  116. <!-- Optional if you want blur -->    
  117. <script src="https://cdn.jsdelivr.net/npm/stackblur-canvas@^1/dist/stackblur.min.js"></script>    
  118.     
  119. <script src="https://cdn.jsdelivr.net/npm/canvg/dist/browser/canvg.min.js"></script>    
  120. <script src="https://cdnjs.cloudflare.com/ajax/libs/highcharts/5.0.7/highcharts.js"></script>    
  121. <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.2.61/jspdf.debug.js"></script>    
  122. <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/0.4.1/html2canvas.min.js"></script>    
  123.     
  124. <script type="text/javascript">    
  125. $(document).ready(function() {    
  126.     Highcharts.chart('testchart', {    
  127.     
  128.         title: {    
  129.             text: 'Solar Employment Growth by Sector, 2010-2016'    
  130.         },    
  131.     
  132.         subtitle: {    
  133.             text: 'Source: thesolarfoundation.com'    
  134.         },    
  135.     
  136.         yAxis: {    
  137.             title: {    
  138.                 text: 'Number of Employees'    
  139.             }    
  140.         },    
  141.         legend: {    
  142.             layout: 'vertical',    
  143.             align: 'right',    
  144.             verticalAlign: 'middle'    
  145.         },    
  146.     
  147.         plotOptions: {    
  148.             series: {    
  149.                 label: {    
  150.                     connectorAllowed: false    
  151.                 },    
  152.                 pointStart: 2010    
  153.             }    
  154.         },    
  155.     
  156.         series: [{    
  157.             name: 'Installation',    
  158.             data: [43934, 52503, 57177, 69658, 97031, 119931, 137133, 154175]    
  159.         }, {    
  160.             name: 'Manufacturing',    
  161.             data: [24916, 24064, 29742, 29851, 32490, 30282, 38121, 40434]    
  162.         }, {    
  163.             name: 'Sales & Distribution',    
  164.             data: [11744, 17722, 16005, 19771, 20185, 24377, 32147, 39387]    
  165.         }, {    
  166.             name: 'Project Development',    
  167.             data: [nullnull, 7988, 12169, 15112, 22452, 34400, 34227]    
  168.         }, {    
  169.             name: 'Other',    
  170.             data: [12908, 5948, 8105, 11248, 8989, 11816, 18274, 18111]    
  171.         }],    
  172.     
  173.         responsive: {    
  174.             rules: [{    
  175.                 condition: {    
  176.                     maxWidth: 500    
  177.                 },    
  178.                 chartOptions: {    
  179.                     legend: {    
  180.                         layout: 'horizontal',    
  181.                         align: 'center',    
  182.                         verticalAlign: 'bottom'    
  183.                     }    
  184.                 }    
  185.             }]    
  186.         }    
  187.     
  188.     });    
  189. });    
  190. $("#btnDownload").click(function(){    
  191.   var doc = new jsPDF('portrait''pt''a4'true);    
  192.     var elementHandler = {    
  193.         '#ignorePDF'function(element, renderer) {    
  194.             return true;    
  195.         }    
  196.     };    
  197.     
  198.     var source = document.getElementById("top-content");    
  199.     doc.fromHTML(source, 15, 15, {    
  200.         'width': 560,    
  201.         'elementHandlers': elementHandler    
  202.     });    
  203.     
  204.     var svg = document.querySelector('svg');    
  205.     var canvas = document.createElement('canvas');    
  206.     var canvasIE = document.createElement('canvas');    
  207.     var context = canvas.getContext('2d');    
  208.     
  209.     
  210.     
  211.     
  212.     var data = (new XMLSerializer()).serializeToString(svg);    
  213.     canvg(canvas, data);    
  214.     var svgBlob = new Blob([data], {    
  215.         type: 'image/svg+xml;charset=utf-8'    
  216.     });    
  217.     
  218.     var url = canvas.toDataURL(svgBlob);//DOMURL.createObjectURL(svgBlob);    
  219.     
  220.     var img = new Image();    
  221.     img.onload = function() {    
  222.         context.canvas.width = $('#testchart').find('svg').width();;    
  223.         context.canvas.height = $('#testchart').find('svg').height();;    
  224.         context.drawImage(img, 0, 0);    
  225.         // freeing up the memory as image is drawn to canvas    
  226.         //DOMURL.revokeObjectURL(url);    
  227.     
  228.         var dataUrl;    
  229.         if (isIEBrowser()) { // Check of IE browser     
  230.             var svg = $('#testchart').highcharts().container.innerHTML;    
  231.             canvg(canvasIE, svg);    
  232.             dataUrl = canvasIE.toDataURL('image/JPEG');    
  233.         } else {    
  234.             dataUrl = canvas.toDataURL('image/jpeg');    
  235.         }    
  236.         doc.addImage(dataUrl, 'JPEG', 20, 365, 560, 350); // 365 is top     
  237.     
  238.         var bottomContent = document.getElementById("bottom-content");    
  239.         doc.fromHTML(bottomContent, 15, 750, {   //700 is bottom content top  if you increate this then you should increase above 365    
  240.             'width': 560,    
  241.             'elementHandlers': elementHandler    
  242.         });    
  243.     
  244.         setTimeout(function() {    
  245.             doc.save('HTML-To-PDF-Dvlpby-Bhavdip.pdf');    
  246.         }, 2000);    
  247.     };    
  248.     img.src = url;    
  249. });    
  250. function isIEBrowser() {    
  251.     var ieBrowser;    
  252.     var ua = window.navigator.userAgent;    
  253.     var msie = ua.indexOf("MSIE ");    
  254.     
  255.     if (msie > 0 || !!navigator.userAgent.match(/Trident.*rv\:11\./)) // Internet Explorer    
  256.     {    
  257.         ieBrowser = true;    
  258.     } else //Other browser    
  259.     {    
  260.         console.log('Other Browser');    
  261.         ieBrowser = false;    
  262.     }    
  263.     
  264.     return ieBrowser;    
  265. };    
  266. </script>    
  267. </html>   
Please see below a live example of the above code.