Newer Version Available

This content describes an older version of this product. View Latest

Render a Visualforce Page as PDF from Apex

You can use the PageReference.getContentAsPDF() method in Apex to render a Visualforce page as PDF data. Then use Apex code to convert that PDF data to an email attachment, a document, a Chatter post, and so on.
The following example is a simple three element form that selects an account and a report format, and then sends the resulting report to the specified email address.
1<apex:page title="Account Summary" tabStyle="Account"
2    controller="PdfEmailerController">
3
4    <apex:pageMessages />
5
6    <apex:form >
7        <apex:pageBlock title="Account Summary">
8    
9        <p>Select a recently modified account to summarize.</p>
10        <p/>
11        
12        <apex:pageBlockSection title="Report Format">
13        
14            <!-- Select account menu -->
15            <apex:pageBlockSectionItem>
16                <apex:outputLabel for="selectedAccount" value="Account"/> 
17                <apex:selectList id="selectedAccount" value="{! selectedAccount }" 
18                                 size="1">
19                    <apex:selectOption /> <!-- blank by default -->
20                    <apex:selectOptions value="{! recentAccounts }" />
21                </apex:selectList>
22            </apex:pageBlockSectionItem>
23
24            <!-- Select report format menu -->
25            <apex:pageBlockSectionItem >
26                <apex:outputLabel for="selectedReport" value="Summary Format"/> 
27                <apex:selectList id="selectedReport" value="{! selectedReport }" 
28                                 size="1">
29                    <apex:selectOptions value="{! reportFormats }" />
30                </apex:selectList>
31            </apex:pageBlockSectionItem>
32
33            <!-- Email recipient input field -->
34            <apex:pageBlockSectionItem >
35                <apex:outputLabel for="recipientEmail" value="Send To"/> 
36                <apex:inputText value="{! recipientEmail }" size="40"/>
37            </apex:pageBlockSectionItem>
38
39        </apex:pageBlockSection>
40            
41        <apex:pageBlockButtons location="bottom">
42            <apex:commandButton action="{! sendReport }" value="Send Account Summary" />
43        </apex:pageBlockButtons>
44    
45    </apex:pageBlock>
46    </apex:form>
47
48</apex:page>

This page is a simple user interface. When you’re generating a PDF file from Apex, all the action is in the Apex code.

In this example, that code is in the PdfEmailerController class that’s specified as the page’s controller.
1public with sharing class PdfEmailerController {
2    
3    // Form fields
4    public Id selectedAccount    { get; set; }  // Account selected on Visualforce page
5    public String selectedReport { get; set; }  // Report selected
6    public String recipientEmail { get; set; }  // Send to this email
7    
8    // Action method for the [Send Account Summary] button
9    public PageReference sendReport() {
10
11        // NOTE: Abbreviated error checking to keep the code sample short
12        //       You, of course, would never do this little error checking
13        if(String.isBlank(this.selectedAccount) || String.isBlank(this.recipientEmail)) {
14            ApexPages.addMessage(new 
15                ApexPages.Message(ApexPages.Severity.ERROR, 
16               'Errors on the form. Please correct and resubmit.'));
17            return null; // early out
18        }
19        
20        // Get account name for email message strings
21        Account account = [SELECT Name 
22                           FROM Account 
23                           WHERE Id = :this.selectedAccount 
24                           LIMIT 1];
25        if(null == account) {
26            // Got a bogus ID from the form submission
27            ApexPages.addMessage(new 
28                ApexPages.Message(ApexPages.Severity.ERROR, 
29               'Invalid account. Please correct and resubmit.'));
30            return null; // early out
31        }
32        
33        // Create email
34        Messaging.SingleEmailMessage message = new Messaging.SingleEmailMessage();
35        message.setToAddresses(new String[]{ this.recipientEmail });
36        message.setSubject('Account summary for ' + account.Name);
37        message.setHtmlBody('Here\'s a summary for the ' + account.Name + ' account.');
38        
39        // Create PDF
40        PageReference reportPage = 
41            (PageReference)this.reportPagesIndex.get(this.selectedReport);
42        reportPage.getParameters().put('id', this.selectedAccount);
43        Blob reportPdf;
44        try {
45            reportPdf = reportPage.getContentAsPDF();
46        }
47        catch (Exception e) {
48            reportPdf = Blob.valueOf(e.getMessage());
49        }
50        
51        // Attach PDF to email and send
52        Messaging.EmailFileAttachment attachment = new Messaging.EmailFileAttachment();
53        attachment.setContentType('application/pdf');
54        attachment.setFileName('AccountSummary-' + account.Name + '.pdf');
55        attachment.setInline(false);
56        attachment.setBody(reportPdf);
57        message.setFileAttachments(new Messaging.EmailFileAttachment[]{ attachment });
58        Messaging.sendEmail(new Messaging.SingleEmailMessage[]{ message });
59        
60        ApexPages.addMessage(new 
61            ApexPages.Message(ApexPages.Severity.INFO,
62           'Email sent with PDF attachment to ' + this.recipientEmail));
63
64        return null; // Stay on same page, even on success
65    }
66    
67    
68    /***** Form Helpers *****/
69    
70    // Ten recently-touched accounts, for the Account selection menu
71    public List<SelectOption> recentAccounts {
72        get {
73            if(null == recentAccounts){
74                recentAccounts = new List<SelectOption>();
75                for(Account acct : [SELECT Id,Name,LastModifiedDate 
76                                    FROM Account 
77                                    ORDER BY LastModifiedDate DESC 
78                                    LIMIT 10]) {
79                    recentAccounts.add(new SelectOption(acct.Id, acct.Name));
80                }
81            }
82            return recentAccounts;
83        }
84        set;
85    }
86    
87    // List of available reports, for the Summary Format selection menu
88    public List<SelectOption> reportFormats {
89        get {
90            if(null == reportFormats) {
91                reportFormats = new List<SelectOption>();
92                for(Map <String,Object> report : reports) {
93                    reportFormats.add(new SelectOption(
94                        (String)report.get('name'), (String)report.get('label')));
95                }
96            }
97            return reportFormats;
98        }
99        set;
100    }
101
102    
103    /***** Private Helpers *****/
104    
105    // List of report templates to make available
106    // These are just Visualforce pages you might print to PDF
107    private Map<String,PageReference> reportPagesIndex;
108    private List<Map<String,Object>> reports {
109        get {
110            if(null == reports) {
111                reports = new List<Map<String,Object>>();
112                // Add one report to the list of reports
113                Map<String,Object> simpleReport = new Map<String,Object>();
114                simpleReport.put('name',  'simple');
115                simpleReport.put('label', 'Simple');
116                simpleReport.put('page',   Page.ReportAccountSimple);
117                reports.add(simpleReport);
118                
119                // Add your own, more complete list of PDF templates here
120
121                // Index the page names for the reports
122                this.reportPagesIndex = new Map<String,PageReference>();
123                for(Map<String,Object> report : reports) {
124                    this.reportPagesIndex.put(
125                        (String)report.get('name'), (PageReference)report.get('page'));
126                }
127            }
128            return reports;
129        }
130        set;
131    }
132}

This Apex controller can be conceptually divided into four parts.

  • The three public properties at the beginning capture the values submitted by the three input elements on the form.
  • The sendReport() action method fires when the Send Account Summary button is clicked.
  • The two public helper properties supply the values to use in the two select list input elements.
  • The private helpers at the end encapsulate the list of possible PDF report formats. You can add your own report by creating a Visualforce page and then adding an entry for it in this section.

When the sendReport() action method fires, the code does the following.

  • It performs rudimentary error checking to ensure that the form fields have useful values.

    This error checking is inadequate for a form that must survive contact with real people. Perform more complete form validation in your production code.

    Note

  • Next it uses the value of the selected account to look up the name of that account. The account name is used in text that’s added to the email message. This lookup is also an opportunity to further validate the form value and ensure that a real account was selected.
  • It uses the Messaging.SingleEmailMessage class to assemble an email message, setting the To, Subject, and Body email message values.
  • The code creates a PageReference for the selected report format and then sets a page request parameter on it. The parameter is named “id”, and its value is set to the selected account’s ID. This PageReference represents a specific request to access this page in the context of the specified account. When getContentAsPdf() is called, the referenced Visualforce page has access to the specified account, and the page is rendered with that account’s details.
  • Finally, the PDF data is added to an attachment, and the attachment is added to the email message created earlier. The message is then sent.

When using PageReference.getContentAsPdf(), the return type of the method call is Blob, which stands for “binary large object.” In Apex, the Blob data type represents untyped binary data. It’s only when the reportPdf variable is added to the Messaging.EmailFileAttachment with a content type of “application/pdf” that the binary data becomes a PDF file.

In addition, the call to getContentAsPdf() is wrapped in a try/catch block. If the call fails, the catch replaces the hoped for PDF data with a Blob version of the exception’s message text.

PDF generation can throw a variety of different exceptions. Not all of them can be caught. Your code should be prepared to manage uncatchable exceptions like System.LimitException. For details, see “Exceptions that Can’t be Caught” in Exception Statements in the Apex Developer Guide.

Note

Rendering a Visualforce page as PDF data is treated semantically as a callout to an external service for various reasons. One reason is that the rendering service can fail in all the same ways that an external service can fail. For instance, the page references external resources that aren’t available. Another example is when the page contains too much data—usually in the form of images—or the rendering time exceeds a limit. For this reason, always wrap the getContentAsPdf() rendering call in a try/catch block when rendering a Visualforce page as PDF data in Apex.

For completeness, here’s the report template page that’s rendered into PDF data by the Apex code.
1<apex:page showHeader="false" standardStylesheets="false"
2    standardController="Account">
3    
4    <!-- 
5    This page must be called with an Account ID in the request, e.g.:
6    https://MyDomainName--PackageName.vf.force.com/apex/ReportAccountSimple?id=001D000000JRBet
7    -->
8
9    <h1>Account Summary for {! Account.Name }</h1>
10    
11    <table>
12        <tr><th>Phone</th>  <td><apex:outputText value="{! Account.Phone }"/></td></tr>
13        <tr><th>Fax</th>    <td><apex:outputText value="{! Account.Fax }"/></td></tr>
14        <tr><th>Website</th><td><apex:outputText value="{! Account.Website }"/></td></tr>
15    </table>
16
17    <p><apex:outputText value="{! Account.Description }"/></p>
18        
19</apex:page>