Chapter 12: Existing Form Fill-in

Contents

12.1 Form Fill-in Overview

12.1.1 SetFieldValue Method

In Chapter 9 of this manual, we already described the process of filling in a form-like document (see Section 9.2).

The approach described in that chapter had one serious drawback: individual blanks in a "form" had to be referenced by their physical coordinates. This is because the Section 9.2 code sample did not involve a real form, but a regular (static) PDF document depicting a form.

Using tools such as Adobe Acrobat, it is possible to place interactive fields on an existing document using a graphical user interface. The form creator chooses a name, size, location and other options for each field. Once a form has been created, it can be filled in either interactively by a user, or automatically using tools such as AspPDF. A form field can be referenced by name, as opposed to (x, y)-coordinates, which simplifies the coding significantly.

The PdfForm object provides the method FindField which returns a PdfAnnot object representing a top-level form field with the specified name. If no field under this name is found, the method returns Nothing.

Set Field = Doc.Form.FindField("txtLastName")

To specify a text value for a field, the method SetFieldValue of the PdfAnnot object should be used. This method accepts two arguments: a text string, and a font to be used to render the string. The SetFieldValue method uses the field's pre-defined alignment and font size parameters. It also preserves an existing field border if there is one.

As of Version 2.6, AspPDF also supports the method SetFieldValueEx which only requires a single argument, the text string. This method is described below.

The SetFieldValue method can be used to set a value not only for text fields, but checkboxes, drop-down list boxes and radio buttons as well. To select or unselect a checkbox, you must specify a state name for the text string argument. The state name for the "on" state should be obtained via the FieldOnValue property. The state name for the "off" state is usually "Off".

When working with radio button groups, you need to use the PdfAnnot.Children collection to reference each individual radio button within a group. The code snippet below deselects radio button #1 (American Express) and selects radio button #2 via the SetFieldValue method.

The following code fragment fills out a credit card form generated in Section 11.2:

' Credit card number
Set Field = Doc.Form.FindField("ccNumber")
Field.SetFieldValue "324234324234", Doc.fonts("Helvetica")

' Expiration month
Set Field = Doc.Form.FindField("ccMonth")
Field.SetFieldValue "Mar", Doc.fonts("Helvetica")

' Expiration year
Set Field = Doc.Form.FindField("ccYear")
Field.SetFieldValue "2010", Doc.fonts("Helvetica")

' "Need Receipt?" checkbox
Set Field = Doc.Form.FindField("Receipt")
Field.SetFieldValue Field.FieldOnValue, Nothing ' "on" state

' Credit card type (radio buttons)
Set Field = Doc.Form.FindField("ccType")
Field.Children(1).SetFieldValue "Off", Nothing ' Unselect Amex
Field.Children(2).SetFieldValue field.Children(2).FieldOnValue, Nothing ' Select Visa

To make a field read-only, Bit 1 of the property FieldFlags needs to be set, as follows:

Field.FieldFlags = Field.FieldFlags Or 1
objField.FieldFlags |= 1;

12.1.2 SetFieldValueEx Method

As of Version 2.6, AspPDF supports a more convenient way to fill in forms, via the PdfAnnot method SetFieldValueEx. This method takes a single argument, the text string. SetFieldValueEx internally calls SetFieldValue with a font object it automatically creates from the information contained in the PDF form being filled.

The SetFieldValueEx method simplifies the coding as the need for a font object is eliminated. For example, the code

Set Font = Doc.Fonts("Helvetica")
Set Field = Doc.Form.FindField("ccNumber")
Field.SetFieldValue "324234324234", Font

can now be replaced with:

Set Field = Doc.Form.FindField("ccNumber")
Field.SetFieldValueEx "324234324234"

Note that the SetFieldValueEx method does not make SetFieldValue completely obsolete as the former relies on the fonts embedded in the PDF form, and those may not be sufficient to display all the necessary characters. In that case, an external font should be used via the method SetFieldValue.

12.2 Adobe Acrobat 7+/Designer 7+ Issues

The emergence of Adobe Acrobat 7.0 (and above) and Designed 7.0 (and above) brought about a number of significant changes to the PDF form specifications. Two changes that are most relevant to AspPDF's form fill-in functionality are a new way of naming form fields, and the Adobe XML Forms Architecture (XFA).

When you switch to PDF forms created by Version 7 and later of the Adobe Acrobat suite, you may have to make some changes to your AspPDF-based applications to accommodate the new Adobe specs.

12.2.1 Field Naming

Adobe Designer 7 assigns hierarchically-built names to form fields, such as form1[0].#subform[0].Address[0]. It is these long names that have to be passed to the Form.FindField method.

If you are not sure as to the full name of a field in your PDF, we recommend opening this document in a text editor (such as WordPad) and searching for your short field name (such as "Address"). You are likely to find a fragment which looks similar to this:

33 0 obj<</Rect[118.701 659.835 501.165 681.165]/TM(form1[0].#subform[0].Address[0]) /Subtype/Widget/TU(Address:)/Parent 29 0 R/F 4/P 8 0 R/Q 0/T<FEFF0041006400640072006500730073005B0030005D> /StructParent 2/DA(/TimesNewRomanPSMT 10.00 Tf 0 g)/FT/Tx/Type/Annot/MK<<>>>>
endobj

The string in parentheses followed by the word /TM is likely this field's long name which you can copy and paste into your script.

To help determine the names of the fields in your form, we have provided the Form Field Finder online application which enables you to upload your PDF document to our server, and the list of full field names in the document will be instantly displayed.

12.2.2 Adobe XML Forms Architecture (XFA)

In a nutshell, XFA is Adobe's new way to describe form structure and content using XML. XFA is only briefly mentioned in the official PDF 1.6 specifications, but the full description of this new standard is available from the Adobe.com web site.

An attempt to programmatically fill in an XFA-based form (such as a form created by Adobe Designer 7) using AspPDF may fail as AspPDF versions prior to 3.0 lacked XFA support. As a workaround, AspPDF 1.5.0.1+ offers the method PdfForm.RemoveXFA which completely removes the XFA information from a form, thus making it compatible with older PDF form specifications, which AspPDF does support. In most cases, the user will not even notice any difference between an XFA-based and XFA-free form.

The following code snippet demonstrates the usage of the new method:

Set Doc = Pdf.OpenDocument("c:\path\TheForm.pdf")
Doc.Form.RemoveXFA

Set field = Doc.Form.FindField("form1[0].#subform[0].Name[0]")
field.SetFieldValue "John Smith", Doc.fonts("Times-Roman")
...
' Fill in other fields

UPDATE: XFA support was added to AspPDF in Version 3.0. For details, see Section 12.7 - XFA Support of this chapter below.

12.3 Code Sample

The following code sample fills in a simple form created in Adobe Designer 7.0.

Set PDF = Server.CreateObject("Persits.PDF")

' Open an existing form
Set Doc = PDF.OpenDocument( Server.MapPath( "SimpleForm.pdf" ) )

' Create font object
Set Font = Doc.Fonts("Helvetica-Bold")

' Remove XFA support from it
Doc.Form.RemoveXFA

' Fill in Name
Set field = Doc.Form.FindField("form1[0].#subform[0].Name[0]")
field.SetFieldValue Request("Name"), Font

' Fill in Address
Set field = Doc.Form.FindField("form1[0].#subform[0].Address[0]")
field.SetFieldValue Request("Address"), Font

' Fill in marital status
Set field = Doc.Form.FindField("form1[0].#subform[0].RadioButtonList[0]")
Set ChildField = field.Children( Request("Type") )
ChildField.SetFieldValue ChildField.FieldOnValue, Nothing

' Fill in "How did you hear about us" checkboxes
If Request("Internet") <> "" Then
   Set field = Doc.Form.FindField("form1[0].#subform[0].Internet[0]")
   field.SetFieldValue field.FieldOnValue, Nothing
End if

If Request("WordOfMouth") <> "" Then
   Set field = Doc.Form.FindField("form1[0].#subform[0].WordOfMouth[0]")
   field.SetFieldValue field.FieldOnValue, Nothing
End if

If Request("Newspaper") <> "" Then
   Set field = Doc.Form.FindField("form1[0].#subform[0].Newspaper[0]")
   field.SetFieldValue field.FieldOnValue, Nothing
End if

'Save document
Path = Server.MapPath( "filledform.pdf")
FileName = Doc.Save( Path, false)

Set Doc = Nothing
Set Pdf = Nothing
public void GeneratePDF(object sender, System.EventArgs args)
{
   // create instance of the PDF manager
   IPdfManager objPDF;
   objPDF = new PdfManager();

   // Open existing form
   IPdfDocument objDoc = objPDF.OpenDocument( Server.MapPath( "SimpleForm.pdf" ), Missing.Value );

   IPdfFont objFont = objDoc.Fonts["Helvetica-Bold", Missing.Value]; // a standard font

   // Remove XFA support from it
   objDoc.Form.RemoveXFA();

   // Fill in Name
   IPdfAnnot objField = objDoc.Form.FindField("form1[0].#subform[0].Name[0]");
   objField.SetFieldValue( txtName.Text, objFont );

   // Fill in Address
   objField = objDoc.Form.FindField("form1[0].#subform[0].Address[0]");
   objField.SetFieldValue( txtAddress.Text, objFont );

   // Fill in marital status
   int nIndex = 1;
   if( rdMarried.Checked )
      nIndex = 2;

   if( rdDivorced.Checked )
      nIndex = 3;

   objField = objDoc.Form.FindField("form1[0].#subform[0].RadioButtonList[0]");
   IPdfAnnot objChildField = objField.Children[nIndex];
   objChildField.SetFieldValue( objChildField.FieldOnValue, null );

   // Fill in "How did you hear about us" checkboxes
   if( chkInternet.Checked )
   {
      objField = objDoc.Form.FindField("form1[0].#subform[0].Internet[0]");
      objField.SetFieldValue( objField.FieldOnValue, null );
   }

   if( chkWordOfMouth.Checked )
   {
      objField = objDoc.Form.FindField("form1[0].#subform[0].WordOfMouth[0]");
      objField.SetFieldValue( objField.FieldOnValue, null );
   }

   if( chkNewspaper.Checked )
   {
      objField = objDoc.Form.FindField("form1[0].#subform[0].Newspaper[0]");
      objField.SetFieldValue( objField.FieldOnValue, null );
   }

   String strFileName = objDoc.Save( Server.MapPath( "filledform.pdf"), false );
}

Click the links below to run this code sample:

12.4 Image Field Handling

As of Version 2.4, AspPDF is capable of filling in image fields via the method SetFieldImage of the PdfAnnot object. This method expects two arguments: an instance of the PdfImage object containing the image, and an optional parameter string or PdfParam object.

The parameters currently supported are Mode, ReadOnly and Alignment, all optional (the Alignment parameter was introduced in version 3.1.0.2). Mode specifies the stretch mode of the image being rendered. The possible values are:

  • 1 - Scale to fit (default);
  • 2 - Stretch to fit;
  • 3 - Do not scale or stretch.

When Mode is set to 1 (Scale-to-fit mode), the Alignment parameter can also be used. The valid values are:

  • 0 - Alignment to the left or top (default);
  • 1 - Alignment to the right or bottom;
  • 2 - Alignment to the center.

By default, SetFieldImage makes the image field read-only. The ReadOnly parameter, if set to False, leaves the image field clickable.

SetFieldImage can only be called on image fields, all other field types will throw an exception. In most cases, calling RemoveXFA is also required for this method to work properly.

The following code sample demonstrates the use of this method:

Set Doc = PDF.OpenDocument("c:\path\form.pdf")
Set Image = Doc.OpenImage("c:\path\image.jpg")
Doc.Form.RemoveXFA

Set Field = Doc.Form.FindField("form1[0].#subform[0].ImageField1[0]")
Field.SetFieldImage Image, "mode=2"
IPdfDocument objDoc = objPDF.OpenDocument(@"c:\path\form.pdf", Missing.Value);
IPdfImage objImage = doc.OpenImage(@"c:\path\image.jpg", Missing.Value);
objDoc.Form.RemoveXFA();

IPdfAnnot objField = objDoc.Form.FindField("form1[0].#subform[0].ImageField1[0]");
objField.SetFieldImage( objImage, "mode=2" );

12.5 Form Flattening

As of Version 2.6, AspPDF offers form-flattening functionality via the PdfForm method Flatten. This method has no arguments.

To flatten a form means to turn all of its interactive fields into static graphics with no possibility for further editing. Once the Flatten method is called, the PDF document stops being a form and becomes a regular static template.

The Flatten method can only be called on an already filled-in form. Calling SetFieldValue/SetFieldValueEx on the same instance of the document has no effect on the output. Therefore, in order to fill in a form and then flatten it, the form has to be filled the regular way, saved, reopened and only then flattened, as follows:

Set Doc = Pdf.OpenDocument( "c:\path\form.pdf")
Set Field1 = Doc.Form.FindField("Field1")
Field1.SetFieldValueEx "Text1"

Set Field2 = Doc.Form.FindField("Field2")
Field2.SetFieldValueEx "Text2"

...

Set Doc2 = Pdf.OpenDocumentBinary( Doc.SaveToMemory )
Doc2.Form.Flatten
Doc2.Save "c:\path\flattened.pdf", False

After flattening, field text information may appear distorted on some documents. For example, character spacing may become incorrect. To avoid that, a "Save State" command should be put at the beginning of each page's existing content, and a matching "Restore State" command at the end of it, as follows:

For Each Page in Doc2.Pages
   Page.Background.SaveState
   Page.Canvas.RestoreState
Next
...
Doc2.Form.Flatten

UPDATE: As of Version 3.4.0.2, as explained in the following section, the code snippet above can be replaced by

Doc2.Form.Modify("Flatten=true; Reset=true")

Form flattening is demonstrated by Live Demo #16 - Form Flattening.

12.6 JavaScript Removal and Other Features

As of Version 2.9, a new PdfForm method, Modify, has been introduced which combines and extends the XFA removal and form flattening functionality described above. The method also enables annotation flattening and JavaScript removal described below. More functions are expected to be added to this method in the future as AspPDF's form-related functionality expands.

The Modify method expects a single argument: a PdfParam object or parameter string. The following Boolean parameters, all optional, are currently supported and can be used in any combination:

  • RemoveXFA - using this parameter is equivalent to calling the old PdfForm.RemoveXFA method.
  • Flatten - using this parameter is equivalent to calling the old PdfForm.Flatten method.
  • FlattenAnnots - this parameter is useful if the PDF form being flattened contains items that are not technically form fields but field-like annotations. The following line of code flattens both the fields and field-like annotations:
    Doc.Form.Modify("Flatten=true; FlattenAnnots=true")
  • RemoveJavaScript - this parameter removes all JavaScript from the document's catalog. PDF forms created with Adobe products often contain JavaScript that displays the message "This PDF form requires a newer version of Adobe Acrobat" after the form has been flattened:

    The following line of code flattens the form and prevents this message from popping up by removing JavaScript from the document:

    Doc.Form.Modify("Flatten=true; RemoveJavaScript=true")

  • Reset (introduced by Version 3.4.0.2) - this parameter attempts to reset the current graphics state of all pages of the document by placing the Save State ("q") command at the beginning of every page's content and the Restore State ("Q") command at the end of it. Resetting the graphics state may be needed if the flattening procedure produces unexpected output or no output at all.

The Modify method is demonstrated by Live Demo #16 - Form Flattening.

12.7 XFA Support

12.7.1 XFA Overview

A traditional PDF form contains annotation objects representing form fields. Adobe LifeCycle Designer 7.0+ introduced a new generation of forms called XFA, which stands for XML Forms Architecture. Under XFA, a form layout is defined by an XML document, called template, describing subforms, fields, static text, fonts, images and other elements of the form. Below is the screenshot of a very simple template containing a submit button, two text fields, and two mutually exclusive radio buttons. To save screen space, most of the XML nodes of that template are shown in a collapsed state:

The content of the form fields is also packaged as an XML document called dataset. Both the template and dataset (as well as other information in XML format) are embedded in a PDF document. A form viewer application such as Adobe Reader merges and template and dataset together using a well-defined set of rules and produces a filled-out form.

Even a blank XFA form usually contains a property structured XML-based dataset, but this XML document just contains empty values. For example, the initial dataset for the form shown above looks as follows:

If the dataset sheet is filled with values, such as

...
<TextField1>Text value 1</TextField1>
<TextField2>Text value 2</TextField2>
...

and plugged back into the PDF form, the fields will contain the specified values when the form is viewed.

One important advantage of the XFA architecture over traditional PDF forms is that is enables dynamically growing forms, while the traditional forms are inherently static.

The Adobe XFA format is extensive. Version 3.3 of the XFA specifications is a 1,585-page document. It is available on the Adobe web site and can be downloaded from here.

12.7.2 AspPDF's XFA Support

AspPDF's support for XFA forms is streamlined: it enables the template and dataset data to be retrieved from a PDF form, and it also enables the modified dataset data to be plugged back into the PDF form. AspPDF provides no functionality for XML parsing and creation but fortunately both classic ASP and .NET provide ample built-in support for XML management.

AspPDF's XFA functionality is encapsulated in the PdfForm object via the read-only property XFATemplate and read/write property XFADatasets (note the plural form of the word "datasets".) Also, there is an auxiliary property HasXFA which returns True if the PDF form contains XFA data. If this property returns False, the form contains no XFA information and the XFATemplate and XFADatasets properties cannot be used.

The online retrieval of the template and dataset of a PDF form is implemented by Live Demo #15.

To demonstrate AspPDF's XFA functionality, a sample PDF purchase order form shipped with Adobe LiveCycle Designer, Purchase Order.pdf, will be used. It can be found under the \Samples\manual_11 subfolder of the installation.

This form contains all major types of fields: text boxes, radio boxes, drop-down lists, checkboxes, and a dynamically growing list of items. This form's dataset which can be retrieved using Live Demo #15 looks as follows:

Our code sample will fill out the following fields: PO Number (<txtPONum>), Ordered by Company (<txtOrderedByCompanyName>), a Terms and Conditions radio box(<TermsConditions>), the State Tax checkbox (<chkStateTax>) and State Tax Rate (<numStateTaxRate>). It will also add three purchase order items to the item list (<detail>).

The valid values for a radio button group (1 for cash and 2 for credit in our example) can be obtained from the form's template. The relevant snippet of the template (simplified for better readability) is shown below:

<exclGroup name="TermsConditions" x="4.7513mm" y="5.08mm">
  <field...>
    <caption>
      <value>
        <text>Cash</text>
      </value>
    </caption>
    <items>
      <integer>1</integer>
    </items>
  </field>
  <field...>
    <caption>
      <value>
        <text>Credit</text>
      </value>
    </caption>
    <items>
      <integer>2</integer>
    </items>
  </field>
</exclGroup>
Set PDF = Server.CreateObject("Persits.PDF")

' Open an XFA form
Set Doc = PDF.OpenDocument( Server.MapPath( "Purchase Order.pdf" ) )

' Check if the form contains XFA data
If Not Doc.Form.HasXFA Then
Response.Write "This form contains no XFA information."
Response.End
End If

' Load XFA dataset from the PDF form to Microsoft XML processor object
Set Xml = Server.CreateObject("Microsoft.XMLDOM")
Xml.Async = False ' need synchronous operation
Xml.LoadXml( Doc.Form.XFADatasets )

' Fill PO number
Set Node = Xml.DocumentElement.SelectSingleNode ("/xfa:datasets/xfa:data/form1/header/txtPONum")
Node.text = "1234456577"

' Fill Ordered By Company
Set Node = Xml.DocumentElement.SelectSingleNode ("/xfa:datasets/xfa:data/form1/header/txtOrderedByCompanyName")
Node.text = "Acme, Inc."

' Fill Terms and Conditions: 1 for cash, 2 for credit
Set Node = Xml.DocumentElement.SelectSingleNode ("/xfa:datasets/xfa:data/form1/total/TermsConditions")
Node.text = "2"

' Fill State Tax checkbox and state tax rate of 8.5%
Set Node = Xml.DocumentElement.SelectSingleNode ("/xfa:datasets/xfa:data/form1/total/chkStateTax")
Node.text = "1"
Set Node = Xml.DocumentElement.SelectSingleNode ("/xfa:datasets/xfa:data/form1/total/numStateTaxRate")
Node.text = "8.5"

' Add purchase order items

' First, delete two existing detail nodes under form1
Set Node = Xml.DocumentElement.SelectSingleNode("/xfa:datasets/xfa:data/form1")
Node.RemoveChild Xml.getElementsByTagName("detail")(0)
Node.RemoveChild Xml.getElementsByTagName("detail")(0)

' Add three new detail nodes
For i = 1 To 3
   Set Detail = Xml.createElement("detail")
   Set Subdetail = Xml.createElement("txtPartNum")
   Subdetail.text = "PART#" & i
   Detail.appendChild Subdetail
   Set Subdetail = Xml.createElement("txtDescription")
   Subdetail.text = "Description #" & i
   Detail.appendChild Subdetail
   Set Subdetail = Xml.createElement("numQty")
   Subdetail.text = 5 * i
   Detail.appendChild Subdetail
   Set Subdetail = Xml.createElement("numUnitPrice")
   Subdetail.text = 100 * i
   Detail.appendChild Subdetail

   Node.insertBefore Detail, Xml.getElementsByTagName("total")(0)
Next

' Plug the dataset back to the PDF form
Doc.Form.XFADatasets = Xml.xml

'Save document
Path = Server.MapPath( "xfaform.pdf")
FileName = Doc.Save( Path, false)
XmlNode Node;

IPdfManager objPdf = new PdfManager();

// Open an XFA form
IPdfDocument objDoc = objPdf.OpenDocument( Server.MapPath( "Purchase Order.pdf" ), Missing.Value );

// Check if the form contains XFA data
if( !objDoc.Form.HasXFA )
{
   lblResult.Text = "This form contains no XFA information.";
   return;
}

// Load XFA dataset from the PDF form to Microsoft XML processor object
XmlDocument Xml = new XmlDocument();
Xml.LoadXml( objDoc.Form.XFADatasets );

XmlNamespaceManager Mgr = new XmlNamespaceManager(Xml.NameTable);
Mgr.AddNamespace("xfa", "http://www.xfa.org/schema/xfa-data/1.0/");

// Fill PO number
Node = Xml.DocumentElement.SelectSingleNode("/xfa:datasets/xfa:data/form1/header/txtPONum", Mgr);
Node.InnerText = "1234456577";

// Fill Ordered By Company
Node = Xml.DocumentElement.SelectSingleNode("/xfa:datasets/xfa:data/form1/header/txtOrderedByCompanyName", Mgr);
Node.InnerText = "Acme, Inc.";

// Fill Terms and Conditions: 1 for cash, 2 for credit
Node = Xml.DocumentElement.SelectSingleNode("/xfa:datasets/xfa:data/form1/total/TermsConditions", Mgr);
Node.InnerText = "2";

// Fill State Tax checkbox and state tax rate of 8.5%
Node = Xml.DocumentElement.SelectSingleNode("/xfa:datasets/xfa:data/form1/total/chkStateTax", Mgr);
Node.InnerText = "1";
Node = Xml.DocumentElement.SelectSingleNode("/xfa:datasets/xfa:data/form1/total/numStateTaxRate", Mgr);
Node.InnerText = "8.5";

// Add purchase order items
// First, delete two existing detail nodes under form1
Node = Xml.DocumentElement.SelectSingleNode("/xfa:datasets/xfa:data/form1", Mgr );
Node.RemoveChild( Xml.GetElementsByTagName("detail")[0] );
Node.RemoveChild( Xml.GetElementsByTagName("detail")[0] );

// Add three new detail nodes
for( int i = 1; i <= 3; i++ )
{
   XmlNode Detail, Subdetail;

   Detail = Xml.CreateElement("detail");
   Subdetail = Xml.CreateElement("txtPartNum");
   Subdetail.InnerText = "PART#" + i.ToString();
   Detail.AppendChild( Subdetail );
   Subdetail = Xml.CreateElement("txtDescription");
   Subdetail.InnerText = "Description #" + i.ToString();
   Detail.AppendChild( Subdetail );
   Subdetail = Xml.CreateElement("numQty");
   Subdetail.InnerText = (5 * i).ToString();
   Detail.AppendChild( Subdetail );
   Subdetail = Xml.CreateElement("numUnitPrice");
   Subdetail.InnerText = (100 * i).ToString();
   Detail.AppendChild( Subdetail );

   Node.InsertBefore( Detail, Xml.GetElementsByTagName("total")[0] );
}

// Plug the dataset back to the PDF form
objDoc.Form.XFADatasets = Xml.InnerXml;

// Save document
string Path = Server.MapPath( "xfaform.pdf");
string FileName = objDoc.Save( Path, false);

Click the links below to run this code sample:

The resultant PDF form looks as follows:

12.8 Barcode-equipped Government Forms

Some U.S. government agencies, U.S. Citizenship and Immigration Services (USCIS) in particular, have started employing barcode-equipped forms. These forms have a large two-dimensional PDF417 barcode on each page representing the content of this page's fields. Every time a field is modified by the user, the barcode is automatically redrawn to reflect the change. The barcode enables the processing agent to transfer the content of a paper form to the computer system instantly.

The official electronic forms I-821, N-400, G-28, I-90, I-131 and I-864 are hybrid: they are based on both the traditional AcroForm and new XFA formats. The easiest way to fill them out programmatically with AspPDF is to circumvent XFA altogether and use the FindField/SetFieldValueEx methods to fill out the individual fields, but doing so breaks the barcode functionality of the form. Therefore, the PDF417 barcode has to be drawn separately according to the government specifications. Afterwards, the form can optionally be flattened. AspPDF's support for PDF417 barcodes is described in detail in Subsection 13.2.2 - PDF417 of the next chapter.

The USCIS requirements for the 2D barcode can be found here: http://www.uscis.gov/forms/uscis-2d-barcode-requirements.

The following code sample fills out Page 1 of USCIS Form N-400 ("Application for Naturalization"). This form can be downloaded from the USCIS.gov web site.

With the help of the Form Field Finder application mentioned above, Form N-400 is found to have the following fields on Page 1:

form1[0].#subform[0].Part1_Eligibility[0]
form1[0].#subform[0].Part1_Eligibility[1]
form1[0].#subform[0].Part1_Eligibility[2]
form1[0].#subform[0].Part1_Eligibility[3]
form1[0].#subform[0].Part1_Eligibility[4]
form1[0].#subform[0].Part1Line5_OtherExplain[0]
form1[0].#subform[0].Line1_MiddleName[0]
form1[0].#subform[0].Line1_GivenName[0]
form1[0].#subform[0].Line1_FamilyName[0]
form1[0].#subform[0].Line2_FamilyName[0]
form1[0].#subform[0].Line2_GivenName[0]
form1[0].#subform[0].Line3_MiddleName2[0]
form1[0].#subform[0].Line3_GivenName2[0]
form1[0].#subform[0].Line3_FamilyName2[0]
form1[0].#subform[0].Line3_MiddleName1[0]
form1[0].#subform[0].Line3_GivenName1[0]
form1[0].#subform[0].Line3_FamilyName1[0]
form1[0].#subform[0].Line2_MiddleName[0]
form1[0].#subform[0].#area[0].Line1_AlienNumber[0]
form1[0].#subform[0].PaperFormsBarcode1[0]

For the sake of demonstration, our code sample will fill out 9 form fields as follows:

Field Title
Field Name
Field Value
A-Number
form1[0].#subform[0].#area[0].Line1_AlienNumber[0]
123456789
Eligibility, "other" checkbox
form1[0].#subform[0].Part1_Eligibility[0]
<checked>
Eligibility, "explain" dropbox: RELIGIOUS DUTIES
form1[0].#subform[0].Part1Line5_OtherExplain[0]
SECTION 317,INA
Your current legal name, Last Name
form1[0].#subform[0].Line1_FamilyName[0]
Smith
Your current legal name, First Name
form1[0].#subform[0].Line1_GivenName[0]
John
Your current legal name, Middle Name
form1[0].#subform[0].Line1_MiddleName[0]
Frederick
Your name as it appears on resident card, Last Name
form1[0].#subform[0].Line2_FamilyName[0]
Smith
Your name as it appears on resident card, First Name
form1[0].#subform[0].Line2_GivenName[0]
John
Your name as it appears on resident card, Middle Name
form1[0].#subform[0].Line2_MiddleName[0]
Frederick

The rest of the fields will be left blank.

According to the government barcode specifications (link above), the text string encoded by the barcode for Page 1 must contain 18 values separated by the "|" character. The first three are form type ("N-400"), form revision ("09/13/13"), and page number ("1").

The other 15 values are the actual content of the form fields: A-Number ("123456789"), Eligibility - option 5 ("D"), Explanation - religious duties ("SECTION 317,INA"), Current legal name ("Smith", "John", "Frederick"), Name as it appears on resident card ("Smith", "John", "Frederick"), other names ("", "", "", "", "", "" ).

Therefore, the entire string to be encoded into a barcode is as follows:

"N-400|09/13/13|1|123456789|D|SECTION 317,INA|Smith|John|Frederick|Smith|John|Frederick|||||||"

To draw the barcode according to the specifications, the method PdfCanvas.DrawBarcode2D should be called with the following parameters (AspPDF 3.2.0.1+ required):

  • X=36, Y=36 (lower-left corner coordinates, obtained empirically);
  • Width=540, Height=108 (barcode size: 7.5 x 1.5 inches or 540 x 108 user units per specs);
  • QZV=6, QZH=8 (vertical and horizontal extents of the quiet zone around the barcode, obtained empirically);
  • ErrorLevel=5 (error correction level 5 per specs);
  • Columns=30 (to roughly match the specified bar width, values of 28 and 29 are also acceptable);
  • Rows=5 (to match the appearance of the barcode on the official form);
  • Binary=true (optional, see below.)

It is worth noting that the barcode on the official N-400 form does not strictly adhere to the government specifications. In particular, the barcode uses the text compression of data (which is more compact) while the specifications call for byte compression. The DrawBarcode2D uses text compression by default. If byte compression is required, the parameter Binary=true should be used. The code sample below does not use it, however.

' Form field values
Dim arrValues(17) ' 18 values

arrValues( 0) = "N-400" ' form type
arrValues( 1) = "09/13/13" ' form revision
arrValues( 2) = "1" ' page number
arrValues( 3) = "123456789" ' A-Nunber
arrValues( 4) = "D" ' Eligibility
arrValues( 5) = "SECTION 317,INA" ' Explanation
arrValues( 6) = "Smith" ' Legal name
arrValues( 7) = "John"
arrValues( 8) = "Frederick"
arrValues( 9) = "Smith" ' Name as it appears on card
arrValues(10) = "John"
arrValues(11) = "Frederick"
arrValues(12) = "" ' Other names
arrValues(13) = ""
arrValues(14) = ""
arrValues(15) = ""
arrValues(16) = ""
arrValues(17) = ""

Set Pdf = Server.CreateObject("Persits.Pdf")
Set Doc = Pdf.OpenDocument( Server.MapPath("n-400.pdf") )

' Disconnect XFA and JavaScript
Doc.Form.Modify( "RemoveXFA=true; RemoveJavaScript=true" )

' Fill out fields
Set Field = Doc.Form.FindField("form1[0].#subform[0].#area[0].Line1_AlienNumber[0]")
Field.SetFieldValueEx arrValues(3)

' Option 5 is selected
Set Field = Doc.Form.FindField("form1[0].#subform[0].Part1_Eligibility[0]")
Field.SetFieldValueEx Field.FieldOnValue

Set Field = Doc.Form.FindField("form1[0].#subform[0].Part1Line5_OtherExplain[0]")
Field.SetFieldValueEx "RELIGIOUS DUTIES"

Set Field = Doc.Form.FindField("form1[0].#subform[0].Line1_FamilyName[0]")
Field.SetFieldValueEx arrValues(6)

Set Field = Doc.Form.FindField("form1[0].#subform[0].Line1_GivenName[0]")
Field.SetFieldValueEx arrValues(7)

Set Field = Doc.Form.FindField("form1[0].#subform[0].Line1_MiddleName[0]")
Field.SetFieldValueEx arrValues(8)

Set Field = Doc.Form.FindField("form1[0].#subform[0].Line2_FamilyName[0]")
Field.SetFieldValueEx arrValues(9)

Set Field = Doc.Form.FindField("form1[0].#subform[0].Line2_GivenName[0]")
Field.SetFieldValueEx arrValues(10)

Set Field = Doc.Form.FindField("form1[0].#subform[0].Line2_MiddleName[0]")
Field.SetFieldValueEx arrValues(11)

' Draw PDF417 barcode
Dim strBarcodeValue ' |-terminated array of values to encode as a barcode
For i = 0 To 17
   strBarcodeValue = strBarcodeValue & arrValues(i) & "|"
Next

Set Page = Doc.Pages(1)
Page.Canvas.DrawBarcode2D strBarcodeValue, "X=36; Y=36; Width=540; Height=108; QZV=6, QZH=8; ErrorLevel=5; Columns=28; Rows=5"

' Flatten form
Set FlatDoc = Pdf.OpenDocumentBinary( Doc.SaveToMemory() )
FlatDoc.Form.Modify( "Flatten=true; FlattenAnnots=true" )

Filename = FlatDoc.Save( Server.MapPath("formbarcode.pdf"), False )
// Form field values
string [] arrValues = new string[18]; // 18 values

arrValues[ 0] = "N-400"; // form type
arrValues[ 1] = "09/13/13"; // form revision
arrValues[ 2] = "1"; // page number
arrValues[ 3] = "123456789"; // A-Nunber
arrValues[ 4] = "D"; // Eligibility
arrValues[ 5] = "SECTION 317,INA"; // Explanation
arrValues[ 6] = "Smith"; // Legal name
arrValues[ 7] = "John";
arrValues[ 8] = "Frederick";
arrValues[ 9] = "Smith"; // Name as it appears on card
arrValues[10] = "John";
arrValues[11] = "Frederick";
arrValues[12] = ""; // Other names
arrValues[13] = "";
arrValues[14] = "";
arrValues[15] = "";
arrValues[16] = "";
arrValues[17] = "";

IPdfManager objPdf = new PdfManager();
IPdfDocument objDoc = objPdf.OpenDocument( Server.MapPath("n-400.pdf"), Missing.Value );

// Disconnect XFA and JavaScript
objDoc.Form.Modify( "RemoveXFA=true; RemoveJavaScript=true" );

// Fill out fields
IPdfAnnot objField = objDoc.Form.FindField("form1[0].#subform[0].#area[0].Line1_AlienNumber[0]");
objField.SetFieldValueEx( arrValues[3] );

// Option 5 is selected
objField = objDoc.Form.FindField("form1[0].#subform[0].Part1_Eligibility[0]");
objField.SetFieldValueEx( objField.FieldOnValue );

objField = objDoc.Form.FindField("form1[0].#subform[0].Part1Line5_OtherExplain[0]");
objField.SetFieldValueEx( "RELIGIOUS DUTIES" );

objField = objDoc.Form.FindField("form1[0].#subform[0].Line1_FamilyName[0]");
objField.SetFieldValueEx( arrValues[6] );

objField = objDoc.Form.FindField("form1[0].#subform[0].Line1_GivenName[0]");
objField.SetFieldValueEx( arrValues[7] );

objField = objDoc.Form.FindField("form1[0].#subform[0].Line1_MiddleName[0]");
objField.SetFieldValueEx( arrValues[8] );

objField = objDoc.Form.FindField("form1[0].#subform[0].Line2_FamilyName[0]");
objField.SetFieldValueEx( arrValues[9] );

objField = objDoc.Form.FindField("form1[0].#subform[0].Line2_GivenName[0]");
objField.SetFieldValueEx( arrValues[10] );

objField = objDoc.Form.FindField("form1[0].#subform[0].Line2_MiddleName[0]");
objField.SetFieldValueEx( arrValues[11] );

// Draw PDF417 barcode
string strBarcodeValue = ""; // |-terminated array of values to encode as a barcode
for( int i = 0; i < 18; i++ )
{
   strBarcodeValue = strBarcodeValue + arrValues[i] + "|";
}

IPdfPage objPage = objDoc.Pages[1];
objPage.Canvas.DrawBarcode2D( strBarcodeValue, "X=36; Y=36; Width=540; Height=108; QZV=6, QZH=8; " +
   "ErrorLevel=5; Columns=28; Rows=5", Missing.Value );

// Flatten form
IPdfDocument objFlatDoc = objPdf.OpenDocumentBinary( objDoc.SaveToMemory(), Missing.Value );
objFlatDoc.Form.Modify( "Flatten=true; FlattenAnnots=true" );

string strFilename = objFlatDoc.Save( Server.MapPath("formbarcode.pdf"), false );

NOTE: This code sample requires that the USCIS form N-400 (file n-400.pdf) be placed in the subfolder \Samples\manual_11 of the installation. This file was not included in the installation because of its large size.

Click the links below to run this code sample: