# Form Signature Fields

Signature fields are a special type of form field in PDF documents, designed exclusively for the addition and management of electronic signatures. This chapter provides a detailed explanation of how to use Foxit PDF SDK for Web to work with form signature fields, including adding, editing, validating signatures, and customizing the signing process and user interface (UI).

For signature functionality, Foxit PDF SDK for Web offers the following support:

  • Create, edit, and delete signature widgets and fields
  • Perform signing and validate signatures
  • Customize signer configuration and signature handlers
  • Customize signature validation handlers
  • Customize signature-related UI, including the signature dialog, signature validation dialog, and signature information dialog

# Add/Edit/Delete Signature Fields in PDF Documents

# Create Signature Fields

The createSignature (opens new window) method of the PDFForm (opens new window) class can be used to add new signature fields to a PDF document. This method requires a CreateSignatureOptions (opens new window) object to configure the properties of the signature field.

const options = {
    pdfRect: {left: 100, top: 100, right: 300, bottom: 200}, // Set the position and size of the signature field
    pageIndex: 0, // Specify the page index where the signature field will be added 
    rotate: 0, // Optional, set the rotation angle of the widget. Possible values are 0, 1, 2, or 3, representing 0°, 90°, 180°, and 270° respectively  
    fieldName: 'SignatureField1' // Optional, specify the name of the signature field; if not provided, a unique field name will be automatically generated
};  

pdfForm.createSignature(options).then(widget => {
    console.log("The signature field has been successfully created:", widget);
}).catch(error => {
    console.error("An error occurred while creating the signature field:", error);
});

After the signature field is successfully created, Foxit PDF SDK for Web will insert a new signature widget into the page and trigger the ViewerEvents.annotationAdded (opens new window) event. When handling this event, you can use the Annot.getType (opens new window) and PDFFormField.getType (opens new window) interfaces to determine whether a signature widget has been added.

pdfui.addViewerEventListener(PDFViewCtrl.ViewerEvents.annotationAdded, (annotations) => {
    const signatureWidgets = annotations.filter(it => {
        return it.getType() === PDF.annots.constant.Annot_Type.widget && it.getField().getType() === PDF.form.constant.FieldType.signature
    });
    // Use signatureWidgets for subsequent operations
})

# Delete Signature

Deleting a signature here refers to deleting the signature widget, not directly deleting the signature field. The method for deleting a signature widget is the same as deleting an annotation and is implemented using the PDFPage.removeAnnotByObjectNumber (opens new window) method.

pdfPage.removeAnnotByObjectNumber(widget.getObjectNumber()).then((removedAnnots) => {
    if(removedAnnots.length === 1) {
        console.log("The signature widget has been successfully deleted");
    } else {
        console.log("The signature widget to be deleted was not found");
    }
}, reason => {
    console.error("An error occurred while deleting the signature widget:", reason);
});

After the signature widget is successfully deleted, an array containing the deleted signature widget will be returned. If the signature widget to be deleted is not found, an empty array will be returned. Therefore, after the deletion operation, it is necessary to check whether the length of the returned array is 1 to confirm if the deletion was successful.

In addition, after the signature widget is successfully deleted, if the associated form field no longer contains any other form widgets, the field will be automatically removed, and the DataEvents.formFieldRemoved (opens new window) event will be triggered.

If you are not familiar with the relationship between form fields and form widgets, you can refer to the Introduction to Basic Concepts of PDF Forms section.

# Start Signing

After the signature field is successfully created, you can sign it by calling the PDFDoc.sign (opens new window) method. Information related to the signature (such as the identity of the signer, the reason for signing, the location of the signature, etc.) can be defined in detail within the signInfo object.

const signInfo = {
    filter: 'Adobe.PPKLite',
    subfilter: 'adbe.pkcs7.sha1',
    flag: 0x1f0,
    distinguishName: 'e=foxitsdk@foxitsoftware.cn',
    location: 'bj',
    reason: 'TestBJ',
    signer: 'web sdk11',
    showTime: true,
    signTime: Date.now(),
    defaultContentsLength: 7942,
    image: 'data:image/png;base64,iVBORw0...',
    rotation: 90,
    timeFormat: {
        format: 'YYYY-MM-DD HH:mm:ss Z',
        timeZoneOptions: { prefix: 'GMT' }
    },
};
// Example of signature service address
const signature_server = 'https://webviewer-demo.foxitsoftware.com/signature'; // Or use the signature service address that you have deployed yourself

const signedDocBlobData = await pdfDoc.sign(signInfo, async function sign(signInfo, plainContent) {
    const formdata = new FormData();
    formdata.append("plain", new Blob([plainContent]), "plain");
    const response = await fetch(signature_server + '/digest_and_sign', {
        method: 'POST',
        body: formdata
    });
    return response.arrayBuffer();
});

During the signing process, Foxit PDF SDK for Web will invoke the sign callback function to submit the signature-related data to the signature service. Once the signature service completes the signing, it will return the signed data.

For the implementation of the signature service, Foxit PDF SDK for Web provides example implementations for both Windows and Linux systems. You can refer to the documentation files in the release package: /server/signature-server-for-linux/README.md and /server/signature-server-for-win/readme.md.

To help you quickly verify the signature functionality, we provide the following test service:

Note: The above services are for testing purposes only and should not be used in a production environment. If the Node.js implementations of signature-server-for-linux and signature-server-for-win provided by us do not meet your requirements, you can refer to their source code and reimplement them using other technology stacks as needed to ensure stability and reliability in a production environment.

After the signing is successfully completed, the returned blob object contains the signed PDF file. You can choose to save the file locally or directly open it using the PDFViewer.openPDFByFile (opens new window) method.

pdfViewer.openPDFByFile(signedDocBlobData, { fileName: 'signed.pdf' });

# Verify Signature

# Signature Verification API

After the signing is successfully completed, you can use the PDFSignature.verify (opens new window) method to validate the signature fields to ensure their validity.

const signatureField = form.getField("Signature field name");
// Example of signature service address
const signature_server = 'https://webviewer-demo.foxitsoftware.com/signature'; // Or use the signature service address that you have deployed yourself

const result = await signatureField.verify({
    force: false,
    handler: async (signatureField, plainContent, signedData, hasDataOutOfScope) => {
        const filter = await signatureField.getFilter();
        const subfilter = await signatureField.getSubfilter();
        const signer = await signatureField.getSigner();
        const formdata = new FormData();
        formdata.append("filter", filter);
        formdata.append("subfilter", subfilter);
        formdata.append("signer", signer);
        formdata.append("plainContent", new Blob([plainContent]), "plainContent");
        formdata.append("signedData", new Blob([signedData]), "signedData");
        
        const response = await fetch(signature_server + '/verify', {
            method: 'POST',
            body: formdata
        });
        const result = parseInt(await response.text());
        return result;
    }
});

After the signature verification is completed, a result value will be returned. Its type is Signature_State (opens new window), which is used to represent the result of the signature verification:

  • Signature Verification Status:

    • verifyValid (4): The signature verification status is valid
    • verifyInvalid (8): The signature verification status is invalid
    • verifyErrorByteRange (64): Unexpected byte range
    • verifyUnknown (2147483648): Unknown signature
  • Document Change Status:

    • verifyChange (128): The document has been modified within the signature scope (signature is invalid)
    • verifyIncredible (256): The signature is untrustworthy (contains risks)
    • verifyNoChange (1024): The document has not been modified within the signature scope
    • verifyChangeLegal (134217728): The document has been modified outside the signature scope, but the modification is allowed
    • verifyChangeIllegal (268435456): The document has been modified outside the signature scope, and the modification invalidates the signature
  • Issuer Verification Status:

    • verifyIssueUnknown (2048): The verification status of the issuer is unknown
    • verifyIssueValid (4096): The verification status of the issuer is valid
    • verifyIssueRevoke (16384): The issuer's certificate has been revoked
    • verifyIssueExpire (32768): The issuer's certificate has expired
    • verifyIssueUncheck (65536): The issuer has not been checked
    • verifyIssueCurrent (131072): The verified issuer is the current issuer
  • Timestamp Status:

    • verifyTimestampNone (262144): No timestamp is present, or the timestamp has not been checked
    • verifyTimestampDoc (524288): The signature is a timestamp signature
    • verifyTimestampValid (1048576): The timestamp verification status is valid
    • verifyTimestampInvalid (2097152): The timestamp verification status is invalid
    • verifyTimestampExpire (4194304): The timestamp verification status has expired
    • verifyTimestampIssueUnknown (8388608): The verification status of the timestamp issuer is unknown
    • verifyTimestampIssueValid (16777216): The verification status of the timestamp issuer is valid
    • verifyTimestampTimeBefore (33554432): The timestamp time is valid because it is before the expiration date

Note: Before verifying a signature, ensure that the signature field has been signed. If the field is not signed, calling the verify method will result in an error. Therefore, it is necessary to first check the signing status of the signature field.

const isSigned = await signatureField.isSigned();
if (isSigned) {
    // Perform signature verification here
}

# Custom Verification Handler

A verification handler is an asynchronous function used to submit data to the signature verification service and return the verification results. You can retrieve the signature management service by calling the PDFViewer.getSignatureService (opens new window) method and set a global default verification handler.

Note: Once a global default verification handler is set, Foxit PDF SDK for Web will automatically use that handler. When the PDFSignature.verify (opens new window) method is called at the application layer without explicitly specifying the handler parameter, the custom verification handler will be used automatically.

const signatureService = pdfViewer.getSignatureService();
// Example of signature service address
const signature_server = 'https://webviewer-demo.foxitsoftware.com/signature'; // Or use the signature service address that you have deployed yourself
signatureService.setVerifyHandler(
    async (signatureField, plainContent, signedData, hasDataOutOfScope) => {
        const filter = await signatureField.getFilter();
        const subfilter = await signatureField.getSubfilter();
        const signer = await signatureField.getSigner();
        const formdata = new FormData();
        formdata.append("filter", filter);
        formdata.append("subfilter", subfilter);
        formdata.append("signer", signer);
        formdata.append("plainContent", new Blob([plainContent]), "plainContent");
        formdata.append("signedData", new Blob([signedData]), "signedData");
        
        const response = await fetch(signature_server + '/verify', {
            method: 'POST',
            body: formdata
        });
        return parseInt(await response.text());
    }
)

# Get Signature Information

The PDFSignature.getSignInfo (opens new window) method is used to get signature information. If the signature has been signed, it returns a SignedFormSignatureInfo (opens new window) object; otherwise, it returns an UnsignedFormSignatureInfo (opens new window) object.

# Customize Signature UI

Foxit PDF SDK for Web provides a set of interfaces for customizing signature-related UI elements. These interfaces are defined in the ISignatureUI and can be accessed via the IViewerUI.getSignatureUI() method. The main interfaces include:

To customize signature-related UI elements, you can inherit the built-in Viewer UI classes provided by the SDK (such as XViewerUI or TinyViewerUI) and override the getSignatureUI method to return a custom implementation of ISignatureUI. For example:

class CustomSignDocDialog implements ISignDocDialog {
    private signatureField: ISignatureField;
    private _isOpened = false;
    async openWith(
        signature: ISignatureField,
        okCallback: (data: SignDocInfo, sign: DigestSignHandler) => Promise<void> | void,
        cancelCallback?: () => Promise<void> | void
    ): Promise<void> {
        this._isOpened = true;
        this.signatureField = signature;
        // Create the signature dialog UI
        // Display various information 
        // Register okCallback and cancelCallback callback events, which will be triggered when the user confirms or cancels  
    }
    isOpened(): boolean {
        return this._isOpened;
    }
    getCurrentSignature(): ISignatureField | undefined {
        return this.signatureField;
    }
    hide(): void {
        this._isOpened = false;
        // Hide the signature dialog
    }
    destroy(): void {
        // Release resources
    }
}
class CustomSignatureUI implements ISignatureUI {
  // Implement the methods in the ISignatureUI interface
  getSignDocumentDialog() {
    return Promise.resolve(new CustomSignDocDialog())
  }
  ...
}

class CustomViewerUI extends UIExtension.XViewerUI {
  getSignatureUI() {
    return Promise.resolve(new CustomSignatureUI());
  }
}

// Use a custom ViewerUI
new PDFUI({
  viewerOptions: {
    viewerUI: new CustomViewerUI()
  }
})

In the custom implementation of ISignatureUI, you can customize the appearance and behavior of each dialog according to your needs. For example, by overriding the ISignDocDialog.openWith method, you can execute specific custom logic when opening the signature dialog.