# 动作(Action)

# 概念简介

PDF 的 "动作 (Action)" 是一种指令,可在特定事件发生时自动执行,例如打开文档、点击链接或操作表单等。动作可以实现页面跳转、表单提交、脚本运行、文件操作等功能,大幅提升 PDF 的交互性与自动化能力。

动作分为两大类:

  • 动作 (Action):PDF 1.1 引入。可绑定在注释(如 Link、Screen、Widget)、书签(outline item)等对象的字典的 A 条目上,或绑定在文档目录(catalog)的 OpenAction 条目上。动作用于在对象被激活(如点击、选择、文档打开)时,由查看器自动执行特定操作,如跳转、启动应用、播放声音、改变注释外观等。

  • 附加动作(Additional Action):PDF 1.2 引入。可绑定在文档(catalog)、页面、注释或表单域等对象的 AA 条目上。附加动作用于响应多种特定事件(Trigger Events),如进入、离开、按下、释放、获得/失去焦点、字段值变更等。每个事件可单独指定要执行的动作,实现更细粒度的事件驱动行为。

⚠️ 注意:Action (opens new window) 类及其相关子类已被废弃,所有与 Action 相关的操作请改用各对象(如 Annot、Widget、PDFFormField、PDFDoc)提供的专属接口。

# 典型使用场景

  • 页面跳转:通过点击链接或按钮等跳转到指定页面或位置(GoTo、GoToR、GoToE)。
  • 表单交互:包括提交表单(SubmitForm)、重置表单(ResetForm)、导入数据(ImportData),以及隐藏/显示字段(Hide)。
  • 脚本自动化:在特定事件(如字段值变更、页面打开、文档保存等)时,自动执行 JavaScript 脚本。
  • 多媒体与外部操作:启动外部应用(Launch)、打开 URI(URI),以及多媒体渲染(Rendition)。

# 支持的动作类型

动作类型 功能描述 可编辑(UI) 可触发 接口支持
GoTo 跳转到当前文档的指定页面/位置/缩放 Y Y Y
GoToR 跳转到其他 PDF 文件的指定位置(非嵌入文件) Y N Y
GoToE 跳转到嵌入在其他 PDF 文件中的 PDF 文件的指定目标位置 N N Y
Hide 隐藏或显示指定的表单字段 Y Y Y
ImportData 向 PDF 导入 FDF 文件以更新表单字段 Y N Y
JavaScript 执行 JavaScript 脚本 Y Y Y
Launch 启动外部应用程序,通常用于打开文件 Y N Y
ResetForm 将表单字段重置为默认值 Y Y Y
SubmitForm 提交表单数据到指定 URL Y N Y
URI 打开指定的 URI 地址 Y N Y
Rendition 多媒体渲染动作 N Y N

详细动作类型及其数据结构请参考 ActionType (opens new window)ActionSpecification (opens new window) 类型定义。

# 触发事件(Trigger Events)

动作的执行依赖于触发事件(Trigger Events),不同对象支持的事件类型不同。常见触发事件包括:

# Action 数据结构

# 主要接口说明

# 动作(Action)相关

  • Annot.getAllActionData():获取注释的所有动作数据,包括主动作和附加动作。
  • Annot.getActionData():获取注释的主动作数据。
  • Annot.setAction(actionSpec):设置注释的主动作。
  • Annot.appendAction(actionSpec):向注释追加主动作。
  • Annot.updateAction(actionObjNumber, actionData):更新注释的主动作。
  • Annot.removeAction(actionObjNumber):移除注释的主动作。
  • Annot.supportsAction():判断当前注释类型是否支持动作(支持类型包括:screen、link、sound、widget)。

# 附加动作(Additional Action)相关

  • getAdditionalAction: 获取附加动作实例。
    • Widget.getAdditionalAction():获取表单域上的附加动作实例。
    • PDFPage.getAdditionalAction():获取页面上的附加动作实例。
    • PDFFormField.getAdditionalAction():获取表单字段上的附加动作实例。
    • PDFDoc.getAdditionalAction():获取文档上的附加动作实例。
  • AdditionalAction.setAction(trigger, actionSpec):设置指定事件的附加动作。
  • AdditionalAction.addAction(trigger, actionSpec):追加附加动作。
  • AdditionalAction.updateActionData(trigger, actionObjNumber, actionData):更新附加动作。
  • AdditionalAction.removeAction(trigger, actionObjNumber):移除附加动作。
  • AdditionalAction.getActions([triggers]):获取指定事件的附加动作列表。

# 文档级动作

  • PDFDoc.setOpenAction(actionType, actionData):设置文档打开时自动执行的动作。
  • PDFDoc.getOpenAction():获取文档打开动作。
  • PDFDoc.removeOpenAction():移除文档打开动作。

# Action 的使用注意事项

# Action 与 Additional Action

# Action(动作)

Action 是 PDF 中的一种核心动作机制,最早在 PDF 1.1 版本中引入,并通过对象的 A 条目(Action entry)定义。其具备以下主要特点:

  • 直接绑定:直接绑定在注释(Annotation)或书签(Bookmark)的字典中。
  • 单一触发:通常在对象被 "激活" 时触发,例如点击链接注释或选择书签项。
  • 链式执行:支持通过 Next 条目构建动作链,实现树状结构的复杂动作序列。
  • 文档级支持:通过文档目录的 OpenAction 条目,可以指定文档打开时执行的动作。

# Additional Action(附加动作)

Additional Action 是 PDF 中的一种扩展动作机制,最早在 PDF 1.2 版本中引入,并通过对象的 AA 条目(Additional Action entry)定义。其具备以下主要特点:

  • 事件驱动:基于特定的触发事件(Trigger Events),而非简单的 "激活"。
  • 多事件支持:单个对象可为不同事件类型绑定不同的动作。
  • 广泛的适用性:支持文档、页面、注释、表单域等多种对象类型。
  • 精细化控制:支持细粒度的事件管理,例如鼠标进入/离开、获得/失去焦点等。

# 主要区别

特性 Action(动作) Additional Action(附加动作)
引入版本 PDF 1.1 PDF 1.2
绑定方式 A 条目 AA 条目
触发机制 对象激活(如点击) 特定事件驱动
事件类型 单一(激活) 多种(进入、离开、按下、释放等)
适用对象 注释、书签、文档 文档、页面、注释、表单域
复杂度 相对简单 更加灵活和复杂
典型用途 页面跳转、链接点击 表单验证、动态交互、状态变更

# 触发事件体系

Additional Action 的强大之处在于其丰富的触发事件体系。根据 Foxit PDF SDK for Web 的 ActionTriggerEvents 定义,触发事件主要分为以下四大类。

# 文档级事件(DocActionTriggerEvents)

  • DOC_WILL_CLOSE:文档关闭前触发 JavaScript 动作。
  • DOC_WILL_SAVE:文档保存前触发 JavaScript 动作。
  • DOC_SAVED:文档保存后触发 JavaScript 动作。
  • DOC_WILL_PRINT:文档打印前触发 JavaScript 动作。
  • DOC_PRINTED:文档打印后触发 JavaScript 动作。

# 页面级事件(PageActionTriggerEvents)

  • PAGE_OPENED:页面打开时触发动作。
  • PAGE_CLOSED:页面关闭时触发动作。

# 注释/表单域级事件(AnnotActionTriggerEvents)

  • ANNOT_CURSOR_ENTER:光标进入注释活动区域时触发动作。
  • ANNOT_CURSOR_EXIT:光标离开注释活动区域时触发动作。
  • ANNOT_MOUSE_BUTTON_PRESSED:鼠标按钮在注释活动区域内按下时触发动作。
  • ANNOT_MOUSE_BUTTON_RELEASED:鼠标按钮在注释活动区域内释放时触发动作。
  • ANNOT_RECEIVE_INPUT_FOCUS:注释(仅限 Widget 注释)获得输入焦点时触发动作。
  • ANNOT_LOSE_INPUT_FOCUS:注释(仅限 Widget 注释)失去输入焦点时触发动作。
  • ANNOT_PAGE_OPENED:包含注释的页面打开时触发动作(在 PAGE_OPENED 和文档打开动作之后执行)。
  • ANNOT_PAGE_CLOSED:包含注释的页面关闭时触发动作(在 PAGE_CLOSED 之后执行)。
  • ANNOT_PAGE_VISIBLE:包含注释的页面在查看器 UI 中变为可见时触发动作。
  • ANNOT_PAGE_INVISIBLE:包含注释的页面在查看器 UI 中变为不可见时触发动作。

# 表单字段级事件(FieldActionTriggerEvents)

  • FIELD_KEY_STROKE:当用户在文本字段或组合框中键入,或修改滚动列表框选择时触发 JavaScript 动作。可用于验证或修改击键。

  • FIELD_WILL_FORMAT:字段值被格式化显示​​之前​​,会触发 JavaScript 动作。可用于对字段值进行​​修改​​。

  • FIELD_VALUE_CHANGED:字段值发生变更时触发 JavaScript 动作。可用于验证新值。

  • FIELD_RECALCULATE_VALUE:当其他字段值发生变更时触发 JavaScript 动作,以重新计算当前字段值。计算顺序可通过 PDFForm.getFieldsInCalculationOrder 函数确定。

# 实际应用场景

# Action 的典型应用

// 设置链接注释的跳转动作
linkAnnot.setAction({
    type: "GoTo",
    dest: { page: 5, view: "FitH" },
});

// 设置文档打开动作
pdfDoc.setOpenAction(PDF.actions.ActionType.javaScript, {
    script: 'app.alert("欢迎使用本文档!");',
});

# Additional Action 的典型应用

// 表单域值变更时的验证
const additionalAction = formField.getAdditionalAction();
additionalAction.setAction(PDF.actions.ActionTriggerEvents.FIELD_VALUE_CHANGED, {
    type: PDF.actions.ActionType.javaScript,
    data: {
        javascript: 'if (event.value < 0) { app.alert("值不能为负数"); event.rc = false; }'
    },
});

// 页面打开时的初始化动作
const pageAdditionalAction = pdfPage.getAdditionalAction();
pageAdditionalAction.setAction(PDF.actions.ActionTriggerEvents.PAGE_OPENED, {
    type: PDF.actions.ActionType.javaScript,
    data: {
        javascript: 'console.log("页面已打开");',
    }
});


// 光标进入注释活动区域时触发的动作。
const annotAdditionalAction = annotation.getAdditionalAction();
annotAdditionalAction.setAction(PDF.actions.ActionTriggerEvents.ANNOT_CURSOR_ENTER, {
    type: PDF.actions.ActionType.javaScript,
    data: {
        javascript: "this.highlight = true;",
    }
});

// 文档保存前的数据验证
const docAdditionalAction = pdfDoc.getAdditionalAction();
docAdditionalAction.setAction(PDF.actions.ActionTriggerEvents.DOC_WILL_SAVE, {
    type: PDF.actions.ActionType.javaScript,
    data: {
        javascript: 'if (!validateFormData()) { event.rc = false; app.alert("请完善必填信息"); }',
    }
});