When you use the Document Viewer to sign documents with fillable form fields, the signature image and form field data are automatically merged into the resulting document. In some cases, however, form fields need to be pre-populated with known data, and the new data and signature image need to be applied on server side.

In most cases, this is required when requesting signatures from multiple signers, where all collected signatures and form data are merged into one resulting document.

The Template

The following screenshot shows the sample template used in this example:

Forms Template

The template contains some form text fields, a drop-down box, and a signature field.

The Prepare Endpoint

The following controller method loads the template and merges known data (CustomerData object) into the template.

[HttpGet]
[Route("Prepare")]
public DocumentData Prepare()
{
// create a new CustomerData object and serialize it to JSON
var customer = new CustomerData {
Name = "Smith",
FirstName = "John",
Street = "Main Street 1",
City = "New York",
Zip = "10001",
Country = "United States"
};
var customerJson = JsonSerializer.Serialize(customer);
byte[] documentBytes;
using (var tx = new ServerTextControl()) {
tx.Create();
tx.Load("App_Data/template.tx", StreamType.InternalUnicodeFormat);
// create a new MailMerge object and merge the JSON data
var merge = new MailMerge {
TextComponent = tx,
FormFieldMergeType = FormFieldMergeType.Preselect,
RemoveEmptyFields = false,
RemoveEmptyImages = false
};
merge.MergeJsonData(customerJson);
// save the document
tx.Save(out documentBytes, BinaryStreamType.InternalUnicodeFormat);
}
var documentBase64 = Convert.ToBase64String(documentBytes);
return new DocumentData { Name = "template.pdf", Document = documentBase64 };
}
view raw test.cs hosted with ❤ by GitHub
public class CustomerData
{
public string Name { get; set; }
public string FirstName { get; set; }
public string Street { get; set; }
public string City { get; set; }
public string Zip { get; set; }
public string Country { get; set; }
}
view raw data.cs hosted with ❤ by GitHub

The document/prepare endpoint is called from the Angular application to load the template and to merge the known data into the form fields. The resulting document is returned as a base64 encoded string.

@HostListener('window:documentViewerLoaded', ['$event'])
onDocumentViewerLoaded() {
var signatureSettings = {
showSignatureBar: true,
redirectUrlAfterSignature: 'https://localhost:7122/document/customsign',
ownerName: 'Paul',
signerName: 'Jacob',
signerInitials: 'PK',
signatureBoxes: [{ name: 'txsign', signingRequired: true, style: 0 }]
};
this.http.get<DocumentData>('/document/prepare').subscribe(
(result) => {
this.documentData = result;
console.log(this.documentData);
TXDocumentViewer.loadDocument(this.documentData.document, this.documentData.name, signatureSettings);
}
);
}
view raw test.ts hosted with ❤ by GitHub

The following is a screenshot of the Document Viewer with the form fields pre-populated with values.

Prepared Form Fields

The CustomSign Endpoint

The following controller method is used to apply the signature and the form data to the document. The signature image is passed as a base64 encoded string and the form data is available in the SignatureData object.

[HttpPost]
[Route("CustomSign")]
public string CustomSign([FromBody] SignatureData signatureData)
{
byte[] pdfBytes;
using (var tx = new ServerTextControl()) {
tx.Create();
tx.Load("App_Data/template.tx", StreamType.InternalUnicodeFormat);
// load the signature image into the signature fields
foreach (var box in signatureData.SignatureBoxes) {
foreach (SignatureField field in tx.SignatureFields) {
if (field.Name == box.Name) {
var stamp = Convert.FromBase64String(signatureData.SignatureImage);
using (var ms = new MemoryStream(stamp, 0, stamp.Length, false, true)) {
field.Image = new SignatureImage(ms);
}
}
}
}
// fill the form fields with the data
foreach (TXTextControl.FormField formField in tx.FormFields) {
foreach (var dataField in signatureData.FormFields) {
if (dataField.Name == formField.Name) {
formField.Text = dataField.Value;
break;
}
}
}
FlattenFormFields(tx);
tx.Save(out pdfBytes, BinaryStreamType.AdobePDF);
}
return Convert.ToBase64String(pdfBytes);
}
view raw test.cs hosted with ❤ by GitHub
private void FlattenFormFields(ServerTextControl textControl)
{
int fieldCount = textControl.FormFields.Count;
for (int i = 0; i < fieldCount; i++) {
TextFieldCollectionBase.TextFieldEnumerator fieldEnum =
textControl.FormFields.GetEnumerator();
fieldEnum.MoveNext();
TXTextControl.FormField curField = (TXTextControl.FormField)fieldEnum.Current;
textControl.FormFields.Remove(curField, true);
}
}
view raw test.cs hosted with ❤ by GitHub

Rather than using the document from the Document Viewer, it loads the template from the file system. It loops through all of the SignatureFields TX Text Control .NET Server for ASP.NET
TXTextControl Namespace
SignatureField Class
An instance of the SignatureField class represents a signature field in a Text Control document.
to apply the representation of the signature image that has been captured from the end user.

Then all FormFields TX Text Control .NET Server for ASP.NET
TXTextControl Namespace
FormField Class
The FormField class is the base class of all form fields.
of the form are looped and the text is replaced with the filled text from the Document Viewer.

Finally, all form fields are flattened so that only the text is available in the resulting PDF document.

The following screenshot shows the resulting document with the signature and the form data applied:

Signed Document

Conclusion

This article showed how to prepare forms for the Document Viewer and how to customize the signing process in Angular and ASP.NET Core. A server-side controller is used to apply the signature and the form data to the document.

Download the sample from GitHub and check out the other articles in this series.

Contact us if you have any questions.