The following code shows a more flexible and complicated scenario when there are multiple signers. Each signer must have a signature on each page, and their signature appearance might be different on each page.
First, the following utility method is defined:
using System;
using GemBox.Pdf.Forms;
namespace GemBox.Pdf
{
static class GemBoxPdfExtensions
{
public static PdfSignatureField AddSignatureToEachPage(this PdfFieldCollection fields, PdfSigner signer, Func<int, (double left, double bottom, double width, double height)> signatureRectangleOnPage, Action<int, PdfSignatureAppearance> signatureAppearanceOnPage, PdfDocument document, int pageNumber = 0, int pageCount = int.MaxValue)
{
(double left, double bottom, double width, double height) = signatureRectangleOnPage(pageNumber);
var signatureField = document.Form.Fields.AddSignature(document.Pages[pageNumber], left, bottom, width, height);
var signatureAppearance = signatureField.Appearance;
signatureField.Sign(signer);
signatureAppearanceOnPage(pageNumber, signatureAppearance);
signatureAppearance.Refresh();
var appearanceForm = signatureAppearance.Get();
var signatureFields = new PdfSignatureField[Math.Min(pageCount - 1, document.Pages.Count - pageNumber - 1)];
for (int i = 0; i < signatureFields.Length; ++i)
{
int currentPageNumber = pageNumber + 1 + i;
(left, bottom, width, height) = signatureRectangleOnPage(currentPageNumber);
var nextSignatureField = document.Form.Fields.AddSignature(document.Pages[currentPageNumber], left, bottom, width, height);
signatureFields[i] = nextSignatureField;
signatureAppearanceOnPage(currentPageNumber, signatureAppearance);
signatureAppearance.Refresh();
nextSignatureField.Appearance.Set(signatureAppearance.Get());
}
signatureAppearance.Set(appearanceForm);
for (int i = 0; i < signatureFields.Length; ++i)
signatureFields[i].Name = signatureField.Name;
return signatureField;
}
}
}
And this utility method is used like in the following code snippet:
var desktop = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
// Signature appearance template - used to specify the signature appearance for each page.
var signatureAppearanceOnPage = (int pageNumber, PdfSignatureAppearance signatureAppearance) =>
{
// Signature appearance will consist of a text above an image.
signatureAppearance.TextPlacement = PdfTextPlacement.TextAboveIcon;
// Text should occupy 40% of the annotation rectangle height. The rest will be occupied by the image.
signatureAppearance.TextExtent = 0.4;
// Text should be right aligned.
signatureAppearance.TextAlignment = PdfTextAlignment.Right;
// Set font. A zero value for font size means that the text is auto-sized to fit the annotation rectangle.
signatureAppearance.Font = new PdfFont("Times New Roman", 0);
// Show a 'Location' label and value.
signatureAppearance.Location = "Page " + (pageNumber + 1);
// Do not show a 'Date' label nor value.
signatureAppearance.DateFormat = string.Empty;
// Set the signature image.
signatureAppearance.Icon = PdfImage.Load(Path.Combine(desktop, "GemBoxSignature.png"));
// The signature image should be scaled only if it is too big to fit.
signatureAppearance.IconScaleCondition = PdfScaleCondition.ContentTooBig;
// The signature image should dock to the bottom (y = 0) right (x = 1) corner.
signatureAppearance.IconAlignment = new PdfPoint(1, 0);
};
var ecdsaIntermediateCertificate = new PdfCertificate(Path.Combine(desktop, "GemBoxECDsa.crt"));
// For brevity of the code, all signers use the same signature appearance template.
var signatureInfos = new (PdfSigner signer, Func<int, (double left, double bottom, double width, double height)> signatureRectangleOnPage, Action<int, PdfSignatureAppearance> signatureAppearanceOnPage, string outputFileName)[]
{
(new PdfSigner(new PdfDigitalId(Path.Combine(desktop, "GemBoxECDsa521.pfx"), "GemBoxPassword"))
{
ValidationInfo = new PdfSignatureValidationInfo(new PdfCertificate[] { ecdsaIntermediateCertificate }, null, null)
},
pageNumber => (300, 500, 250, 100),
signatureAppearanceOnPage,
"Multiple Digital Signature on each page.pdf"),
(new PdfSigner(new PdfDigitalId(Path.Combine(desktop, "GemBoxECDsa192.pfx"), "GemBoxPassword"))
{
ValidationInfo = new PdfSignatureValidationInfo(new PdfCertificate[] { ecdsaIntermediateCertificate }, null, null)
},
pageNumber => (300, 300, 250, 100),
signatureAppearanceOnPage,
string.Empty),
(new PdfSigner(new PdfDigitalId(Path.Combine(desktop, "GemBoxRSA4096.pfx"), "GemBoxPassword"))
{
ValidationInfo = new PdfSignatureValidationInfo(new PdfCertificate[] { new PdfCertificate(Path.Combine(desktop, "GemBoxRSA.crt")) }, null, null)
},
pageNumber => (300, 100, 250, 100),
signatureAppearanceOnPage,
string.Empty),
};
using (var document = PdfDocument.Load(Path.Combine(desktop, "Lines.pdf")))
{
foreach (var signatureInfo in signatureInfos)
{
document.Form.Fields.AddSignatureToEachPage(signatureInfo.signer, signatureInfo.signatureRectangleOnPage, signatureInfo.signatureAppearanceOnPage, document);
if (string.IsNullOrEmpty(signatureInfo.outputFileName))
document.Save();
else
document.Save(Path.Combine(desktop, signatureInfo.outputFileName));
}
}
Resources used in this code snippet can be downloaded from the example PDF digital signatures in C# and VB.NET .
Note that Adobe Reader has unusual behavior. It seems that it doesn’t support signature field widgets with different appearances, so all signatures from the same signer have the same appearance.
Foxit PDF Reader, Edge browser, and Firefox browser support signature field widgets with different appearances.
Regards,
Stipo