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.
Once you generate your license key, it will automatically be included in your sample code below.
Apryse collects some data regarding your usage of the SDK for product improvement.
The data that Apryse collects include:
For clarity, no other data is collected by the SDK and Apryse has no access to the contents of your documents.
If you wish to continue without data collection, contact us and we will email you a no-tracking trial key for you to get started.
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
7const licenseKey = 'YOUR_WEBVIEWER_LICENSE_KEY';
8
9function initializeWebViewer() {
10 WebViewer(
11 {
12 path: '/lib',
13 initialDoc: 'https://apryse.s3.us-west-1.amazonaws.com/public/files/samples/digital_signature_walkthrough.pdf',
14 enableFilePicker: true, // Enable file picker to open files. In WebViewer -> menu icon -> Open File
15 fullAPI: true, // Enable the full API to access PDFNet and signature features.
16 licenseKey: licenseKey, // Replace with your license key
17 },
18 document.getElementById('viewer')
19 ).then((instance) => {
20 const { UI } = instance;
21 const { documentViewer, Tools, Annotations, annotationManager } = instance.Core;
22
23 // Set the toolbar group to the Fill & Sign tools
24 UI.setToolbarGroup('toolbarGroup-FillAndSign');
25
26 documentViewer.addEventListener('documentLoaded', () => {
27 widgetsToDigitallySign = [];
28 });
29
30 UI.VerificationOptions.addTrustedCertificates([certificate]);
31
32 // Sets the Signature Tool to sign with appearance mode for use with digital signatures
33 const tool = documentViewer.getTool(Tools.ToolNames.SIGNATURE);
34 tool.setSigningMode(Tools.SignatureCreateTool.SigningModes.APPEARANCE);
35
36 // Customize the webviewer left panel
37 UIElements.customizeUI(instance);
38
39 UI.openElements([UIElements.tabPanel.dataElement]);
40 UI.setPanelWidth(UIElements.tabPanel.dataElement, 400);
41
42 // Capture the signature fields that are signed by the user
43 annotationManager.addEventListener('annotationChanged', async (annotations, action) => {
44 const actionsOfInterest = ['add', 'delete'];
45
46 if (actionsOfInterest.includes(action)) {
47 const signatureWidgetAnnots = annotationManager
48 .getAnnotationsList()
49 .filter((annot) => annot instanceof Annotations.SignatureWidgetAnnotation);
50
51 const widgetsWithSignatures = signatureWidgetAnnots.filter(
52 (widget) => widget.isSignedByAppearance() || widget.getAssociatedSignatureAnnotation()
53 );
54 // If signature field is signed, enable the apply approval button
55 const widgetsToSign = widgetsWithSignatures.map((widget) => {
56 if (widget.isSignedByAppearance()) {
57 const applyApprovalButton = UIElements.digitalSignaturePanel.render.querySelector('#applyApprovalButton');
58 applyApprovalButton.disabled = false;
59 applyApprovalButton.style.backgroundColor = 'blue';
60 applyApprovalButton.style.color = 'white';
61 }
62 return {
63 label: widget.getField().name,
64 };
65 });
66
67 widgetsToDigitallySign = widgetsToSign;
68 console.log('Annotation changed:', annotations, action, widgetsToSign);
69 }
70 });
71 console.log('WebViewer loaded successfully.');
72 }).catch((error) => {
73 console.error('Failed to initialize WebViewer:', error);
74 });
75}
76
77// Apply the digital signature approval
78window.applyApproval = async (instance) => {
79 const { UI } = instance;
80 const { annotationManager, SaveOptions, PDFNet, documentViewer } = instance.Core;
81 const xfdfString = await annotationManager.exportAnnotations();
82 const data = await documentViewer.getDocument().getFileData({
83 xfdfString,
84 flags: SaveOptions.INCREMENTAL,
85 });
86
87 await PDFNet.initialize();
88 await PDFNet.runWithCleanup(async () => {
89 const doc = await PDFNet.PDFDoc.createFromBuffer(new Uint8Array(data));
90 const digSigFieldIterator = await doc.getDigitalSignatureFieldIteratorBegin();
91 let foundOneDigitalSignature = false;
92 for (digSigFieldIterator; await digSigFieldIterator.hasNext(); digSigFieldIterator.next()) {
93 const field = await digSigFieldIterator.current();
94 if (await field.hasVisibleAppearance()) {
95 foundOneDigitalSignature = true;
96 break;
97 }
98 }
99 await doc.lock();
100
101 try {
102 /**
103 * Create a deep copy of widgetsToDigitallySign so that we can safely
104 * modify the contents of the array if needed (i.e. if we want to push
105 * a field to the array in the event that the document has no fields, or
106 * the user chose not to sign a signature field)
107 */
108 const widgetsToSign = JSON.parse(JSON.stringify(widgetsToDigitallySign));
109
110 /**
111 * If the user did not explicitly sign a field in the document,
112 * arbitrarily create an invisible signature field
113 */
114 if (!widgetsToSign.length) {
115 const fieldName = 'Signature1-invisible';
116 const field = await doc.fieldCreate(fieldName, PDFNet.Field.Type.e_signature);
117 const page1 = await doc.getPage(1);
118 const widgetAnnot = await PDFNet.WidgetAnnot.create(
119 await doc.getSDFDoc(),
120 await PDFNet.Rect.init(0, 0, 0, 0),
121 field
122 );
123 page1.annotPushBack(widgetAnnot);
124 widgetAnnot.setPage(page1);
125 const widgetObj = await widgetAnnot.getSDFObj();
126 widgetObj.putNumber('F', 132);
127 widgetObj.putName('Type', 'Annot');
128 widgetsToSign.push({
129 label: fieldName,
130 });
131 }
132
133 const visited = [];
134 let buf;
135
136 for (let i = 0; i < widgetsToSign.length; i++) {
137 let sigField;
138 const widgetFieldName = widgetsToSign[i].label;
139 const fieldIterator = await doc.getFieldIteratorBegin();
140 for (; await fieldIterator.hasNext(); fieldIterator.next()) {
141 const field = await fieldIterator.current();
142 if (
143 !(await field.isValid()) ||
144 (await field.getType()) !== PDFNet.Field.Type.e_signature
145 ) {
146 continue;
147 }
148 const fieldName = await field.getName();
149 if (!visited.includes(fieldName) && widgetFieldName === fieldName) {
150 visited.push(fieldName);
151 sigField = await PDFNet.DigitalSignatureField.createFromField(field);
152 break;
153 }
154 }
155 if (!sigField) {
156 /**
157 * A guard clause in-case a field with the given `label` could not
158 * be found, but this should never happen, as widgetInfo.label
159 * can only be programmatically populated from a user interacting
160 * with an existing field in the document
161 */
162 throw Error('The document does not contain a signature field');
163 }
164 if (!foundOneDigitalSignature) {
165 /**
166 * No Signature Field with a Cryptographic signature was found in
167 * the document, therefore we should explicitly set DocMDP
168 */
169 await sigField.setDocumentPermissions(
170 PDFNet.DigitalSignatureField.DocumentPermissions
171 .e_annotating_formfilling_signing_allowed
172 );
173 }
174 // Determine whether to sign with a digitalID (selected file) or the default certificate URL.
175 if (UIElements.certificateUrl !== null) {
176 if (UIElements.certificateUrl !== String.empty &&
177 UIElements.certificateUrl !== defaultCertificateUrl)
178 digitalID = UIElements.certificateUrl;
179 }
180 // Sign with a digitalID (selected file)
181 if (digitalID) {
182 const fileArrayBuffer = await digitalID.arrayBuffer();
183 await sigField.signOnNextSaveFromBuffer(fileArrayBuffer, UIElements.password);
184 }
185 // Sign with the default certificate URL
186 else
187 await sigField.signOnNextSaveFromURL(UIElements.certificateUrl, UIElements.password);
188
189 // Set optional signature information
190 await sigField.setLocation(UIElements.signatureInformation[0].value);
191 await sigField.setReason(UIElements.signatureInformation[1].value);
192 await sigField.setContactInfo(UIElements.signatureInformation[2].value);
193
194 buf = await doc.saveMemoryBuffer(PDFNet.SDFDoc.SaveOptions.e_incremental);
195 }
196 const blob = new Blob([buf], { type: 'application/pdf' });
197 UI.loadDocument(blob, { filename: documentViewer.getDocument().filename });
198 } catch (e) {
199 console.log(e);
200 UI.showWarningMessage({
201 title: 'Digital ID Error',
202 message:
203 'There is an issue with the Digital ID file or password. The private key could not be parsed.',
204 });
205 }
206 });
207};
208
209// Open the signature panel and verify the signature
210window.verifySignature = (instance) => {
211 instance.UI.setActiveTabInPanel({ tabPanel: UIElements.tabPanel.dataElement, tabName: 'signaturePanel' });
212 setTimeout(() => {
213 const shadowRoot = document.getElementById('wc-viewer').shadowRoot;
214 const signaturePanelElement = shadowRoot.querySelector('[data-element="signaturePanel"]');
215 const signaturePanelButtons = Array.from(signaturePanelElement.querySelectorAll('button'));
216 const signaturePanelExpandButton = signaturePanelButtons.find((element) => {
217 return element.ariaLabel.includes('Expand Signed by Apryse');
218 });
219 if (signaturePanelExpandButton)
220 signaturePanelExpandButton.click();
221 setTimeout(() => {
222 const verifyButton = signaturePanelElement.querySelector(
223 'button[aria-label="Signature Details"]'
224 );
225 if (verifyButton)
226 verifyButton.click();
227 }, 200);
228 }, 500);
229};
230
231// Clear the digital ID information
232window.clearDigitalIDInformation = (instance) => {
233 instance.UI.showWarningMessage({
234 title: 'Confirm Clearing Digital ID Information',
235 message:
236 'This will reset the inputted password and clear the uploaded .pfx file. Are you sure?',
237 onConfirm: () => {
238 UIElements.certificateUrl = defaultCertificateUrl;
239 UIElements.password = 'password';
240 digitalID = null;
241
242 // Reset the digital ID file name label and password field
243 const digitalIDFileNameLabel = UIElements.digitalSignaturePanel.render.querySelector('#digitalIDFileNameLabel');
244 digitalIDFileNameLabel.textContent = '';
245 const passwordField = UIElements.digitalSignaturePanel.render.querySelector('#inputPassword');
246 passwordField.value = UIElements.password;
247 passwordField.disabled = true;
248 }
249 });
250};
251
252//helper function to load the ui-elements.js script
253function loadUIElementsScript() {
254 return new Promise((resolve, reject) => {
255 if (window.UIElements) {
256 console.log('UIElements already loaded');
257 resolve();
258 return;
259 }
260 const script = document.createElement('script');
261 script.src = '/showcase-demos/digital-signatures/ui-elements.js';
262 script.onload = function () {
263 console.log('✅ UIElements script loaded successfully');
264 resolve();
265 };
266 script.onerror = function () {
267 console.error('Failed to load UIElements script');
268 reject(new Error('Failed to load ui-elements.js'));
269 };
270 document.head.appendChild(script);
271 });
272}
273
274// The url to the PKCS #12 private keyfile to use to certify this digital signature.
275const defaultCertificateUrl = '/assets/certificates/apryse.pfx';
276// The X.509 Public Key Certificates to be used for validating Digital Signatures on a document.
277const certificate = '/assets/certificates/apryse.cer';
278// The annotation widgets to sign
279let widgetsToDigitallySign = [];
280// The digital ID file (PKCS #12) selected by the user
281let digitalID = null;
282// Load UIElements script first, then initialize WebViewer
283loadUIElementsScript().then(() => {
284 initializeWebViewer();
285}).catch((error) => {
286 console.error('Failed to load UIElements:', error);
287});
288
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