Signature Appearance Modules

Introduction

A digital signature in a PDF document can be created with an invisible or visible signature field.

The SetaPDF-Signer component comes with simple modules that may create the signature appearance for a visible signature field for you or allow you to define your own appearance.

A module instance has to be passed to the SetaPDF_Signer::setAppearance() method. 

In version 2.0 an appearance module was a proxy module for a signature module and also implements the SetaPDF_Signer_Signature_Module_ModuleInterface interface.
Since version 2.1 the modules are separated and a refactor is needed! 

Dynamic Appearance

The SetaPDF-Signer comes with an appearance module that will create a signature appearance simliar to the appearance that can be created with e.g. Acrobat.

The module allows you to define a background, a graphic and it will automatically extract information from the signature certificate. Its simplest output will look like: 

PHP
<?php
require_once('library/SetaPDF/Autoload.php');

// create a writer
$writer = new SetaPDF_Core_Writer_Http('simple.pdf', true);
// create a new document instance
$document = SetaPDF_Core_Document::loadByFilename(
    'files/pdfs/tektown/Laboratory-Report.pdf', $writer
);

// create a signer instance
$signer = new SetaPDF_Signer($document);

// set some signature properties
$signer->setReason('Testing');
$signer->setLocation('SetaPDF-Signer Manual');

// create a signature module
$module = new SetaPDF_Signer_Signature_Module_OpenSsl();
// load the certificate
$certificate = 'file://files/certificates/setapdf-no-pw.pem';
$module->setCertificate($certificate);
$module->setPrivateKey(array($certificate, '' /* no password */));

// create a visible signature field
$signer->addSignatureField(
    SetaPDF_Signer_SignatureField::DEFAULT_FIELD_NAME,
    1,
    SetaPDF_Signer_SignatureField::POSITION_RIGHT_TOP,
    array('x' => -160, 'y' => -100),
    180,
    60
);

// create the appearance
$appearance = new SetaPDF_Signer_Signature_Appearance_Dynamic($module);

// define the appearance
$signer->setAppearance($appearance);

// sign the document and send the final document to the initial writer
$signer->sign($module);

Configure the Background

An appearance background can be defined by passing a XObject (Image or Form XObject) to the setBackgroundLogo() method. The second parameter allows you to define an opacity value (between 0 and 1): 

PHP
<?php
require_once('library/SetaPDF/Autoload.php');

// create a writer
$writer = new SetaPDF_Core_Writer_Http('simple.pdf', true);
// create a new document instance
$document = SetaPDF_Core_Document::loadByFilename(
    'files/pdfs/tektown/Laboratory-Report.pdf', $writer
);

// create a signer instance
$signer = new SetaPDF_Signer($document);

// set some signature properties
$signer->setReason('Testing');
$signer->setLocation('SetaPDF-Signer Manual');

// create a signature module
$module = new SetaPDF_Signer_Signature_Module_OpenSsl();
// load the certificate
$certificate = 'file://files/certificates/setapdf-no-pw.pem';
$module->setCertificate($certificate);
$module->setPrivateKey(array($certificate, '' /* no password */));

// create a visible signature field
$signer->addSignatureField(
    SetaPDF_Signer_SignatureField::DEFAULT_FIELD_NAME,
    1,
    SetaPDF_Signer_SignatureField::POSITION_RIGHT_TOP,
    array('x' => -160, 'y' => -100),
    180,
    60
);

// load a PDF for the background appearance
$bgDocument = SetaPDF_Core_Document::loadByFilename('files/pdfs/tektown/Logo.pdf');
// convert the first page to a XObject
$xObject = $bgDocument
    ->getCatalog()
    ->getPages()
    ->getPage(1)
    ->toXObject($document, SetaPDF_Core_PageBoundaries::ART_BOX);

// create the appearance
$appearance = new SetaPDF_Signer_Signature_Appearance_Dynamic($module);
$appearance->setBackgroundLogo($xObject, .3);

// define the appearance
$signer->setAppearance($appearance);

// sign the document and send the final document to the initial writer
$signer->sign($module);

Configure the Graphic

The graphic is the text or graphic that should be displayed in the left row of the appearance. A boolean value defines if the graphic will be shown or not. If you pass an XObject to its setter method the XObject will be displayed: 

PHP
<?php
require_once('library/SetaPDF/Autoload.php');

// create a writer
$writer = new SetaPDF_Core_Writer_Http('simple.pdf', true);
// create a new document instance
$document = SetaPDF_Core_Document::loadByFilename(
    'files/pdfs/tektown/Laboratory-Report.pdf', $writer
);

// create a signer instance
$signer = new SetaPDF_Signer($document);

// set some signature properties
$signer->setReason('Testing');
$signer->setLocation('SetaPDF-Signer Manual');

// create a signature module
$module = new SetaPDF_Signer_Signature_Module_OpenSsl();
// load the certificate
$certificate = 'file://files/certificates/setapdf-no-pw.pem';
$module->setCertificate($certificate);
$module->setPrivateKey(array($certificate, '' /* no password */));

// create a visible signature field
$signer->addSignatureField(
    SetaPDF_Signer_SignatureField::DEFAULT_FIELD_NAME,
    1,
    SetaPDF_Signer_SignatureField::POSITION_RIGHT_TOP,
    array('x' => -160, 'y' => -100),
    180,
    60
);

// load a PDF for the graphic appearance
$graphicDocument = SetaPDF_Core_Document::loadByFilename('files/pdfs/misc/Handwritten-Signature.pdf');
// convert the first page to a XObject
$xObject = $graphicDocument
    ->getCatalog()
    ->getPages()
    ->getPage(1)
    ->toXObject($document, SetaPDF_Core_PageBoundaries::ART_BOX);

// create the appearance
$appearance = new SetaPDF_Signer_Signature_Appearance_Dynamic($module);
$appearance->setGraphic($xObject);

// define the appearance
$signer->setAppearance($appearance);

// sign the document and send the final document to the initial writer
$signer->sign($module);

Configure Labels and Certificate Data

The right column displays data that was extracted from the certificate that was passed to the signature module. The configuration of this text pieces can be controlled by the setShow() and setShowTpl() methods. Their names are represented via class constants (CONFIG_XXX).

Following example shows how to format the labels, hide a value and format the date value individually: 

PHP
<?php
require_once('library/SetaPDF/Autoload.php');

// create a writer
$writer = new SetaPDF_Core_Writer_Http('simple.pdf', true);
// create a new document instance
$document = SetaPDF_Core_Document::loadByFilename(
    'files/pdfs/tektown/Laboratory-Report.pdf', $writer
);

// create a signer instance
$signer = new SetaPDF_Signer($document);

// set some signature properties
$signer->setReason('Testing');
$signer->setLocation('SetaPDF-Signer Manual');

// create a signature module
$module = new SetaPDF_Signer_Signature_Module_OpenSsl();
// load the certificate
$certificate = 'file://files/certificates/setapdf-no-pw.pem';
$module->setCertificate($certificate);
$module->setPrivateKey(array($certificate, '' /* no password */));

// create a visible signature field
$signer->addSignatureField(
    SetaPDF_Signer_SignatureField::DEFAULT_FIELD_NAME,
    1,
    SetaPDF_Signer_SignatureField::POSITION_RIGHT_TOP,
    array('x' => -160, 'y' => -100),
    180,
    60
);

// create the appearance
$appearance = new SetaPDF_Signer_Signature_Appearance_Dynamic($module);
// disable the distinguished name
$appearance->setShow(
    SetaPDF_Signer_Signature_Appearance_Dynamic::CONFIG_DISTINGUISHED_NAME, false
);

// Translate the labels to german:
$appearance->setShowTpl(
    SetaPDF_Signer_Signature_Appearance_Dynamic::CONFIG_REASON, 'Grund: %s'
);

$appearance->setShowTpl(
    SetaPDF_Signer_Signature_Appearance_Dynamic::CONFIG_LOCATION, 'Grund: %s'
);

$appearance->setShowTpl(
    SetaPDF_Signer_Signature_Appearance_Dynamic::CONFIG_DATE, 'Datum: %s'
);

$appearance->setShowTpl(
    SetaPDF_Signer_Signature_Appearance_Dynamic::CONFIG_NAME,
    'Digital signiert durch: %s'
);

// format the date
$appearance->setShowFormat(
    SetaPDF_Signer_Signature_Appearance_Dynamic::CONFIG_DATE, 'd.m.Y H:i:s'
);

// define the appearance
$signer->setAppearance($appearance);

// sign the document and send the final document to the initial writer
$signer->sign($module);

XObject Appearance

The class SetaPDF_Signer_Signature_Appearance_XObject representing a signature appearance based on an existing XObject.

An XObject could be an image, an extracted page or a fresh unique instance of a Form XObject.

To create an appearance of an existing image, you just have to pass the image instance to the constructor of the module: 

PHP
...

// load the image
$image = SetaPDF_Core_Image::getByPath('files/pdfs/tektown/Logo.png');
// get an XObject instance of it
$xObject = $image->toXObject($document);
// pass it to the appearance modules constructor
$appearance = new SetaPDF_Signer_Signature_Appearance_XObject($xObject);

// pass the appearance
$signer->setAppearance($appearance);

// pass the module to the sign method
$signer->sign($module);

An XObject can also be created from a page of an existing document: 

PHP
...

// load a document
$appDocument = SetaPDF_Core_Document::loadByFilename('files/pdfs/handwritten-signature.pdf');

$pageXObject = $appDocument
    ->getCatalog()
    ->getPages()
    ->getPage(1)->toXObject($document, SetaPDF_Core_PageBoundaries::ART_BOX);

// create a static visible appearance from the xObject
$appearance= new SetaPDF_Signer_Signature_Appearance_XObject($pageXObject);

// pass the appearance
$signer->setAppearance($appearance);

// pass the module to the sign method
$signer->sign($module);

At the end it is also possible to create an XObject instance manually and create the appearance by its Canvas instance. 

Appearances on Several Pages

The SetaPDF-Signer component does not support multiple appearances of a single signature field.

A simple quote from the Digital Signature Appearances specifications of Adobe will confirm this limitation:

The location of a signature within a document can have a bearing on its legal meaning. For this reason, signature fields never refer to more than one annotation. If more than one location is associated with a signature, the meaning may become ambiguous.[...]