> For the complete documentation index, see [llms.txt](https://docs.apryse.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.apryse.com/web/get-started/samples/showcase-demo-digital-signatures.md).

# Secure Digital Signatures Showcase Demo Code Sample

Add digital signatures using cryptographic certificates to create secure PDFs.

{% hint style="info" %}
**Requirements**

*These packages are required to use these features in production. Trial keys have unlimited access to all features*

<a href="/web/get-started/readme.md" class="button primary">Web SDK</a><a href="/web/full-api/full-api-overview.md" class="button primary">Full API</a><a href="https://apryse.com/capabilities#DigitalSignature" class="button primary">Package: Digital Signature</a><a href="https://showcase.apryse.com/digital-signatures" class="button primary">Live demo</a>
{% endhint %}

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:

* Add PFX certificate to the signature.
* Validate digital signature on the sample PDF.
* Download a digitally signed PDF.

**Implementation steps** To add Digital Signature capability with WebViewer:

Step 1: [Get started with WebViewer](/web/get-started/readme.md) 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-license-key/apryse-license-key platform="WEB\_VIEWER" variant="compact" %}

{% tabs %}
{% tab title="index.js" %}

<pre class="language-js" data-line-numbers><code class="lang-js">// ES6 Compliant Syntax
// Copilot name: GitHub Copilot, version: 1.0.0, model: GPT-4, version: 2024-06, date: 2025-10-13
// File: showcase-demos/digital-signatures/index.js

import WebViewer from '@pdftron/webviewer';

const licenseKey = '<code class="expression">visitor.claims.wvKey || "YOUR_WEBVIEWER_LICENSE_KEY"</code>';

function initializeWebViewer() {
  WebViewer(
    {
      path: '/lib',
      initialDoc: 'https://apryse.s3.us-west-1.amazonaws.com/public/files/samples/digital_signature_walkthrough.pdf',
      enableFilePicker: true, // Enable file picker to open files. In WebViewer -> menu icon -> Open File
      fullAPI: true, // Enable the full API to access PDFNet and signature features.
      licenseKey: licenseKey, // Replace with your license key
    },
    document.getElementById('viewer')
  ).then((instance) => {
    const { UI } = instance;
    const { documentViewer, Tools, Annotations, annotationManager } = instance.Core;

    // Set the toolbar group to the Fill &#x26; Sign tools
    UI.setToolbarGroup('toolbarGroup-FillAndSign');

    documentViewer.addEventListener('documentLoaded', () => {
      widgetsToDigitallySign = [];
    });

    UI.VerificationOptions.addTrustedCertificates([certificate]);

    // Sets the Signature Tool to sign with appearance mode for use with digital signatures
    const tool = documentViewer.getTool(Tools.ToolNames.SIGNATURE);
    tool.setSigningMode(Tools.SignatureCreateTool.SigningModes.APPEARANCE);

    // Customize the webviewer left panel
    UIElements.customizeUI(instance);

    UI.openElements([UIElements.tabPanel.dataElement]);
    UI.setPanelWidth(UIElements.tabPanel.dataElement, 400);

    // Capture the signature fields that are signed by the user
    annotationManager.addEventListener('annotationChanged', async (annotations, action) => {
      const actionsOfInterest = ['add', 'delete'];

      if (actionsOfInterest.includes(action)) {
        const signatureWidgetAnnots = annotationManager
          .getAnnotationsList()
          .filter((annot) => annot instanceof Annotations.SignatureWidgetAnnotation);

        const widgetsWithSignatures = signatureWidgetAnnots.filter(
          (widget) => widget.isSignedByAppearance() || widget.getAssociatedSignatureAnnotation()
        );
        // If signature field is signed, enable the apply approval button
        const widgetsToSign = widgetsWithSignatures.map((widget) => {
          if (widget.isSignedByAppearance()) {
            const applyApprovalButton = UIElements.digitalSignaturePanel.render.querySelector('#applyApprovalButton');
            applyApprovalButton.disabled = false;
            applyApprovalButton.style.backgroundColor = 'blue';
            applyApprovalButton.style.color = 'white';
          }
          return {
            label: widget.getField().name,
          };
        });

        widgetsToDigitallySign = widgetsToSign;
        console.log('Annotation changed:', annotations, action, widgetsToSign);
      }
    });
    console.log('WebViewer loaded successfully.');
  }).catch((error) => {
    console.error('Failed to initialize WebViewer:', error);
  });
}

// Apply the digital signature approval
window.applyApproval = async (instance) => {
  const { UI } = instance;
  const { annotationManager, SaveOptions, PDFNet, documentViewer } = instance.Core;
  const xfdfString = await annotationManager.exportAnnotations();
  const data = await documentViewer.getDocument().getFileData({
    xfdfString,
    flags: SaveOptions.INCREMENTAL,
  });

  await PDFNet.initialize();
  await PDFNet.runWithCleanup(async () => {
    const doc = await PDFNet.PDFDoc.createFromBuffer(new Uint8Array(data));
    const digSigFieldIterator = await doc.getDigitalSignatureFieldIteratorBegin();
    let foundOneDigitalSignature = false;
    for (digSigFieldIterator; await digSigFieldIterator.hasNext(); digSigFieldIterator.next()) {
      const field = await digSigFieldIterator.current();
      if (await field.hasVisibleAppearance()) {
        foundOneDigitalSignature = true;
        break;
      }
    }
    await doc.lock();

    try {
      /**
       * Create a deep copy of widgetsToDigitallySign so that we can safely
       * modify the contents of the array if needed (i.e. if we want to push
       * a field to the array in the event that the document has no fields, or
       * the user chose not to sign a signature field)
       */
      const widgetsToSign = JSON.parse(JSON.stringify(widgetsToDigitallySign));

      /**
       * If the user did not explicitly sign a field in the document,
       * arbitrarily create an invisible signature field
       */
      if (!widgetsToSign.length) {
        const fieldName = 'Signature1-invisible';
        const field = await doc.fieldCreate(fieldName, PDFNet.Field.Type.e_signature);
        const page1 = await doc.getPage(1);
        const widgetAnnot = await PDFNet.WidgetAnnot.create(
          await doc.getSDFDoc(),
          await PDFNet.Rect.init(0, 0, 0, 0),
          field
        );
        page1.annotPushBack(widgetAnnot);
        widgetAnnot.setPage(page1);
        const widgetObj = await widgetAnnot.getSDFObj();
        widgetObj.putNumber('F', 132);
        widgetObj.putName('Type', 'Annot');
        widgetsToSign.push({
          label: fieldName,
        });
      }

      const visited = [];
      let buf;

      for (let i = 0; i &#x3C; widgetsToSign.length; i++) {
        let sigField;
        const widgetFieldName = widgetsToSign[i].label;
        const fieldIterator = await doc.getFieldIteratorBegin();
        for (; await fieldIterator.hasNext(); fieldIterator.next()) {
          const field = await fieldIterator.current();
          if (
            !(await field.isValid()) ||
            (await field.getType()) !== PDFNet.Field.Type.e_signature
          ) {
            continue;
          }
          const fieldName = await field.getName();
          if (!visited.includes(fieldName) &#x26;&#x26; widgetFieldName === fieldName) {
            visited.push(fieldName);
            sigField = await PDFNet.DigitalSignatureField.createFromField(field);
            break;
          }
        }
        if (!sigField) {
          /**
           * A guard clause in-case a field with the given `label` could not
           * be found, but this should never happen, as widgetInfo.label
           * can only be programmatically populated from a user interacting
           * with an existing field in the document
           */
          throw Error('The document does not contain a signature field');
        }
        if (!foundOneDigitalSignature) {
          /**
           * No Signature Field with a Cryptographic signature was found in
           * the document, therefore we should explicitly set DocMDP
           */
          await sigField.setDocumentPermissions(
            PDFNet.DigitalSignatureField.DocumentPermissions
              .e_annotating_formfilling_signing_allowed
          );
        }
        // Determine whether to sign with a digitalID (selected file) or the default certificate URL.
        if (UIElements.certificateUrl !== null) {
          if (UIElements.certificateUrl !== String.empty &#x26;&#x26;
            UIElements.certificateUrl !== defaultCertificateUrl)
            digitalID = UIElements.certificateUrl;
        }
        // Sign with a digitalID (selected file)
        if (digitalID) {
          const fileArrayBuffer = await digitalID.arrayBuffer();
          await sigField.signOnNextSaveFromBuffer(fileArrayBuffer, UIElements.password);
        }
        // Sign with the default certificate URL
        else
          await sigField.signOnNextSaveFromURL(UIElements.certificateUrl, UIElements.password);

        // Set optional signature information
        await sigField.setLocation(UIElements.signatureInformation[0].value);
        await sigField.setReason(UIElements.signatureInformation[1].value);
        await sigField.setContactInfo(UIElements.signatureInformation[2].value);

        buf = await doc.saveMemoryBuffer(PDFNet.SDFDoc.SaveOptions.e_incremental);
      }
      const blob = new Blob([buf], { type: 'application/pdf' });
      UI.loadDocument(blob, { filename: documentViewer.getDocument().filename });
    } catch (e) {
      console.log(e);
      UI.showWarningMessage({
        title: 'Digital ID Error',
        message:
          'There is an issue with the Digital ID file or password.  The private key could not be parsed.',
      });
    }
  });
};

// Open the signature panel and verify the signature
window.verifySignature = (instance) => {
  instance.UI.setActiveTabInPanel({ tabPanel: UIElements.tabPanel.dataElement, tabName: 'signaturePanel' });
  setTimeout(() => {
    const shadowRoot = document.getElementById('wc-viewer').shadowRoot;
    const signaturePanelElement = shadowRoot.querySelector('[data-element="signaturePanel"]');
    const signaturePanelButtons = Array.from(signaturePanelElement.querySelectorAll('button'));
    const signaturePanelExpandButton = signaturePanelButtons.find((element) => {
      return element.ariaLabel.includes('Expand Signed by Apryse');
    });
    if (signaturePanelExpandButton)
      signaturePanelExpandButton.click();
    setTimeout(() => {
      const verifyButton = signaturePanelElement.querySelector(
        'button[aria-label="Signature Details"]'
      );
      if (verifyButton)
        verifyButton.click();
    }, 200);
  }, 500);
};

// Clear the digital ID information
window.clearDigitalIDInformation = (instance) => {
  instance.UI.showWarningMessage({
    title: 'Confirm Clearing Digital ID Information',
    message:
      'This will reset the inputted password and clear the uploaded .pfx file. Are you sure?',
    onConfirm: () => {
      UIElements.certificateUrl = defaultCertificateUrl;
      UIElements.password = 'password';
      digitalID = null;

      // Reset the digital ID file name label and password field
      const digitalIDFileNameLabel = UIElements.digitalSignaturePanel.render.querySelector('#digitalIDFileNameLabel');
      digitalIDFileNameLabel.textContent = '';
      const passwordField = UIElements.digitalSignaturePanel.render.querySelector('#inputPassword');
      passwordField.value = UIElements.password;
      passwordField.disabled = true;
    }
  });
};

//helper function to load the ui-elements.js script
function loadUIElementsScript() {
  return new Promise((resolve, reject) => {
    if (window.UIElements) {
      console.log('UIElements already loaded');
      resolve();
      return;
    }
    const script = document.createElement('script');
    script.src = '/showcase-demos/digital-signatures/ui-elements.js';
    script.onload = function () {
      console.log('✅ UIElements script loaded successfully');
      resolve();
    };
    script.onerror = function () {
      console.error('Failed to load UIElements script');
      reject(new Error('Failed to load ui-elements.js'));
    };
    document.head.appendChild(script);
  });
}

// The url to the PKCS #12 private keyfile to use to certify this digital signature.
const defaultCertificateUrl = '/assets/certificates/apryse.pfx';
// The X.509 Public Key Certificates to be used for validating Digital Signatures on a document.
const certificate = '/assets/certificates/apryse.cer';
// The annotation widgets to sign
let widgetsToDigitallySign = [];
// The digital ID file (PKCS #12) selected by the user
let digitalID = null;
// Load UIElements script first, then initialize WebViewer
loadUIElementsScript().then(() => {
  initializeWebViewer();
}).catch((error) => {
  console.error('Failed to load UIElements:', error);
});

</code></pre>

{% endtab %}

{% tab title="ui-elements.js" %}
{% code title="ui-elements.js" lineNumbers="true" %}

```js
// ES6 Compliant Syntax
// Copilot name: GitHub Copilot, version: 1.0.0, model: GPT-4, version: 2024-06, date: 2025-10-13
// File: showcase-demos/digital-signatures/ui-elements.js

// Class with static UI elements and related functions for the digital signature demo

class UIElements {

    // The list of registered panels in the webviewer
    static viewerPanels = null;

    // The tab panel, representing the webviewer left panel
    static tabPanel = {
        handle: null,
        dataElement: 'tabPanel'
    };

    // The digital signature sub-panel to be registered
    static digitalSignaturePanel = {
        handle: null,
        dataElement: 'digitalSignaturePanel',
        render: null,
    };

    // The password to open the private key file
    static password = 'password';

    static signatureInformation = [
        {
            id: 'Location',
            label: 'Location',
            value: 'Vancouver, BC, Canada'
        },
        {
            id: 'Reason',
            label: 'Reason',
            value: 'Cryptographic signature demo'
        },
        {
            id: 'ContactInfo',
            label: 'Contact Information',
            value: 'apryse.com'
        }
    ];

    // The url to the PKCS #12 private keyfile to use to certify this digital signature.
    static certificateUrl = '/assets/certificates/apryse.pfx';

    // Customize the webviewer left panel
    static customizeUI = (instance) => {
        const { UI } = instance;

        // Close the tab panel (if it's open) for refreshment.
        UI.closeElements([UIElements.tabPanel.dataElement]);

        // Get the list of registered panels in the webviewer
        UIElements.viewerPanels = UI.getPanels();

        // Find the Tab Panel to modify. The digital signature sub-panel will be added to this Tab panel.
        UIElements.tabPanel.handle = UIElements.viewerPanels.find((panel) => panel.dataElement === UIElements.tabPanel.dataElement);

        // Register the digital signature sub-panel
        UIElements.RegisterDigitalSignaturePanel(instance);

        // Add the new digital signature sub-panel to list of sub-panels under the Tab Panel
        UIElements.digitalSignaturePanel.handle = { render: UIElements.digitalSignaturePanel.dataElement };
        UIElements.tabPanel.handle.panelsList = [UIElements.digitalSignaturePanel.handle, ...UIElements.tabPanel.handle.panelsList];
    };

    // Register the digital signature sub-panel
    static RegisterDigitalSignaturePanel = (instance) => {
        UIElements.digitalSignaturePanel.render = UIElements.createDigitalSignaturePanelElements(instance);
        instance.UI.addPanel({
            dataElement: UIElements.digitalSignaturePanel.dataElement,
            location: 'left',
            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>',
            title: 'Digital Signature',
            render: () => UIElements.digitalSignaturePanel.render,
        });
    };

    // Create the digital signature panel elements.
    static createDigitalSignaturePanelElements = (instance) => {
        let panelDiv = document.createElement('div');
        panelDiv.id = 'digitalSignaturePanel';

        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.');
        panelDiv.appendChild(paragraph);

        let dividerDiv = document.createElement('div');
        dividerDiv.style.borderTop = '1px solid #ccc';
        dividerDiv.style.margin = '10px 0';
        panelDiv.appendChild(dividerDiv);

        // Digital ID division
        let digitalIDDiv = document.createElement('div');
        digitalIDDiv.id = 'digitalIDDiv';

        let digitalIDDivTitle = document.createElement("h3");
        digitalIDDivTitle.textContent = "Digital ID (Optional)";
        digitalIDDiv.appendChild(digitalIDDivTitle);

        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.');
        digitalIDDiv.appendChild(paragraph);

        panelDiv.appendChild(digitalIDDiv);

        // Digital ID File button
        let digitalIDFileButton = document.createElement('button');
        digitalIDFileButton.textContent = 'Select Digital ID File';
        enableButton(digitalIDFileButton, true);
        digitalIDFileButton.onclick = () => {
            // file input field
            const inputFile = document.createElement('input');
            inputFile.id = 'inputFile';
            inputFile.type = 'file';
            inputFile.style.display = 'none';
            inputFile.accept = '.pfx';
            inputFile.onchange = e => {
                UIElements.certificateUrl = e.target.files[0];
                if (UIElements.certificateUrl !== null) {
                    enableButton(clearDigitalIDButton, true);
                    digitalIDFileNameLabel.textContent = `${UIElements.certificateUrl.name}`;
                }
                passwordField.disabled = false;
            }
            inputFile.click();
        }
        panelDiv.appendChild(document.createElement('p'));
        panelDiv.appendChild(digitalIDFileButton);

        // Digital ID File name label
        panelDiv.appendChild(document.createElement('p'));
        const digitalIDFileNameLabel = document.createElement('span');
        digitalIDFileNameLabel.id = 'digitalIDFileNameLabel';
        digitalIDFileNameLabel.textContent = '';
        panelDiv.appendChild(digitalIDFileNameLabel);

        // Password label
        panelDiv.appendChild(document.createElement('p'));
        panelDiv.appendChild(document.createTextNode('Digital ID Password:'));
        panelDiv.appendChild(document.createElement('p'));

        // Password input field
        const passwordField = document.createElement('input');
        passwordField.id = 'inputPassword';
        passwordField.type = 'password';
        passwordField.disabled = true;
        passwordField.value = UIElements.password;
        passwordField.style.width = '100%';
        passwordField.addEventListener("change", () => { UIElements.password = passwordField.value.trim(); });
        panelDiv.appendChild(passwordField);

        // Show password button
        let showPasswordButton = document.createElement('button');
        showPasswordButton.textContent = 'Show';
        enableButton(showPasswordButton, true);
        showPasswordButton.onclick = () => {
            showPasswordButton.textContent = showPasswordButton.textContent === 'Show' ? 'Hide' : 'Show';
            passwordField.type = passwordField.type === 'password' ? 'text' : 'password';
        };
        panelDiv.appendChild(showPasswordButton);

        // Clear Digital ID File button
        panelDiv.appendChild(document.createElement('p'));
        let clearDigitalIDButton = document.createElement('button');
        clearDigitalIDButton.textContent = 'Clear Digital ID Information';
        enableButton(clearDigitalIDButton, false);
        clearDigitalIDButton.onclick = () => {
            clearDigitalIDInformation(instance);
            passwordField.value = UIElements.password;
            enableButton(clearDigitalIDButton, false);
        }
        panelDiv.appendChild(clearDigitalIDButton);

        panelDiv.appendChild(dividerDiv.cloneNode());

        // Signature Information division
        let signatureInfoDiv = document.createElement('div');
        signatureInfoDiv.id = 'signatureInfoDiv';

        let signatureInfoDivTitle = document.createElement("h3");
        signatureInfoDivTitle.textContent = "Signature Information (Optional)";
        signatureInfoDiv.appendChild(signatureInfoDivTitle);
        panelDiv.appendChild(signatureInfoDiv);

        // signature information labels and input fields
        UIElements.signatureInformation.forEach(info => {
            // input field label
            panelDiv.appendChild(document.createElement('p'));
            panelDiv.appendChild(document.createTextNode(`${info.label}:`));
            panelDiv.appendChild(document.createElement('p'));

            // input field
            const inputField = document.createElement('input');
            inputField.id = `input${info.id}`;
            inputField.type = 'text';
            inputField.value = info.value;
            inputField.style.width = '100%';
            inputField.addEventListener("input", () => info.value = inputField.value.trim());
            panelDiv.appendChild(inputField);
        });

        // Apply Approval Signature button
        let applyApprovalButton = document.createElement('button');
        applyApprovalButton.id = 'applyApprovalButton';
        applyApprovalButton.textContent = 'Apply Approval Signature';
        enableButton(applyApprovalButton, false);
        applyApprovalButton.onclick = () => {
            applyApproval(instance);
            enableButton(verifySignatureButton, true);
        }

        panelDiv.appendChild(document.createElement('p'));
        panelDiv.appendChild(applyApprovalButton);

        // Verify Signature button
        let verifySignatureButton = document.createElement('button');
        verifySignatureButton.textContent = 'Verify Signature';
        enableButton(verifySignatureButton, false);
        verifySignatureButton.onclick = () => verifySignature(instance);

        panelDiv.appendChild(document.createElement('p'));
        panelDiv.appendChild(verifySignatureButton);

        return panelDiv;
    };
}

// Enable or disable a button based on the state
const enableButton = (button, state) => {
    button.disabled = !state;
    button.style.backgroundColor = (state) ? 'blue' : 'gray';
    button.style.color = (state) ? 'white' : 'darkgray';
};
```

{% endcode %}
{% endtab %}
{% endtabs %}

[View the full sample on GitHub](https://github.com/ApryseSDK/webviewer-samples/tree/main/showcase-demos-playground)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.apryse.com/web/get-started/samples/showcase-demo-digital-signatures.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
