PDF 表单属性
本章节详细介绍了如何使用 福昕 PDF SDK Web 版 实现 PDF 表单属性操作的技术方案,包括动态修改属性、变更监听机制,以及基于 form-designer 扩展的自定义属性组件开发。在阅读本章节之前,建议您先了解以下内容:
- 核心概念:
- UI 定制开发基础:
表单控件属性
PDF 表单控件属性主要分为两大类:视觉呈现属性(如旋转角度、颜色方案)和 行为控制属性(如可编辑状态、验证规则)。本节将深入解析 福昕 PDF SDK Web 版 提供的属性操作接口以及其实时监听机制。
获取表单控件
福昕 PDF SDK Web 版 提供了两种模式,用于获取表单控件实例。
主动检索模式
- 坐标定位 PDFForm.getWidgetAtPoint:
- 输入: 页面索引、坐标点、可选表单域类型过滤器
- 输出: 匹配指定位置及类型的表单控件实例
- 对象标识检索 PDFPage.getAnnotsByObjectNumArray:
- 输入: 预定义的 objectNumber 数组
- 输出: PDF 标注对象集合(需要进行二次类型筛选)
- 全量遍历 PDFPage.getAnnots:
- 输出: 当前页所有标注对象(需筛选 Annot_Type.widget 类型)
- 表单域关联检索 PDFFormField.getWidget:
- 前置条件: 需结合 getWidgetsCount 获取有效索引范围
- 输出: 指定表单域的关联表单控件集合
javascript
// 坐标定位示例
const widget = await form.getWidgetAtPoint(0, {x: 100, y: 100});
const pushButtonWidget = await form.getWidgetAtPoint(0, {x: 100, y: 100}, PDF.form.FieldType.PushButton);
// 对象标识检索示例
const [widget] = await page.getAnnotsByObjNumArray([12345]);
// 遍历页面中的所有注释,根据类型筛选表单控件
const annots = await page.getAnnots();
const filteredWidgets = annots.filter(it => {
return it.getType() === PDF.annots.constant.Annot_Type.widget;
});
// 表单域关联检索示例
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);
})
);
事件驱动模式
通过注册数据事件监听器,实现动态获取:
DataEvents.annotationAdded
标注创建事件: 当添加标注或表单控件时触发,需要根据类型筛选表单控件。DataEvents.annotationUpdated
标注属性更新事件: 当标注或表单控件的属性发生变更时触发,同样需要根据类型进行筛选。DataEvents.annotationRemoved
标注移除事件: 当标注或表单控件被删除时触发,并且被删除的对象仅保留getObjectNumber
和getAnnotId
可用。
获取和设置表单控件属性
通过直接调用 Widget 接口,可以实现属性的修改和获取操作。以下是一个简单示例:
javascript
// 获取所有标注
const annots = await page.getAnnots();
// 筛选表单控件
const widgets = annots.filter(it => {
return it.getType() === PDF.annots.constant.Annot_Type.widget;
});
// 设置所有表单控件旋转90°
await Promise.all(
widgets.map(widget => {
return widget.setRotation(PDF.form.FormWidgetRotation.ROTATION_90)
})
);
// 获取第一个表单控件的旋转角度
const rotation = await widgets[0].getRotation();
监听表单控件属性变更事件
通过监听 DataEvents.annotationUpdated
事件,可实现对表单控件属性变更的监控。
javascript
pdfui.addPDFViewerEventListener(DataEvents.annotationUpdated, (annots, page, updateType) => {
})
事件回调的参数:
annots
:发生属性变更的标注(或表单控件)对象数组,需要根据类型筛选表单控件;page
:发生属性变更的标注(或表单控件)所在的页面对象;updateType
:变更的类型,可以是PDF.constant.AnnotUpdatedType
中的一个值。
通过 updateType
参数可以确定具体发生变更的属性。详细信息可参考 AnnotUpdatedType 。
表单域属性
获取表单域
获取表单域的实例可以通过以下方式:
- PDFForm.getField 方法:
- 根据表单域名称获取表单域对象。
- PDFForm.getFieldAtPosition 方法:
- 根据页面索引和点坐标获取表单域对象。
- Widget.getField 方法:
- 通过表单控件获取表单域。
示例:
javascript
// 通过表单域名称获取表单域
const field = await form.getField("Field name");
// 通过页面索引和点坐标获取表单域
const field = await form.getFieldAtPosition(0, {x: 100, y: 100});
// 通过表单控件获取表单域
const field = widget.getField();
获取和设置表单域属性
表单域属性的获取和设置可参考 PDFFormField API 文档。以下是示例代码:
javascript
const field = await form.getField("Field name");
// 获取表单域的值
const value = await field.getValue();
// 更新表单域的值
await field.setValue(value);
监听表单域属性变更事件
表单域属性变更事件是在表单域属性发生变化时触发的。可以通过监听 DataEvents.formFieldPropertyUpdated
事件来获取表单域属性变更。
javascript
pdfui.addPDFViewerEventListener(DataEvents.formFieldPropertyUpdated, (doc, fieldName, propertyName) => {
})
在表单域属性变更事件的回调中,可以获取以下三个参数:
doc
:发生属性变更表单域所属的文档对象;fieldName
:发生属性变更的表单域名称;propertyName
:变更的属性名称,参考PDF.form.FormFieldPropertyName
。
自定义属性编辑组件
form-designer Addon 为 "表单设计" 提供了强大的功能和组件。从 11.0.0 版本开始,该插件支持自定义属性编辑组件,进一步提升了表单设计的灵活性和可扩展性。
PDFFormProperty 用法
PDFFormProperty 是 form-designer Addon 专门为自定义属性编辑组件提供支持的工具类。其提供了以下功能:
- 可以合并选中的多个表单域属性值。如果属性值不同且无法合并,则属性值会自动置为空,并将状态设置为不可用;
- 可以根据选中的表单域类型,判断是否显示对应的属性编辑组件;
- 可以根据选中的表单域类型和属性值,判断是否启用或禁用对应的属性编辑组件。
form-designer 已根据支持的表单属性预先构造了一系列 PDFFormProperty 实例,开发者可以通过 PDFFormPropertiesService 直接获取这些实例。
javascript
const formDesigner = await pdfui.getAddonInstance('FormDesigner')
const propertiesService = formDesigner.getPDFFormPropertiesService();
const fieldNameProperty = propertiesService.getFieldName();
fieldNameProperty.onChange(() => {
const {
// 表示属性是否有值。当用户未选中表单域,或选中多个表单域但属性值无法合并时,为 false;其他情况为 true。
hasValue,
// 属性值
value,
// 表示属性是否可用。当选中的表单域不支持该属性,或不支持同时编辑选中的多个表单域的属性时,为 false;其他情况为 true。
available,
// 表示属性编辑组件是否可见。当选中的表单域不支持该属性,或不支持同时编辑选中的多个表单域的属性时,为 false;其他情况为 true。
visible
} = fieldNameProperty;
})
const exportValueProperty = propertiesService.getExportValueProperty()
下面是几个典型的场景,用于解释 PDFFormProperty 的状态:
用户只选中一个 PushButton 表单域,对于
fieldNameProperty
, 其状态值如下:hasValue
: truevalue
: 'PushButton 0'available
: truevisible
: true 此时,fieldName 属性编辑组件上显示的值为 'PushButton 0', 组件可见且可编辑。
用户选中了一个 PushButton 和一个 ListBox 表单域,对于
fieldNameProperty
, 其状态值如下:hasValue
: falsevalue
: undefinedavailable
: falsevisible
: true 此时,fieldName 属性编辑组件上显示为空白,组件可见但不可编辑,因为无法同时设置两个表单域名字为相同。
用户选中了一个 PushButton, 对于
exportValueProperty
, 其状态值如下:hasValue
: falsevalue
: undefinedavailable
: falsevisible
: false 此时,ExportValue
属性编辑组件不可见且不可用,因为 PushButton 不支持该属性。
PDFFormProperty.onChange
方法:
该方法用于监听属性变更,当用户选中表单域或表单域的属性值发生变更时,会触发变更事件回调。以下以 React 组件代码作为示例演示:
首先,构造 hook 用于获取 PDFFormProperty 实例:
jsxfunction useFormDesignerAddonInstance() { const context = useContext(PDFUIContext); // PDFUIContext,用于共享 PDFUI 对象 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; }
在 React 组件中使用:
jsxfunction FieldNameEditor() { const formPropertiesService = useFormPropertiesService(); const fieldNameProperty = usePDFFormProperty(pdfFormPropertyService => { return pdfFormPropertyService.getFieldName(); }) const [value, setValue] = useState() // 由于 fieldNameProperty.value 是一个只读属性,直接在 <input> 上使用会导致用户无法编辑。因此,需要构造一个新的 state 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(); // 当用户修改内容后,需要将更新的数据设置到表单域中 fields.forEach(field => { field.setName(newFieldName); }); }} ></input> }
注意: 以上示例仅用于演示用法,实际开发时需根据具体场景进行调整和实现。
内置表单属性编辑组件
内置表单属性编辑组件可以参考 内置表单属性编辑组件 文档。