Adding Dependency Injection to the PDFDocument class
Handling Form Field Name Conflicts
One of the most common use cases for iText is the creation or manipulation of PDF forms, also known as AcroForms. However, when processing PDFs containing form fields you might encounter an issue where updating one form field causes another form field to update. A common reason for this is when PDFs containing form fields are, or have been merged, since fields with the same name are treated as the same field across the entire document.
This behavior is part of the PDF specification for AcroForms, and so this is the default for iText when merging documents. However, in some cases linking identically or similarly-named fields may be unwanted. The obvious solution is to edit the form fields to give them a unique name, though depending on your circumstances, that's easier said than done!
Introducing the RegisterDefaultDiContainer class
Previously, the general approach was to use something like a static factory - see for example PdfFormFactory (Java/.NET). This works reasonably well, but has some disadvantages. Since you set it for your whole program, if you are require different behaviors it can be quite tricky to implement.
In addition, you also have to ensure your code is thread-safe for multi-threaded environments. To avoid such issues, it's better to have those behaviors defined in the PdfDocument itself, and allow each instance to be responsible for its own behavior.
Because iText is a general use case PDF library we can’t really make assumptions about certain kinds of behavior, since it depends heavily on how the library is used. That’s why in some cases we provide a default behavior which can be extended or modified by our users, ensuring maximum flexibility of the library.
So, we now provide another strategy. Added to the com.itextpdf.forms.util package in iText Core version 8.0.2, the RegisterDefaultDiContainer class (Java/.NET) provides a customizable way to handle such field names by effectively overriding the PdfFormField classes, without affecting the whole application.
Using RegisterDefaultDiContainer to Resolve Field Name Conflicts:
So, how do you use it? The following code sample demonstrates how to resolve form field conflicts, by simply adding a counter to the form field names so they are no longer linked. This is achieved by using the OnDuplicateFormFieldNameStrategy class Java/.NET).
If you look at the code comments below, you can see how to select this strategy by overriding the default behavior using dependency injection.
public void incrementFieldNameStrategy() throws IOException, InterruptedException {
String destination = DESTINATION_FOLDER + "incrementFieldNameEven.pdf";
//Defining the custom behaviour is as simple as registering your dependency.
DocumentProperties properties = new DocumentProperties();
//Overwrite the default behavior with the strategy you want
properties.registerDependency(OnDuplicateFormFieldNameStrategy.class, new AddIndexStrategy());
// Don't forget to pass your document properties which contains your configuration
try (PdfDocument pdfDocument = new PdfDocument(new PdfWriter(destination), properties)) {
// This code is just a showcase and is not really important
PdfAcroForm form = PdfAcroForm.getAcroForm(pdfDocument, true);
for (int i = 1; i < 3; i++) {
Rectangle rect = new Rectangle(20, 20);
rect.setY(100 * i);
rect.setX(100);
PdfButtonFormField field1 = new CheckBoxFormFieldBuilder(pdfDocument, "test")
.setWidgetRectangle(rect)
.createCheckBox();
form.addField(field1);
}
PdfFormField field1 = form.getField("test");
PdfFormField field3 = form.getField("test_1");
Assertions.assertNotNull(field1);
Assertions.assertNotNull(field2);
}
}
As you can see, the way to set your dependencies is quite straightforward. The only thing you have to make sure of is that it’s registered with the correct Type.
Technical Details
At a high level it works in the following way.
PdfDocumenthas a container which stores all the dependencies by Type and its implementation.Based on the modules of iText you use, some sane defaults get loaded. Those can be found in the respective module's
RegisterDefaultDiContainerclasses.If you need custom implementations other than the default, register them in your
DocumentPropertiesby using theregisterDependencymethod.When executing a code path that relies on specific behavior, it will look up the implementation and execute it.
Written by | Guust Ysebie, Software Developer, iText SDK |