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:
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 }; | |
} |
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; } | |
} |
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); | |
} | |
); | |
} |
The following is a screenshot of the Document Viewer with the form fields pre-populated with values.
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); | |
} |
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); | |
} | |
} |
Rather than using the document from the Document Viewer, it loads the template from the file system. It loops through all of the Signature ╰ 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 Form ╰ 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:
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.