Stat Tracker

Sunday, February 26, 2012

Generating a PDF in an Apex Trigger

There are a number of ways you can generate PDF documents on Force.com. The easiest way is to simply use the 'RenderAs' attribute on a Visualforce Page. You can also generate quote PDF easily with templates out of the box. While these options are great and work in a number of situations, they will not work within the context of a trigger.

There are many times during the life of a record where we may need to automatically generate a PDF document and attach it to the record. To do this we can use the Blob object and its method toPDF(String content). I was actually surprised by how little information there is available on this method. There are lots of blogs and articles about rendering a Visualforce page as a PDF, but this method hardly gets any press!

The Blob.toPDF() method takes in the body of the PDF document as a String. And guess what! It can take in HTML content and render the PDF according to the HTML you specify. Pretty awesome! Let's take a look at this in action. I've created a simple Apex Trigger on my Account object which will generate a simple PDF of all the information on the Account object.

Here's the Account Trigger:

trigger AccountTrigger on Account (after delete, after insert, after undelete,
after update, before delete, before insert, before update)
{
    if(trigger.isAfter)
    {
        if(trigger.isInsert)
        {
            if(trigger.new.size() == 1) // Lets only do this if trigger size is 1
            {
                AccountPDFGenerator.generateAccountPDF(trigger.new[0]);
            }
        }else if(trigger.isUpdate)
        {
            if(trigger.new.size() == 1) // Lets only do this if trigger size is 1
            {
                AccountPDFGenerator.generateAccountPDF(trigger.new[0]);
            }
        }
    }
}


And here is the PDF Apex class which will use the Blob.toPDF() method to generate the PDF content and attach it to the Account record as an Attachment. Notice that ths Blob.toPDF() takes in a String as the input, and that the String content can be formatted with HTML.

public with sharing class AccountPDFGenerator
{
   
    public static final String FORM_HTML_START = '<HTML><BODY>';
    public static final String FORM_HTML_END = '</BODY></HTML>';

    public static void generateAccountPDF(Account account)
    {
        String pdfContent = '' + FORM_HTML_START;
        try
        {
            pdfContent = '' + FORM_HTML_START;
            pdfContent = pdfContent + '<H2>Account Information in PDF</H2>';
           
            //Dynamically grab all the fields to store in the PDF
            Map<String, Schema.SObjectType> sobjectSchemaMap = Schema.getGlobalDescribe();
            Schema.DescribeSObjectResult objDescribe = sobjectSchemaMap.get('Account').getDescribe();
            Map<String, Schema.SObjectField> fieldMap = objDescribe.fields.getMap();
           
            //Append each Field to the PDF
            for(Schema.SObjectField fieldDef : fieldMap.values())
            {
                Schema.Describefieldresult fieldDescResult = fieldDef.getDescribe();
                String name = fieldDescResult.getName();
                pdfContent = pdfContent + '<P>' + name + ': ' + account.get(name) + '</P>';
            }
            pdfContent = pdfContent + FORM_HTML_END;
        }catch(Exception e)
        {
            pdfContent = '' + FORM_HTML_START;
            pdfContent = pdfContent + '<P>THERE WAS AN ERROR GENERATING PDF: ' + e.getMessage() + '</P>';
            pdfContent = pdfContent + FORM_HTML_END;
        }
        attachPDF(account,pdfContent);
    }
   
    public static void attachPDF(Account account, String pdfContent)
    {
        try
        {
            Attachment attachmentPDF = new Attachment();
            attachmentPDF.parentId = account.Id;
            attachmentPDF.Name = account.Name + '.pdf';
            attachmentPDF.body = Blob.toPDF(pdfContent); //This creates the PDF content
            insert attachmentPDF;
        }catch(Exception e)
        {
            account.addError(e.getMessage());
        }
    }
   
}


And here is the PDF that was generated in the Apex Trigger and attached to the Account record.


Pretty awesome, right? Since you can't get a Visualforce page's content in a Apex Trigger this method is a lifesaver when you need to automatically generate simple PDF documents via Triggers.

Let me know your thoughts and if you have any other feedback on this method!