This sample is a ready-to-deploy Salesforce implementation of Apryse SDK's WebViewer. This Lightning Web Component (LWC) will have the ability to enable client-side viewing, editing, annotation and redaction and much more in your Salesforce environment using Salesforce files or external files. For more information, see this blog.
WebViewer provides a slick out-of-the-box responsive UI that enables you to view, annotate and manipulate PDFs and other document types inside any web project.
Click the button below to view the full project in GitHub.
1import { LightningElement, wire, track, api } from "lwc";
2import { CurrentPageReference } from "lightning/navigation";
3import { loadScript } from "lightning/platformResourceLoader";
4import libUrl from "@salesforce/resourceUrl/lib";
5import myfilesUrl from "@salesforce/resourceUrl/myfiles";
6import { ShowToastEvent } from "lightning/platformShowToastEvent";
7import mimeTypes from "./mimeTypes";
8import { fireEvent, registerListener, unregisterAllListeners } from "c/pubsub";
9import saveDocument from "@salesforce/apex/PDFTron_ContentVersionController.saveDocument";
10import getUser from "@salesforce/apex/PDFTron_ContentVersionController.getUser";
11
12function _base64ToArrayBuffer(base64) {
13 var binary_string = window.atob(base64);
14 var len = binary_string.length;
15 var bytes = new Uint8Array(len);
16 for (let i = 0; i < len; i++) {
17 bytes[i] = binary_string.charCodeAt(i);
18 }
19 return bytes.buffer;
20}
21
22export default class PdftronWvInstance extends LightningElement {
23 //initialization options
24 fullAPI = true;
25 enableRedaction = true;
26 enableFilePicker = true;
27
28 uiInitialized = false;
29
30 source = "My file";
31 @api recordId;
32
33 @wire(CurrentPageReference)
34 pageRef;
35
36 username;
37
38 connectedCallback() {
39 registerListener("blobSelected", this.handleBlobSelected, this);
40 registerListener("closeDocument", this.closeDocument, this);
41 registerListener("downloadDocument", this.downloadDocument, this);
42 window.addEventListener("message", this.handleReceiveMessage);
43 }
44
45 disconnectedCallback() {
46 unregisterAllListeners(this);
47 window.removeEventListener("message", this.handleReceiveMessage);
48 }
49
50 handleBlobSelected(record) {
51 const blobby = new Blob([_base64ToArrayBuffer(record.body)], {
52 type: mimeTypes[record.FileExtension]
53 });
54
55 const payload = {
56 blob: blobby,
57 extension: record.cv.FileExtension,
58 filename: record.cv.Title + "." + record.cv.FileExtension,
59 documentId: record.cv.Id
60 };
61 console.log("payload", payload);
62 this.iframeWindow.postMessage({ type: "OPEN_DOCUMENT_BLOB", payload }, "*");
63 }
64
65 renderedCallback() {
66 var self = this;
67
68 if (this.uiInitialized) {
69 return;
70 }
71
72 Promise.all([loadScript(self, libUrl + "/webviewer.min.js")])
73 .then(() => this.handleInitWithCurrentUser())
74 .catch(console.error);
75 }
76
77 handleInitWithCurrentUser() {
78 getUser()
79 .then((result) => {
80 this.username = result;
81 this.error = undefined;
82
83 this.initUI();
84 })
85 .catch((error) => {
86 console.error(error);
87 this.showNotification("Error", error.body.message, "error");
88 });
89 }
90
91 initUI() {
92 var myObj = {
93 libUrl: libUrl,
94 fullAPI: this.fullAPI || false,
95 namespacePrefix: "",
96 username: this.username
97 };
98 var url = myfilesUrl + "/webviewer-demo-annotated.pdf";
99
100 const viewerElement = this.template.querySelector("div");
101 // eslint-disable-next-line no-unused-vars
102 const viewer = new WebViewer.Iframe(
103 {
104 path: libUrl, // path to the PDFTron 'lib' folder on your server
105 custom: JSON.stringify(myObj),
106 backendType: "ems",
107 config: myfilesUrl + "/config_apex.js",
108 fullAPI: this.fullAPI,
109 enableFilePicker: this.enableFilePicker,
110 enableRedaction: this.enableRedaction,
111 enableMeasurement: this.enableMeasurement,
112 enableOptimizedWorkers: true,
113 loadAsPDF: true,
114 // l: 'YOUR_LICENSE_KEY_HERE',
115 },
116 viewerElement
117 );
118
119 viewerElement.addEventListener("ready", () => {
120 this.iframeWindow = viewerElement.querySelector("iframe").contentWindow;
121 });
122 }
123
124 handleReceiveMessage = (event) => {
125 const me = this;
126 if (event.isTrusted && typeof event.data === "object") {
127 switch (event.data.type) {
128 case "SAVE_DOCUMENT":
129 let cvId = event.data.payload.contentDocumentId;
130 saveDocument({
131 json: JSON.stringify(event.data.payload),
132 recordId: this.recordId ? this.recordId : "",
133 cvId: cvId
134 })
135 .then((response) => {
136 me.iframeWindow.postMessage(
137 { type: "DOCUMENT_SAVED", response },
138 "*"
139 );
140 fireEvent(this.pageRef, "refreshOnSave", response);
141 })
142 .catch((error) => {
143 me.iframeWindow.postMessage(
144 { type: "DOCUMENT_SAVED", error },
145 "*"
146 );
147 fireEvent(this.pageRef, "refreshOnSave", error);
148 console.error(event.data.payload.contentDocumentId);
149 console.error(JSON.stringify(error));
150 this.showNotification("Error", error.body, "error");
151 });
152 break;
153 default:
154 break;
155 }
156 }
157 };
158
159 downloadDocument() {
160 this.iframeWindow.postMessage({ type: "DOWNLOAD_DOCUMENT" }, "*");
161 }
162
163 @api
164 closeDocument() {
165 this.iframeWindow.postMessage({ type: "CLOSE_DOCUMENT" }, "*");
166 }
167}
168
Did you find this helpful?
Trial setup questions?
Ask experts on DiscordNeed other help?
Contact SupportPricing or product questions?
Contact Sales