Skip to main content
Skip table of contents

Part IV - Appearances

When people sign a PDF, they often do not merely want to see that signature in some extra signature panel, they want to see a presentation of that signature in the document itself. This is even more true for documents with multiple signatures when the signers have different roles.

The iText signing API does allow creating such a presentation. You have the choice of using one of a few default appearances or generating the appearance completely yourself.

If you create a signature in an already existing signature field of the PDF, that field may already have an appearance which your signature then would inherit unless you change it explicitly. As this is an interesting case and as you would choose to sign in an existing field by selecting it by its name, we start with a short excursion on the names of signature fields.

Excursion: Signature Field Names

A signature field is a special AcroForm form field. Each such form field is identified by its respective name.

The PdfSigner constructor creates a new, unused field name for the new signature. If you want to use an existing, unsigned signature field (or even if you simply don’t want an arbitrary signature name but a specific one), you can override that default name like this:

JAVA
PdfSigner signer = new PdfSigner(...);
signer.setFieldName("My Signature Name");
C#
PdfSigner signer = new PdfSigner(...);
signer.SetFieldName("My Signature Name");

If an unsigned signature field named "My Signature Name" exists in the PDF, that field will be used. If "My Signature Name" is the name of an existing signed signature field or of a non-signature field, an exception will be thrown. Otherwise, a new signature field with that name will be created.

Beware: iText does not allow names with dots in them. Dots in PDF form field names are used to separate levels of a field hierarchy in the document. But the iText signing API does not support signature fields that are not base fields of the form field hierarchy.

Creating Signatures With Appearances

When you create a signature, you can control the details of its in-document appearance via the properties of your PdfSigner. By default, a new signature field has no in-document appearance. To trigger the creation of an appearance, you have to set the area in which you want the signature to be visualized (and also the page unless the default, page 1, is ok for you). For example:

JAVA
PdfSigner signer = new PdfSigner(...);
signer.setPageRect(new Rectangle(100, 500, 300, 100));
signer.setPageNumber(2);
C#
PdfSigner signer = new PdfSigner(...);
signer.SetPageRect(new Rectangle(100, 500, 300, 100));
signer.SetPageNumber(2);

This positions the new signature appearance on page 2 in a 300×100 rectangle with the lower left corner at (100, 500). The coordinate system is the one defined by the Media Box and the Crop Box of the page; you will want to make sure that the signature indeed is on-page, in particular in case of third-party documents that may have defined those boxes with unexpected values.

Alternatively you can use the predefined location and page of an existing, unused signature field simply by setting its name in the PdfSigner, see the excursion above.

Built-in iText Signature Appearance Layouts

iText offers a few built-in layouts that organize certain bits of information in the signature appearance rectangle. These bits of information can be set using immediate and nested properties of the PdfSigner. They are:

Description - a text containing multiple lines:

  • "Digitally signed by " plus the signer’s name by default derived from the signer certificate.

            For details see the Name item below.

  • "Date: " plus the signing date and time.

            The date and time used here is initialized with the current date and time in the PdfSigner constructor but can be overwritten by setting its sign date property.

  • "Reason: " plus the signing reason (only if reason is set).

            The signing reason is a PdfSigner property that is empty by default.

            This line can be customized using the signature appearance text reason line property.

  • "Location: " plus the signing location (only if location is set).

           The signing location is a PdfSigner property that is empty by default.

           This line can be customized using the signature appearance text location line property.

Graphic - an arbitrary image, e.g. of the signer themselves or their wet signature. This image is set using the signature field appearance graphic.

Name - the signer’s name.

If you sign using signDetached, the first certificate from the certificate chain by default is used to determine the signer’s name. iText uses the signer certificate subject common name. If the subject has no common name entry, iText uses the e-mail address. If the subject has neither a common name nor an e-mail address, the default name remains empty.

If you use signExternalContainer, you have to explicitly set the signature field appearance signer name.

The following layouts are built-in:

Description only - this layout contains only the Description.

image-20240122-135854.png

This is the default layout, we only need to set the information to display:

JAVA
signer.setReason("My reason for signing");
signer.setLocation("My location for signing");
C#
signer.SetReason("My reason for signing");
signer.SetLocation("My location for signing");

Graphic only - this layout contains only the Graphic.

This layout requires graphic content to be set in the signature field appearance.

JAVA
SignatureFieldAppearance appearance = new SignatureFieldAppearance("app");
appearance.setContent(ImageDataFactory.create(...));
signer.setSignatureAppearance(appearance);
C#
SignatureFieldAppearance appearance = new SignatureFieldAppearance("app");
appearance.SetContent(ImageDataFactory.Create(...));
signer.SetSignatureAppearance(appearance);

Graphic and Description - this layout contains both the Graphic and the Description; depending on the aspect ratio of the appearance rectangle, the Graphic is arranged above or left of the Description.

This layout requires graphic and appearance text content to be set in the signature field appearance.

JAVA
signer.setReason("Specimen");
signer.setLocation("Boston");

SignatureFieldAppearance appearance = new SignatureFieldAppearance("app");
appearance.setContent(new SignedAppearanceText(), ImageDataFactory.create(...));
signer.setSignatureAppearance(appearance);
C#
signer.SetReason("Specimen");
signer.SetLocation("Boston");

SignatureFieldAppearance appearance = new SignatureFieldAppearance("app");
appearance.SetContent(new SignedAppearanceText(), ImageDataFactory.Create(...));
signer.SetSignatureAppearance(appearance);

The SignedAppearanceText details will be filled in automatically.

Name and Description - this layout contains both the Name and the Description; depending on the aspect ratio of the appearance rectangle, the Name is arranged above or left of the Description.

This layout requires signer name and appearance text content to be set in the signature field appearance.

JAVA
signer.setReason("Specimen");
signer.setLocation("Boston");

SignatureFieldAppearance appearance = new SignatureFieldAppearance("app");
appearance.setContent("", new SignedAppearanceText());
signer.setSignatureAppearance(appearance);
C#
signer.SetReason("Specimen");
signer.SetLocation("Boston");

SignatureFieldAppearance appearance = new SignatureFieldAppearance("app");
appearance.SetContent("", new SignedAppearanceText());
signer.SetSignatureAppearance(appearance);

The SignedAppearanceText details will be filled in automatically and the empty string "" will be replaced by the signer’s name from the signer certificate.

Simple Customizations

iText allows customizing the standard layouts described in section Built-in iText Signature Appearance Layouts above.

  • Re-using the appearance of the signature field as background.

If you are signing in an existing, empty signature field with a visualization, you can ask iText to keep it as background in the new signature appearance. For example, signing this empty signature field

you can get

(Please note that some PDF viewers don’t show the appearances of empty signature fields.)

JAVA
signer.getSignatureField().setReuseAppearance(true);
C#
signer.GetSignatureField().SetReuseAppearance(true);

To re-use the appearance of an existing field you of course also have to set the signature field name to the name of that existing field, see the Excursion: Signature Field Names above.

  • Adding a background image

In addition to the signature graphic foreground image you can use another bitmap image as background of the whole appearance, optionally at a given scale.

JAVA
SignatureFieldAppearance appearance = new SignatureFieldAppearance("app");
appearance.setContent("", new SignedAppearanceText());
BackgroundSize size = new BackgroundSize();
size.setBackgroundSizeToContain();
appearance.setBackgroundImage(
    new BackgroundImage.Builder()
        .setImage(new PdfImageXObject(data))
        .setBackgroundSize(size)
        .build());
signer.setSignatureAppearance(appearance);
C#
SignatureFieldAppearance appearance = new SignatureFieldAppearance("app");
appearance.SetContent("", new SignedAppearanceText());
BackgroundSize size = new BackgroundSize();
size.SetBackgroundSizeToContain();
appearance.SetBackgroundImage(
    new BackgroundImage.Builder()
        .SetImage(new PdfImageXObject(data))
        .SetBackgroundSize(size)
        .Build());
signer.SetSignatureAppearance(appearance);

The BackgroundSize can be set to have the image cover the whole appearance rectangle (scaled by the minimum factor that makes the image cover the whole rectangle), to be contained in it (scaled by the maximum factor at which the image still does not exceed the rectangle), or to explicit absolute or relative scale factors using UnitValues.

  • Customizing description captions

As already mentioned above, you can customize the reason and location lines in the description to arbitrary strings.

JAVA
signer.setReason("Specimen");
signer.setLocation("Boston");

SignedAppearanceText appearanceText = new SignedAppearanceText();
appearanceText.setReasonLine("Objective: " + signer.getReason());
appearanceText.setLocationLine("Whereabouts: " + signer.getLocation());
SignatureFieldAppearance appearance = new SignatureFieldAppearance("app");
appearance.setContent(appearanceText);
signer.setSignatureAppearance(appearance);
C#
signer.SetReason("Specimen");
signer.SetLocation("Boston");

SignedAppearanceText appearanceText = new SignedAppearanceText();
appearanceText.SetReasonLine("Objective: " + signer.GetReason());
appearanceText.SetLocationLine("Whereabouts: " + signer.GetLocation());
SignatureFieldAppearance appearance = new SignatureFieldAppearance("app");
appearance.SetContent(appearanceText);
signer.SetSignatureAppearance(appearance);
  • Customizing the text style

You can also change text font, text size, and text color.

JAVA
SignatureFieldAppearance appearance = new SignatureFieldAppearance("app");
appearance.setContent("", new SignedAppearanceText());
appearance.setFont(PdfFontFactory.createFont(StandardFonts.COURIER));
appearance.setFontColor(new DeviceRgb(0xF9, 0x9D, 0x25));
appearance.setFontSize(10);
signer.setSignatureAppearance(appearance);
C#
SignatureFieldAppearance appearance = new SignatureFieldAppearance("app");
appearance.SetContent("", new SignedAppearanceText());
appearance.SetFont(PdfFontFactory.CreateFont(StandardFonts.COURIER));
appearance.SetFontColor(new DeviceRgb(0xF9, 0x9D, 0x25));
appearance.SetFontSize(10);
signer.SetSignatureAppearance(appearance);
  • Customizing the description text

Instead of letting iText assemble the description, you can select a completely custom one.

(This custom description will only be used in the visual appearance, it will not be present in an easily machine-readable form. To provide the text also in machine-readable form, consider setting the reason property to the same text.)

JAVA
String restriction = "...";
signer.setReason(restriction);

SignatureFieldAppearance appearance = new SignatureFieldAppearance("app");
appearance.setContent(restriction, ...);
signer.setSignatureAppearance(appearance);
C#
String restriction = "...";
signer.SetReason(restriction);

SignatureFieldAppearance appearance = new SignatureFieldAppearance("app");
appearance.SetContent(restriction, ...);
signer.SetSignatureAppearance(appearance);

Completely Custom Appearances

You can even design complete signature appearances (foreground and/oer background) yourself! For this purpose simply call the getLayer0 or getLayer2 method to retrieve a PdfFormXObject which is yours to draw on.

You can customize the background upon which a standard signature appearance is drawn like this:

JAVA
Rectangle rectangle = new Rectangle(100, 500, 300, 100);
signer.setPageRect(rectangle);
signer.setPageNumber(1);

PdfFormXObject backgroundLayer = new PdfFormXObject(rectangle);
PdfCanvas canvas = new PdfCanvas(backgroundLayer, pdfSigner.getDocument());
canvas.setStrokeColor(new DeviceRgb(0xF9, 0x9D, 0x25)).setLineWidth(2);
for (int i = (int)(rectangle.getLeft() - rectangle.getHeight()); i < rectangle.getRight(); i += 5)
    canvas.moveTo(i, rectangle.getBottom()).lineTo(i + rectangle.getHeight(), rectangle.getTop());
canvas.stroke();
signer.getSignatureField().setBackgroundLayer(backgroundLayer);
C#
Rectangle rectangle = new Rectangle(100, 500, 300, 100);
signer.SetPageRect(rectangle);
signer.SetPageNumber(1);

PdfFormXObject backgroundLayer = new PdfFormXObject(rectangle);
PdfCanvas canvas = new PdfCanvas(backgroundLayer, signer.getDocument());
canvas.SetStrokeColor(new DeviceRgb(0xF9, 0x9D, 0x25)).SetLineWidth(2);
for (int i = (int)(rectangle.GetLeft() - rectangle.GetHeight()); i < rectangle.GetRight(); i += 5)
    canvas.MoveTo(i, rectangle.GetBottom()).LineTo(i + rectangle.GetHeight(), rectangle.GetTop());
canvas.Stroke();
signer.GetSignatureField().SetBackgroundLayer(backgroundLayer);

And you can customize the foreground itself like this:

JAVA
ImageData badge = ImageDataFactory.create(...);
ImageData sign = ImageDataFactory.create(...);

PdfSigner signer = new PdfSigner(pdfReader, result, new StampingProperties());

Rectangle rectangle = new Rectangle(100, 500, 300, 100);
signer.setPageRect(rectangle);
signer.setPageNumber(1);

PdfFormXObject foregroundLayer = new PdfFormXObject(rectangle);
PdfCanvas canvas = new PdfCanvas(foregroundLayer, signer.getDocument());

float xCenter = rectangle.getLeft() + rectangle.getWidth() / 2;
float yCenter = rectangle.getBottom() + rectangle.getHeight() / 2;

float badgeWidth = rectangle.getHeight() - 20;
float badgeHeight = badgeWidth * badge.getHeight() / badge.getWidth();

canvas.setLineWidth(20)
      .setStrokeColorRgb(.9f, .1f, .1f)
      .moveTo(rectangle.getLeft(), rectangle.getBottom())
      .lineTo(rectangle.getRight(), rectangle.getTop())
      .moveTo(xCenter + rectangle.getHeight(), yCenter - rectangle.getWidth())
      .lineTo(xCenter - rectangle.getHeight(), yCenter + rectangle.getWidth())
      .stroke();

sign.setTransparency(new int[] {0, 0});
canvas.addImageFittedIntoRectangle(sign,
    new Rectangle(0, yCenter, badgeWidth * sign.getWidth() / sign.getHeight() / 2, badgeWidth / 2), false);

canvas.concatMatrix(AffineTransform
    .getRotateInstance(Math.atan2(rectangle.getHeight(), rectangle.getWidth()), xCenter, yCenter));
canvas.addImageFittedIntoRectangle(badge,
    new Rectangle(xCenter - badgeWidth / 2, yCenter - badgeHeight + badgeWidth / 2, badgeWidth, badgeHeight), false);
signer.getSignatureField().setSignatureAppearanceLayer(foregroundLayer);
C#
ImageData badge = ImageDataFactory.Create(...);
ImageData sign = ImageDataFactory.Create(...);

PdfSigner signer = new PdfSigner(pdfReader, result, new StampingProperties());

Rectangle rectangle = new Rectangle(100, 500, 300, 100);
signer.SetPageRect(rectangle);
signer.SetPageNumber(1);

PdfFormXObject foregroundLayer = new PdfFormXObject(rectangle);
PdfCanvas canvas = new PdfCanvas(foregroundLayer, signer.GetDocument());

float xCenter = rectangle.GetLeft() + rectangle.GetWidth() / 2;
float yCenter = rectangle.GetBottom() + rectangle.GetHeight() / 2;

float badgeWidth = rectangle.GetHeight() - 20;
float badgeHeight = badgeWidth * badge.GetHeight() / badge.GetWidth();

canvas.SetLineWidth(20)
      .SetStrokeColorRgb(.9f, .1f, .1f)
      .MoveTo(rectangle.GetLeft(), rectangle.GetBottom())
      .LineTo(rectangle.GetRight(), rectangle.GetTop())
      .MoveTo(xCenter + rectangle.GetHeight(), yCenter - rectangle.GetWidth())
      .LineTo(xCenter - rectangle.GetHeight(), yCenter + rectangle.GetWidth())
      .Stroke();

sign.SetTransparency(new int[] {0, 0});
canvas.AddImageFittedIntoRectangle(sign,
    new Rectangle(0, yCenter, badgeWidth * sign.GetWidth() / sign.GetHeight() / 2, badgeWidth / 2), false);

canvas.ConcatMatrix(AffineTransform
    .GetRotateInstance(Math.atan2(rectangle.GetHeight(), rectangle.GetWidth()), xCenter, yCenter));
canvas.AddImageFittedIntoRectangle(badge,
    new Rectangle(xCenter - badgeWidth / 2, yCenter - badgeHeight + badgeWidth / 2, badgeWidth, badgeHeight), false);
signer.GetSignatureField().SetSignatureAppearanceLayer(foregroundLayer);

Of course you can also customize both foreground and background. Using both code pieces from above you get:

And using the custom foreground in combination with the reuse of the existing appearance of the empty signature field above in the background you get:

Thus, you can give your signature any appearance you want, and with a creative vein you can even make it look good… 😉

Finally customization also is possible using elements of the layout module, leaving layouting details up to iText.

image-20240126-123052.png

Simply set the signature field appearance content to a Div:

JAVA
ImageData badge = ImageDataFactory.create(...);
ImageData sign = ImageDataFactory.create(...);

Paragraph paragraph = new Paragraph();
Image signImage = new Image(sign);
signImage.setAutoScale(true);
paragraph.add(signImage);
Image badgeImage = new Image(badge);
badgeImage.setRotationAngle(- Math.PI / 16);
badgeImage.setAutoScale(true);
paragraph.add(badgeImage);

Div div = new Div();
div.add(paragraph);

SignatureFieldAppearance appearance = new SignatureFieldAppearance("app");
appearance.setContent(div);
signer.setSignatureAppearance(appearance);
C#
ImageData badge = ImageDataFactory.Create(...);
ImageData sign = ImageDataFactory.Create(...);

Paragraph paragraph = new Paragraph();
Image signImage = new Image(sign);
signImage.SetAutoScale(true);
paragraph.Add(signImage);
Image badgeImage = new Image(badge);
badgeImage.SetRotationAngle(- Math.PI / 16);
badgeImage.SetAutoScale(true);
paragraph.Add(badgeImage);

Div div = new Div();
div.Add(paragraph);

SignatureFieldAppearance appearance = new SignatureFieldAppearance("app");
appearance.SetContent(div);
signer.SetSignatureAppearance(appearance);

For a more detailed example see iText Core: Signature Appearance Improvements .

Signer Attributes Used Outside Of The Appearance

The PdfSigner class has some properties which are not (or not only) used for creating the visual appearance of the signature in the document but instead (or also) are stored in a machine-readable manner in the signature field value.

  • Contact Information

The value of the signature appearance Contact property is stored in the ContactInfo entry of the signature field value. This entry is specified as the Information provided by the signer to enable a recipient to contact the signer to verify the signature. A phone number is given as an example.

JAVA
String oldValue = signer.getContact();
signer.setContact(newValue);
C#
String oldValue = signer.GetContact();
signer.SetContact(newValue);
  • Location

The value of the signature appearance Location property is used in the visual appearance Description and is stored in the Location entry of the signature field value. This entry is specified as The CPU host name or physical location of the signing.

JAVA
String oldValue = signer.getLocation();
signer.setLocation(newValue);
C#
String oldValue = signer.GetLocation();
signer.SetLocation(newValue);
  • Reason

The value of the signature appearance Reason property is used in the visual appearance Description and is stored in the Reason entry of the signature field value. This entry is specified as The reason for the signing, such as ( I agree…).

JAVA
String oldValue = signer.getReason();
signer.setReason(newValue);
C#
String oldValue = signer.GetReason();
signer.SetReason(newValue);
  • Signature Creator

The value of the signature appearance SignatureCreator property is stored in the Name entry of the App dictionary in the Prop_Build dictionary in the signature field value. This entry is specified as The name of the software module used to create the signature.

JAVA
String oldValue = signer.getSignatureCreator();
signer.setSignatureCreator(newValue);
C#
String oldValue = signer.GetSignatureCreator();
signer.SetSignatureCreator(newValue);

Setting all four values you can find them in Adobe Reader in the signature panel,

in the signature properties,

and in the advanced signature properties

In Foxit Reader you find them in the signature properties

and the advanced signature properties

JavaScript errors detected

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

If this problem persists, please contact our support.