# # PDF Form Properties

This chapter provides a detailed explanation of how to use Foxit PDF SDK for Web to manage PDF form field properties, including dynamically modifying properties, change monitoring mechanisms, and developing custom property components based on the `form-designer` extension. Before reading this chapter, it is recommended that you first familiarize yourself with the following topics:

1. Core Concepts:
   - Basic Concepts of PDF Forms
   - Form Interactive Mode
2. Fundamentals of UI Customization Development:
   - UI Fragments

## # Form Widget Properties

PDF form widget properties are primarily divided into two categories: visual presentation properties (e.g., rotation angle, color scheme) and behavior control properties (e.g., editable state, validation rules). This section provides an in-depth analysis of the property manipulation interfaces offered by Foxit PDF SDK for Web, as well as its real-time monitoring mechanism.

### # Get Form Widget

Foxit PDF SDK for Web provides two methods to get form widget instances.

### # Active Retrieval Mode

1. Coordinate-Based Retrieval【PDFForm.getWidgetAtPoint
   (opens new window)】：
   - Input: Page index, coordinate point, optional field type filter
   - Output: Form widget instance matching the specified location and type
2. Object Identifier Retrieval【PDFPage.getAnnotsByObjectNumArray
   (opens new window)】：
   - Input: Predefined array of object numbers
   - Output: Collection of PDF annotation objects (requires additional type filtering)
3. Full Page Annotation Iteration 【PDFPage.getAnnots
   (opens new window)】：
   - Output: All annotation objects on the current page (needs filtering for Annot_Type.widget type)
4. Field Association Retrieval 【PDFFormField.getWidget
   (opens new window)】：
   - Prerequisite: Use getWidgetsCount
     (opens new window) to obtain the valid index range
   - Output: Collection of associated form widget instances for the specified form field

    // Coordinate-based retrieval example
    const widget = await form.getWidgetAtPoint(0, { x: 100, y: 100 });
    const pushButtonWidget = await form.getWidgetAtPoint(0, { x: 100, y: 100 }, PDF.form.FieldType.PushButton);

    // Object identifier retrieval example
    const [widget] = await page.getAnnotsByObjNumArray([12345]);

    // Full page annotation iteration example
    const annots = await page.getAnnots();
    const filteredWidgets = annots.filter(it => {
        return it.getType() === PDF.annots.constant.Annot_Type.widget;
    });

    // Field association retrieval example
    const field = await form.getField("Field name");
    const widgetCount = await field.getWidgetsCount();
    const widgets = await Promise.all(
        Array.from({ length: widgetCount }, (_, i) => {
            return field.getWidget(i);
        })
    );

### # Event-Driven Mode

Dynamic retrieval can be achieved by registering data event listeners:

- `DataEvents.annotationAdded` Annotation creation event: Triggered when an annotation or form widget is added. Requires filtering to identify form widgets based on their type.

- `DataEvents.annotationUpdated` Annotation property update event: Triggered when the properties of an annotation or form widget are updated. Similarly, filtering is needed to identify form widgets based on their type.

- `DataEvents.annotationRemoved` Annotation removal event: Triggered when an annotation or form widget is deleted. The removed object only retains access to `getObjectNumber` and `getAnnotId`.

### # Get and Set Form widget Properties

By directly calling the Widget
(opens new window) interface, you can perform operations to modify and retrieve properties. Below is a simple example:

    // Get all annotations
    const annots = await page.getAnnots();
    // Filter form widgets
    const widgets = annots.filter(it => {
        return it.getType() === PDF.annots.constant.Annot_Type.widget;
    });

    // Rotate all form widgets by 90°
    await Promise.all(
        widgets.map(widget => {
            return widget.setRotation(PDF.form.FormWidgetRotation.ROTATION_90)
        })
    );
    // Get the rotation angle of the first form widget
    const rotation = await widgets[0].getRotation();

### # Listen for Form Widget Property Change Events

You can monitor changes to form widget properties by listening to the `DataEvents.annotationUpdated` event.

    pdfui.addPDFViewerEventListener(DataEvents.annotationUpdated, (annots, page, updateType) => {

    })

Parameters for the event callback:

- `annots`: An array of annotation (or form widget) objects whose properties have changed. Need to filter form widgets based on type;

- `page`: The page object where the annotation (or form widget) with the changed attribute is located;

- `updateType`: The type of change, which can be one of the values in `PDF.constant.AnnotUpdatedType`
  (opens new window).

The `updateType` parameter can be used to determine the specific attribute that has been changed. For more details, refer to AnnotUpdatedType
(opens new window).

## # Form Field Properties

### # Get Form Fields

You can get instances of form fields using the following methods:

- PDFForm.getField
  (opens new window)：
  - Gets a field object based on the field name.
- PDFForm.getFieldAtPosition
  (opens new window) 方法：
  - Gets a field object based on the page index and coordinates.
- Widget.getField
  (opens new window) 方法：
  - Gets a field through a form widget.

Example:

    // Gets a field object based on the field name
    const field = await form.getField("Field name");
    // Gets a field object based on the page index and coordinates
    const field = await form.getFieldAtPosition(0, { x: 100, y: 100 });
    // Gets a field through a form widget
    const field = widget.getField();

### # Get and Set Form Field Properties

You can refer to the PDFFormField
(opens new window) API documentation to get and set field properties. Below is the sample code:

    const field = await form.getField("Field name");
    // Get the value of the form field
    const value = await field.getValue();
    // Update the value of the form field
    await field.setValue(value);

### # Listen for Form Field Property Change Events

Form field property change events are triggered when a form field's property is updated. You can listen to the `DataEvents.formFieldPropertyUpdated` event to monitor changes in form field properties.

    pdfui.addPDFViewerEventListener(DataEvents.formFieldPropertyUpdated, (doc, fieldName, propertyName) => {

    })

In the callback for the form field property change event, you can access the following three parameters:

- `doc`：The document object containing the form field where the property change occurred;

- `fieldName`：The name of the form field whose property was changed;

- `propertyName`：The name of the property that was changed, which can be referenced in `PDF.form.FormFieldPropertyName`
  (opens new window).

## # Customize Property Editing Components

The form-designer Addon provides powerful features and components for "Form Design". Starting from version 11.0.0, the addon supports customizing property editing components, enhancing the flexibility and extensibility of form design.

### # PDFFormProperty Usage

PDFFormProperty
(opens new window) is a utility class in the form-designer Addon specifically designed to support customizing property editing components. It offers the following features:

- Allows merging the property values of multiple selected form widgets. If the property values differ and cannot be merged, the property value will automatically be set to empty, and the status will be marked as unavailable;
- Determines whether the corresponding property editing component should be displayed based on the types of the selected form widgets;
- Enables or disables the corresponding property editing component based on the types of the selected form fields and their property values.

The form-designer addon has pre-constructed a series of PDFFormProperty instances based on supported form properties. Developers can directly access these instances through the PDFFormPropertiesService
(opens new window).

    const formDesigner =await pdfui.getAddonInstance('FormDesigner')
    const propertiesService = formDesigner.getPDFFormPropertiesService();

    const fieldNameProperty = propertiesService.getFieldName();

    fieldNameProperty.onChange(() => {
        const {
            // Indicates whether the property has a value. It is false when no form widget is selected or when multiple form widgets are selected but the property values cannot be merged, otherwise, it is true.
            hasValue,
            // Property value
            value,
            // Indicates whether the property is available. It is false when the selected form widgets do not support the property or when editing the property for multiple selected form widgets is not supported, otherwise, it is true.
            available,
            // Indicates whether the property editing component is visible. It is false when the selected form widgets do not support the property or when editing the property for multiple selected form widgets is not supported, otherwise, it is true.
            visible
        } = fieldNameProperty;
    })

    const exportValueProperty = propertiesService.getExportValueProperty()

Here are several typical scenarios to explain the state of PDFFormProperty:

1. User has selected only one PushButton form widget. For `fieldNameProperty`, its state values are as follows:

   - `hasValue`: true
   - `value`: 'PushButton 0'
   - `available`: true
   - `visible`: true At this point, the value displayed on the fieldName property editing component is 'PushButton 0'. The component is visible and editable.

2. User has selected one PushButton and one ListBox form widgets. For `fieldNameProperty`, its state values are as follows:

   - `hasValue`: false
   - `value`: undefined
   - `available`: false
   - `visible`: true At this point, the fieldName property editing component displays as blank. The component is visible but not editable because it is not possible to set the names of two fields to be the same simultaneously.

3. User has selected one PushButton form widget. For `exportValueProperty`, its state values are as follows:

   - `hasValue`: false
   - `value`: undefined
   - `available`: false
   - `visible`: false At this point, the `ExportValue` property editing component is not visible and not available because the PushButton form widget does not support this property.

`PDFFormProperty.onChange` method：

This method is used to listen for property changes. When a user selects a form widget or the property value of a form widget changes, the change event callback will be triggered. Below is a demonstration using React component code as an example.

1. First, construct a hook to get the PDFFormProperty instance:

       function useFormDesignerAddonInstance() {
           const context = useContext(PDFUIContext); // PDFUIContext，used to share the PDFUI object
           const pdfui = context.current;
           const [instance, setInstance] = useState();
           if (pdfui && instance) {
               pdfui.getAddonInstance('FormDesigner').then(instance => {
                   setInstance(instance);
               });
           }
           return instance;
       }
       function useFormPropertiesService() {
           const formDesigner = useFormDesignerAddonInstance();
           return formDesigner?.getPDFFormPropertiesService();
       }
       function usePDFFormProperty(pdfFormPropertyFactory) {
           const formPropertiesService = useFormPropertiesService();
           const [property, setProperty] = useState();
           useEffect(() => {
               if(property) {
                   return;
               }
               if(!formPropertiesService) {
                   return;
               }
               const property = pdfFormPropertyFactory(formPropertiesService);
               setProperty(property);
           }, [property, formPropertiesService])

           useEffect(() => {
               if(!property) {
                   return;
               }
               return property.onChange(() => {
                   setProperty(property);
               });
           }, [property])
           return property;
       }

2. Use in React components:

       function FieldNameEditor() {
           const formPropertiesService = useFormPropertiesService();
           const fieldNameProperty = usePDFFormProperty(pdfFormPropertyService => {
               return pdfFormPropertyService.getFieldName();
           })
           const [value, setValue] = useState()
           // Since fieldNameProperty.value is a read-only property, using it directly on the <input> element will prevent the user from editing. Therefore, a new state needs to be constructed
           useEffect(() => {
               if(!fieldNameProperty?.hasValue) {
                   setValue('');
               } else {
                   setValue(fieldNameProperty.value)
               }
           }, [fieldNameProperty?.hasValue, fieldNameProperty?.value])
           return <input
               readonly={!fieldNameProperty?.available}
               className={fieldNameProperty?.visible ? '' : 'hide'}
               value={value}
               onChange={(event) => {
                   const newFieldName = event.target.value;
                   setValue(newFieldName);
                   const fields = formPropertiesService.getSelectedFields();
                   // When the user modifies the content, the updated data needs to be set to the form field
                   fields.forEach(field => {
                       field.setName(newFieldName);
                   });
               }}
           ></input>
       }

Note: The above examples are for demonstration purposes only. In actual development, adjustments and implementations should be made according to specific scenarios.

### # Built-in Form Properties Editing Components

Refer to the Built-in Form Properties Editing Components documentation.

### # Customize Form Properties Dialog

Refer to the Customize Form Properties Dialog documentation.