Create a signature visual appearance
A signature can have a visual appearance on a PDF document page. For example, the signature can appear as a company stamp or an individual's handwritten signature. The Pdftools SDK lets you configure a visual appearance using an XML or a JSON file. The visual appearance can contain text, images, and pages of PDFs.
Download the full sample now in C# and Java.
Interested in C or other language samples? Let us know on the contact page and we'll add it to our samples backlog.
The Pdftools SDK creates invisible signatures by default, because a signature's visual appearance can obscure important content on the page. The validity of the signature is not affected by its visual appearance.
Steps to add a signature visual appearance:
- Create a signature and set its visual appearance
- Create a signature visual appearance for an existing signature field
Configure visual appearance
Configure the signature visual appearance either in an XML file or in a JSON file. The appearance comprises multiple text elements, images, or pages of PDFs that are placed inside the surrounding visual appearance container.
Examples:
- XML
- JSON
<?xml version="1.0" encoding="UTF-8"?>
<content size="130pt 90pt" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="schema-visual-appearance.xsd">
<image src="DigitalSignature.jpg"/>
<pdf src="butterfly.pdf" margin-left="110pt" width="12pt" height="12pt" margin-top="5pt"/>
<image src="pdftools-icon.png" margin-left="114pt" width="8pt" margin-top="79pt"/>
<text margin-top="20pt" margin-left="1pt" text-align="left" font-size="10">
Signed by: <span color="1 0.2 0" font="Bell MT Bold" text-mode="stroke">Max Muster</span>
</text>
<text margin-top="43pt" margin-left="1pt" text-align="left" font-size="6">
Signing date: <span font="Courier" color="0.2 0.2 0.2">[signature:date,localtime,%d.%m.%Y %H:%M:%S]</span>
</text>
<text margin-top="55pt" margin-left="1pt" text-align="left" font-size="6">
Company: <span font="OpenSans-Semibold.ttf" color="0 0 1">[custom:company]</span>
</text>
<text font-size="4" font="Arial" margin-top="78pt" margin-left="90pt">
Powered by:
</text>
</content>
{
"size": "130pt 90pt",
"content": [
{
"image": "DigitalSignature.jpg"
},
{
"pdf": "butterfly.pdf",
"margin-left": "110pt",
"width": "12pt",
"height": "12pt",
"margin-top": "5pt"
},
{
"image": "pdftools-icon.png",
"margin-left": "114pt",
"width": "8pt",
"margin-top": "79pt"
},
{
"margin-top": "20pt",
"margin-left": "1pt",
"text-align": "left",
"font-size": "10",
"text": [
"Signed by: ",
{
"color": "1 0.2 0",
"font": "Bell MT Bold",
"text-mode": "stroke",
"text": "Max Muster"
}
]
},
{
"margin-top": "43pt",
"margin-left": "1pt",
"text-align": "left",
"font-size": "6",
"text": [
"Signing date: ",
{
"font": "Courier",
"color": "0.2 0.2 0.2",
"text": "[signature:date,localtime,%d.%m.%Y %H:%M:%S]"
}
]
},
{
"margin-top": "55pt",
"margin-left": "1pt",
"text-align": "left",
"font-size": "6",
"text": [
"Company: ",
{
"font": "OpenSans-Semibold.ttf",
"color": "0 0 1",
"text": "[custom:company]"
}
]
},
{
"font-size": "4",
"font": "Arial",
"margin-top": "78pt",
"margin-left": "90pt",
"text": [ "Powered by:" ]
}
]
}
The XML or JSON file specifies only the size of the visual appearance container but not its actual location. In the case of signing, the location can be explicitly specified in the code, while for signing of an existing unsigned signature field, the location is predetermined.
The schema file defines the visual appearance of an XML file. The structure of a JSON file is equivalent.
The Pdftools SDK creates the appearance according to the configuration as follows:
- .NET
- Java
// Create appearance from an XML
using var appStream = File.OpenRead(appearanceXml);
Appearance appearance = Appearance.CreateFromXml(appStream);
// Create appearance from a JSON file
using var appStream = File.OpenRead(appearanceJson);
Appearance appearance = Appearance.CreateFromJson(appStream);
try (
// Create appearance from an XML
FileStream appConfigStr = new FileStream(appearanceXml, FileStream.Mode.READ_ONLY))
{
Appearance appearance = Appearance.createFromXml(appConfigStr));
}
try (
// Create appearance from a JSON file
FileStream appConfigStr = new FileStream(appearanceJson, FileStream.Mode.READ_ONLY))
{
Appearance appearance = Appearance.createFromJson(appConfigStr);
}
Apply text variables
Text variables are placeholders in the text. Placeholders are substituted with specific values just before signing the document. There are two types of text variables:
- Pre-defined variables (such as location, title, author)
- Custom variables (defined by you)
In other words, the text can be parametrized using text variables, either by pre-defined text variables or by custom text variables.
Text variables take the general form of [<variable_type>:<name>]
.
They are replaced by their actual values just before signing the document.
Pre-defined text variables
There are a couple of pre-defined text variables for which values are set during code execution:
[signature:contactinfo]
[signature:location]
[signature:reason]
[document:title]
[document:author]
[document:subject]
The special variable [signature:date,<type>,<format>]
describes the signing date. <type>
takes the values utc
or localtime
. Different date formats <format>
can be specified. Some typical formats are:
- EU:
%d.%m.%Y %H:%M:%S
- US:
%m/%d/%Y %H:%M:%S
- UK:
%d/%m/%Y %H:%M:%S
For a complete description of the date formatting options consult https://strftime.org/.
Custom text variables
You can also store custom text variables and their values as key-value pairs in the map Appearance.CustomTextVariables
. For more information, see an example in Create a signature and set visual appearance section. The value
is accessed in the text by [custom:<key>]
.
Create a signature and set its visual appearance
Learn more about signing a PDF document.
- .NET
- Java
// Create a session to the built-in cryptographic provider
using var session = new BuiltIn.Provider();
// Open certificate file
using var pfxStr = File.OpenRead(certificateFile);
// Create signature configuration from PFX (or P12) file
SignatureConfiguration signature = session.CreateSignatureFromCertificate(pfxStr, password);
// Create appearance from either an XML or a JSON file
using var appStream = File.OpenRead(appConfigFile);
if (Path.GetExtension(appConfigFile) == ".xml")
signature.Appearance = Appearance.CreateFromXml(appStream);
else
signature.Appearance = Appearance.CreateFromJson(appStream);
signature.Appearance.PageNumber = 1;
signature.Appearance.CustomTextVariables.Add("company", "Daily Planet");
// Set location of appearance
signature.Appearance.Bottom = Length.pt(20);
signature.Appearance.Right = Length.pt(15);
// Open input document
using var inStr = File.OpenRead(inPath);
using var inDoc = Document.Open(inStr);
// Create stream for output file
using var outStr = File.Create(outPath);
// Sign the input document
using var outDoc = new Signer().Sign(inDoc, signature, outStr);
try (
// Create a session to the built-in cryptographic provider
Provider session = new Provider();
// Open certificate file
FileStream pfxStr = new FileStream(certificateFile, FileStream.Mode.READ_ONLY))
{
// Create signature configuration from PFX (or P12) file
SignatureConfiguration signature = session.createSignatureFromCertificate(pfxStr, password);
try (
// Create appearance from either an XML or a JSON file
FileStream appConfigStr = new FileStream(appConfigFile, FileStream.Mode.READ_ONLY))
{
if (appConfigFile.toLowerCase().endsWith(".xml"))
signature.setAppearance(Appearance.createFromXml(appConfigStr));
else
signature.setAppearance(Appearance.createFromJson(appConfigStr));
signature.getAppearance().setPageNumber(1);
signature.getAppearance().getCustomTextVariables().put("company", "Daily Planet");
// Set location of appearance
signature.getAppearance().setBottom(new Length(20));
signature.getAppearance().setTop(new Length(15));
try(
// Open input document
FileStream inStr = new FileStream(inPath, FileStream.Mode.READ_ONLY);
Document inDoc = Document.open(inStr);
// Create a stream for the output file
FileStream outStr = new FileStream(outPath, FileStream.Mode.READ_WRITE_NEW);
// Sign the input document
Document outDoc = new Signer().sign(inDoc, signature, outStr))
{
}
}
}
Create a signature visual appearance for an existing signature field
Learn more about signing a PDF document.
You can apply a signature visual appearance when signing an existing unsigned signature field. In the following example, the input PDF's first unsigned signature field (of potentially many unsigned signature fields) is signed, and a visual appearance is added.
- .NET
- Java
// Create a session to the built-in cryptographic provider
using var session = new PdfTools.Crypto.Providers.BuiltIn.Provider();
// Create signature configuration from PFX (or P12) file
using var pfxStr = File.OpenRead(certificateFile);
Signature signature = session.CreateSignatureFromCertificate(pfxStr, password);
// Open input document
using var inStr = File.OpenRead(inPath);
using var inDoc = Document.Open(inStr);
// Choose first signature field
foreach (var field in inDoc.SignatureFields)
{
if (field != null)
{
signature.FieldName = field.FieldName;
break;
}
}
// Create stream for output file
using var outStr = File.Create(outPath);
// Create appearance from either an XML or a JSON file
using var appStream = File.OpenRead(appConfigFile);
if (Path.GetExtension(appConfigFile) == ".xml")
signature.Appearance = Appearance.CreateFromXml(appStream);
else
signature.Appearance = Appearance.CreateFromJson(appStream);
signature.Appearance.CustomTextVariables.Add("company", "Daily Planet");
// Sign the input document
using var outDoc = new Signer().Sign(inDoc, signature, outStr);
try (
// Create a session to the built-in cryptographic provider
Provider session = new Provider();
// Open certificate file
FileStream pfxStr = new FileStream(certificateFile, FileStream.Mode.READ_ONLY);
// Open input document
FileStream inStr = new FileStream(inPath, FileStream.Mode.READ_ONLY);
Document inDoc = Document.open(inStr);)
{
// Create signature configuration from PFX (or P12) file
SignatureConfiguration signature = session.createSignatureFromCertificate(pfxStr, password);
// Choose first signature field
for (int i = 0; i < inDoc.getSignatureFields().size(); i++) {
if (inDoc.getSignatureFields().get(i) != null) {
signature.setFieldName(inDoc.getSignatureFields().get(i).getFieldName());
break;
}
}
try (
// Create appearance from either an XML or a JSON file
FileStream appConfigStr = new FileStream(appConfigFile, FileStream.Mode.READ_ONLY))
{
if (appConfigFile.toLowerCase().endsWith(".xml"))
signature.setAppearance(Appearance.createFromXml(appConfigStr));
else
signature.setAppearance(Appearance.createFromJson(appConfigStr));
signature.getAppearance().getCustomTextVariables().put("company", "Daily Planet");
try(
// Create a stream for the output file
FileStream outStr = new FileStream(outPath, FileStream.Mode.READ_WRITE_NEW);
// Sign the input document
Document outDoc = new Signer().sign(inDoc, signature, outStr))
{
}
}
}