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
  • Traversing parent-child field hierarchies
  • Accessing widgets for appearance updates
Download sample

How Nutrient helps

Nutrient Python SDK handles form field dictionaries, property updates, and hierarchy traversal.

The SDK handles:

  • PDF form field dictionaries and annotation structures
  • Recursive hierarchy traversal for parent-child fields
  • Flag manipulation at the byte level
  • Low-level widget access for appearance customization

Complete implementation

This example edits multiple form field properties in one pass:

from nutrient_sdk import Document
from nutrient_sdk import PdfEditor
from nutrient_sdk import PdfFormFieldType
from nutrient_sdk import NutrientException

Create the main function:

def main():

Opening a document with form fields

Open the PDF in a context manager(opens in a new tab) so resources are cleaned up after processing.

Then call get_form_field_collection() to access:

  • Terminal fields (leaf fields with values)
  • Non-terminal fields (parent fields with children)
try:
with Document.open("input_forms.pdf") as document:
editor = PdfEditor.edit(document)
form_fields = editor.get_form_field_collection()

Inspecting field properties

Iterate over fields to inspect key properties.

This sample reads:

  • Field name and full name
  • Field type
  • Terminal/parent state
  • Read-only and required flags
  • Current value and default value

Use this step for form auditing and validation setup.

for field in form_fields:
print(f"Name: {field.get_name()}")
print(f"FullName: {field.get_full_name()}")
print(f"FieldType: {field.get_field_type()}")
print(f"IsTerminal: {field.get_is_terminal()}")
print(f"IsReadOnly: {field.get_is_read_only()}")
print(f"IsRequired: {field.get_is_required()}")
print(f"Value: {field.get_value()}")
print(f"DefaultValue: {field.get_default_value()}")
print("---")

Making a field read-only

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

In this sample, Text1 is locked with set_is_read_only(True). If Text1 doesn’t exist in your input file, find_by_full_name() returns None and no update is applied.

text_field = form_fields.find_by_full_name("Text1")
if text_field is not None:
text_field.set_is_read_only(True)

Making a field required

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

In this sample, Check1 is marked required with set_is_required(True). If Check1 isn’t present in your input form, this lookup performs no operation.

check_field = form_fields.find_by_full_name("Check1")
if check_field is not None:
check_field.set_is_required(True)

Reading a default value

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

In this sample:

  • get_value() returns the current value.
  • get_default_value() returns the reset value.

If Dropdown1 isn’t present in your input form, the lookup returns None and the sample skips this read.

country_field = form_fields.find_by_full_name("Dropdown1")
if country_field is not None:
default_value = country_field.get_default_value()
print(f"Default value: {default_value}")

Working with field hierarchy

Traverse field hierarchies by checking whether a field is terminal.

For non-terminal fields, use:

  • get_child_count() to read child count.
  • get_child(i) to access each child.

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

for field in form_fields:
if not field.get_is_terminal():
child_count = field.get_child_count()
print(f"Parent field '{field.get_name()}' has {child_count} children:")
for i in range(child_count):
child = field.get_child(i)
print(f" - Child: {child.get_name()}")

Accessing widget annotations

Access widgets for terminal fields to inspect or update visual presentation.

In this sample:

  • get_widget_count() returns the number of widgets.
  • get_widget(i) returns a widget by index.

A field can have one or more widgets across pages:

for field in form_fields:
if field.get_is_terminal():
widget_count = field.get_widget_count()
print(f"Field '{field.get_name()}' has {widget_count} widget(s)")
for i in range(widget_count):
widget = field.get_widget(i)
# Access widget properties for appearance customization

Batch updating field properties

Apply updates to multiple fields using type-based conditions.

In this sample:

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

Use this pattern for workflow-driven form rules:

get_field_type() returns an integer value. In that case, compare against enum values (for example, PdfFormFieldType.TEXT.value and PdfFormFieldType.SIGNATURE.value) instead of enum objects.

for field in form_fields:
if field.get_is_terminal():
# Make all text fields required
if field.get_field_type() == PdfFormFieldType.TEXT:
field.set_is_required(True)
# Make all signature fields read-only until other fields are filled
if field.get_field_type() == PdfFormFieldType.SIGNATURE:
field.set_is_read_only(True)

Saving the modified form

Save the output PDF and close the editor:

editor.save_as("output.pdf")
editor.close()
except NutrientException as e:
print(f"Error: {e}")
if __name__ == "__main__":
main()

Conclusion

Use this workflow to edit form field properties:

  1. Open the document using a context manager(opens in a new tab) for automatic resource cleanup.
  2. Create an editor and access the form field collection with get_form_field_collection().
  3. The form field collection provides iteration and lookup operations for all fields in the document.
  4. Inspect field properties using method calls: get_name(), get_full_name(), get_field_type(), get_is_terminal(), get_is_read_only(), get_is_required(), get_value(), and get_default_value().
  5. Fully qualified names use dot-notation for hierarchical fields (e.g. "Address.Street").
  6. Set fields to read-only with find_by_full_name() and set_is_read_only(True) to lock fields after workflow milestones.
  7. Mark fields as required with set_is_required(True) to enforce validation, preventing form submission until values are provided.
  8. Read default values with get_default_value() for reset operations and change detection.
  9. Navigate field hierarchy with get_is_terminal(), get_child_count(), and get_child(i) for parent-child relationships.
  10. Access widget annotations with get_widget_count() and get_widget(i) for appearance customization.
  11. Batch update properties by iterating terminal fields and applying conditional logic based on get_field_type() comparisons.
  12. Save the document with save_as() to persist all property modifications.

Sample field names such as Text1, Check1, and Dropdown1 are illustrative. If those names don’t exist in your input PDF, those targeted operations are safely skipped.

For related form workflows, refer to the Python SDK editor guides.