Merge and Download Pdf from Salesforce

Hello folks, In this blog I will show how you can Merge and Download Pdf from Salesforce into a single pdf file. You may encounter this issue when your client wants to download all of its attachments and files into a single pdf. You may find some Apps available on the Salesforce AppExchange but lets try it in our own way.

Key Requirements

  • Must Know basics of admin and development in salesforce.
  • Must know how to work over LWC in Salesforce. You can read more about LWC here.
  • Must Know some third party integration process.

For fulfilling our requirement we are using a third-party library named PDF-LIB. You can use any other library if you want.

Merge and Download Pdf from Salesforce

1.  Download PDF-LIB library from here and Upload the file in Static Resources of your org.

2.  Get all  file data in base64 format from Apex Controller. For Ex

@AuraEnabled    
public static List<String> getData(String accountId){
        System.debug('ID is '+accountId);
        Map<String,String> mapOfData = new Map<String,String>();
        List<ContentDocumentLink> lsOfCDL = [SELECT Id, LinkedEntityId, ContentDocumentId FROM ContentDocumentLink WHERE LinkedEntityId=:accountId];
        List<Id> tempsId =new List<Id>();
        for(ContentDocumentLink tempLoop:lsOfCDL){
            tempsId.add(tempLoop.ContentDocumentId);
        }
        List<ContentVersion> lsOfCV = [SELECT Id,VersionData,FileExtension FROM ContentVersion WHERE ContentDocumentId=:tempsId];
        List<String> lsOfFiles = new  List<String>();
        for(ContentVersion tempLoopCv :lsOfCV){
            lsOfFiles.add(EncodingUtil.base64Encode(tempLoopCv.VersionData));
            System.debug('File Extension is  '+tempLoopCv.FileExtension);
            if((''+tempLoopCv.FileExtension).toLowerCase() == 'pdf'){
               mapOfData.put(''+tempLoopCv.Id+','+'PDF',EncodingUtil.base64Encode(tempLoopCv.VersionData));    
            }else{
                 mapOfData.put(''+tempLoopCv.Id+','+'PDF',EncodingUtil.base64Encode(tempLoopCv.VersionData));
            }
               
        }
        return lsOfFiles;
    }

3. Now load your PDF-LIB into your lightning component environment by calling loadscript method and later calling your base64 file data from your Apex controller. For ex:

renderedCallback() {
        loadScript(this, pdflib).then(() => {
        });
 
        console.log('recode ud  ' + this.recordId)
        if (this.recordId) {
            getData({ accountId: this.recordId })
                .then((result) => {
                    this.docData = JSON.parse(JSON.stringify(result));
                    console.log('Size of File are ' + this.docData.length)
                    this.error = undefined;
//                   this.createPdf()
                })
                .catch((error) => {
                    console.log('error while calling ' + error)
                }
                )
        }
    }

Inside the loadscript method pdflib denotes the name you have given to your static resource.

4. Define createPdf where actual work is done of merging .

 async createPdf() {
        const pdfDoc = await PDFLib.PDFDocument.create();
        console.log('pdfDoc is ', pdfDoc)
        if (this.docData.length < 1)
            return
 
        var tempBytes = Uint8Array.from(atob(this.docData[0]), (c) => c.charCodeAt(0));
        console.log('tempBytes', tempBytes)
        const [firstPage] = await pdfDoc.embedPdf(tempBytes);
        const americanFlagDims = firstPage.scale(0.99);
        var page = pdfDoc.addPage();
        console.log('page is ', page)
 
        page.drawPage(firstPage, {
            ...americanFlagDims,
            x: page.getWidth() - americanFlagDims.width,
            y: page.getHeight() - americanFlagDims.height - 10,
        });
 
 
        if (this.docData.length > 1) {
            for (let i = 1; i < this.docData.length; i++) {
                tempBytes = Uint8Array.from(atob(this.docData[i]), (c) => c.charCodeAt(0));
                console.log('tempBtes>> ', tempBytes)
                page = pdfDoc.addPage();
                const usConstitutionPdf = await PDFLib.PDFDocument.load(tempBytes);
                console.log('After ', usConstitutionPdf, usConstitutionPdf.getPages())
                const preamble = await pdfDoc.embedPage(usConstitutionPdf.getPages()[0]);
                console.log(' Inside page is ', page)
 
                const preambleDims = preamble.scale(0.95);
 
                page.drawPage(preamble, {
                    ...preambleDims,
                    x: page.getWidth() - americanFlagDims.width,
                    y: page.getHeight() - americanFlagDims.height - 10,
                });
            }
 
        }
        const pdfBytes = await pdfDoc.save();
        this.saveByteArray("My PDF", pdfBytes);
    }

5. Create saveByteArray method to download your merge file.

saveByteArray(pdfName, byte) {
        var blob = new Blob([byte], { type: "application/pdf" });
        var link = document.createElement("a");
        link.href = window.URL.createObjectURL(blob);
        var fileName = pdfName;
        link.download = fileName;
        link.click();
    }

You can get the full code from here.

Nishchal vashisht
Nishchal vashisht

Nishchal vashisht
5x certified, Developer at Yoti

Articles: 8

7 Comments

  1. Hi, I was trying to implement your code and came across an error such as – Uncaught (in promise) TypeError: Cannot read properties of undefined (reading ‘create’).
    Can you help me in understanding if I am making any mistake in the code?

  2. Hi, really helpful, thank you for your effort but how can we save the merged pdf on to related files on account?

  3. hi, this post really helpful thank you, instead of download how can we save the merged pdf file on to related files on account. TIA

  4. Thanks , I have another question about this solution, how can save this PDF like attachment in any object like related file in Salesforce! If this is posible?

Leave a Reply

Your email address will not be published. Required fields are marked *