Signature Appliance Local API C#: Signing a PDF File using Field Locators

This recipe demonstrates how to use Field Locators to control the location of a signature in a PDF.

API Recipe source files are available on GitHub.

Introduction to Field Locators

Most digital signatures include a graphic representation of the signature visible within the signed document. But where exactly should the signature be placed within the document?

The Signature Appliance Local API enables the signature to be located in PDF files by either specifying the page number, location and size of the signature image, or by using Field Locators. A Field Locator is a special text sequence that is included in the PDF document itself. Later, a Local API program can easily use the Field Locator(s) to create signature(s) where the Field Locator appears in the document.

Benefits

Field Locators enable the document layout, including future signature locations to be set once, when the document itself is laid out. For example, Word can be used to compose and layout a document. At the same time, by including a Field Locator, Word can also specify the signature location for the final PDF version of the file. Note that a Field Locator is quite different from a Word Signature Line. The former is used by the API to locate signatures in a PDF file, the latter is used to sign .docx files.

The common alternative to Field Locators requires the document editor to communicate the desired signature location to the API programmer. Field Locators enable the document editor to manage the signature location too.

Field Locators are also useful when the document length varies. The Field Locators will “float” with the document. The alternative for specifying the signature location is to place the signature on the “last” page–by using filed locators, the signature API will automatically adjust for varying document lengths.

Please see the SAPISignatureFieldLocatorEnumInit function description in the API Reference Manual for more information on Field Locators.

Creating Field Locators

Field Locator will often have a format similar to this example:

<<<w=100;h=120;n=employee;a=1;>>>

 

In this example, <<< are used to start the Field Locator and >>> ends it. Other opening/closing character sequences can be used, see the manual for details.

The Field Locator can include parameters in the format shown above,

parameter=value;

Note that a trailing ; is required before the closing character sequence.

Parameter Meaning
FileName PDF file name
w or W the width of the intended signature field
h or H the height of the intended signature field
n or N the name of the intended signature field
a or A the appearance mask for the intended signature field

Use the background color

When the SAPISignatureFieldLocatorEnumInit function is called, it scans the PDF file for Locator Fields. It does not remove any fields that it finds. Therefore, to prevent the Locator Field text from being in the final PDF file, you should set the character color of the Locator Field to the background color of the page–usually white.

Example: Processing Field Locators

The example uses the API to read a file and insert a Signature Field for each Locator Field found in the document. The extract below shows the main loop for the example.

string startDelim = "<<";
string endDelim = ">>";
//
// Locate all field tags in the PDF
// The PDF filename to be used is defined above. 
// The format of each field locator in the PDF should be:
//   << w={width}; h={height}; n={field name}; a={appearance mask} >>
//  
// For example: 
//  << w=120; h=80; n=Signer1; a=15; >>
//
SAPIContext SigLocatorCtx = new SAPIContext();
 
Array fileBytes = File.ReadAllBytes(InFileName);
SAPIByteArray doc = new SAPIByteArrayClass();
doc.FromArray(ref fileBytes);
FileHandle fh;
if ((rc = SAPI.CreateFileHandleByMem(out fh, SAPI_ENUM_FILE_TYPE.SAPI_ENUM_FILE_ADOBE, 0, doc)) != 0)
{
    SAPI.HandleRelease(SesHandle);
    throw new Exception("Failed to create file handle with rc = " + rc.ToString("X"));
}
 
//  Initiate a new Field Locator enumerator.
//  The argument LocNumber will contain the number of signature tags found in the PDF document.
//
if ((rc = SAPI.SignatureFieldLocatorEnumInit(SesHandle, SigLocatorCtx, fh, startDelim, endDelim, 0, out LocNumber)) != 0)
{
    SAPI.HandleRelease(fh);
    SAPI.HandleRelease(SesHandle);
    throw new Exception("LocatorEnumInit failed with rc = " + rc.ToString("X"));
}
 
// Do for all tags in the document
string strEncMsg;
for (int i = 0; i < LocNumber; i++)
{
    // Get settings for the next field locator.
    // (Use strEncMsg string to parse the field locator content if a custom string format has been used)
    if ((rc = SAPI.SignatureFieldLocatorEnumCont(SesHandle, SigLocatorCtx, SFS, out strEncMsg)) != 0)
    {
        SAPI.ContextRelease(SigLocatorCtx);
        SAPI.HandleRelease(fh);
        SAPI.HandleRelease(SesHandle);
        throw new Exception("LocatorEnumCont failed with rc = " + rc.ToString("X"));
    }
 
    // The following values of the SignatureFieldSettings will be automatically set by SAPI:
    //   X/Y Location (including page number)
    //   Width
    //   Height
    //   Field Name
    //   Appearance Mask
    //
    SFS.LabelsMask = 0;
    SFS.DependencyMode = SAPI_ENUM_DEPENDENCY_MODE.SAPI_ENUM_DEPENDENCY_MODE_INDEPENDENT;
    SFS.SignatureType = SAPI_ENUM_SIGNATURE_TYPE.SAPI_ENUM_SIGNATURE_DIGITAL;
    SFS.Flags = 0;
 
    // time:
    TF.DateFormat = "dd MMM yyyy";
    TF.TimeFormat = "hh:mm:ss";
    TF.ExtTimeFormat = SAPI_ENUM_EXTENDED_TIME_FORMAT.SAPI_ENUM_EXTENDED_TIME_FORMAT_NONE;
    SFS.TimeFormat = TF;
 
    //  Now create the signature field on the PDF
    //  We make use of SignatureFieldCreateEx function in order to pass the FileHandle for the in-memory
    //  file rather than a UNC file path.
    if ((rc = SAPI.SignatureFieldCreateSignEx2(SesHandle, SAPI_ENUM_FILE_TYPE.SAPI_ENUM_FILE_ADOBE,
        "", fh, SFS, Flags, null)) != 0)
    {
        SAPI.ContextRelease(SigLocatorCtx);
        SAPI.HandleRelease(fh);
        SAPI.HandleRelease(SesHandle);
        throw new Exception("Failed to create new signature field with rc = " + rc.ToString("X"));
    }
}