[SalesForce] How to render a PDF generated with jsPDF in a LWC

I'm new to Salesforce development and currently try to render a PDF that has been generated with jsPDF in a modal (i.e., I managed to generate PDFs from an LWC using jsPDF, yet I'd like to render it using a PDF viewer). Equipping the PDF viewer with download and email buttons would be great, since I'd like to replace the standard "Create PDF" button.

I followed the discussions here and here.

My test html looks as follows:

<template>
    <!-- lightning button for open modal window -->
    <lightning-button variant="brand"
       label="Create PDF (DE)"
       title="Create PDF (DE)"
       onclick={openModal}
       class="slds-m-left_x-small">
    </lightning-button>
    <!--Use template if:true to display/hide popup based on isModalOpen value--> 
    <template if:true={isModalOpen}>
        <!-- Modal/Popup Box LWC starts here -->
        <section role="dialog" tabindex="-1" aria-labelledby="modal-heading-01" aria-modal="true" aria-describedby="modal-content-id-1" class="slds-modal slds-fade-in-open">
            <div class="slds-modal__container" style="width: 90% !important; max-width: 100rem">
                <!-- Modal/Popup Box LWC header here -->
                <header class="slds-modal__header">
                    <button class="slds-button slds-button_icon slds-modal__close slds-button_icon-inverse" title="Close" onclick={closeModal}>
                        <lightning-icon icon-name="utility:close"
                            alternative-text="close"
                            variant="inverse"
                            size="small" ></lightning-icon>
                        <span class="slds-assistive-text">Close</span>
                    </button>
                    <h2 id="modal-heading-01" class="slds-text-heading_medium slds-hyphenate">Create Quote PDF in German</h2>
                </header>
                <!-- Modal/Popup Box LWC body starts here -->
                <div class="slds-modal__content slds-p-around_medium" style="height: 85%" id="modal-content-id-1">
                 <iframe id="pdfFrame" src="/resource/pdfjs/web/viewer.html?file=" width="100%" height="100%" class="pdfFrame" onload={generatePdf}></iframe>
                </div>
                <!-- Modal/Popup Box LWC footer starts here -->
                <footer class="slds-modal__footer">
                    <button class="slds-button slds-button_neutral" onclick={closeModal} title="Cancel">Cancel</button>
                    <button class="slds-button slds-button_brand" onclick={submitDetails} title="OK">OK</button>
                </footer>
            </div>
        </section>
        <div class="slds-backdrop slds-backdrop_open"></div>
    </template>
 </template>
</template>

The js:


import {LightningElement, track} from 'lwc';
import {loadScript} from "lightning/platformResourceLoader";
import JSPDF from '@salesforce/resourceUrl/jspdf';

export default class CreateQuote extends LightningElement {
    //Boolean tracked variable to indicate if modal is open or not default value is false as modal is closed when page is loaded 
    @track isModalOpen = false;
    
    openModal() {
        // to open modal set isModalOpen track value as true
        this.isModalOpen = true;
        //this.generateData();
    }
    closeModal() {
        // to close modal set isModalOpen track value as false
        this.isModalOpen = false;
    }
    submitDetails() {
        // to close modal set isModalOpen track value as false
        //Add your code to call apex method or do some processing
        this.isModalOpen = false;
    }

    renderedCallback() {
        Promise.all([
            loadScript(this, JSPDF)
        ]);
    }

    generatePdf(){
        const { jsPDF } = window.jspdf;
        const pdf = new jsPDF({
            encryption: {
                userPermissions: ["print", "modify", "copy", "annot-forms"]
            }
        });

        pdf.text("Hello World", 20, 20);
        console.log(pdf.output('datauristring'));

        this.template.querySelector('iframe').contentWindow.postMessage(pdf.output('datauristring').split(',')[1], window.location.origin);
    }
}

Here's a screenshot of the empty viewer:
enter image description here

If I remove the ?file= from the iframe:

<iframe id="pdfFrame" src="/resource/pdfjs/web/viewer.html" width="100%" height="100%" class="pdfFrame" onload={generatePdf}></iframe>

pdfjs viewer shows the default document (delivered with pdfjs):
enter image description here

However, the following is not clear to me:

Thanks for your help!

Best Answer

I am able to answer this as I have answered on both of those and I have recently developed exactly what you are trying to do.

Where does "/resource/pdfjs/web/viewer.html" come from? What do I need to install and how?

  • You can get Pdfjs here
  • Once you download it, load that as a Static resource in Setup > Static resources (you can load a zip file too, SF unzips it automatically), refer to this trailhead to learn more about how to use static resources
  • viewer.html is part of PDFJs library which you can access by using /resource/pdfjs/web/viewer.html where pdfjs is the API name of your static resource.

Because you are directly passing your viewer.html as iFrame source, you do not need to use loadScript().

To show PDF in your iFrame, you can display text like you are doing or you can pass your base64data in this line :

this.template.querySelector('iframe').contentWindow.postMessage(this.pdfData, window.location.origin);

Also, add a lightning-button that you can click and invoke your generateData() to see if your pdf viewer UI is showing, that gives you confirmation that the PDFJS resource is being loaded successfully.

To download you basically need to generate download link for browser behind the scene and click on it.

<lightning-button variant="brand-outline" label="Download" title="Primary action" onclick={downloadPdf} ></lightning-button>

JS :

downloadPdf(event){
    
    let downloadLink = document.createElement("a");
    downloadLink.setAttribute("type", "hidden");
    downloadLink.href = "data:application/octet-stream;base64,"+this.pdfData;
    downloadLink.download = "PDF NAME".pdf";
    document.body.appendChild(downloadLink);
    downloadLink.click();
    downloadLink.remove();
        
}

Let me know if this helps and answers your question. Happy to help further if you are stuck.

Related Topic