Blog Post

How to Programmatically Stamp and Digitally Sign a Document on Android

Illustration: How to Programmatically Stamp and Digitally Sign a Document on Android

PSPDFKit 8 for Android came with a shiny new signing UI and feature set as part of our Electronic Signatures component. If you’re wondering how to use it, you’re in luck: This blog post will guide you through using the new features to create a signature stamp from an image, stamp the document, and digitally sign it using a certificate.

Using this method allows for powerful automation of your signing workflow and empowers your customers to easily create custom signatures and cryptographically sign documents to protect them from being tampered with.

Getting Started

Let’s start with our stamp image that we’ll use as a visual indication that the document has been signed. For this, I prepared stampImage.png. It has a custom signature and stamp design that I want to use as my digital signature image.

Custom PNG image we can use as our stamp of approval for signing documents!

Note that the image has an alpha component, which will allow us to stamp over the document’s contents without completely obscuring the background — just like a real-life stamp!

We also need a digital certificate that has our public key and some identification information such as a name. You can read more about certificates here. For this example, we’re using a self-signed test certificate that hasn’t been issued by a certificate authority (CA), so it’ll appear with a self-signed warning. If a recognized CA had issued the certificate, it would have validated without a warning, provided it was within the expiration date. You can create a self-signed certificate using the Keychain Access app on Mac, or with the PowerShell New-SelfSignedCertificate Windows tool. More information on creating certificates can be found in our relevant guide.

Below, you can download our self-signed test certificate.

Preparing the Signature Form Field

Now that we have our resources, we can go ahead and create our Android app using the PSPDFKit PDF SDK as a dependency.

To digitally sign the document using a PDF digital signature, we need to create a signature form field. Using a signature form field is the only way to cryptographically sign a document, and in our example, the original document doesn’t have the requisite form field. Lucky for us, PSPDFKit for Android provides an API to create one!

For this example, we already have our signature image (the stamp PNG shown above), so after signing, we need to make sure we only see our stamp image — and not any other visible signature indication — in the document. To do this, the signature form field we create must have a size of zero, which means width and height values of 0. If the form field has some size, our signing code will generate a default signing appearance that includes details taken from the signer’s certificate and other configurable visuals. Since we already have a signature stamp image, we don’t want any of the defaults.

To create our signature form field of size zero, we can use the following code:

// Our PDF activity we'll use to stamp and sign documents.
// See our website guides for how to set up a `PdfActivity` and load a document.
class MyPdfSigningActivity : PdfActivity() {

    ...

    // Creates a signature form field of size `0` that can be used to digitally sign a document
    // without adding any signature appearance.
    private fun createInvisibleSignatureField(
            pageXPosition: Float,
            pageYPosition: Float,
            pageIndex: Int,
            formFieldId: String) {
        // Bounding box of size `0` at the specified page coordinates.
        val bbox = RectF(pageXPosition, pageYPosition, pageXPosition, pageYPosition)
        val signatureField = document?.formProvider?.addFormElementToPage(
            formFieldId,
            SignatureFormConfiguration.Builder(pageIndex, bbox).build()
        )
    }

    // Our main calling code can pass in the page coordinates and page where we want our stamp to be:
    private fun stampAndSign() {
        // We want the signature around the bottom-right corner of the page. We use PDF points as the unit for coordinates.
        // Remember the
        `createInvisibleSignatureField(`.
            pageXPosition = 500f,
            pageYPosition = 200f,
            pageIndex = 0,
            formFieldId = "SignatureStampField"
        )
    }
}

Now that our invisible signature field has been created, it’s time to add our visible stamp image.

Stamping It

To stamp our PDF, we first need to copy our image asset to a place where our app can load it. For this example, we’ll use myapp/src/main/assets/images/stampImage.png.

Within the app, let’s load the stamp image and add it to the document. The stamp annotation can be marked as a signature using the StampAnnotation#setIsSignature method:

// Place the stamp image at the location of the invisible form field created previously.
// Pass the form field ID as an argument so we can reuse this function to add the stamp
// to multiple signature locations in the document.
fun addStampToDocumentPage(formFieldId: String) {
    // Load the image from assets into a bitmap.
    val image = BitmapFactory.decodeStream(assets.open("images/stampImage.png"))

    // Our asset is too big (~900×600 pixels),
    // so we can shrink it 6× to better fit the page.
    val shrinkCoefficient = 6
    val imageWidth = image.width / shrinkCoefficient
    val imageHeight = image.height / shrinkCoefficient

    // Find the form field we made earlier.
    val signatureField = document?.formProvider?.getFormElementWithName(formFieldId)

    signatureField?.let {
        val formBbox = it.annotation.boundingBox

        // Create the annotation at the location of the form field.
        val stamp = StampAnnotation(
            it.annotation.pageIndex,
            RectF(
                formBbox.left,
                formBbox.top,
                formBbox.left + imageWidth,
                formBbox.top - imageHeight),
            image
        )

        // Mark it as a signature.
        stamp.setIsSignature(true)

        // Add it to the document.
        document?.annotationProvider?.addAnnotationToPage(stamp)
    }
}

The image should now appear on the page and position set in createInvisibleSignatureField. Now that our signature is visible, we’re ready to sign the document with our digital certificate.

Signing the Document

Digitally signing the document with a certificate means that any further modifications to the document will invalidate the signature. Furthermore, a document that has been modified will usually be shown on PDF readers that can verify digital signatures as a document that has been tampered with. Pretty cool stuff. This is the de facto digital method for verifying the contents of a digital document, and it serves as a legally recognized method of signing PDF documents.

We recently upgraded our signing component to include eIDAS standard signatures, which are legally recognized in the EU. You can read more about digital signatures in our What PDF Digital Signatures Are and Why They Are Important blog post.

Back to our application. Let’s first take the certificate we created at the start of the blog post and add it to the SignatureManager:

// Load the certificate from the assets folder and add it to the signature manager.
// We return the `Signer` for use later when signing the document.
fun loadCertificate(): Signer {
    // The signer is a named entity holding a certificate (usually a person) and has a display
    // name shown in the app.
    val signer = Pkcs12Signer(
        "John Appleseed",
        Uri.parse("file:///android_asset/JohnAppleseed.p12")
    )

    // We need to register the signer with the signature manager for signing to work.
    SignatureManager.addSigner("john_appleseed", signer)

    // Provide a password to the signer, which will be used to unlock its private key.
    if (signer is InteractiveSigner) {
        (signer as InteractiveSigner).unlockPrivateKeyWithPassword("test")
    }

    return signer
}

Now we can sign the document by calling into the SignatureSignerDialog API:

// Sign the document using a certificate located in the assets directory.
fun digitallySignDocument(formFieldId: String) {
    val signer = loadCertificate()

    // Find the form field we made earlier.
    val signatureField = document?.formProvider?.getFormElementWithName(formFieldId)

    // Show the signer dialog that handles the signing process.
    SignatureSignerDialog.show(
        supportFragmentManager,
        SignatureSignerDialog.Options.Builder(document, signatureField, signer).build(),
        this
    )
}

That’s it! PSPDFKit for Android will now sign the document using the certificate’s public key and process it into a new document.

Reloading It

To display the newly signed document, we can hook into the onDocumentSigned method using the DocumentSigningListener.

To do this, our PdfActivity must also inherit from the DocumentSigningListener. Then we can load the signed document by implementing onDocumentSigned:

class MyPdfSigningActivity :
    PdfActivity(),
    DocumentSigningListener {

    // Implementation of the abstract listener function.
    // Reloads the digitally signed document.
    override fun onDocumentSigned(signedDocumentUri: Uri) {
        setDocumentFromUri(signedDocumentUri, null)
    }

    ...

Putting the Pieces Together

All our hard work has been done. Now it’s a case of calling all the functions we wrote, in the correct order:

class MyPdfSigningActivity :
    PdfActivity(),
    DocumentSigningListener {

    fun stampAndSignPdf() {
        // An ID for the invisible form field we create to place our stamp.
        val formFieldId = "SignatureStampField"

        // Create the signature field.
        createInvisibleSignatureField(
            pageXPosition = 500f,
            pageXPosition = 200f,
            pageIndex = 0,
            formFieldId = formFieldId
        )

        // Add the signature stamp image.
        addStampToDocumentPage(formFieldId)

        // Load the certificate from our assets.
        val signer = loadCertificate()

        // Sign the document using the new `Signer`.
        digitallySignDocument(signer)
    }

    // Reload the digitally signed document.
    override fun onDocumentSigned(signedDocumentUri: Uri) {
        // Replace the loaded document with the signed document.
        setDocumentFromUri(signedDocumentUri, null)
    }

    ...

Final document with a stamp on top

Conclusion

In this blog post, we saw how to use PSPDFKit’s powerful Digital Signatures and Electronic Signatures APIs to stamp and sign an initially unsignable document. We used a document without a signature field, created one, and made sure our custom stamp image was the only thing shown after signing the document.

For more examples, check out our Android PDF library catalog example projects. Or, head over to our new getting started guides to try the SDK for yourself and see how easy it is to do powerful document processing!

Author
Amit Nayar Android Team Lead

Amit would rather spend his time making pizza, poking campfires, eating cheese and crisps, or climbing trees, but sadly he has to write great software to help save the world from deforestation. It’s a hard life, but someone’s gotta do it.

Free 60-Day Trial Try PSPDFKit in your app today.
Free Trial

Related Articles

Explore more
DEVELOPMENT  |  Android • Jetpack Compose

How to Implement Drag-to-Reorder List Functionality with Jetpack Compose

DEVELOPMENT  |  iOS • Android • Room • Kotlin Multiplatform • Tips

Seamless Room Database Integration for Kotlin Multiplatform Projects

PRODUCTS  |  Android • Releases

Android 2024.1 Update: Advanced Content Editing and Digital Signatures, Plus Expanded Jetpack Compose Support