1

I have the below HTML:

<template>
    <lightning-button onclick={generateData} label="Generate PDF"></lightning-button>

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

JS:

import { LightningElement } from 'lwc';
import { loadScript } from "lightning/platformResourceLoader";
import JSPDF from '@salesforce/resourceUrl/jspdf';
import getContacts from '@salesforce/apex/PdfGenerator.getContactsController';

export default class JspdfDemo extends LightningElement {
    contactList = [];
    headers = this.createHeaders([
        "Id",
        "FirstName",
        "LastName"
    ]);

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

    generatePdf() {
        const { jsPDF } = window.jspdf;
        const doc = new jsPDF({
            encryption: {
                userPassword: "user",
                ownerPassword: "owner",
                userPermissions: ["print", "modify", "copy", "annot-forms"]
                // try changing the user permissions granted
            }
        });

        doc.text("Hi I'm Matt", 20, 20);
        doc.table(30, 30, this.contactList, this.headers, { autosize: true });
        doc.save("demo.pdf");
    }

    generateData() {
        getContacts().then(result => {
            this.contactList = result;
            this.generatePdf();
        });
    }

    createHeaders(keys) {
        var result = [];
        for (var i = 0; i < keys.length; i += 1) {
            result.push({
                id: keys[i],
                name: keys[i],
                prompt: keys[i],
                width: 65,
                align: "center",
                padding: 0
            });
        }
        return result;
    }
}

This is the JS PDF librarythat I am using as a static resource:

After clicking the button it successfully downloads the PDF file.

But instead of downloading the file, I need to display the PDF file using iframe on salesforce I created iFrame tag in the HTML and tried below JS

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

I tried passing blob or base64 as below

this.pdfData = doc.output('blob);

or

this.pdfData = new Blob([doc.output('blob')], { type: 'application/pdf' });

or

this.pdfData = doc.output('datauristring');

But the above did not work as expected, and also the generate PDF button doesn't work on salesforce 1 mobile app, can someone please suggest how to display the PDF on salesforce on both mobile and desktop view instead of downloading it. The other solution I am thinking of is saving this PDF to a content document and then use the content document to display the PDF file, but it would be a lot better if the iframe solution works so that we can eliminate server-side call.

Update:

I am not able to save the file to the content document also I have created another thread for that Saving PDF file to content document saves as black pages

screenshot of the page:

enter image description here

7
  • Can you check with this similar implementation? Commented May 4, 2021 at 8:50
  • Thank you for your comment, this is about showing the PDF file from the content document on the browser but in my case, I have to generate the file and then display it on the iframe.
    – gs650x
    Commented May 4, 2021 at 8:52
  • In that case this post could provide more insight on showing the pdf in same tab or opening in a new tab. For iframe,I still suspect whether it would be possible or not. Commented May 4, 2021 at 9:48
  • Thank you so much for your kind help here but none of the solutions suggested in the above forum worked so I am going with the content document approach, could you please take a look into this salesforce.stackexchange.com/questions/342163/… thread where it is not saving the file properly.
    – gs650x
    Commented May 4, 2021 at 11:01
  • @gs650x I am currently working on showing pdf in lwc but in my case I am getting base64data for pdf file and I M showing it in iFrame using pdfjs library. I am on phone but I will link my question and answer in sometime. I had hard time only with postMessage as lwc wasnt able to find the iframe. Commented May 4, 2021 at 12:38

2 Answers 2

1

Update your postMessage in iframe to below,

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

for me Origin fixed the issue in iframe.

9
  • thank you for your answer, but unfortunately, this is not working.
    – gs650x
    Commented May 4, 2021 at 13:18
  • is your pdfjs loading a pdf viewer? that would be a big one as atleast the pdf viewer should be able to load if the library has been imported correctly. Commented May 4, 2021 at 13:20
  • yes it is loading, adding a screenshot to the question.
    – gs650x
    Commented May 4, 2021 at 13:22
  • did you try to console pdfData, is it correct base64 format string? Commented May 4, 2021 at 13:24
  • can you see if that same base64 you are generating is able to generate PDF here Commented May 4, 2021 at 13:25
0

Here's the reason why you're seeing your content blocked/black screen...

In 2020 Spring, Salesforce blocked content within iframes with the following explanation and suggested alternative:

The frame-src directive of the Content Security Policy prevents access to a blob URL from an iframe. This change was introduced in the Spring ’20 release, but wasn’t documented until now.

Where: This change applies to Lightning Experience and all versions of the Salesforce app.

Why: This restriction prevents an attacker from injecting arbitrary content into an iframe in a clickjacking attempt.

How: Use a regular link to a blob URL and open the content in a new tab or window instead of using an iframe.

Source: Spring 2020: Blob Schema Blocked for iframes

Per the suggestion above, here is a code snippet that will display a base64 string as a PDF in a new browser tab:

let binaryString = atob(base64String);
let uint8Array = new Uint8Array(binaryString.length);
for (let i = 0; i < binaryString.length; i++) {
uint8Array[i] = binaryString.charCodeAt(i);
}
let blob = new Blob([uint8Array], { type: "application/pdf" });
let url = URL.createObjectURL(blob);
window.open(url, '_blank');

Note: In the 2023 Winter Release, Lightning Web Security now permits Lightning web components to access content in iframe elements. With this change, you can use another web development feature that Lightning Locker blocks, such as displaying a PDF in a modal within Salesforce.

Source: Winter 2023: Access iframe Content in Lightning Web Security

From the FAQ's, here is how Lightning Web Security differs from Lightning Locker in regards to iframes:

Access to iframe content and identity. Your Lightning components can access content in iframe elements, even when the content is from a different origin. The iframe identity is also maintained across the sandbox boundary, so that checking the identity of postMessage event origin works normally. Lightning Locker blocks access to content in iframes.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .