Skip to main content
Skip table of contents

Advanced examples for SignatureFieldAppearance

Introduction

With the release of iText Core version 8.0.2, a new class for customizing the appearance of digital signature fields was introduced to the Forms module - SignatureFieldAppearance (Java/.NET)  - enhancing the capabilities for controlling how the visual appearance of digital signatures can be displayed in PDF documents. For more information, please refer to the original article.

To go along with the release of version 8.0.3, we’ve crafted some additional examples to dive deeper into the new functionalities and provide users with a more comprehensive insight into the possibilities it unlocks.

Example One:

This example illustrates the process of creating a signature field using the signature appearance layout element, and subsequently signing it.

Java
JAVA
package org.example;

import com.itextpdf.commons.utils.FileUtil;
import com.itextpdf.forms.form.element.SignatureFieldAppearance;
import com.itextpdf.kernel.colors.ColorConstants;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.Cell;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.element.Table;
import com.itextpdf.signatures.DigestAlgorithms;
import com.itextpdf.signatures.PdfPadesSigner;
import com.itextpdf.signatures.PrivateKeySignature;
import com.itextpdf.signatures.SignerProperties;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.Certificate;
import java.util.Enumeration;

/**
 * Basic example of creating the signature field via signature appearance layout element.
 */
public class AddSignatureUsingAppearanceInstanceExample {
    private static final String CERT_PATH = "YOUR_CERTIFICATE_PATH";

    public static final String SRC = "src/main/resources/hello.pdf";
    public static final String DEST = "target/signSignatureAddedUsingAppearance.pdf";
    public static final String DOC_TO_SIGN = "target/signatureAddedUsingAppearance.pdf";
   
    private static final String SIGNATURE_NAME = "Signature1";

    private static final char[] PASSWORD = "password".toCharArray();

    public static void main(String[] args) throws Exception {
        File file = new File(DEST);
        file.getParentFile().mkdirs();
        Security.addProvider(new BouncyCastleProvider());

        new AddSignatureUsingAppearanceInstanceExample().createAndSignSignature(SRC, DOC_TO_SIGN, DEST, SIGNATURE_NAME);
    }

    /**
     * Basic example of creating the signature field via signature appearance layout element and signing it.
     *
     * @param src           source file
     * @param doc           destination for the file with added signature field
     * @param dest          final destination file
     * @param signatureName the name of the signature field to be added and signed
     *
     * @throws Exception in case some exception occurred.
     */
    public void createAndSignSignature(String src, String doc, String dest, String signatureName) throws Exception {
        addSignatureFieldAppearanceToTheDocument(src, doc, signatureName);

        PdfPadesSigner padesSigner = new PdfPadesSigner(new PdfReader(FileUtil.getInputStreamForFile(doc)),
                FileUtil.getFileOutputStream(dest));
        // We can pass the appearance through the signer properties.
        SignerProperties signerProperties = createSignerProperties(signatureName);

        padesSigner.signWithBaselineBProfile(signerProperties, getCertificateChain(CERT_PATH,PASSWORD), getPrivateKey(CERT_PATH,PASSWORD));
    }

    /**
     * Basic example of creating the signature field via signature appearance layout element.
     *
     * @param src           source file
     * @param dest          destination for the file with added signature field
     * @param signatureName the name of the signature field to be added
     *
     * @throws IOException in case an I/O error occurs.
     */
    protected void addSignatureFieldAppearanceToTheDocument(String src, String dest, String signatureName)
            throws IOException {
        try (Document document = new Document(new PdfDocument(new PdfReader(src), new PdfWriter(dest)))) {
            Table table = new Table(2);
            Cell cell = new Cell(0, 2).add(new Paragraph("Test signature").setFontColor(ColorConstants.WHITE));
            cell.setBackgroundColor(ColorConstants.GREEN);
            table.addCell(cell);
            cell = new Cell().add(new Paragraph("Signer"));
            cell.setBackgroundColor(ColorConstants.LIGHT_GRAY);
            table.addCell(cell);

            // Add signature field to the table.
            cell = new Cell().add(
                    new SignatureFieldAppearance(signatureName)
                            .setContent("Sign here")
                            .setHeight(50)
                            .setWidth(100)
                            .setInteractive(true));
            table.addCell(cell);

            document.add(table);
        }
    }

    /**
     * Creates properties to be used in signing operations. Also creates the appearance that will be passed to the
     * PDF signer through the signer properties.
     *
     * @param signatureName the name of the signature field to be signed
     *
     * @return {@link SignerProperties} properties to be used for main signing operation.
     */
    protected SignerProperties createSignerProperties(String signatureName) {
        SignerProperties signerProperties = new SignerProperties().setFieldName(signatureName);

        // Create the appearance instance and set the signature content to be shown and different appearance properties.
        SignatureFieldAppearance appearance = new SignatureFieldAppearance(signerProperties.getFieldName())
                .setContent("Signer", "Signature description. " +
                        "Signer is replaced to the one from the certificate.")
                .setBackgroundColor(ColorConstants.YELLOW);

        // Set created signature appearance and other signer properties.
        signerProperties
                .setSignatureAppearance(appearance)
                .setReason("Reason")
                .setLocation("Location");
        return signerProperties;
    }

    /**
     * Creates signing chain for the sample. This chain shouldn't be used for the real signing.
     * @param certificatePath
     * @param password
     * @return the chain of certificates to be used for the signing operation.
     */
    private static Certificate[] getCertificateChain(String certificatePath, char[] password) throws Exception {
        Certificate[] certChain = null;

        KeyStore p12 = KeyStore.getInstance("pkcs12");
        p12.load(new FileInputStream(certificatePath), password);

        Enumeration<String> aliases = p12.aliases();
        while (aliases.hasMoreElements()) {
            String alias = aliases.nextElement();
            if (p12.isKeyEntry(alias)) {
                certChain = p12.getCertificateChain(alias);
                break;
            }
        }
        return certChain;
    }

    /**
     * Creates private key for the sample. This key shouldn't be used for the real signing.
     * @param certificatePath
     * @param password
     * @return {@link PrivateKey} instance to be used for the main signing operation.
     */
    private static PrivateKeySignature getPrivateKey(String certificatePath, char[] password) throws Exception {
        PrivateKey pk = null;

        KeyStore p12 = KeyStore.getInstance("pkcs12");
        p12.load(new FileInputStream(certificatePath), password);

        Enumeration<String> aliases = p12.aliases();
        while (aliases.hasMoreElements()) {
            String alias = aliases.nextElement();
            if (p12.isKeyEntry(alias)) {
                pk = (PrivateKey) p12.getKey(alias, password);
                break;
            }
        }
        Security.addProvider(new BouncyCastleProvider());
        return new PrivateKeySignature(pk, DigestAlgorithms.SHA512, BouncyCastleProvider.PROVIDER_NAME);
    }
}
C#
C#
#nullable enable
using System;
using System.IO;
using iText.Bouncycastle.Crypto;
using iText.Bouncycastle.X509;
using iText.Commons.Bouncycastle.Cert;
using iText.Commons.Bouncycastle.Crypto;
using iText.Commons.Utils;
using iText.Forms.Form.Element;
using iText.Kernel.Colors;
using iText.Kernel.Pdf;
using iText.Layout;
using iText.Layout.Element;
using iText.Signatures;
using Org.BouncyCastle.Pkcs;

namespace example{

    public class AddSignatureUsingAppearanceInstanceExample
    {
        private static readonly string SRC = "hello.pdf";
        private static readonly string DOC_TO_SIGN = "signatureAddedUsingAppearance.pdf";
        private static readonly string DEST = "signSignatureAddedUsingAppearance.pdf";

        private static readonly string SIGNATURE_NAME = "Signature1";
        private static readonly char[] PASSWORD = "password".ToCharArray();
        private static readonly string CERT_PATH = "YOUR_CERTIFICATE_PATH";

        public static void Main(String[] args)
        {
            var file = new FileInfo(DEST);
            file.Directory?.Create();
            new AddSignatureUsingAppearanceInstanceExample().CreateAndSignSignature(SRC, DOC_TO_SIGN, DEST,
                SIGNATURE_NAME);
        }

        /// <summary>Basic example of creating the signature field via signature appearance layout element and signing it.</summary>
        /// <param name="src">source file</param>
        /// <param name="doc">destination for the file with added signature field</param>
        /// <param name="dest">final destination file</param>
        /// <param name="signatureName">the name of the signature field to be added and signed</param>
        public void CreateAndSignSignature(string src, string doc, string dest, string signatureName)
        {
            AddSignatureToTheDocument(src, doc, signatureName);
            var padesSigner = new PdfPadesSigner(new PdfReader(
                FileUtil.GetInputStreamForFile(doc)), FileUtil.GetFileOutputStream(dest));
            // We can pass the appearance through the signer properties.
            var signerProperties = CreateSignerProperties(signatureName);
            padesSigner.SignWithBaselineBProfile(signerProperties, GetCertificateChain(CERT_PATH, PASSWORD),
                GetPrivateKey(CERT_PATH, PASSWORD));
        }

        /// <summary>Basic example of creating the signature field via signature appearance layout element.</summary>
        /// <param name="src">source file</param>
        /// <param name="dest">destination for the file with added signature field</param>
        /// <param name="signatureName">the name of the signature field to be added</param>
        protected void AddSignatureToTheDocument(string src, string dest, string signatureName)
        {
            var document = new Document(new PdfDocument(new PdfReader(src), new PdfWriter(dest)));
            var table = new Table(2);
            var cell = new Cell(0, 2).Add(new Paragraph("Test signature").SetFontColor(ColorConstants.WHITE));
            cell.SetBackgroundColor(ColorConstants.GREEN);
            table.AddCell(cell);
            cell = new Cell().Add(new Paragraph("Signer"));
            cell.SetBackgroundColor(ColorConstants.LIGHT_GRAY);
            table.AddCell(cell);

            // Add signature field to the table.
            cell = new Cell().Add(
                new SignatureFieldAppearance(signatureName)
                    .SetContent("Sign here")
                    .SetHeight(50)
                    .SetWidth(100)
                    .SetInteractive(true));
            table.AddCell(cell);

            document.Add(table);
            document.Close();
        }

        /// <summary>
        /// Creates properties to be used in signing operations. Also creates the appearance that will be passed to the
        /// PDF signer through the signer properties.
        /// </summary>
        /// <param name="signatureName">the name of the signature field to be added</param>
        /// <returns>Signer properties to be used for main signing operation.</returns>
        protected SignerProperties CreateSignerProperties(string signatureName)
        {
            var signerProperties = new SignerProperties().SetFieldName(signatureName);

            // Create the appearance instance and set the signature content to be shown and different appearance properties.
            var appearance = new SignatureFieldAppearance(signerProperties.GetFieldName())
                .SetContent("Signer", "Signature description. " +
                                      "Signer is replaced to the one from the certificate.")
                .SetBackgroundColor(ColorConstants.YELLOW);

            // Set created signature appearance and other signer properties.
            signerProperties
                .SetSignatureAppearance(appearance)
                .SetReason("Reason")
                .SetLocation("Location");
            return signerProperties;
        }

        /// <summary>Creates private key for the sample. This key shouldn't be used for the real signing.</summary>
        /// <param name="certificatePath">Certificate path</param>
        /// <param name="password">password</param>
        /// <returns>IPrivateKey instance to be used for the main signing operation.</returns>
        private static PrivateKeySignature GetPrivateKey(string certificatePath, char[] password)
        {
            string? alias = null;
            var pk12 = new Pkcs12StoreBuilder().Build();
            pk12.Load(new FileStream(certificatePath, FileMode.Open, FileAccess.Read), password);

            foreach (var a in pk12.Aliases)
            {
                alias = ((string)a);
                if (pk12.IsKeyEntry(alias))
                {
                    break;
                }
            }

            IPrivateKey pk = new PrivateKeyBC(pk12.GetKey(alias).Key);
            return new PrivateKeySignature(pk, DigestAlgorithms.SHA512);
        }

        /// <summary>Creates signing chain for the sample. This chain shouldn't be used for the real signing.</summary>
        /// <param name="certificatePath">Certificate path</param>
        /// <param name="password">password</param>
        /// <returns>The chain of certificates to be used for the signing operation.</returns>
        private static IX509Certificate[] GetCertificateChain(string certificatePath, char[] password)
        {
            string? alias = null;
            var pk12 = new Pkcs12StoreBuilder().Build();
            pk12.Load(new FileStream(certificatePath, FileMode.Open, FileAccess.Read), password);

            foreach (var a in pk12.Aliases)
            {
                alias = ((string)a);
                if (pk12.IsKeyEntry(alias))
                {
                    break;
                }
            }

            X509CertificateEntry[] ce = pk12.GetCertificateChain(alias);
            var chain = new IX509Certificate[ce.Length];
            for (var k = 0; k < ce.Length; ++k)
            {
                chain[k] = new X509CertificateBC(ce[k].Certificate);
            }

            return chain;
        }
    }
}

Example Two:

This example defines properties for use in signing operations, and demonstrates how the SignerProperties (Java/.NET) object is utilized to pass the signature appearance to the PdfPadesSigner (Java/.NET) class.

Java
JAVA
package org.example;

import com.itextpdf.commons.utils.FileUtil;
import com.itextpdf.forms.form.element.SignatureFieldAppearance;
import com.itextpdf.kernel.colors.DeviceRgb;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.layout.element.Div;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.properties.TextAlignment;
import com.itextpdf.signatures.DigestAlgorithms;
import com.itextpdf.signatures.PdfPadesSigner;
import com.itextpdf.signatures.PrivateKeySignature;
import com.itextpdf.signatures.SignerProperties;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import java.io.File;
import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.Certificate;
import java.util.Enumeration;

/**
 * Basic example of the signature appearance customizing during the document signing.
 */
public class CustomSignatureAppearanceExample {
    private static final String CERT_PATH = "YOUR_CERTIFICATE_PATH";
    
    public static final String SRC = "src/main/resources/hello.pdf";
    public static final String DEST = "target/customSignatureAppearanceExample.pdf";

    private static final char[] PASSWORD = "password".toCharArray();
    
    public static void main(String[] args) throws Exception {
        File file = new File(DEST);
        file.getParentFile().mkdirs();
        Security.addProvider(new BouncyCastleProvider());

        new CustomSignatureAppearanceExample().signSignature(SRC, DEST);
    }

    /**
     * Basic example of the signature appearance customizing during the document signing.
     *
     * @param src  source file
     * @param dest destination file
     *
     * @throws Exception in case some exception occurred.
     */
    public void signSignature(String src, String dest) throws Exception {
        PdfPadesSigner padesSigner = new PdfPadesSigner(new PdfReader(FileUtil.getInputStreamForFile(src)),
                FileUtil.getFileOutputStream(dest));
        // We can pass the appearance through the signer properties.
        SignerProperties signerProperties = createSignerProperties();
        padesSigner.signWithBaselineBProfile(signerProperties, getCertificateChain(CERT_PATH,PASSWORD), getPrivateKey(CERT_PATH,PASSWORD));
    }

    /**
     * Creates properties to be used in signing operations. Also creates the appearance that will be passed to the
     * PDF signer through the signer properties.
     *
     * @return {@link SignerProperties} properties to be used for main signing operation.
     */
    protected SignerProperties createSignerProperties() {
        SignerProperties signerProperties = new SignerProperties().setFieldName("Signature1");
        // Create the custom appearance as div.
        Div customAppearance = new Div()
                .add(new Paragraph("Test").setFontSize(20).setCharacterSpacing(10))
                .add(new Paragraph("signature\nappearance").setFontSize(15).setCharacterSpacing(5))
                .setTextAlignment(TextAlignment.CENTER);

        // Create the appearance instance and set the signature content to be shown and different appearance properties.
        SignatureFieldAppearance appearance = new SignatureFieldAppearance(signerProperties.getFieldName())
                .setContent(customAppearance)
                .setBackgroundColor(new DeviceRgb(255, 248, 220))
                .setFontColor(new DeviceRgb(160, 82, 45));

        // Set created signature appearance and other signer properties.
        signerProperties
                .setSignatureAppearance(appearance)
                .setPageNumber(1)
                .setPageRect(new Rectangle(50, 650, 200, 100))
                .setReason("Reason")
                .setLocation("Location");
        return signerProperties;
    }

    /**
     * Creates signing chain for the sample. This chain shouldn't be used for the real signing.
     * @param certificatePath
     * @param password
     * @return the chain of certificates to be used for the signing operation.
     */
    private static Certificate[] getCertificateChain(String certificatePath, char[] password) throws Exception {
        Certificate[] certChain = null;

        KeyStore p12 = KeyStore.getInstance("pkcs12");
        p12.load(new FileInputStream(certificatePath), password);

        Enumeration<String> aliases = p12.aliases();
        while (aliases.hasMoreElements()) {
            String alias = aliases.nextElement();
            if (p12.isKeyEntry(alias)) {
                certChain = p12.getCertificateChain(alias);
                break;
            }
        }
        return certChain;
    }

    /**
     * Creates private key for the sample. This key shouldn't be used for the real signing.
     * @param certificatePath
     * @param password
     * @return {@link PrivateKey} instance to be used for the main signing operation.
     */
    private static PrivateKeySignature getPrivateKey(String certificatePath, char[] password) throws Exception {
        PrivateKey pk = null;

        KeyStore p12 = KeyStore.getInstance("pkcs12");
        p12.load(new FileInputStream(certificatePath), password);

        Enumeration<String> aliases = p12.aliases();
        while (aliases.hasMoreElements()) {
            String alias = aliases.nextElement();
            if (p12.isKeyEntry(alias)) {
                pk = (PrivateKey) p12.getKey(alias, password);
                break;
            }
        }
        Security.addProvider(new BouncyCastleProvider());
        return new PrivateKeySignature(pk, DigestAlgorithms.SHA512, BouncyCastleProvider.PROVIDER_NAME);
    }
}
C#
C#
#nullable enable
using System;
using System.IO;
using iText.Bouncycastle.Crypto;
using iText.Bouncycastle.X509;
using iText.Commons.Bouncycastle.Cert;
using iText.Commons.Bouncycastle.Crypto;
using iText.Commons.Utils;
using iText.Forms.Form.Element;
using iText.Kernel.Colors;
using iText.Kernel.Geom;
using iText.Kernel.Pdf;
using iText.Layout.Element;
using iText.Layout.Properties;
using iText.Signatures;
using Org.BouncyCastle.Pkcs;

namespace example
{
  /// <summary>Basic example of the signature appearance customizing during the document signing.</summary>
    public class CustomSignatureAppearanceExample
    {
        private static readonly string SRC = "hello.pdf";
        private static readonly string DEST = "customSignatureAppearanceExample.pdf";

        private static readonly char[] PASSWORD = "password".ToCharArray();
        private static readonly string CERT_PATH = "YOUR_CERTIFICATE_PATH";
      
        public static void Main(string[] args)
        {
            var file = new FileInfo(DEST);
            file.Directory?.Create();
            new CustomSignatureAppearanceExample().SignSignature(SRC, DEST);
        }

        /// <summary>Basic example of the signature appearance customizing during the document signing.</summary>
        /// <param name="src">source file</param>
        /// <param name="dest">destination file</param>
        public void SignSignature(string src, string dest)
        {
            var padesSigner = new PdfPadesSigner(new PdfReader(
                FileUtil.GetInputStreamForFile(src)), FileUtil.GetFileOutputStream(dest));
            // We can pass the appearance through the signer properties.
            var signerProperties = CreateSignerProperties();
            padesSigner.SignWithBaselineBProfile(signerProperties, GetCertificateChain(CERT_PATH,PASSWORD), GetPrivateKey(CERT_PATH,PASSWORD));
        }

        /// <summary>
        /// Creates properties to be used in signing operations. Also creates the appearance that will be passed to the
        /// PDF signer through the signer properties.
        /// </summary>
        /// <returns>Signer properties to be used for main signing operation.</returns>
        protected SignerProperties CreateSignerProperties()
        {
            var signerProperties = new SignerProperties().SetFieldName("Signature1");
            // Create the custom appearance as div.
            var customAppearance = new Div()
                .Add(new Paragraph("Test").SetFontSize(20).SetCharacterSpacing(10))
                .Add(new Paragraph("signature\nappearance").SetFontSize(15).SetCharacterSpacing(5))
                .SetTextAlignment(TextAlignment.CENTER);

            // Create the appearance instance and set the signature content to be shown and different appearance properties.
            var appearance = new SignatureFieldAppearance(signerProperties.GetFieldName())
                .SetContent(customAppearance)
                .SetBackgroundColor(new DeviceRgb(255, 248, 220))
                .SetFontColor(new DeviceRgb(160, 82, 45));

            // Set created signature appearance and other signer properties.
            signerProperties
                .SetSignatureAppearance(appearance)
                .SetPageNumber(1)
                .SetPageRect(new Rectangle(50, 650, 200, 100))
                .SetReason("Reason")
                .SetLocation("Location");
            return signerProperties;
        }
        /// <summary>Creates private key for the sample. This key shouldn't be used for the real signing.</summary>
        /// <param name="certificatePath">Certificate path</param>
        /// <param name="password">password</param>
        /// <returns>IPrivateKey instance to be used for the main signing operation.</returns>
        private static PrivateKeySignature GetPrivateKey(string certificatePath, char[] password)
        {
            string? alias = null;
            var pk12 = new Pkcs12StoreBuilder().Build();
            pk12.Load(new FileStream(certificatePath, FileMode.Open, FileAccess.Read), password);

            foreach (var a in pk12.Aliases)
            {
                alias = ((string)a);
                if (pk12.IsKeyEntry(alias))
                {
                    break;
                }
            }

            IPrivateKey pk = new PrivateKeyBC(pk12.GetKey(alias).Key);
            return new PrivateKeySignature(pk, DigestAlgorithms.SHA512);
        }

        /// <summary>Creates signing chain for the sample. This chain shouldn't be used for the real signing.</summary>
        /// <param name="certificatePath">Certificate path</param>
        /// <param name="password">password</param>
        /// <returns>The chain of certificates to be used for the signing operation.</returns>
        private static IX509Certificate[] GetCertificateChain(string certificatePath, char[] password)
        {
            string? alias = null;
            var pk12 = new Pkcs12StoreBuilder().Build();
            pk12.Load(new FileStream(certificatePath, FileMode.Open, FileAccess.Read), password);

            foreach (var a in pk12.Aliases)
            {
                alias = ((string)a);
                if (pk12.IsKeyEntry(alias))
                {
                    break;
                }
            }
            X509CertificateEntry[] ce = pk12.GetCertificateChain(alias);
            var chain = new IX509Certificate[ce.Length];
            for (var k = 0; k < ce.Length; ++k)
            {
                chain[k] = new X509CertificateBC(ce[k].Certificate);
            }
            return chain;
        }
    }
}

Example Three:

This example focuses on creating and customizing the appearance of a signature field.

Java
JAVA
package org.example;


import com.itextpdf.commons.utils.FileUtil;
import com.itextpdf.forms.fields.properties.SignedAppearanceText;
import com.itextpdf.forms.form.element.SignatureFieldAppearance;
import com.itextpdf.io.font.PdfEncodings;
import com.itextpdf.io.image.ImageDataFactory;
import com.itextpdf.kernel.colors.ColorConstants;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.layout.borders.SolidBorder;
import com.itextpdf.signatures.DigestAlgorithms;
import com.itextpdf.signatures.PdfPadesSigner;
import com.itextpdf.signatures.PrivateKeySignature;
import com.itextpdf.signatures.SignerProperties;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.Certificate;
import java.util.Enumeration;

/**
 * Basic example of the signature appearance customizing during the document signing.
 */
public class PadesSignatureAppearanceExample {
    private static final String CERT_PATH = "Cprivate static final String CERT_PATH = "YOUR_CERTIFICATE_PATH;

    public static final String SRC = "src/main/resources/hello.pdf";
    public static final String DEST = "target/padesSignatureAppearanceExample.pdf";

    private static final char[] PASSWORD ="password".toCharArray();

    public static final String IMAGE_PATH = "src/main/resources/image.png";
    public static final String BOLD = "src/main/resources/font/OpenSans-Bold.ttf";

    public static void main(String[] args) throws Exception {
        File file = new File(DEST);
        file.getParentFile().mkdirs();
        Security.addProvider(new BouncyCastleProvider());

        new PadesSignatureAppearanceExample().signSignature(SRC, DEST);
    }

    /**
     * Basic example of the signature appearance customizing during the document signing.
     *
     * @param src  source file
     * @param dest destination file
     *
     * @throws Exception in case some exception occurred.
     */
    public void signSignature(String src, String dest) throws Exception {
        PdfPadesSigner padesSigner = new PdfPadesSigner(new PdfReader(FileUtil.getInputStreamForFile(src)),
                FileUtil.getFileOutputStream(dest));
        // We can pass the appearance through the signer properties.
        SignerProperties signerProperties = createSignerProperties();

        padesSigner.signWithBaselineBProfile(signerProperties, getCertificateChain(CERT_PATH,PASSWORD), getPrivateKey(CERT_PATH,PASSWORD));
    }

    /**
     * Creates properties to be used in signing operations. Also creates the appearance that will be passed to the
     * PDF signer through the signer properties.
     *
     * @return {@link SignerProperties} properties to be used for main signing operation.
     *
     * @throws IOException in case an I/O error occurs when reading the file.
     */
    protected SignerProperties createSignerProperties() throws IOException {
        SignerProperties signerProperties = new SignerProperties().setFieldName("Signature1");

        // Create the appearance instance and set the signature content to be shown and different appearance properties.
        SignatureFieldAppearance appearance = new SignatureFieldAppearance(signerProperties.getFieldName())
                .setContent(new SignedAppearanceText().setReasonLine("Customized reason: Reason").setLocationLine("Customized location: Location"), ImageDataFactory.create(IMAGE_PATH))
                .setBorder(new SolidBorder(ColorConstants.DARK_GRAY, 2))
                .setFont(PdfFontFactory.createFont(BOLD, PdfEncodings.IDENTITY_H));

        // Note that if SignedAppearanceText is set as a content, description text will be generated automatically, but
        // any `visual` values can be changed by using the appropriate setters. This won't affect the signature dictionary.

        // Set created signature appearance and other signer properties.
        signerProperties
                .setSignatureAppearance(appearance)
                .setPageNumber(1)
                .setPageRect(new Rectangle(50, 650, 200, 100))
                .setReason("Reason")
                .setLocation("Location");
        return signerProperties;
    }

    /**
     * Creates signing chain for the sample. This chain shouldn't be used for the real signing.
     * @param certificatePath
     * @param password
     * @return the chain of certificates to be used for the signing operation.
     */
    private static Certificate[] getCertificateChain(String certificatePath, char[] password) throws Exception {
        Certificate[] certChain = null;

        KeyStore p12 = KeyStore.getInstance("pkcs12");
        p12.load(new FileInputStream(certificatePath), password);

        Enumeration<String> aliases = p12.aliases();
        while (aliases.hasMoreElements()) {
            String alias = aliases.nextElement();
            if (p12.isKeyEntry(alias)) {
                certChain = p12.getCertificateChain(alias);
                break;
            }
        }
        return certChain;
    }

    /**
     * Creates private key for the sample. This key shouldn't be used for the real signing.
     * @param certificatePath
     * @param password
     * @return {@link PrivateKey} instance to be used for the main signing operation.
     */
    private static PrivateKeySignature getPrivateKey(String certificatePath, char[] password) throws Exception {
        PrivateKey pk = null;

        KeyStore p12 = KeyStore.getInstance("pkcs12");
        p12.load(new FileInputStream(certificatePath), password);

        Enumeration<String> aliases = p12.aliases();
        while (aliases.hasMoreElements()) {
            String alias = aliases.nextElement();
            if (p12.isKeyEntry(alias)) {
                pk = (PrivateKey) p12.getKey(alias, password);
                break;
            }
        }
        Security.addProvider(new BouncyCastleProvider());
        return new PrivateKeySignature(pk, DigestAlgorithms.SHA512, BouncyCastleProvider.PROVIDER_NAME);
    }
}
C#
C#
#nullable enable
using System;
using System.IO;
using iText.Bouncycastle.Crypto;
using iText.Bouncycastle.X509;
using iText.Commons.Bouncycastle.Cert;
using iText.Commons.Bouncycastle.Crypto;
using iText.Commons.Utils;
using iText.Forms.Fields.Properties;
using iText.Forms.Form.Element;
using iText.IO.Font;
using iText.IO.Image;
using iText.Kernel.Colors;
using iText.Kernel.Font;
using iText.Kernel.Geom;
using iText.Kernel.Pdf;
using iText.Layout.Borders;
using iText.Signatures;
using Org.BouncyCastle.Pkcs;

namespace example{

    public class PadesSignatureAppearanceExample
    {
        private static readonly string SRC = "hello.pdf";
        private static readonly string DEST = "padesSignatureAppearanceExample.pdf";


        private static readonly char[] PASSWORD = "password".ToCharArray();

        private static readonly string IMAGE_PATH = "sign.jpg";
        private static readonly string BOLD = "OpenSans-Bold.ttf";
        private static readonly string CERT_PATH = "YOUR_CERTIFICATE_PATH";

        public static void Main(String[] args)
        {
            var file = new FileInfo(DEST);
            file.Directory?.Create();
            new PadesSignatureAppearanceExample().SignSignature(SRC, DEST);
        }

        /// <summary>Basic example of the signature appearance customizing during the document signing.</summary>
        /// <param name="src">source file</param>
        /// <param name="dest">destination file</param>
        public void SignSignature(String src, String dest)
        {
            var padesSigner = new PdfPadesSigner(new PdfReader(
                FileUtil.GetInputStreamForFile(src)), FileUtil.GetFileOutputStream(dest));
            // We can pass the appearance through the signer properties.
            var signerProperties = CreateSignerProperties();
            padesSigner.SignWithBaselineBProfile(signerProperties, GetCertificateChain(CERT_PATH, PASSWORD),
                GetPrivateKey(CERT_PATH, PASSWORD));
        }

        /// <summary>
        /// Creates properties to be used in signing operations. Also creates the appearance that will be passed to the
        /// PDF signer through the signer properties.
        /// </summary>
        /// <returns>Signer properties to be used for main signing operation.</returns>
        protected SignerProperties CreateSignerProperties()
        {
            var signerProperties = new SignerProperties().SetFieldName("Signature1");

            // Create the appearance instance and set the signature content to be shown and different appearance properties.
            var appearance = new SignatureFieldAppearance(signerProperties.GetFieldName())
                .SetContent(new SignedAppearanceText().SetReasonLine("Customized reason: Reason")
                    .SetLocationLine("Customized location: Location"), ImageDataFactory.Create(IMAGE_PATH))
                .SetBorder(new SolidBorder(ColorConstants.DARK_GRAY, 2))
                .SetFont(PdfFontFactory.CreateFont(BOLD, PdfEncodings.IDENTITY_H));

            // Note that if SignedAppearanceText is set as a content, description text will be generated automatically, but
            // any `visual` values can be changed by using the appropriate setters. This won't affect the signature dictionary.

            // Set created signature appearance and other signer properties.
            signerProperties
                .SetSignatureAppearance(appearance)
                .SetPageNumber(1)
                .SetPageRect(new Rectangle(50, 650, 200, 100))
                .SetReason("Reason")
                .SetLocation("Location");
            return signerProperties;
        }

        /// <summary>Creates private key for the sample. This key shouldn't be used for the real signing.</summary>
        /// <param name="certificatePath">Certificate path</param>
        /// <param name="password">password</param>
        /// <returns>IPrivateKey instance to be used for the main signing operation.</returns>
        private static PrivateKeySignature GetPrivateKey(string certificatePath, char[] password)
        {
            string? alias = null;
            var pk12 = new Pkcs12StoreBuilder().Build();
            pk12.Load(new FileStream(certificatePath, FileMode.Open, FileAccess.Read), password);

            foreach (var a in pk12.Aliases)
            {
                alias = ((string)a);
                if (pk12.IsKeyEntry(alias))
                {
                    break;
                }
            }

            IPrivateKey pk = new PrivateKeyBC(pk12.GetKey(alias).Key);
            return new PrivateKeySignature(pk, DigestAlgorithms.SHA512);
        }

        /// <summary>Creates signing chain for the sample. This chain shouldn't be used for the real signing.</summary>
        /// <param name="certificatePath">Certificate path</param>
        /// <param name="password">password</param>
        /// <returns>The chain of certificates to be used for the signing operation.</returns>
        private static IX509Certificate[] GetCertificateChain(string certificatePath, char[] password)
        {
            string? alias = null;
            var pk12 = new Pkcs12StoreBuilder().Build();
            pk12.Load(new FileStream(certificatePath, FileMode.Open, FileAccess.Read), password);

            foreach (var a in pk12.Aliases)
            {
                alias = ((string)a);
                if (pk12.IsKeyEntry(alias))
                {
                    break;
                }
            }

            X509CertificateEntry[] ce = pk12.GetCertificateChain(alias);
            var chain = new IX509Certificate[ce.Length];
            for (var k = 0; k < ce.Length; ++k)
            {
                chain[k] = new X509CertificateBC(ce[k].Certificate);
            }

            return chain;
        }
    }
}

Conclusion

These examples not only showcase the extended capabilities of iText's new SignatureFieldAppearance class, but also aim to familiarize users with the powerful features you now have at your disposal to enhance and elevate your digital signature workflows.


Resources

signatureAddedUsingAppearance.pdf

padesSignatureAppearanceExample.pdf

customSignatureAppearanceExample.pdf

hello.pdf

JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.