Please refer here for limitations:
- Visualforce PDF Rendering Considerations and Limitations
- Visualforce Charting Limitations and Considerations
In order to bypass those limitation, we need to save chart as image and after that use the image in PDF.Currently there a lot chart library that offers saving the chart to user machine.In my scenario, user want to be able to get chart along with Opportunity information in PDF.So saving chart into the local machine and upload it back as Attachment is not ideal solution.
In order to achieve this, we must include canvg library that you can get it here.This library will help to convert SVG to PNG[also please checkout other format].I get this information from here . Note that Visualforce Chart only render in browsers that support SVG, which mean this is a good sign.
1 2 3 | <script type="text/javascript" src="http://canvg.github.io/canvg/rgbcolor.js"></script> <script type="text/javascript" src="http://canvg.github.io/canvg/StackBlur.js"></script> <script type="text/javascript" src="http://canvg.github.io/canvg/canvg.js"></script> |
In order Visualforce page , add canvas element in page.In this example, I use demo from Visualforce Chart for pie chart.
1 2 3 4 5 6 7 | <input type="button" id="save_img_att" value="Save as Attachment"/> <div id="container" style="min-width: 400px; max-width: 600px; height: 400px; margin: 0 auto"></div> <apex:chart height="350" width="450" data="{!pieData}" id="pieChart" renderTo="container"> <apex:pieSeries dataField="data" labelField="name" id="pieSeries"/> <apex:legend position="right"/> </apex:chart> <canvas id="canvas" style="display:none;"></canvas> |
In Javascript,note 'container' is the div element where chart is render to.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | $("#save_img_att").click(function(){ var mainDiv=document.getElementById('container'); var svg = mainDiv.children[0].children[0].innerHTML; console.log('@@svg .... ' + svg); canvg(document.getElementById('canvas'),svg); var img = canvas.toDataURL("image/png"); //img is data:image/png;base64 img = img.replace('data:image/png;base64,', ''); var data = "bin_data=" + img; console.log('@@oppId =' + '{!Opportunity.Id}'); Visualforce.remoting.Manager.invokeAction('{!$RemoteAction.VFChart_Server.saveChartAsAttachment}', '{!Opportunity.Id}',img, function(result,event){ if(event.status){ console.log('@@@ success'); } else { console.log('@@@ fail'); } } ); }); |
We use var svg = mainDiv.children[0].children[0].innerHTML; in order to get the div that generated by Salesforce.You can right click at the chart and search for something like below.We don't want to hardcode so we need to find it from 'container' div.
Create @RemoteAction in controller class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | @RemoteAction global static void saveChartAsAttachment(String oppId,String attachmentBody){ system.debug('@@@saveChartAsAttachment oppId ='+ oppId); List<Attachment> listAttachment =[Select Name,Id,Body from Attachment Where ParentId=:oppId]; Attachment att; if(listAttachment.size()>0){ att =listAttachment[0]; att.Body = EncodingUtil.base64Decode(attachmentBody); update att; } else{ att = new Attachment(); att.Name='spiderChart_VF'; att.ParentId=oppId; att.ContentType='image/png'; att.Body = EncodingUtil.base64Decode(attachmentBody); insert att; } } |
I include this visualforce page in Opportunity page layout like below.This is nothing to do with saving it as attachment, it just because I am lazy to create my own custom page.
Click on Save as Attachment. Refresh it, you should see the image is save in Attachment.Here the sample of image that generated from Visualforce Chart.
Once it save as Attachment, it easy for us to render as PDF which I will update in other entry.
For complete source code please check it out here.