Use form field editing to control validation and behavior in PDF workflows.

Common use cases include:

  • Setting fields to read-only after approval
  • Marking fields as required for validation
  • Reading default values for reset logic
  • Navigating parent-child field hierarchies
  • Accessing widgets for appearance-related workflows
Download sample

How Nutrient helps

Nutrient Java SDK handles form field structures, property updates, and hierarchy traversal.

The SDK handles:

  • Parsing form field dictionaries and annotation widget references
  • Managing field hierarchy traversal and parent-child relationships
  • Handling field flag bit manipulation for read-only and required properties
  • Complex widget annotation access and appearance state management

Complete implementation

This example updates form field properties and inspects field hierarchy:

package io.nutrient.Sample;
import io.nutrient.sdk.Document;
import io.nutrient.sdk.editors.PdfEditor;
import io.nutrient.sdk.editors.pdf.formfields.PdfFormFieldCollection;
import io.nutrient.sdk.editors.pdf.formfields.PdfFormField;
import io.nutrient.sdk.editors.pdf.annotations.PdfWidgetAnnotation;
import io.nutrient.sdk.enums.PdfFormFieldType;
public class EditingPdfFormFields {

Create the main method as the sample entry point:

public static void main(String[] args) {

Opening a document with form fields

Open the form document with try-with-resources, create an editor, and get the form field collection.

The collection supports iteration and lookup across field types:

try (Document document = Document.open("input_forms.pdf")) {
PdfEditor editor = PdfEditor.edit(document);
PdfFormFieldCollection formFields = editor.getFormFieldCollection();

Inspecting field properties

Iterate over fields and inspect key properties.

This sample prints:

  • Name and full name
  • Field type
  • Terminal/parent status
  • Read-only and required flags
  • Current and default values
for (PdfFormField field : formFields) {
System.out.println("Name: " + field.getName());
System.out.println("FullName: " + field.getFullName());
System.out.println("FieldType: " + field.getFieldType());
System.out.println("IsTerminal: " + field.getIsTerminal());
System.out.println("IsReadOnly: " + field.getIsReadOnly());
System.out.println("IsRequired: " + field.getIsRequired());
System.out.println("Value: " + field.getValue());
System.out.println("DefaultValue: " + field.getDefaultValue());
System.out.println("---");
}

Making a field read-only

Find a field by full name and set it to read-only.

In this sample, Text1 is locked with setIsReadOnly(true):

PdfFormField textField = formFields.findByFullName("Text1");
if (textField != null) {
textField.setIsReadOnly(true);
}

Making a field required

Find a field by full name and mark it as required.

In this sample, Name is marked required with setIsRequired(true):

PdfFormField nameField = formFields.findByFullName("Name");
if (nameField != null) {
nameField.setIsRequired(true);
}

Reading a default value

Read a field’s default value for reset and change-tracking logic.

In this sample:

  • getValue() returns the current value.
  • getDefaultValue() returns the reset value.
PdfFormField countryField = formFields.findByFullName("Dropdown1");
if (countryField != null) {
String defaultValue = countryField.getDefaultValue();
System.out.println("Default value: " + defaultValue);
}

Working with field hierarchy

Traverse field hierarchies by checking the terminal status.

For non-terminal fields:

  • getChildCount() returns the child count.
  • getChild(i) retrieves each child.

Full names commonly use dot notation — for example, Address.Street:

for (PdfFormField field : formFields) {
if (!field.getIsTerminal()) {
int childCount = field.getChildCount();
System.out.println("Parent field '" + field.getName() + "' has " + childCount + " children:");
for (int i = 0; i < childCount; i++) {
PdfFormField child = field.getChild(i);
System.out.println(" - Child: " + child.getName());
}
}
}

Accessing widget annotations

Access widget annotations for terminal fields.

In this sample:

  • getWidgetCount() returns the widget count.
  • getWidget(i) returns a widget by index.

Fields can have more than one widget across pages:

for (PdfFormField field : formFields) {
if (field.getIsTerminal()) {
int widgetCount = field.getWidgetCount();
System.out.println("Field '" + field.getName() + "' has " + widgetCount + " widget(s)");
for (int i = 0; i < widgetCount; i++) {
PdfWidgetAnnotation widget = field.getWidget(i);
// Access widget properties for appearance customization
}
}
}

Batch updating field properties

Apply batch updates by field type.

In this sample:

  • Text fields are marked required.
  • Signature fields are set to read-only.

Use this pattern for workflow-driven form policies:

for (PdfFormField field : formFields) {
if (field.getIsTerminal()) {
// Make all text fields required
if (field.getFieldType() == PdfFormFieldType.Text) {
field.setIsRequired(true);
}
// Make all signature fields read-only until other fields are filled
if (field.getFieldType() == PdfFormFieldType.Signature) {
field.setIsReadOnly(true);
}
}
}

Saving the modified form

Save the output PDF and close the editor:

editor.saveAs("output.pdf");
editor.close();
} catch (Exception e) {
System.err.println("Error: " + e.getMessage());
e.printStackTrace();
}
}
}

Conclusion

Use this workflow to edit PDF form fields:

  1. Open the document using try-with-resources for automatic resource cleanup.
  2. Create a PDF editor and retrieve the form field collection.
  3. Iterate through all form fields to inspect properties and field types.
  4. Access field properties, including name, full name, field type, terminal status, read-only flag, required flag, current value, and default value.
  5. Set fields to read-only using setIsReadOnly(true) to prevent user modifications.
  6. Mark fields as required using setIsRequired(true) for validation enforcement.
  7. Read default values using getDefaultValue() for form reset operations.
  8. Navigate hierarchical field structures using getIsTerminal(), getChildCount(), and getChild().
  9. Access widget annotations from terminal fields using getWidgetCount() and getWidget().
  10. Implement batch updates by iterating fields and applying conditional logic based on field type.
  11. Use findByFullName() to locate specific fields by their hierarchical names.
  12. Save and close the editor to persist field property changes.

For related form workflows, refer to the Java SDK guides.