PDF 中 JavaScript 的 10大实用技巧

#『Java分布式系统开发:从理论到实践』征文活动#

本文深入探讨了如何在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 引擎暴露了“宿主对象”,如 windowdocument (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 进行表单验证,那么您仍然需要进行服务器端验证。原因如下:

  1. 兼容性:并非所有 PDF 查看器应用程序都具有 JavaScript 引擎。
  2. 标准符合性:PDF/A 标准明确禁止使用 JavaScript。
  3. 用户设置:用户可能在其查看器应用程序中禁用 JavaScript。
  4. 数字签名:数字签名可能会给 JavaScript 的使用带来复杂性。
  5. 开发难度:添加和调试 JavaScript 并非易事。

因此,请将 Acrobat JavaScript 视为增强用户体验和提供客户端交互的有效工具,但始终确保核心功能不依赖于它。

8. 核心对象关系

为了更清晰地理解Acrobat JavaScript中核心对象的关系,可以参考以下简化模型:

包含
1
*
操作
调用
获取
App
+alert()
..(其他全局方法)
Document
+numPages
+getField(fieldName)
+submitForm()
+closeDoc()
+print()
..(其他文档属性和方法)
Console
+show()
+clear()
+println()
..(其他控制台方法)
Field
+valueAsString
+setAction(event, script)
..(其他字段属性和方法)

此图描述了:

  • App 对象作为一个顶级入口,可以调用 DocumentConsole 的功能。
  • Document 对象是核心,它包含多个 Field(字段)对象,并拥有控制文档本身(如打印、关闭)和提交表单的方法。
  • Field 对象代表表单字段,可以设置值和行为(setAction)。

9. 单词、短语表

单词/短语 (Word/Phrase)音标 (Phonetic)词性 (Part of Speech)词根/词缀 (Root/Affix)释义 (Definition)搭配/例子 (Collocation/Example)
associate with/əˈsoʊʃieɪt wɪð/phrasal verbassociate: 联系将…与…联系起来People associate JavaScript with Web browsers.
alternative/ɔːlˈtɜːrnətɪv/noun/adjalternat(e): 交替 + -ive替代品;可供选择的VBScript was an alternative for JavaScript.
phenomenon/fəˈnɑːmɪnən/nounpheno-: 显示 + -menon现象Node.js is not a recent phenomenon.
standardized/ˈstændərdaɪzd/adjstandard: 标准 + -ize: 化 + -ed标准化的The language is standardized by ECMA.
expose/ɪkˈspoʊz/verbex-: 出 + pose: 放置暴露;使接触The host exposes objects to the engine.
super-charge/ˈsuːpər tʃɑːrdʒ/verbsuper-: 超 + charge: 充电使威力大增;增强JavaScript can super-charge your PDFs.
snippet/ˈsnɪpɪt/nounsnip: 剪下 + -et: 小(代码)片段This code snippet uses the app object.
foreboding/fɔːrˈboʊdɪŋ/adj/nounfore-: 前 + bod(e): 预示 + -ing不祥的(预感)The alert can be made less foreboding.
annotation/ˌænəˈteɪʃn/nounannotate: 注释 + -ion注解;注释A JavaScript added to a link annotation.
refer to/rɪˈfɜːr tuː/phrasal verbrefer: 参考指的是;指代this refers to different things.
console/ˈkɑːnsoʊl/nouncon-: 共同 + sole: 底座控制台Check the JavaScript error console.
infamous/ˈɪnfəməs/adjin-: 不 + famous: 著名的声名狼藉的;臭名昭著的The infamous getElementById method.
behaviour/bɪˈheɪvjər/nounbehave: 行为 + -iour行为;习性Add some behaviour to a button.
validation/ˌvælɪˈdeɪʃn/nounvalid: 有效的 + -ation验证Form validation with JavaScript.
evaluate/ɪˈvæljueɪt/verbe-: 出 + value: 价值 + -ate评估;求值Evaluate its value using valueAsString.
complication/ˌkɑːmplɪˈkeɪʃn/nouncom-: 一起 + plic: 折叠 + -ation并发症;复杂化Digital signatures add complications.
fallback/ˈfɔːlbæk/nounfall: 回落 + back: 回备用机制;退路You need a fallback mechanism.
expressly/ɪkˈspresli/advexpress: 明确的 + -ly明确地;特意地PDF/A expressly forbids JavaScript.
foray/ˈfɔːreɪ/noun-尝试;涉足Good luck in your forays into AcroJS.

希望这篇详细的文章能帮助您更好地理解和应用 Acrobat JavaScript!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

纸上笔下

承蒙厚爱,不胜感激。铭记于心!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值