Reading Form Fields and Values
Once a customer has filled in a PDF form and sent it back, your WPF application needs the values, not the pages. Forms.GetAcroFormFields reads them straight from the PDF file or a byte array, without loading the document into the TextControl of a window. The values can be shown, stored or transferred back into the form in the editor, where a user checks and corrects them.
The examples read form-filled.pdf, the form of Create a PDF Form with the fields name, country and newsletter, filled in and saved in a PDF reader. Creating AcroForms describes how such forms are made.
How GetAcroFormFields Reads a Form
Forms.GetAcroFormFields, introduced in version 29.0, is a static method of the TXTextControl.DocumentServer.PDF namespace. It takes the path of a PDF file or the PDF as a byte array and returns an array of FormField objects of the TXTextControl.DocumentServer.PDF.AcroForms namespace. Each object is of the class of its field:
| Class | Field in the PDF | Value |
|---|---|---|
| FormTextField | Text field; also the date form fields of TX Text Control | Value: the text entered |
| FormCheckBox | Check box | IsChecked |
| FormRadioButton | Radio button, derived from FormCheckBox | IsChecked |
| FormChoiceField | The base class of combo boxes and list boxes | Value: the selected item; Options: all items |
| FormComboBox | Combo box or drop-down list, derived from FormChoiceField | Value |
| FormListBox | List box, derived from FormComboBox | Value |
| FormButton | Push button | None |
The base class gives every field these members:
- FieldName: the name of the field, for forms of TX Text Control the
Nameof the form field. - AlternateFieldName: the alternate name, shown by PDF readers as the tooltip.
- FieldType:
Txfor text,Btnfor buttons,Chfor choice fields andSigfor signature fields. - Bounds: the rectangle of the field in the document.
Forms filled in with any of the readers in Where Users Fill In the Form can be read. In Google Chrome, users save the filled form with the Download button and choose the version with their changes.
Show the Values of a Filled Form
Add a button with the handler ReadFormButton_Click to MainWindow.xaml. The following code in MainWindow.xaml.cs collects the values of a PDF form by field name and shows them in a message box:
private Dictionary<string, string> ReadFormValues(string path)
{
Dictionary<string, string> values = new Dictionary<string, string>();
foreach (AcroForms.FormField field in Forms.GetAcroFormFields(path))
{
switch (field)
{
case AcroForms.FormTextField textField:
values[field.FieldName] = textField.Value;
break;
case AcroForms.FormCheckBox checkBox: // check boxes and radio buttons
values[field.FieldName] = checkBox.IsChecked ? "true" : "false";
break;
case AcroForms.FormChoiceField choiceField: // combo boxes and list boxes
values[field.FieldName] = choiceField.Value;
break;
}
}
return values;
}
private void ReadFormButton_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog dialog = new OpenFileDialog() { Filter = "PDF forms|*.pdf" };
if (dialog.ShowDialog(this) != true)
{
return;
}
Dictionary<string, string> values;
try
{
values = ReadFormValues(dialog.FileName);
}
catch (FilterException exception)
{
MessageBox.Show(this, "The form could not be read: " + exception.Reason, "PDF Form");
return;
}
if (values.Count == 0)
{
MessageBox.Show(this, "The PDF document has no form fields.", "PDF Form");
return;
}
MessageBox.Show(this, string.Join("\n", values.Select(value => value.Key + ": " + value.Value)), "PDF Form");
}
The code needs these using directives:
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using Microsoft.Win32;
using TXTextControl;
using TXTextControl.DocumentServer.PDF;
using AcroForms = TXTextControl.DocumentServer.PDF.AcroForms;
The code works as follows:
- The alias
AcroFormskeeps the PDF classes apart from the form fields of the document: both namespaces contain a class namedFormField. - The
switchstatement handles check boxes and radio buttons throughFormCheckBox, and combo boxes and list boxes through the base classFormChoiceField, because only their values are needed. To tell the derived classes apart, testFormRadioButtonbeforeFormCheckBoxandFormListBoxbeforeFormComboBox. - A name can occur more than once in the array. The indexer of the dictionary keeps the last value, where
ToDictionarywould throw. - A file that can't be read throws a FilterException.
GetAcroFormFieldshas no password parameter, so PDF forms that require a password to open can't be read.
For the form of Getting Started, the message box shows lines such as name: Tim Typer, country: Germany and newsletter: true.
Put the Values Back Into the Form
TX Text Control doesn't write values into an existing PDF. Load your document of the form, the one the PDF was created from, into textControl1, and transfer the values into its form fields:
private void FillEditorFormButton_Click(object sender, RoutedEventArgs e)
{
Dictionary<string, string> values = ReadFormValues("form-filled.pdf");
// The main text, headers, footers and text frames
foreach (IFormattedText textPart in textControl1.TextParts)
{
foreach (FormField formField in textPart.FormFields)
{
if (string.IsNullOrEmpty(formField.Name) ||
!values.TryGetValue(formField.Name, out string value) || value == null)
{
continue;
}
switch (formField)
{
case CheckFormField checkFormField:
checkFormField.Checked = value == "true";
break;
case SelectionFormField selectionFormField:
// -1 selects nothing if the value isn't one of the items
selectionFormField.SelectedIndex = Array.IndexOf(selectionFormField.Items, value);
break;
case TextFormField textFormField:
textFormField.Text = value;
break;
}
}
}
}
The code works as follows:
- TextParts of the WPF
TextControlcontains the main text, the headers and footers and the text frames, each with its own FormFields collection. - Each form field type receives the value through its own property: Checked, SelectedIndex of the Items or Text.
- Date form fields are skipped, because their value arrives as the displayed text. To set one, parse the text with the culture of the form and assign DateFormField.Date.
The user sees the values in the form at once. Saved again as PDF, the document is a new fillable form; flattened first, it has fixed values, as Exporting Interactive and Flattened Forms shows. A MailMerge with FormFieldMergeType.Preselect fills form fields from data as well, as Merge Fields describes.
Next Steps
- Exporting Interactive and Flattened Forms: save the form with fixed values.
- Creating AcroForms: create the PDF forms you read.
- The blog article Export and Import Adobe PDF XFDF XML Files in .NET C#: store the values of a form as XFDF.