Easily add secure digital signatures to your PDFs using cryptographic certificates. A digital signature acts like a unique fingerprint, verifying the sender’s identity and ensuring document authenticity.
This demo allows you to:
Implementation steps
To add Digital Signature capability with WebViewer:
Step 1: Get started with WebViewer in your preferred web stack
Step 2: Add the ES6 JavaScript sample code provided in this guide
1// ES6 Compliant Syntax
2// Copilot name: GitHub Copilot, version: 1.0.0, model: GPT-4, version: 2024-06, date: 2025-10-13
3// File: showcase-demos/digital-signatures/index.js
4
5import WebViewer from '@pdftron/webviewer';
6
7function initializeWebViewer() {
8 WebViewer(
9 {
10 path: '/lib',
11 initialDoc: 'https://apryse.s3.us-west-1.amazonaws.com/public/files/samples/digital_signature_walkthrough.pdf',
12 enableFilePicker: true, // Enable file picker to open files. In WebViewer -> menu icon -> Open File
13 fullAPI: true, // Enable the full API to access PDFNet and signature features.
14 licenseKey: 'YOUR_LICENSE_KEY', // Replace with your license key
15 },
16 document.getElementById('viewer')
17 ).then((instance) => {
18 const { UI } = instance;
19 const { documentViewer, Tools, Annotations, annotationManager } = instance.Core;
20
21 // Set the toolbar group to the Fill & Sign tools
22 UI.setToolbarGroup('toolbarGroup-FillAndSign');
23
24 documentViewer.addEventListener('documentLoaded', () => {
25 widgetsToDigitallySign = [];
26 });
27
28 UI.VerificationOptions.addTrustedCertificates([certificate]);
29
30 // Sets the Signature Tool to sign with appearance mode for use with digital signatures
31 const tool = documentViewer.getTool(Tools.ToolNames.SIGNATURE);
32 tool.setSigningMode(Tools.SignatureCreateTool.SigningModes.APPEARANCE);
33
34 // Customize the webviewer left panel
35 UIElements.customizeUI(instance);
36
37 UI.openElements([UIElements.tabPanel.dataElement]);
38 UI.setPanelWidth(UIElements.tabPanel.dataElement, 400);
39
40 // Capture the signature fields that are signed by the user
41 annotationManager.addEventListener('annotationChanged', async (annotations, action) => {
42 const actionsOfInterest = ['add', 'delete'];
43
44 if (actionsOfInterest.includes(action)) {
45 const signatureWidgetAnnots = annotationManager
46 .getAnnotationsList()
47 .filter((annot) => annot instanceof Annotations.SignatureWidgetAnnotation);
48
49 const widgetsWithSignatures = signatureWidgetAnnots.filter(
50 (widget) => widget.isSignedByAppearance() || widget.getAssociatedSignatureAnnotation()
51 );
52 // If signature field is signed, enable the apply approval button
53 const widgetsToSign = widgetsWithSignatures.map((widget) => {
54 if (widget.isSignedByAppearance()) {
55 const applyApprovalButton = UIElements.digitalSignaturePanel.render.querySelector('#applyApprovalButton');
56 applyApprovalButton.disabled = false;
57 applyApprovalButton.style.backgroundColor = 'blue';
58 applyApprovalButton.style.color = 'white';
59 }
60 return {
61 label: widget.getField().name,
62 };
63 });
64
65 widgetsToDigitallySign = widgetsToSign;
66 console.log('Annotation changed:', annotations, action, widgetsToSign);
67 }
68 });
69 console.log('WebViewer loaded successfully.');
70 }).catch((error) => {
71 console.error('Failed to initialize WebViewer:', error);
72 });
73}
74
75// Apply the digital signature approval
76window.applyApproval = async (instance) => {
77 const { UI } = instance;
78 const { annotationManager, SaveOptions, PDFNet, documentViewer } = instance.Core;
79 const xfdfString = await annotationManager.exportAnnotations();
80 const data = await documentViewer.getDocument().getFileData({
81 xfdfString,
82 flags: SaveOptions.INCREMENTAL,
83 });
84
85 await PDFNet.initialize();
86 await PDFNet.runWithCleanup(async () => {
87 const doc = await PDFNet.PDFDoc.createFromBuffer(new Uint8Array(data));
88 const digSigFieldIterator = await doc.getDigitalSignatureFieldIteratorBegin();
89 let foundOneDigitalSignature = false;
90 for (digSigFieldIterator; await digSigFieldIterator.hasNext(); digSigFieldIterator.next()) {
91 const field = await digSigFieldIterator.current();
92 if (await field.hasVisibleAppearance()) {
93 foundOneDigitalSignature = true;
94 break;
95 }
96 }
97 await doc.lock();
98
99 try {
100 /**
101 * Create a deep copy of widgetsToDigitallySign so that we can safely
102 * modify the contents of the array if needed (i.e. if we want to push
103 * a field to the array in the event that the document has no fields, or
104 * the user chose not to sign a signature field)
105 */
106 const widgetsToSign = JSON.parse(JSON.stringify(widgetsToDigitallySign));
107
108 /**
109 * If the user did not explicitly sign a field in the document,
110 * arbitrarily create an invisible signature field
111 */
112 if (!widgetsToSign.length) {
113 const fieldName = 'Signature1-invisible';
114 const field = await doc.fieldCreate(fieldName, PDFNet.Field.Type.e_signature);
115 const page1 = await doc.getPage(1);
116 const widgetAnnot = await PDFNet.WidgetAnnot.create(
117 await doc.getSDFDoc(),
118 await PDFNet.Rect.init(0, 0, 0, 0),
119 field
120 );
121 page1.annotPushBack(widgetAnnot);
122 widgetAnnot.setPage(page1);
123 const widgetObj = await widgetAnnot.getSDFObj();
124 widgetObj.putNumber('F', 132);
125 widgetObj.putName('Type', 'Annot');
126 widgetsToSign.push({
127 label: fieldName,
128 });
129 }
130
131 const visited = [];
132 let buf;
133
134 for (let i = 0; i < widgetsToSign.length; i++) {
135 let sigField;
136 const widgetFieldName = widgetsToSign[i].label;
137 const fieldIterator = await doc.getFieldIteratorBegin();
138 for (; await fieldIterator.hasNext(); fieldIterator.next()) {
139 const field = await fieldIterator.current();
140 if (
141 !(await field.isValid()) ||
142 (await field.getType()) !== PDFNet.Field.Type.e_signature
143 ) {
144 continue;
145 }
146 const fieldName = await field.getName();
147 if (!visited.includes(fieldName) && widgetFieldName === fieldName) {
148 visited.push(fieldName);
149 sigField = await PDFNet.DigitalSignatureField.createFromField(field);
150 break;
151 }
152 }
153 if (!sigField) {
154 /**
155 * A guard clause in-case a field with the given `label` could not
156 * be found, but this should never happen, as widgetInfo.label
157 * can only be programmatically populated from a user interacting
158 * with an existing field in the document
159 */
160 throw Error('The document does not contain a signature field');
161 }
162 if (!foundOneDigitalSignature) {
163 /**
164 * No Signature Field with a Cryptographic signature was found in
165 * the document, therefore we should explicitly set DocMDP
166 */
167 await sigField.setDocumentPermissions(
168 PDFNet.DigitalSignatureField.DocumentPermissions
169 .e_annotating_formfilling_signing_allowed
170 );
171 }
172 // Determine whether to sign with a digitalID (selected file) or the default certificate URL.
173 if (UIElements.certificateUrl !== null) {
174 if (UIElements.certificateUrl !== String.empty &&
175 UIElements.certificateUrl !== defaultCertificateUrl)
176 digitalID = UIElements.certificateUrl;
177 }
178 // Sign with a digitalID (selected file)
179 if (digitalID) {
180 const fileArrayBuffer = await digitalID.arrayBuffer();
181 await sigField.signOnNextSaveFromBuffer(fileArrayBuffer, UIElements.password);
182 }
183 // Sign with the default certificate URL
184 else
185 await sigField.signOnNextSaveFromURL(UIElements.certificateUrl, UIElements.password);
186
187 // Set optional signature information
188 await sigField.setLocation(UIElements.signatureInformation[0].value);
189 await sigField.setReason(UIElements.signatureInformation[1].value);
190 await sigField.setContactInfo(UIElements.signatureInformation[2].value);
191
192 buf = await doc.saveMemoryBuffer(PDFNet.SDFDoc.SaveOptions.e_incremental);
193 }
194 const blob = new Blob([buf], { type: 'application/pdf' });
195 UI.loadDocument(blob, { filename: documentViewer.getDocument().filename });
196 } catch (e) {
197 console.log(e);
198 UI.showWarningMessage({
199 title: 'Digital ID Error',
200 message:
201 'There is an issue with the Digital ID file or password. The private key could not be parsed.',
202 });
203 }
204 });
205};
206
207// Open the signature panel and verify the signature
208window.verifySignature = (instance) => {
209 instance.UI.setActiveTabInPanel({ tabPanel: UIElements.tabPanel.dataElement, tabName: 'signaturePanel' });
210 setTimeout(() => {
211 const shadowRoot = document.getElementById('wc-viewer').shadowRoot;
212 const signaturePanelElement = shadowRoot.querySelector('[data-element="signaturePanel"]');
213 const signaturePanelButtons = Array.from(signaturePanelElement.querySelectorAll('button'));
214 const signaturePanelExpandButton = signaturePanelButtons.find((element) => {
215 return element.ariaLabel.includes('Expand Signed by Apryse');
216 });
217 if (signaturePanelExpandButton)
218 signaturePanelExpandButton.click();
219 setTimeout(() => {
220 const verifyButton = signaturePanelElement.querySelector(
221 'button[aria-label="Signature Details"]'
222 );
223 if (verifyButton)
224 verifyButton.click();
225 }, 200);
226 }, 500);
227};
228
229// Clear the digital ID information
230window.clearDigitalIDInformation = (instance) => {
231 instance.UI.showWarningMessage({
232 title: 'Confirm Clearing Digital ID Information',
233 message:
234 'This will reset the inputted password and clear the uploaded .pfx file. Are you sure?',
235 onConfirm: () => {
236 UIElements.certificateUrl = defaultCertificateUrl;
237 UIElements.password = 'password';
238 digitalID = null;
239
240 // Reset the digital ID file name label and password field
241 const digitalIDFileNameLabel = UIElements.digitalSignaturePanel.render.querySelector('#digitalIDFileNameLabel');
242 digitalIDFileNameLabel.textContent = '';
243 const passwordField = UIElements.digitalSignaturePanel.render.querySelector('#inputPassword');
244 passwordField.value = UIElements.password;
245 passwordField.disabled = true;
246 }
247 });
248};
249
250//helper function to load the ui-elements.js script
251function loadUIElementsScript() {
252 return new Promise((resolve, reject) => {
253 if (window.UIElements) {
254 console.log('UIElements already loaded');
255 resolve();
256 return;
257 }
258 const script = document.createElement('script');
259 script.src = '/showcase-demos/digital-signatures/ui-elements.js';
260 script.onload = function () {
261 console.log('✅ UIElements script loaded successfully');
262 resolve();
263 };
264 script.onerror = function () {
265 console.error('Failed to load UIElements script');
266 reject(new Error('Failed to load ui-elements.js'));
267 };
268 document.head.appendChild(script);
269 });
270}
271
272// The url to the PKCS #12 private keyfile to use to certify this digital signature.
273const defaultCertificateUrl = '/assets/certificates/apryse.pfx';
274// The X.509 Public Key Certificates to be used for validating Digital Signatures on a document.
275const certificate = '/assets/certificates/apryse.cer';
276// The annotation widgets to sign
277let widgetsToDigitallySign = [];
278// The digital ID file (PKCS #12) selected by the user
279let digitalID = null;
280// Load UIElements script first, then initialize WebViewer
281loadUIElementsScript().then(() => {
282 initializeWebViewer();
283}).catch((error) => {
284 console.error('Failed to load UIElements:', error);
285});
286
1// ES6 Compliant Syntax
2// Copilot name: GitHub Copilot, version: 1.0.0, model: GPT-4, version: 2024-06, date: 2025-10-13
3// File: showcase-demos/digital-signatures/ui-elements.js
4
5// Class with static UI elements and related functions for the digital signature demo
6
7class UIElements {
8
9 // The list of registered panels in the webviewer
10 static viewerPanels = null;
11
12 // The tab panel, representing the webviewer left panel
13 static tabPanel = {
14 handle: null,
15 dataElement: 'tabPanel'
16 };
17
18 // The digital signature sub-panel to be registered
19 static digitalSignaturePanel = {
20 handle: null,
21 dataElement: 'digitalSignaturePanel',
22 render: null,
23 };
24
25 // The password to open the private key file
26 static password = 'password';
27
28 static signatureInformation = [
29 {
30 id: 'Location',
31 label: 'Location',
32 value: 'Vancouver, BC, Canada'
33 },
34 {
35 id: 'Reason',
36 label: 'Reason',
37 value: 'Cryptographic signature demo'
38 },
39 {
40 id: 'ContactInfo',
41 label: 'Contact Information',
42 value: 'apryse.com'
43 }
44 ];
45
46 // The url to the PKCS #12 private keyfile to use to certify this digital signature.
47 static certificateUrl = '/assets/certificates/apryse.pfx';
48
49 // Customize the webviewer left panel
50 static customizeUI = (instance) => {
51 const { UI } = instance;
52
53 // Close the tab panel (if it's open) for refreshment.
54 UI.closeElements([UIElements.tabPanel.dataElement]);
55
56 // Get the list of registered panels in the webviewer
57 UIElements.viewerPanels = UI.getPanels();
58
59 // Find the Tab Panel to modify. The digital signature sub-panel will be added to this Tab panel.
60 UIElements.tabPanel.handle = UIElements.viewerPanels.find((panel) => panel.dataElement === UIElements.tabPanel.dataElement);
61
62 // Register the digital signature sub-panel
63 UIElements.RegisterDigitalSignaturePanel(instance);
64
65 // Add the new digital signature sub-panel to list of sub-panels under the Tab Panel
66 UIElements.digitalSignaturePanel.handle = { render: UIElements.digitalSignaturePanel.dataElement };
67 UIElements.tabPanel.handle.panelsList = [UIElements.digitalSignaturePanel.handle, ...UIElements.tabPanel.handle.panelsList];
68 };
69
70 // Register the digital signature sub-panel
71 static RegisterDigitalSignaturePanel = (instance) => {
72 UIElements.digitalSignaturePanel.render = UIElements.createDigitalSignaturePanelElements(instance);
73 instance.UI.addPanel({
74 dataElement: UIElements.digitalSignaturePanel.dataElement,
75 location: 'left',
76 icon: '<svg fill="#000000" width="100px" height="100px" viewBox="0 0 64 64" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="m 1.0324444,11.139308 c 0.0179,-0.1218 0.061,-0.2215 0.0958,-0.2215 0.0348,0 0.0633,-0.064 0.0633,-0.1428 0,-0.079 0.0321,-0.1428 0.0714,-0.1428 0.0393,0 0.0714,-0.064 0.0714,-0.1427 0,-0.079 0.0321,-0.1428 0.0714,-0.1428 0.0393,0 0.0714,-0.047 0.0714,-0.1045 0,-0.058 0.08,-0.2479001 0.17776,-0.4230001 0.12606,-0.2258 0.16557,-0.3794 0.13583,-0.528 -0.0254,-0.1269 0.002,-0.2942 0.0687,-0.4236 0.0608,-0.1177 0.18066,-0.4193 0.26627,-0.6702 0.0856,-0.251 0.17774,-0.4885 0.20472,-0.5277 0.027,-0.039 0.0925,-0.216 0.14571,-0.3927 0.0532,-0.1766 0.1232,-0.3517 0.15563,-0.389 0.0324,-0.037 0.059,-0.1417 0.059,-0.232 0,-0.09 0.0321,-0.1642 0.0714,-0.1642 0.0393,0 0.0714,-0.094 0.0714,-0.21 0,-0.1154 0.0321,-0.2298 0.0714,-0.254 0.0393,-0.024 0.073,-0.099 0.0749,-0.1649 0.002,-0.066 0.16547,-0.2492 0.36338,-0.4062 0.1979,-0.1571 0.43577,-0.3579 0.5286,-0.4462 0.0928,-0.088 0.1866,-0.1606 0.20838,-0.1606 0.0218,0 0.13637,-0.093 0.25466,-0.2056 0.11829,-0.113 0.3509,-0.2977 0.51691,-0.4104 0.16601,-0.1128 0.31254,-0.2291 0.32563,-0.2585 0.0131,-0.029 0.0683,-0.053 0.12276,-0.053 0.0544,0 0.21335,-0.064 0.35316,-0.1428 0.26925,-0.1512 0.60679,-0.1909 0.60679,-0.071 0,0.039 0.0421,0.071 0.0936,0.071 0.0515,0 0.15589,0.058 0.23201,0.1285 0.24872,0.231 0.37942,0.24 0.55068,0.038 0.30396,-0.3586 1.0957,-1.1308 1.25041,-1.2194 0.18638,-0.1068 0.51461,-0.1182 0.51461,-0.018 0,0.039 0.043,0.071 0.0956,0.071 0.12754,0 0.26131,0.3072 0.26131,0.6002 0,0.2369 -0.24982,0.6817 -0.50955,0.9073 -0.0731,0.063 -0.13294,0.139 -0.13294,0.1677 0,0.029 -0.0964,0.1422 -0.21416,0.2523 -0.23429,0.2188 -0.26247,0.3229 -0.12217,0.4512 0.18171,0.1662 0.89017,0.5482 1.01669,0.5482 0.0577,0 0.10491,0.029 0.10491,0.063 0,0.035 0.20949,0.1202 0.46554,0.1895 0.4572796,0.1237 0.4683696,0.1234 0.6246396,-0.018 0.1522,-0.1379 0.20395,-0.1411 1.19421,-0.074 0.56932,0.038 1.0438,0.078 1.05441,0.087 0.0106,0.01 0.0719,0.2706 0.13625,0.5806 0.22137,1.0669 0.1397,2.7256 -0.19548,3.9704001 l -0.0726,0.2698 -1.10376,0 -1.10375,0 0,-0.2142 c 0,-0.1178 -0.0321,-0.2142 -0.0714,-0.2142 -0.0393,0 -0.0714,-0.068 -0.0714,-0.1504 0,-0.1163 -0.0283,-0.1381 -0.12493,-0.096 -0.0687,0.03 -0.2373696,0.067 -0.3747896,0.083 -0.13742,0.016 -0.31799,0.063 -0.40128,0.1034 -0.15948,0.078 -0.59017,0.053 -1.4191,-0.084 -0.56756,-0.093 -0.48797,-0.091 -1.12627,-0.028 -0.26608,0.026 -0.50088,0.076 -0.52177,0.1096 -0.0539,0.087 -1.79571,0.078 -1.84994,-0.01 -0.0243,-0.039 -0.15276,-0.071 -0.28555,-0.071 -0.13279,0 -0.26128,-0.032 -0.28555,-0.071 -0.0243,-0.039 -0.15836,-0.071 -0.29798,-0.071 -0.20152,0 -0.30517,0.055 -0.50271,0.2677 -0.13687,0.1473 -0.29749,0.34 -0.35693,0.4284 -0.0594,0.088 -0.16674,0.1606 -0.23843,0.1606 -0.0717,0 -0.15021,0.032 -0.17447,0.071 -0.0243,0.039 -0.10154,0.071 -0.17172,0.071 -0.0702,0 -0.22087,0.046 -0.33487,0.1034 -0.11401,0.057 -0.33873,0.1244 -0.49939,0.1501 l -0.29211,0.047 0.0325,-0.2215 z m 0.83725,-0.2929 c 0.0243,-0.039 0.10648,-0.071 0.18268,-0.071 0.0762,0 0.13857,-0.032 0.13857,-0.071 0,-0.039 0.0482,-0.071 0.10708,-0.071 0.0589,0 0.10708,-0.048 0.10708,-0.1065 0,-0.1314 -0.28157,-0.4646 -0.39263,-0.4646 -0.11106,0 -0.39263,0.3332 -0.39263,0.4646 0,0.059 -0.0321,0.1065 -0.0714,0.1065 -0.0393,0 -0.0714,0.064 -0.0714,0.1428 0,0.1038 0.0476,0.1428 0.17426,0.1428 0.0958,0 0.19411,-0.032 0.21837,-0.071 z m 9.7914796,-0.4796 c 0.56959,-0.044 0.51279,0.02 0.6796,-0.7697001 0.10998,-0.5207 0.15529,-2.1091 0.0628,-2.2016 -0.0416,-0.042 -0.0756,-0.1661 -0.0756,-0.2766 0,-0.1106 -0.0399,-0.329 -0.0888,-0.4855 l -0.0888,-0.2844 -0.54608,0 c -0.62148,0 -0.64971,0.026 -0.55725,0.5046 0.11336,0.5873 0.075,1.9136 -0.0772,2.6663 -0.15299,0.7568001 -0.13618,1.0112001 0.0617,0.9332001 0.0652,-0.026 0.34848,-0.064 0.62959,-0.086 z M 6.1529444,9.9283079 c 0.15705,-0.042 0.48897,-0.1306 0.73759,-0.1971 0.4275,-0.1144 0.48566,-0.1141 1.07082,0.01 0.34032,0.07 0.81151,0.1273 1.04709,0.1279 0.50257,0.001 1.3830896,-0.1952 1.5309896,-0.3415 0.10718,-0.1061 0.23238,-0.8318 0.23976,-1.3898 0.005,-0.3722 -0.0687,-0.9074 -0.18342,-1.3327 -0.0786,-0.2915 -0.0876,-0.2983 -0.44116,-0.3348 -0.5742096,-0.059 -0.8963196,-0.1281 -0.8963196,-0.1915 0,-0.032 -0.0723,-0.082 -0.16062,-0.1096 -0.0883,-0.028 -0.36468,-0.1746 -0.61409,-0.326 -0.24941,-0.1514 -0.48231,-0.2753 -0.51756,-0.2753 -0.0352,0 -0.0641,-0.032 -0.0641,-0.071 0,-0.1875 -0.27918,-0.03 -0.65538,0.3706 -0.37178,0.3956 -0.41543,0.474 -0.41543,0.7454 0,0.1668 -0.0321,0.3232 -0.0714,0.3474 -0.0393,0.024 -0.0714,0.1069 -0.0714,0.1837 0,0.1551 -0.11414,0.3784 -0.30339,0.5936 -0.0687,0.078 -0.12493,0.1681 -0.12493,0.1999 0,0.1 0.34485,0.063 0.75135,-0.079 0.38822,-0.1364 0.39085,-0.1364 0.39085,0 0,0.078 -0.0993,0.2321 -0.22063,0.3429 l -0.22062,0.2015 -1.10194,0 c -0.7526,0 -1.12849,0.023 -1.18571,0.08 -0.0461,0.046 -0.17371,0.084 -0.28365,0.084 -0.10994,0 -0.19989,0.032 -0.19989,0.071 0,0.039 -0.0642,0.071 -0.14277,0.071 -0.0785,0 -0.14278,0.027 -0.14278,0.059 0,0.033 -0.0779,0.101 -0.17302,0.152 -0.18219,0.098 -0.28332,0.3465 -0.21403,0.5271 0.0461,0.1201 0.52553,0.3324 0.75054,0.3324 0.0805,0 0.19232,-0.046 0.24841,-0.1019 0.1151,-0.1151 0.14054,-0.6833 0.0306,-0.6833 -0.0393,0 -0.0714,-0.064 -0.0714,-0.1428 0,-0.1852 0.0407,-0.18 0.25311,0.033 0.12743,0.1274 0.17522,0.2528 0.17522,0.4598 0,0.1565 -0.0321,0.3044 -0.0714,0.3287 -0.13127,0.081 -0.0734,0.2215 0.12493,0.3028 0.22116,0.091 0.76798,0.071 1.19574,-0.043 z m -3.49888,-1.0793 c 0.20573,-0.3259 0.22956,-0.6039 0.0658,-0.7677 -0.0966,-0.097 -0.12355,-0.099 -0.1804,-0.013 -0.0368,0.055 -0.0861,0.1892 -0.10953,0.2971 -0.0235,0.108 -0.0656,0.1964 -0.0937,0.1964 -0.0642,0 -0.2167,0.3289 -0.2167,0.4673 0,0.063 0.0699,0.1038 0.17757,0.1038 0.12939,0 0.22625,-0.077 0.35694,-0.2842 z m 0.48514,0.048 c 0.26307,-0.271 0.4081,-0.5016 0.4081,-0.6489 0,-0.056 0.0241,-0.1128 0.0535,-0.1259 0.0294,-0.013 0.13385,-0.1992 0.23201,-0.4135 0.0982,-0.2144 0.31499,-0.5268 0.48186,-0.6942 0.16687,-0.1674 0.3034,-0.321 0.3034,-0.3412 0,-0.068 0.24679,-0.3411 1.08866,-1.2037 0.46134,-0.4727 0.8388,-0.888 0.8388,-0.9229 0,-0.1533 -0.39705,-0.4103 -0.63371,-0.4103 -0.16776,0 -0.63626,0.2586 -0.80241,0.443 -0.0635,0.071 -0.14859,0.1281 -0.18909,0.1281 -0.0405,0 -0.23766,0.1606 -0.43816,0.3569 -0.20049,0.1963 -0.3832,0.3569 -0.40602,0.3569 -0.0846,0 -0.78643,0.7789 -0.8785,0.9749 -0.0525,0.1117 -0.12374,0.2588 -0.15842,0.327 -0.0347,0.068 -0.0631,0.1726 -0.0631,0.232 0,0.059 -0.0321,0.1081 -0.0714,0.1081 -0.0393,0 -0.0714,0.094 -0.0714,0.2082 0,0.1145 -0.0388,0.2211 -0.0862,0.2369 -0.0576,0.019 -0.0357,0.083 0.0658,0.1919 0.22734,0.2441 0.34549,0.5655 0.24483,0.6662 -0.0449,0.045 -0.0817,0.1537 -0.0817,0.2417 0,0.088 -0.0321,0.1798 -0.0714,0.2041 -0.0732,0.045 -0.10182,0.3212 -0.0333,0.3212 0.0209,0 0.1414,-0.1064 0.2677,-0.2365 z m 2.72831,-1.0663 c 0.13638,-0.088 0.24836,-0.2008 0.24885,-0.2499 4.8e-4,-0.049 0.033,-0.089 0.0723,-0.089 0.0393,0 0.0714,-0.064 0.0714,-0.1428 0,-0.079 0.0321,-0.1427 0.0714,-0.1427 0.0393,0 0.0714,-0.08 0.0714,-0.1785 0,-0.2216 -0.0932,-0.2288 -0.23214,-0.018 -0.0582,0.088 -0.26617,0.3284 -0.4622,0.5335 -0.19602,0.2051 -0.3395,0.3899 -0.31884,0.4105 0.0753,0.075 0.23533,0.034 0.4779,-0.123 z"/></svg>',
77 title: 'Digital Signature',
78 render: () => UIElements.digitalSignaturePanel.render,
79 });
80 };
81
82 // Create the digital signature panel elements.
83 static createDigitalSignaturePanelElements = (instance) => {
84 let panelDiv = document.createElement('div');
85 panelDiv.id = 'digitalSignaturePanel';
86
87 let paragraph = document.createTextNode('In this demo, a digital signature will be applied to the first signature field or an invisible signature field will be added. Sign PDFs with certificates using our JavaScript digital signature library and securely protect digital documents by creating a signing fingerprint uniquely identifying a sender.');
88 panelDiv.appendChild(paragraph);
89
90 let dividerDiv = document.createElement('div');
91 dividerDiv.style.borderTop = '1px solid #ccc';
92 dividerDiv.style.margin = '10px 0';
93 panelDiv.appendChild(dividerDiv);
94
95 // Digital ID division
96 let digitalIDDiv = document.createElement('div');
97 digitalIDDiv.id = 'digitalIDDiv';
98
99 let digitalIDDivTitle = document.createElement("h3");
100 digitalIDDivTitle.textContent = "Digital ID (Optional)";
101 digitalIDDiv.appendChild(digitalIDDivTitle);
102
103 paragraph = document.createTextNode('A default Apryse Digital ID will be used if no Digital ID is provided. We encourage using a non-confidential Digital ID file, but none of the provided data is saved to Apryse servers.');
104 digitalIDDiv.appendChild(paragraph);
105
106 panelDiv.appendChild(digitalIDDiv);
107
108 // Digital ID File button
109 let digitalIDFileButton = document.createElement('button');
110 digitalIDFileButton.textContent = 'Select Digital ID File';
111 enableButton(digitalIDFileButton, true);
112 digitalIDFileButton.onclick = () => {
113 // file input field
114 const inputFile = document.createElement('input');
115 inputFile.id = 'inputFile';
116 inputFile.type = 'file';
117 inputFile.style.display = 'none';
118 inputFile.accept = '.pfx';
119 inputFile.onchange = e => {
120 UIElements.certificateUrl = e.target.files[0];
121 if (UIElements.certificateUrl !== null) {
122 enableButton(clearDigitalIDButton, true);
123 digitalIDFileNameLabel.textContent = `${UIElements.certificateUrl.name}`;
124 }
125 passwordField.disabled = false;
126 }
127 inputFile.click();
128 }
129 panelDiv.appendChild(document.createElement('p'));
130 panelDiv.appendChild(digitalIDFileButton);
131
132 // Digital ID File name label
133 panelDiv.appendChild(document.createElement('p'));
134 const digitalIDFileNameLabel = document.createElement('span');
135 digitalIDFileNameLabel.id = 'digitalIDFileNameLabel';
136 digitalIDFileNameLabel.textContent = '';
137 panelDiv.appendChild(digitalIDFileNameLabel);
138
139 // Password label
140 panelDiv.appendChild(document.createElement('p'));
141 panelDiv.appendChild(document.createTextNode('Digital ID Password:'));
142 panelDiv.appendChild(document.createElement('p'));
143
144 // Password input field
145 const passwordField = document.createElement('input');
146 passwordField.id = 'inputPassword';
147 passwordField.type = 'password';
148 passwordField.disabled = true;
149 passwordField.value = UIElements.password;
150 passwordField.style.width = '100%';
151 passwordField.addEventListener("change", () => { UIElements.password = passwordField.value.trim(); });
152 panelDiv.appendChild(passwordField);
153
154 // Show password button
155 let showPasswordButton = document.createElement('button');
156 showPasswordButton.textContent = 'Show';
157 enableButton(showPasswordButton, true);
158 showPasswordButton.onclick = () => {
159 showPasswordButton.textContent = showPasswordButton.textContent === 'Show' ? 'Hide' : 'Show';
160 passwordField.type = passwordField.type === 'password' ? 'text' : 'password';
161 };
162 panelDiv.appendChild(showPasswordButton);
163
164 // Clear Digital ID File button
165 panelDiv.appendChild(document.createElement('p'));
166 let clearDigitalIDButton = document.createElement('button');
167 clearDigitalIDButton.textContent = 'Clear Digital ID Information';
168 enableButton(clearDigitalIDButton, false);
169 clearDigitalIDButton.onclick = () => {
170 clearDigitalIDInformation(instance);
171 passwordField.value = UIElements.password;
172 enableButton(clearDigitalIDButton, false);
173 }
174 panelDiv.appendChild(clearDigitalIDButton);
175
176 panelDiv.appendChild(dividerDiv.cloneNode());
177
178 // Signature Information division
179 let signatureInfoDiv = document.createElement('div');
180 signatureInfoDiv.id = 'signatureInfoDiv';
181
182 let signatureInfoDivTitle = document.createElement("h3");
183 signatureInfoDivTitle.textContent = "Signature Information (Optional)";
184 signatureInfoDiv.appendChild(signatureInfoDivTitle);
185 panelDiv.appendChild(signatureInfoDiv);
186
187 // signature information labels and input fields
188 UIElements.signatureInformation.forEach(info => {
189 // input field label
190 panelDiv.appendChild(document.createElement('p'));
191 panelDiv.appendChild(document.createTextNode(`${info.label}:`));
192 panelDiv.appendChild(document.createElement('p'));
193
194 // input field
195 const inputField = document.createElement('input');
196 inputField.id = `input${info.id}`;
197 inputField.type = 'text';
198 inputField.value = info.value;
199 inputField.style.width = '100%';
200 inputField.addEventListener("input", () => info.value = inputField.value.trim());
201 panelDiv.appendChild(inputField);
202 });
203
204 // Apply Approval Signature button
205 let applyApprovalButton = document.createElement('button');
206 applyApprovalButton.id = 'applyApprovalButton';
207 applyApprovalButton.textContent = 'Apply Approval Signature';
208 enableButton(applyApprovalButton, false);
209 applyApprovalButton.onclick = () => {
210 applyApproval(instance);
211 enableButton(verifySignatureButton, true);
212 }
213
214 panelDiv.appendChild(document.createElement('p'));
215 panelDiv.appendChild(applyApprovalButton);
216
217 // Verify Signature button
218 let verifySignatureButton = document.createElement('button');
219 verifySignatureButton.textContent = 'Verify Signature';
220 enableButton(verifySignatureButton, false);
221 verifySignatureButton.onclick = () => verifySignature(instance);
222
223 panelDiv.appendChild(document.createElement('p'));
224 panelDiv.appendChild(verifySignatureButton);
225
226 return panelDiv;
227 };
228}
229
230// Enable or disable a button based on the state
231const enableButton = (button, state) => {
232 button.disabled = !state;
233 button.style.backgroundColor = (state) ? 'blue' : 'gray';
234 button.style.color = (state) ? 'white' : 'darkgray';
235};
Did you find this helpful?
Trial setup questions?
Ask experts on DiscordNeed other help?
Contact SupportPricing or product questions?
Contact Sales