[SalesForce] Corrupt PDF when sending as email-attachment from Apex

I've created a VF page that allows users to select a list of custom objects related to a particular case, update some field information and attach pictures to any of these related objects. When done, the user clicks on an email that generates an email based on a VF Email template. This part works great.

Instead of sending multiple image attachments on this email, I created a VF page that displays all of these images, and was attempting to generate it as a PDF attachment to attach to the case.

When I load the VF page (which has renderAS="PDF"), it looks great and works as expected.

When I call it from my custom controller using the getContent() method, it is sending a file that I am unable to open using Adobe. When I try I receive the error "insufficient data for an image", and even the text on the page does not render (though, oddly, the table is visible).

I have no clue how to attempt and troubleshoot it, so I will appreciate any help!

Here is my controller extension part which calls my page:

Messaging.EmailFileAttachment myAttach = new Messaging.EmailFileAttachment();
myAttach.setFilename(This.Cas.CaseNumber+'_Related_Pictures.pdf');
myAttach.setContentType('application/pdf');
PageReference myPdf = Page.IDS_Quote_Pictures_PDF;
mypdf.setRedirect(true);
myPDF.getParameters().put('ID' , this.Cas.ID);
blob b = myPdf.getContentAsPdf();
myAttach.setbody(b);
Messaging.SingleEmailMessage email = new Messaging.SingleEmailMessage();
email.setWhatID(Cas.ID);
email.setTargetObjectId(toContacts[0].theContact.id);
EmailTemplate templateId = [Select id from EmailTemplate where name = 'IDS RMA Quote Email'];
email.setTemplateID(templateId.Id);
email.setSaveAsActivity(true);
email.setToAddresses(toAddresses);
email.setCCAddresses(ccAddresses);
email.setOrgWideEmailAddressId([SELECT id, displayName FROM OrgWideEmailAddress WHERE DisplayName = 'I.D. Systems RMA Department'].id);
if (myAttach != NULL) {
    email.setFileAttachments(new Messaging.EmailFileAttachment[] {myAttach});
        }

And here is the custom controller that the actual PDF VF page calls:

public class IDS_RMA_Quote_Pictures_Getter{

// Wrapper class to wrap an attachment with a selection and some related details in text
public class attachFile {
    public Attachment theFile {get;set;}
    public String belongsTo {get;set;}

    public attachFile(Attachment theFile) {
        this.theFile = theFile;
    }
}

public final Case cas;
//private final ID cas;
private List<attachFile> readyAttachments;

public IDS_RMA_Quote_Pictures_Getter(){
    //this.cas = (case)stdController.getRecord();        
    Cas = [Select ID, Subject, CaseNumber, Type_of_RMA__c, Date_RMA_Received__c FROM Case WHERE ID =: ApexPages.CurrentPage().getParameters().get('id')];
}

public Case getCas() {
    return cas;   
}


public List<attachFile> getReadyAttachments() {
    System.debug('Starting Getter');
    List<device__C> deviceList = new List<Device__c>();
    deviceList = [SELECT Name,
                  Part_Number__R.Name,
                  Part_Number__R.Description__c,
                  Problem__c,
                  Visual_Inspection_Notes__c,
                  Quoted_Price__c,
                  Quote_Status__c,
                  Customer_Damage__c,
                  In_Warranty__c
                  FROM Device__c 
                  where Quote_Status__c =: 'Quoting' 
                  AND Case__c =: Cas.ID]; 
    System.debug('Devices: '+deviceList);

    List<ID> allDeviceIDs = new List<ID>();
    For (device__c d : deviceList) {
        allDeviceIDs.add(d.ID);   
    }
    System.Debug('Device IDs: '+allDeviceIDs);

    List<Attachment> allAttachments = new List<Attachment>();
    allAttachments = [SELECT ID, Name, ParentID, body, ContentType 
                      FROM Attachment 
                      WHERE name != : 'Device_QR_Code.png'
                      AND ContentType LIKE 'image/%'
                      AND (ParentID IN : allDeviceIDs
                             OR ParentID = : cas.ID)
                     Order by ID desc];
    System.debug('Selected # of Attachments: '+allAttachments.size());

    List<attachFile> readyAttachments = new List<attachFile>();
    integer counter = 1;

    for (attachment att : allAttachments) {
        attachFile AF = new attachFile(att);
        if (att.ParentID == cas.ID) {
            af.belongsTo = 'Attached to case';
            System.Debug('Device being added to list: '+af);
        } else {
            for (device__c dvc : deviceList) {
                if (att.ParentID == dvc.ID){
                    af.belongsTo = 'For Part Number ' +dvc.part_number__r.name+' Serial Number: '+dvc.Name;                    
                }
            }   
        }
        readyAttachments.add(af);
        System.Debug('Device being added to list: '+af);
    }
    System.Debug('Added file to readyAttachments');
    System.debug('Ready Attachments: '+readyAttachments.size());

    return readyAttachments;
}

}

and lastly, here is the PDF VF page:

<apex:page title="Quote for Repair" showHeader="false" controller="IDS_RMA_Quote_Pictures_Getter">
<apex:stylesheet value="{!URLFOR($Resource.RepairPDF, 'RFStyle.css')}"/>
<Apex:form >
    <apex:pageBlock >
    <apex:pageMessages />
        <table width = "100%" align="center">
            <tr width = "100%">
                <td width = "50%" align="left">
                    <apex:image value="{!URLFOR($Resource.RepairPDF, 'logo.jpg')}"/>
                </td>
            </tr>
            <tr>
                <td height="20px">
                    <!-- Spacing between logo and table-->
                </td>
            </tr>
            <!--<apex:outputText value="Dear Customer"/>-->
            <table width="100%" Class="grid">
                <tr>                        
                    <td width="50%" colspan="2" align = "center">
                        <apex:outputText styleClass="headertext" value="Pictures related to RMA #{!cas.CaseNumber}"/>
                    </td>
                </tr>
                <tr>
                    <td width="50%" align = "left" >
                        <apex:outputText value="RMA Type : "/>
                        <apex:outputText value="{!cas.Type_of_RMA__c}"/>
                    </td>
                    <td align = "left">
                        <apex:outputText value="Date RMA Received: "/>  
                        <apex:outputText value="{!cas.Date_RMA_Received__c}"/>
                    </td>
                </tr>
            </table>
            <!--Here starts the Device List-->
            <apex:variable value="{!1}" var="pgCount"/> 
            <apex:pageBlockSection title="Attached Images" columns="1">
                <apex:repeat value="{!readyAttachments}" var="ra">
                    <div style="{!if(pgCount = 1, 'page-break-after:auto;', 'page-break-after:always;')}">
                        <apex:outputText value="{!ra.belongsTo}" />
                        <apex:image url="/servlet/servlet.FileDownload?file={!ra.theFile.ID}"/>
                        <apex:variable var="pgCount" value="{!pgCount+ 1}"/>
                    </div>
                </apex:repeat>
            </apex:pageBlockSection>
        </table>            
    </apex:pageBlock>            
</Apex:form>

Best Answer

I strongly suspect the line below in your visualforce code and it's closing tag are at the root of your problem:

<apex:pageBlockSection title="Attached Images" columns="1">

</apex:pageBlockSection>

If you look at Best Practices for Rendering PDFs you'll see that <apex:pageBlock> is listed under "Components That Are Unsafe to Use in a PDF".

I recommend you redesign your page to use other HTML (another table perhaps) instead of the <apex page block> and see if that doesn't solve your problem.