Converting a Visualforce Page into a PDF

July 20, 2017

Businesses often need webpage data to be converted into a PDF so they can see a snapshot of business critical pages. If you need to display a Visualforce page as a PDF, all you have to do is set “Render As” equal to PDF, and voila, it works!

If you want to send a Visualforce page as a PDF attachment, you can achieve this with four to five lines of code in the controller. This is because anything rendered through JavaScript won’t display on the PDF page.

In reality, the Visualforce page to PDF conversion happens on the server side, and the stuff that's supposed to render on the client side will not appear in the converted PDF.

If you want to only convert a chart, there's a great solution: Google Chart. But keep in mind that these charts are deprecated ones. The latest Google Chart will be rendered on the client side.

A no-excuse solution helps to resolve not only this PDF rendering issue but also lots of print-related issues too. What happens if we take a screenshot of the page and render that screenshot image as a PDF? The PDF will have the same look as what the end user will see.

There are numerous available JavaScript libraries that can convert a page into an image, and an image into a PDF. Once you have access to these libraries, your solution implementation can vary depending on your exact requirements.

I have put a sample working process here as an example — you can enhance or modify it based on your requirements:

Visualforce Page:

<apex:page docType="html-5.0" controller="SEM_BlogController" showHeader="false" sidebar="false" standardStylesheets="false">

   <head>

       <title>Demo</title>

       <!-- Libraries to take screenshot and image to pdf conversion-->

       <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>

       <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.3.2/jspdf.min.js"></script>

       <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/0.4.1/html2canvas.min.js"></script>

       <script src="https://cdnjs.cloudflare.com/ajax/libs/amcharts/3.13.0/exporting/rgbcolor.js"></script>

       <script src="https://cdnjs.cloudflare.com/ajax/libs/amcharts/3.13.0/exporting/canvg.js"></script>

       <!-- Code to take screenshot and image to pdf conversion-->

       <script>

           function render(targetElem) {

               var nodesToRecover = []

               var nodesToRemove = []

               var svgs = $(targetElem).find('svg');

               svgs.each(function(index, node) {

                   var parentNode = node.parentNode

                   var svg = parentNode.innerHTML

                   var canvas = document.createElement('canvas')

                   xml = (new XMLSerializer()).serializeToString(node)

                   xml = xml.replace(/xmlns=\"http:\/\/www\.w3\.org\/2000\/svg\"/, '')

                   canvg(canvas, xml); // html to image

                   nodesToRecover.push({

                       parent: parentNode,

                       child: node

                   })

                   parentNode.removeChild(node)

                   nodesToRemove.push({

                       parent: parentNode,

                       child: canvas

                   })

                   parentNode.appendChild(canvas)

               })

               

               html2canvas(targetElem, {

                   onrendered: function(canvas) {

                       canvas.style.visibility = 'hidden'

                       

                       document.body.appendChild(canvas);

                       var doc = new jsPDF('p', 'pt', [canvas.height,canvas.width]); // create pdf file

                       doc.addHTML(canvas, {}, function() { // add image to pdf file

                           sendEmail(btoa(doc.output()));

                           document.body.removeChild(canvas)

                       })

                   }

               })

           }

       </script>

   </head>

   <body>

       <apex:form >

           <apex:actionFunction action="{!sendEmail}" name="sendEmail" rerender="" status="counterStatus" >

               <apex:param name="pdfContent" assignTo="{!pdfContent}" value="" />

           </apex:actionFunction>

           

           <div class="buttonAndMessageContainer">

               <apex:actionStatus startText="Sending Email...."

                                  stopText="Email Sent Successfully!" startStyle="display:block;" stopStyle="display:none;" id="counterStatus"/>

               

               <div class="buttonContainer" >

                   <apex:commandButton reRender="messageForSendMail" value="Send Result as Email Attachment" onclick="render(document.getElementById('resultForPdf'))"></apex:commandButton>

               </div>

           </div>

           

           <div id="resultForPdf">

               <apex:repeat value="{!questionMap}" var="question">

                   <div>

                       <span id="{!question}"></span>

                   </div>

                   

                   <apex:chart renderTo="{!question}" data="{!questionMap[question].chartData}" height="300" width="300" background="#F5F5F5">

                       <apex:legend position="bottom"/>

                       <apex:pieSeries labelField="name" dataField="data" donut="50">

                           <apex:chartLabel display="none" orientation="vertical"

                                            font="bold 18px Helvetica"/>

                       </apex:pieSeries>

                   </apex:chart>

               </apex:repeat>

           </div>

       </apex:form>

   </body>

</apex:page>

Controller:

public class SEM_BlogController {

   public Map<String,Question> questionMap {get;set;}

   public String message {get;set;}

   private String emailId;

   

   //constructor

   public SEM_BlogController(){

       questionMap = new Map<String,Question>();

       emailId = 'demo@gmail.com';

       populateChartData();

   }

   public void populateChartData() {

       Question que;

       List<chartDataWrapper> data;

       chartDataWrapper chartData;

       for(Integer i = 0 ; i < 5 ; i++){

           data = new List<chartDataWrapper>();

           for(Integer j = 0 ; j < 3 ; j++){

               chartData = new chartDataWrapper('ans' + j,  j + 8);

               data.add(chartData);

           }

           que = new Question();

           que.question = 'Demo Question ' + i + '?';

           que.chartData = data;

           questionMap.put(que.question,que);

       }

   }

   

    public void sendEmail(){

       

       String body = Apexpages.currentPage().getParameters().get('pdfContent');

       Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();

       String[] toAddresses = new String[]{emailId};

       

           mail.setToAddresses(toAddresses);

           mail.setSubject('Demo PDF');

           mail.setPlainTextBody('Hi, PFB the sample PDF file.');

           mail.setBccSender(false);

           mail.setUseSignature(false);

           mail.saveAsActivity = false;

           body = body.replace('https://c.cs83.visual.force.com/', '');

           // Add to attachment file list

           List<Messaging.Emailfileattachment> fileAttachments = new List<Messaging.Emailfileattachment>();

           Messaging.Emailfileattachment efa = new Messaging.Emailfileattachment();

           efa.setFileName('demo.pdf');

           efa.setBody(EncodingUtil.base64Decode(body));

           fileAttachments.add(efa);

           mail.setFileAttachments(fileAttachments);

           //Send email

           

           Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });

           message = 'Email has been send successfully!';

   }

 

// Wrapper class

   public class chartDataWrapper {

 

       public String name { get; set; }

       public Integer data { get; set; }

 

       public chartDataWrapper(String name, Integer data) {

           this.name = name;

           this.data = data;

       }

   }

   public class Question{

       public String question {get;set;}

       public List<chartDataWrapper> chartData {get;set;}

   }

}

Good luck! Hope you learned some tips that can help you next time you have to convert a Visualforce Page into a PDF. For more tech tips and tricks, follow the Appirio blog

 

Previous Video
Georgetown University: Productivity &amp; Happiness
Georgetown University: Productivity &amp; Happiness

Next Article
The Ultimate Salesforce Data Migration Best Practices Guide
The Ultimate Salesforce Data Migration Best Practices Guide

By Harshvardhan Rathore “If you don’t have time to do it right, when will you have time to do it over?” – J...

Have questions about Salesforce implementation?

Let's Talk