Creating an Accreditation Card using a PDF AcroForm

There are two types of form in PDF:

  • AcroForms: this type of form was introduced in Acrobat 3. Each PDF file can have one ‘AcroForm’ with different AcroFields. These fields are positioned on absolute positions in a specific page. You can fill them out and submit the entered data to a server in the form of an HTML request, a Forms Data Format (FDF) file, an XFDF file (the XML version of an FDF file), or if the end-user has the full Acrobat: the complete filled in PDF file. You can use a library such as iText to change the properties of an AcroField and/or to fill in the form from a software application (for instance to serve online, prefilled forms to your customers using a web application). Basically, there are 4 types of AcroFields:
    • Text fields
    • Choice fields
    • Button fields
    • Signature fields (since Acrobat 4)
  • XFA forms: Acrobat Professional 7 ships with Adobe LiveCycle Designer. Adobe LiveCycle Designer is used to create XFA forms. XFA stands for XML Forms Architecture. You won’t find many tools that support these XFA forms yet; iText can fill out an XFA form, but it can’t change the properties of the fields as we’ll do in the next example. Note that you still can create AcroForms using Acrobat (look for ‘add text annotation’ in the menu), just make sure you don’t use Designer.

Using a PDF form as a template

If we think about forms in HTML, we think about functionality to retrieve data from the user. That’s possible with PDF forms too, but that’s not how we are going to use PDF forms in this example. Have a look at class AccreditationCard. It has a method createForm(OutputStream out) that uses iText to create a form with some text fields and some buttons:

TextField name = new TextField(writer, new Rectangle(Measurements.mm2pt(26),
Measurements.mm2pt(28), WIDTH - Measurements.mm2pt(1.5f), Measurements.mm2pt(40)), "name");
name.setText("Name");
name.setFontSize(8);
name.setOptions(TextField.MULTILINE);
writer.addAnnotation(name.getTextField());
 
PushbuttonField barcode = new PushbuttonField(writer,
  new Rectangle(Measurements.mm2pt(3), Measurements.mm2pt(23),
  WIDTH - Measurements.mm2pt(3), Measurements.mm2pt(28)), "barcode");
PdfTemplate t2 = cb.createTemplate(WIDTH - Measurements.mm2pt(6), Measurements.mm2pt(5));
t2.setColorFill(GRAY);
t2.rectangle(0, 0, WIDTH - Measurements.mm2pt(6), Measurements.mm2pt(5));
t2.fill();
barcode.setTemplate(t2);
barcode.setLayout(PushbuttonField.LAYOUT_ICON_ONLY);
writer.addAnnotation(barcode.getField());

You recognize some of the methods you already used when writing to the direct content in the calendar example, only now we’re writing to a PdfTemplate object, which is a canvas with a specific size. Note that the method mm2pt translates millimeters into points.
I won’t go any deeper into the process of creating a form with iText. This is discussed in detail in chapters 15 and 16 of the book iText in Action. There you’ll learn all about TextField, PushbuttonField, PdfAnnotation, and much more. What matters in this example, is how to change the properties of these fields, and how to fill or replace them.

The accreditation data

Our form has six fields:

  • name: a text field with default value “Name”.
  • number: a text field with default value “N° 0000000”.
  • barcode: a pushbutton field with a gray rectangle as icon.
  • photo: a pushbutton field with a gray rectangle as icon.
  • type: a text field with font color gray and default value “TYPE”.
  • filmfestival: a read-only field with the filmfestival URL in white letters on a gray background.

These fields correspond more or less with the member variables of class AccreditationData.

  • String name: the name of the owner of the accreditation card.
  • String number: the number of the accreditation card.
  • Image photo: a picture of the owner of the accreditation card.
  • String typeName: the name of the type of accreditation: Guest, Jury, Press, Student,...
  • Color typeColor: a color corresponding with a certain type of accreditation: no admittance, admittance to press screenings only,... This color code is important for the ushers at the entrance of the movie theatre.
  • boolean flatten: does the form has to be flattened (we’ll explain this later on).

The AccreditationData class is a simple javabean with only one extra method: getNumber(boolean barcode) returns the number preceded with “N° " if barcode == false, and adds leading zeros if barcode == true. We’ll use this javabean class to pass the information we want to add to our template form:

// we make up some data
AccreditationData data = new AccreditationData();
data.setTypeColor(COLOR[2]);
data.setTypeName(TYPE[2]);
data.setName("Ingeborg Willaert");
data.setNumber("12345");
data.setPhoto(Image.getInstance(PHOTO));
data.setFlatten(false);
// we fill the form
fillForm(SRC, data, new FileOutputStream(DEST1));

SRC is the path to the empty form; DEST1 the path to the new PDF document (the filled out form). Let’s examine the fillform method.

Filling the form

In the previous example you used the PdfContentByte object to add lines and text at an absolute position. You can also use this object to draw content on top of, or under the pages of an existing PDF document. This is done with classes PdfReader and PdfStamper. The following code snippet gives you an idea of how to do it:

PdfReader reader = new PdfReader("existing.pdf");
PdfStamper stamp = new PdfStamper(reader, new FileOutputStream("stamped.pdf"));
PdfContentByte under = stamp.getUnderContent(1);
// change the content beneath page 1
PdfContentByte over = stamp.getOverContent(1);
// change the content on top of page 1
stamp.close();

There are plenty of other PDF manipulation classes in iText. For more info, please read chapter 2 of iText in Action. In this example we are going to use PdfReader, PdfStamper and AcroFields to change the properties and values of the fields in an AcroForm.
Normally, when you enter data into a field, Acrobat or Adobe Reader creates an appearance: it draws the text on the page. With iText, you are going to change the value key in the field dictionary, but iText is also going to create an appearance:internally, iText will perform something that is very similar to a beginText(), setFont(), showTextAligned(), endText() sequence.
You don’t have to worry about this. To you, it’s as simple as:

PdfReader reader = new PdfReader(filename);
PdfStamper stamper = new PdfStamper(reader, out);
AcroFields form = stamper.getAcroFields();
form.setField(key, value);
stamper.close();

The result of this operation will be a new PDF file with a PDF form where the value of one field has been changed. When opening the PDF in Adobe Reader, you will see the appearance of the field as it was created by iText. Note that if you click and change the field, you could experience some differences: some versions of Adobe Reader use a different offset.
In the accreditation card example, you are also going to change some of the properties of the field; for example: the text of the type field was gray; you are now going to change this color into the color that indicates the admittance level:

form.setFieldProperty("type", "textcolor", data.getTypeColor(), null);
form.setField("type", data.getTypeName());

We’ll use the same color for the read only field with the filmfestival’s URL:

form.setFieldProperty("filmfestival", "bgcolor", data.getTypeColor(), null);
form.regenerateField("filmfestival");

Observe that we have to tell iText to create a new appearance for this field. As we didn’t change its value, just one of its properties, the appearance stored in the PDF remained intact, including the gray backgroundcolor. The method regenerateField() creates a new appearance, with the altered backgroundcolor. The different ways to change the properties of a field are listed in chapter 16 of iText in Action.
Something that is not mentioned in the book (because the functionality was added after the book was printed), is how to replace a button, for instance if you want to have a button containing a photo instead of a gray rectangle. Let’s have a look at the complete fillForm method:

public static void fillForm(String filename, AccreditationData data,
  OutputStream out) throws IOException, DocumentException {
  PdfReader reader = new PdfReader(filename);
  PdfStamper stamper = new PdfStamper(reader, out);
 
  AcroFields form = stamper.getAcroFields();
  form.setField("name", data.getName());
  form.setFieldProperty("type", "textcolor", data.getTypeColor(), null);
  form.setField("type", data.getTypeName());
  form.setField("number", data.getNumber(false));
  form.setFieldProperty("filmfestival", "bgcolor", data.getTypeColor(), null);
  form.regenerateField("filmfestival");
 
  if (data.getPhoto() != null) {
    PushbuttonField bt = form.getNewPushbuttonFromField("photo");
    bt.setLayout(PushbuttonField.LAYOUT_ICON_ONLY);
    bt.setProportionalIcon(true);
    bt.setImage(data.getPhoto());
    form.replacePushbuttonField("photo", bt.getField());
  }
 
  try {
    BarcodeInter25 code = new BarcodeInter25();
    code.setGenerateChecksum(true);
    code.setBarHeight(Measurements.mm2pt(3));
    code.setCode(data.getNumber(true));
    code.setFont(null);
    PdfContentByte cb = new PdfContentByte(stamper.getWriter());
    PdfTemplate template = code.createTemplateWithBarcode(cb, null, null);
    PushbuttonField bt = form.getNewPushbuttonFromField("barcode");
    bt.setLayout(PushbuttonField.LAYOUT_ICON_ONLY);
    bt.setProportionalIcon(false);
    bt.setTemplate(template);
    form.replacePushbuttonField("barcode", bt.getField());
  } catch (Exception e) {
    // not a valid code, do nothing
  }
 
  stamper.setFormFlattening(data.isFlatten());
  stamper.close();
}

The code to change the photo demands some extra explanation. First we create a new PushbuttonField with exactly the same properties as the photo field in the existing form. We set the layout to LAYOUT_ICON_ONLY just to be sure (the original button may have another layout with text). We tell the button that it should resize its icon to fit the rectangle, and we set the Image. We now have a new PushbuttonField that looks exactly the way we want it. All we have to do, is to replace the existing field with the new one.
The code to change the barcode is very similar, but as the barcode is created as a PdfTemplate, we use the method setTemplate instead of setImage. Note that you can find more info on creating barcodes with iText in Appendix B of iText in Action.

Flattening the form

We overlooked one line when we discussed the fillForm method:

stamper.setFormFlattening(data.isFlatten());

Let’s return to the main method of the AccreditationCard example.

public static void main(String[] args) {
  try {
    // we create the form
    createForm(new FileOutputStream(SRC));
    // we make up some data
    AccreditationData data = new AccreditationData();
    data.setTypeColor(COLOR[2]);
    data.setTypeName(TYPE[2]);
    data.setName("Ingeborg Willaert");
    data.setNumber("12345");
    data.setPhoto(Image.getInstance(PHOTO));
    data.setFlatten(false);
    // we fill the form
    fillForm(SRC, data, new FileOutputStream(DEST1));
    data.setTypeColor(COLOR[3]);
    data.setTypeName(TYPE[4]);
    data.setFlatten(true);
    fillForm(SRC, data, new FileOutputStream(DEST2));
  } catch (Exception e) {
    e.printStackTrace();
  }
}

The first time we fill the form, flattening is set to false. This results in the file accreditation_1.pdf. This PDF document still contains an AcroForm. You could use iText to retrieve the name that was filled in. You could even use it to fill it with other data (manually as well as using iText).
The second time we fill the form (after changing the color and name of the type to produce an accreditation card that is clearly different from the first one), we set flattening to true. This results in the file accreditation_2.pdf. This PDF document no longer has a form! It is a plain, flat, traditional PDF document. This is typically what you want if you are using a PDF AcroForm as a template to produce PDF documents on a large scale, for instance filling the same form over and over again based on the resultset of a database query, with the intention of sending the PDF to a printer or a printing office. There are different ways to achieve this; I’ll post an extra example on this subject soon.

Conclusion

In this section, we have taken an existing PDF document that had several AcroFields: accreditation_form.pdf, and we have filled its text fields with similar data twice: once without flattening (accreditation_1.pdf), once flattened (accreditation_2.pdf). At the same time we have changed the properties of the AcroFields, and we have replaced the icon of a PushbuttonField with an Image or a PdfTemplate.
Note that this example was integrated into a Servlet deployed on itext.ugent.be. This concludes the filmfestival example. For more info on forms, form fields, and even digital signatures, please read chapters 15 and 16 of the book.

 
Back to top
filmfestival/accreditation.txt · Last modified: 2007/03/15 09:48 by bruno_lowagie