I'm trying to test the feasibility of using the library jsPDF to render a PDF from the HTML rendered by an LWC. I've created a simple test project in a scratch org as follows:
Created static resources for jsPDF and html2canvas, which is needed by jsPDF to read rendered html.
Created a shadow-dom LWC to act as the parent, since Light Dom components cannot be top-level components.
// shadowParent.html
<template>
<c-light-child ></c-light-child>
</template>
//shadowParent.js
import {LightningElement} from 'lwc';
export default class ShadowParent extends LightningElement {
}
- Created a light-dom LWC component to render some content and PDF it:
lightChild.html
<template lwc:render-mode="light">
<div class="main-container">
<h1>Light Dom Component</h1>
<div for:each={sections} for:item="section" key={section}>
<h2>{section}</h2>
<p>This is the {section} section.</p>
</div>
</div>
<button onclick={testPdf}>Test</button>
</template>
import {LightningElement} from 'lwc';
import { loadScript } from "lightning/platformResourceLoader";
import JS_PDF from '@salesforce/resourceUrl/jspdf';
import HTML2CANVAS from '@salesforce/resourceUrl/html2canvas';
export default class LightChild extends LightningElement {
static renderMode = 'light';
jsPdfInitialized = false;
sections = ['Heading One', 'Heading Two', 'Heading Three'];
renderedCallback() {
if (this.jsPdfInitialized) {
return;
}
this.jsPdfInitialized = true;
Promise.all([
loadScript(this, JS_PDF),
loadScript(this, HTML2CANVAS)
]).then(() => {
console.log('jsPdf loaded.');
});
}
testPdf() {
const content = this.querySelector('.main-container');
const { jsPDF } = window.jspdf;
const doc = new jsPDF();
doc.html(content, {
callback: function() {
console.log('callback...');
doc.save();
console.log('complete!');
}
});
}
}
Console logging has shown me that I appear to be getting a valid jsPDF object assigned to doc
, but no logging occurs from the callback; instead I get the error Lightning Web Security: Cannot access write:
aura_prod.js:53 Uncaught (in promise) Error: Lightning Web Security: Cannot access write.
at HTMLDocument.<anonymous> (aura_prod.js:53:2040)
at Object.apply (aura_prod.js:59:36251)
at aura_prod.js:59:21437
at wt.eval [as St] (eval at bc (aura_prod.js:59:27602), <anonymous>:3:4554)
at wt.eval (eval at bc (aura_prod.js:59:27602), <anonymous>:3:3993)
at fn.toIFrame (eval at <anonymous> (eval at bc (aura_prod.js:59:27602)), <anonymous>:20:147482)
at eval (eval at <anonymous> (eval at bc (aura_prod.js:59:27602)), <anonymous>:20:194607)
at eval (eval at <anonymous> (eval at bc (aura_prod.js:59:27602)), <anonymous>:20:1976)
at Object.eval [as next] (eval at <anonymous> (eval at bc (aura_prod.js:59:27602)), <anonymous>:20:2081)
at eval (eval at <anonymous> (eval at bc (aura_prod.js:59:27602)), <anonymous>:20:1023)
At this point, I'm not sure how to troubleshoot further. LWS runs everything in JS "Sandboxes", so all the stack trace lines point to aura_prod.js
instead of my code or the jsPDF code. The error indicates that a 'write' method is being called which is being disallowed, but I don't know where or why. I had hoped that by using Light DOM, jsPDF would be able to modify the DOM (I think it renders the HTML to a canvas element).
I've tried running in Debug mode; this causes hundreds of "Mutations on the membrane of an object originating outside of the sandbox will not be reflected on the object itself" messages, but no additional information about the actual error above.
I've tried turning off LWS and running under Locker Service; this causes loadScript
to blow up with an undocumented error, plus I don't expect LS to allow the DOM manipulation jsPDF needs.
What are my options for tracking down and possibly addressing this error?
servlet.FileDownload
url so I had to customize html2canvas to use SalesforceProxy headers and pass it a Proxy function to fetch them and convert them todata:
urls, and I ended up using pdflib to combine multiple pages into one PDF since I coudn't get jsPDF to handle mutlipage output correctly, but it is possible to use jsPDF from a VF page.