Render VisualForce page report of custom object to PDF and create attachment via page detail button

apexattachmentcustom-objectpdfvisualforce

I have a VisualForce page which shows a report of a custom object MyCustomObject.

I have two page detail buttons, one for "Show report" only showing the report rendered as PDF and one "Attach report" to render the report as PDF, and attach it to the object as PDF-attachment.

While the VisualForce page (launched via "Show report") is viewing my example PDF properly, opening the attached file (after clicking "Attach report") results in "Error. Failed to load PDF document." – expectable since the file seems to be empty-sized.

Following examples in https://developer.salesforce.com/forums/?id=9062I000000IDp6QAG I created the following code

VisualForce page MyCustomObjectReport

Example VF page to represent the report PDF to be generated, viewable by "Show report" button

<apex:page renderAs="pdf" standardController="MyCustomObject__c">
    <h1>MyCustomObject report</h1>
    This is a report of the MyCustomObject report to be filled with further parameters.
</apex:page>

Apex Class MyCustomObjectAttachPdf

Apex class with action code to generate PDF, attach it to current MyCustomObject object and redirect to current MyCustomObject object details page

public class MyCustomObjectAttachPdf {
    
    private final MyCustomObject__c obj;
    
    //constructor
    public MyCustomObjectAttachPdf(ApexPages.StandardController standardPageController) {
        // instantiate object of current record
        obj = (MyCustomObject__c)standardPageController.getRecord();
    }
    
    //action method - returns a page reference
    public PageReference attachPDF() {        
        // Get PDF content from VF page
        PageReference pdfPage = Page.MyCustomObjectReport;
        pdfPage.getParameters().put('id',obj.Id);
        Blob pdfBlob = pdfPage.getContent();
        
        // Create attachment and attach
        Attachment attach = new Attachment();
        attach.Body = pdfBlob;
        attach.Name = 'MyCustomObjectReport.pdf';
        attach.IsPrivate = false;
        attach.ParentId = obj.Id;        
        attach.ContentType = 'application/pdf';
        insert attach; //insert the attachment
        
        // Redirect the user to object page
        PageReference destPage = new ApexPages.StandardController(obj).view();
        destPage.setRedirect(true);
        return destPage;
    }
}

VisualForce page MyCustomObjectAttachPdf

VF page triggered by "Attach report" button, calling MyCustomObjectAttachPdf apex class, hence generating pdf from MyCustomObjectReport VF and attaching it to the MyCustomObject object

<apex:page action="{!attachPDF}" extensions="MyCustomObjectAttachPdf" standardController="MyCustomObject__c">
</apex:page>

Issue

The VF page MyCustomObjectReport can be viewed via "Show report" button without any problems, showing the example PDF output.

Via the "Attach report" button, an action is happening and a file 'MyCustomObjectReport.pdf' is attached to the MyCustomObject object and the details page is shown. The resulting *.pdf file however seems to be empty-sized and fails to open: "Error. Failed to load PDF document."

Any ideas what I am missing?

Best Answer

The issue for me was active developer mode User Settings > Advanced User Details > Developer Mode, which seems to change the return-behaviour of the getContentAsPdf() function. After disabling this option the generated *.pdf file matched the PDF preview using my "Show report" button.

Additionally use Blob pdfBlob = pdfPage.getContentAsPdf(); rathern than getContent(), even if your VF page has the attribute renderAs="pdf" property.

Note that the protection mentioned in some other posts online DID NOT WORK for me to prevent this strange behaviour with development mode enabled.

Blob pdfBlob; //create a blob for the PDF content
if (!Test.isRunningTest()) { //if we are not in testing context
    pdfBlob = pdfPage.getContent(); //generate the pdf blob
} else { //otherwise, we are in testing context and getContent() gets funky so create the blob manually
    pdfBlob = Blob.valueOf('Some Text for a boring PDF file...');
}