本文深入探讨了如何在PDF文档和表单中使用JavaScript,超越其常见的Web浏览器环境。我们将介绍Acrobat JavaScript的基本概念、核心API对象、并通过丰富的C#代码示例(使用PDFOne .NET库)展示其强大功能,包括文档操作、表单验证、交互元素创建等。文章还包含了学习辅助工具,如关键词表和UML关系图,旨在为开发者提供一份全面且实用的指南。
1. JavaScript:不止于浏览器
大多数人将 JavaScript 与 Web 浏览器和 HTML 页面联系在一起。(许多人可能不记得 Internet Explorer 曾支持 VBScript 作为 JavaScript 的替代方案。)JavaScript 也曾作为 VBScript 的替代品,用于服务器端的经典 ASP (Classic ASP) 开发。随着 Node.js 等技术的出现,JavaScript 在服务器端的应用已不是新现象。著名的 Adobe Flash 播放器运行一种称为 ActionScript 的 JavaScript 变体。至少大多数病毒编写者都知道,JavaScript 可以从 Windows 命令提示符运行。
尽管 JavaScript 需要一个脚本宿主应用程序才能运行,但它并不绑定于任何特定的实现。该语言由 ECMA 独立于脚本宿主进行开发和标准化。
Web 浏览器中的脚本宿主向 JavaScript 引擎暴露了“宿主对象”,如 window
、document
(DOM) 和 location
对象。类似地,在 Adobe Reader、Adobe Acrobat 和其他应用程序中,查看器也会向 JavaScript 引擎暴露多个宿主对象。
HTML+JavaScript 或 DHTML 曾一度被向大众宣传为“打了激素的 HTML”。JavaScript 确实可以极大地增强您的 PDF 文档。本文将探讨您可以用它来做些什么。我使用了 PDFOne .NET 来向 PDF 文档添加 JavaScript。您也可以使用任何其他 PDF 库来实现,例如 PDFOne (for Java) 和 PDFtoolkit VCL。
2. 开发环境与“Hello, World!”
Acrobat JavaScript 主要在 Adobe Acrobat 和 Adobe Reader 环境中执行。虽然本文示例使用 Gnostice PDFOne .NET 库以编程方式创建和注入 JavaScript,但您也可以使用 Adobe Acrobat Pro 的工具栏中的“JavaScript”工具手动为PDF添加脚本。
让我们从一个经典的“Hello, World!”示例开始。此代码片段使用了 app
对象的 alert()
方法。此警报被添加到文档的“打开”视图应用程序事件中。您还可以在其他情境下添加此类 JavaScript 脚本,例如“关闭”、“保存”或“打印”。
// C# 使用 PDFOne .NET 添加文档打开动作
static void create_PdfWithDocumentOpenAction() {
// 创建一个新的PDF文档对象,并传入许可证密钥
PDFDocument doc = new PDFDocument(PDFOne_License.KEY);
// 添加一个打开文档时执行的JavaScript动作
// 该动作会显示一个警告框,内容是'Hello, World!'
doc.AddOpenActionJavaScript("app.alert('Hello, World!')");
// 将文档保存到名为"Hello.pdf"的文件中
doc.Save("Hello.pdf");
// 关闭文档,释放资源
doc.Close();
}
查阅 API 参考手册后,我们可以让上面的警告框显示得更加友好(例如,添加标题、修改图标类型)。
static void create_PdfWithDocumentOpenAction() {
PDFDocument doc = new PDFDocument(PDFOne_License.KEY);
// app.alert(message, iconType, buttonType, title)
// 参数说明:
// - 'Hello, World!': 提示信息内容
// - 3: 图标类型 (3 代表“信息”图标)
// - 0: 按钮类型 (0 代表“确定”按钮)
// - 'Acrobat JavaScript Examples': 警告框的标题
doc.AddOpenActionJavaScript(
"app.alert('Hello, World!', 3, 0, 'Acrobat JavaScript Examples');");
doc.Save("Hello.pdf");
doc.Close();
}
3. 为PDF对象添加JavaScript
JavaScript 脚本不仅可以添加到文档级事件,还可以添加到 PDF 中的各种对象,例如注释、表单字段甚至书签。在以下示例中,一个 JavaScript 脚本被添加到了一个链接注释上。
static void create_PDFJavaScriptLinkAnnotation() {
// 创建一个新的PDF文档
PDFDocument doc = new PDFDocument(PDFOne_License.KEY);
// 设置文档的测量单位为英寸(便于定位)
doc.MeasurementUnit = PDFMeasurementUnit.Inches;
// 创建一个链接注释(Link Annotation)
// 参数是一个矩形区域,定义了链接的可点击范围(左上角x=1英寸, y=1英寸, 宽=2英寸, 高=0.3英寸)
PDFLinkAnnot la1 = new PDFLinkAnnot(new RectangleF(1f, 1f, 2f, 0.3f));
// 向该注释添加一个JavaScript动作
// 当点击该链接时,会显示一个警告框,内容为当前文档的总页数
// `this` 在此上下文中通常指向宿主文档(Document对象),`numPages` 是文档的总页数属性
la1.AddActionJavaScript("app.alert('Page count is ' + this.numPages);");
// 在链接注释的位置上写入提示文本“Click here for page count”
doc.WriteText("Click here for page count", 1f, 1f);
// 将创建好的链接注释添加到文档中
doc.AddAnnot(la1);
// 保存文档
doc.Save("link-annotation1.pdf");
}
注意: this
关键字在不同的上下文中指代不同的对象。请务必查阅文档以避免错误。另一点需要注意的是,在 Adobe Reader 中,文档被视为只读的,修改能力有限。任何试图对文档进行永久性更改的 JavaScript 代码都会失败,或者需要最终用户将更改保存到新文件中。
4. 调试:JavaScript 错误控制台
我知道您在想什么。如果在 JavaScript 脚本中犯了错误怎么办?别担心,您可以检查控制台。
Adobe Reader 和 Adobe Acrobat 有一个错误控制台。您可以从菜单的“首选项”(Preferences)中启用它,或者使用同名的 console
对象以编程方式启动它。
static void create_PdfWithConsoleWindowOpen() {
PDFDocument doc = new PDFDocument(PDFOne_License.KEY);
// 添加打开动作JavaScript:
// console.show(); - 显示控制台窗口
// console.clear(); - 清除控制台之前的消息
// console.println('How are ya doing!'); - 向控制台打印一行信息
doc.AddOpenActionJavaScript(
"console.show(); console.clear(); console.println('How are ya doing!');");
doc.Save("Hello_de_console.pdf");
doc.Close();
}
5. 获取与操作表单字段
Web 程序员熟悉著名的 document.getElementById()
方法。Acrobat JavaScript 有类似的东西吗?有几种方法。下面的示例使用 getField()
方法获取一个按钮表单字段,并在启动时为其添加一些行为。
static void create_PdfWithPrintCloseButtons() {
PDFDocument doc = new PDFDocument(PDFOne_License.KEY);
doc.MeasurementUnit = PDFMeasurementUnit.Inches;
// 创建一个“关闭”按钮
PDFFormPushButton btn1 = new PDFFormPushButton(new RectangleF(1f, 1f, 2f, 0.3f));
btn1.NormalCaption = "Close"; // 按钮上显示的文字
btn1.FieldName = "btn1"; // 字段名称,用于在JavaScript中标识该字段
btn1.ToolTip = "Closes this document"; // 鼠标悬停时的提示文本
doc.AddFormField(btn1); // 将按钮字段添加到文档
// 创建一个“打印”按钮
PDFFormPushButton btn2 = new PDFFormPushButton(new RectangleF(4f, 1f, 2f, 0.3f));
btn2.NormalCaption = "Print";
btn2.FieldName = "btn2";
btn2.ToolTip = "Prints this document";
doc.AddFormField(btn2);
// 在文档打开时添加按钮行为(设置按钮动作)
doc.AddOpenActionJavaScript(
"var oBtn1 = this.getField('btn1'); "+ // 获取名为'btn1'的字段对象
"oBtn1.setAction('MouseDown', 'this.closeDoc()'); " + // 为btn1设置鼠标按下动作:关闭文档
"var oBtn2 = this.getField('btn2'); " + // 获取名为'btn2'的字段对象
"oBtn2.setAction('MouseDown', 'this.print()'); "); // 为btn2设置鼠标按下动作:打印文档
// 保存文档
doc.Save("Le_boutons_de_JavaScript.pdf");
doc.Close();
}
6. 使用JavaScript进行PDF表单验证
在您“获取”(get
)到一个表单字段后,可以使用 valueAsString
等属性评估其值。如果您发现表单字段有效,可以使用 Document.submitForm()
方法提交表单。
static void create_PDFWithFormValidation() {
PDFDocument doc = new PDFDocument(PDFOne_License.KEY);
doc.OpenAfterCreate = true; // 创建后立即打开文档
doc.MeasurementUnit = PDFMeasurementUnit.Inches;
// 创建一个文本表单字段(用于输入姓名)
PDFFormTextField tf = new PDFFormTextField(new RectangleF(1f, 1f, 1f, 0.3f));
tf.FieldName = "FullName"; // 字段名称
tf.BackgroundColor = Color.LightGray; // 设置背景色为浅灰色
tf.NameAsUnicode = false;
// 创建一个提交按钮表单字段
PDFFormPushButton pb = new PDFFormPushButton(new RectangleF(1f, 2f, 1f, 0.3f));
pb.FieldName = "SubmitButton";
pb.ActionType = PDFFormFieldActionType.Javascript_Action; // 设置动作类型为JavaScript
pb.NormalCaption = "Submit"; // 按钮文字
// 为按钮设置JavaScript脚本
pb.JavaScript =
"var oNameField = this.getField('FullName'); " + // 获取姓名文本框对象
"if (oNameField.valueAsString.length > 2) { " + // 验证:姓名长度是否大于2个字符
" var arFields = new Array('FullName'); " + // 创建一个数组,包含要提交的字段名
" this.submitForm({ " + // 调用提交表单方法
" cURL: 'http://www.gnostice.com/newsletters/demos/200804/forms_test.asp', " + // 提交的目标URL
" aFields: arFields, " + // 指定要提交的字段数组
" cSubmitAs: 'HTML', " + // 指定提交数据的格式为HTML
" }); " +
"} else { " + // 如果验证失败
" app.alert('Nhyet! Nhyet! Nhyet!');" + // 弹出警告框
"}";
// 将表单字段添加到文档
doc.AddFormField(tf);
doc.AddFormField(pb);
doc.Save("Valider_le_form.pdf");
doc.Close();
}
7. 注意事项与局限性 (Caveat Emptor)
在 PDF 文档中运行 JavaScript 无疑非常强大,但您不应在没有备用机制的情况下依赖它。例如,如果您使用 JavaScript 进行表单验证,那么您仍然需要进行服务器端验证。原因如下:
- 兼容性:并非所有 PDF 查看器应用程序都具有 JavaScript 引擎。
- 标准符合性:PDF/A 标准明确禁止使用 JavaScript。
- 用户设置:用户可能在其查看器应用程序中禁用 JavaScript。
- 数字签名:数字签名可能会给 JavaScript 的使用带来复杂性。
- 开发难度:添加和调试 JavaScript 并非易事。
因此,请将 Acrobat JavaScript 视为增强用户体验和提供客户端交互的有效工具,但始终确保核心功能不依赖于它。
8. 核心对象关系
为了更清晰地理解Acrobat JavaScript中核心对象的关系,可以参考以下简化模型:
此图描述了:
App
对象作为一个顶级入口,可以调用Document
和Console
的功能。Document
对象是核心,它包含多个Field
(字段)对象,并拥有控制文档本身(如打印、关闭)和提交表单的方法。Field
对象代表表单字段,可以设置值和行为(setAction
)。
9. 单词、短语表
单词/短语 (Word/Phrase) | 音标 (Phonetic) | 词性 (Part of Speech) | 词根/词缀 (Root/Affix) | 释义 (Definition) | 搭配/例子 (Collocation/Example) |
---|---|---|---|---|---|
associate with | /əˈsoʊʃieɪt wɪð/ | phrasal verb | associate: 联系 | 将…与…联系起来 | People associate JavaScript with Web browsers. |
alternative | /ɔːlˈtɜːrnətɪv/ | noun/adj | alternat(e): 交替 + -ive | 替代品;可供选择的 | VBScript was an alternative for JavaScript. |
phenomenon | /fəˈnɑːmɪnən/ | noun | pheno-: 显示 + -menon | 现象 | Node.js is not a recent phenomenon. |
standardized | /ˈstændərdaɪzd/ | adj | standard: 标准 + -ize: 化 + -ed | 标准化的 | The language is standardized by ECMA. |
expose | /ɪkˈspoʊz/ | verb | ex-: 出 + pose: 放置 | 暴露;使接触 | The host exposes objects to the engine. |
super-charge | /ˈsuːpər tʃɑːrdʒ/ | verb | super-: 超 + charge: 充电 | 使威力大增;增强 | JavaScript can super-charge your PDFs. |
snippet | /ˈsnɪpɪt/ | noun | snip: 剪下 + -et: 小 | (代码)片段 | This code snippet uses the app object. |
foreboding | /fɔːrˈboʊdɪŋ/ | adj/noun | fore-: 前 + bod(e): 预示 + -ing | 不祥的(预感) | The alert can be made less foreboding. |
annotation | /ˌænəˈteɪʃn/ | noun | annotate: 注释 + -ion | 注解;注释 | A JavaScript added to a link annotation. |
refer to | /rɪˈfɜːr tuː/ | phrasal verb | refer: 参考 | 指的是;指代 | this refers to different things. |
console | /ˈkɑːnsoʊl/ | noun | con-: 共同 + sole: 底座 | 控制台 | Check the JavaScript error console. |
infamous | /ˈɪnfəməs/ | adj | in-: 不 + famous: 著名的 | 声名狼藉的;臭名昭著的 | The infamous getElementById method. |
behaviour | /bɪˈheɪvjər/ | noun | behave: 行为 + -iour | 行为;习性 | Add some behaviour to a button. |
validation | /ˌvælɪˈdeɪʃn/ | noun | valid: 有效的 + -ation | 验证 | Form validation with JavaScript. |
evaluate | /ɪˈvæljueɪt/ | verb | e-: 出 + value: 价值 + -ate | 评估;求值 | Evaluate its value using valueAsString . |
complication | /ˌkɑːmplɪˈkeɪʃn/ | noun | com-: 一起 + plic: 折叠 + -ation | 并发症;复杂化 | Digital signatures add complications. |
fallback | /ˈfɔːlbæk/ | noun | fall: 回落 + back: 回 | 备用机制;退路 | You need a fallback mechanism. |
expressly | /ɪkˈspresli/ | adv | express: 明确的 + -ly | 明确地;特意地 | PDF/A expressly forbids JavaScript. |
foray | /ˈfɔːreɪ/ | noun | - | 尝试;涉足 | Good luck in your forays into AcroJS. |
希望这篇详细的文章能帮助您更好地理解和应用 Acrobat JavaScript!