# # 动作（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），不同对象支持的事件类型不同。常见触发事件包括：

- 文档级（DocActionTriggerEvents
  (opens new window)）：如文档关闭、保存、打印等。
- 页面级（PageActionTriggerEvents
  (opens new window)）：如页面打开、关闭。
- 注释/表单域级（AnnotActionTriggerEvents
  (opens new window)）：如鼠标进入、离开、按下、释放。
- 表单字段级（FieldActionTriggerEvents
  (opens new window)）：如键入、格式化、值变更、重新计算。

## # Action 数据结构

- ActionSpecification
  (opens new window)：描述动作类型及其参数（如跳转目标、脚本内容等）。
- ActionHierarchy
  (opens new window)：描述动作的层级结构，支持嵌套子动作。
- ActionData
  (opens new window)：具体动作的数据载体。

## # 主要接口说明

### # 动作（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 的使用注意事项

- 仅部分注释类型支持动作，包括 screen、link、sound 和 widget。

- 仅部分注释类型支持附加动作，包括 screen 和 widget。

- 附加动作的触发事件类型需与对象类型匹配，否则无效（AnnotActionTriggerEvents
  (opens new window)， DocActionTriggerEvents
  (opens new window)， FieldActionTriggerEvents
  (opens new window), PageActionTriggerEvents
  (opens new window)）。 PageActionTriggerEvents
  (opens new window)：适用于页面）。

- 某些动作类型（如 GotoR、GoToE、Rendition）受 SDK 的支持情况限制，仅支持触发或仅支持通过接口设置。

## # 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("请完善必填信息"); }',
        }
    });