本文经Vildan Softic同行评审。感谢所有SitePoint的同行评审员,让SitePoint的内容达到最佳状态!
在Web应用程序中处理PDF文件一直以来都非常棘手。如果幸运的话,你的用户只需要下载文件即可。但有时,用户需要更多功能。过去,我比较幸运,但这次,我们的用户需要应用程序显示PDF文档,以便他们可以保存与每个页面相关的元数据。以前,人们可能使用昂贵的PDF插件(例如Adobe Reader)在浏览器中运行来实现此目的。然而,经过一番时间和实验,我找到了一种更好的方法来在Web应用程序中集成PDF查看器。今天,我们将了解如何使用Aurelia和PDF.js简化PDF处理。
我们今天的目标是在Aurelia中构建一个PDF查看器组件,允许查看器和我们的应用程序之间进行双向数据流。我们有三个主要要求:
您可以在我们的GitHub仓库中找到本教程的代码,以及此处完成代码的演示。
PDF.js是一个由Mozilla基金会编写的JavaScript库。它加载PDF文档,解析文件和相关的元数据,并将页面输出渲染到DOM节点(通常是<canvas></canvas>
元素)。项目中包含的默认查看器为Chrome和Firefox中的嵌入式PDF查看器提供支持,可以用作独立页面或资源(嵌入在iframe中)。
这确实很酷。这里的问题是,默认查看器虽然有很多功能,但它被设计为一个独立的网页。这意味着,虽然它可以集成到Web应用程序中,但它基本上必须在iframe沙箱内运行。默认查看器设计为通过其查询字符串获取配置输入,但是我们不能在初始加载后轻松更改配置,也不能轻松地从查看器获取信息和事件。为了将其与Aurelia Web应用程序集成——包括事件处理和双向绑定——我们需要创建一个Aurelia自定义组件。
注意:如果您需要关于PDF.js的复习,请查看我们的教程:使用Mozilla的PDF.js在JavaScript中进行自定义PDF渲染
为了实现我们的目标,我们将创建一个Aurelia自定义元素。但是,我们不会将默认查看器放入我们的组件中。相反,我们将创建自己的查看器,它连接到PDF.js核心和查看器库,以便我们可以最大限度地控制我们的可绑定属性和渲染。对于我们的初始概念验证,我们将从Aurelia骨架应用程序开始。
样板代码
正如您从上面的链接中看到的那样,骨架应用程序有很多文件,其中许多文件我们不需要。为了简化操作,我们准备了一个精简版本的骨架,并在其中添加了一些内容:
所以让我们启动并运行应用程序。
首先,确保全局安装了gulp和jspm:
npm install -g gulp jspm
然后克隆骨架并进入其中:
git clone git@github.com:sitepoint-editors/aurelia-pdfjs.git -b skeleton cd aurelia-pdfjs
然后安装必要的依赖项:
npm install jspm install -y
最后运行gulp watch并导航到http://localhost:9000。如果一切按计划进行,您应该会看到一条欢迎消息。
更多设置
接下来要做的是找到几个PDF文件并将它们放在src/documents中。将它们命名为one.pdf和two.pdf。为了最大限度地测试我们的自定义组件,最好其中一个PDF文件非常长,例如可以在古腾堡计划中找到的《战争与和平》。
将PDF文件放在适当位置后,打开src/app.html和src/app.js(按照约定,App组件是Aurelia应用程序的根组件),并将其中的代码替换为这两个文件的代码:src/app.html和src/app.js。在本教程中,我们将不会讨论这些文件,但代码中有很好的注释。
Gulp将自动检测这些更改,您应该会看到我们的应用程序UI呈现。设置就是这样。现在开始展示……
我们希望创建一个可以直接用于任何Aurelia视图的组件。由于Aurelia视图只是一个包含在HTML5模板标签中的HTML片段,因此一个示例可能如下所示:
npm install -g gulp jspm
<pdf-document>
标签是自定义元素的一个示例。它及其属性(如scale和page)不是HTML的原生属性,但我们可以使用Aurelia自定义元素来创建它。自定义元素易于创建,使用Aurelia的基本构建块:视图和ViewModel。因此,我们将首先搭建我们的ViewModel,命名为pdf-document.js,如下所示:
git clone git@github.com:sitepoint-editors/aurelia-pdfjs.git -b skeleton cd aurelia-pdfjs
这里要注意的主要内容是@bindable
装饰器;通过创建具有配置defaultBindingMode: bindingMode.twoWay
的可绑定属性,并通过在我们的ViewModel中创建处理程序方法(urlChanged、pageChanged等),我们可以监控和响应我们放置在自定义元素上的相关属性的更改。这将允许我们简单地通过更改元素上的属性来控制我们的PDF查看器。
然后,我们将创建与我们的ViewModel配对的初始视图。
npm install jspm install -y
(以下内容与原文基本一致,只是对部分语句进行了细微的调整,以保持流畅性和可读性,并避免重复。)
PDF.js分为三个部分:核心库(处理PDF文档的解析和解释)、显示库(在核心层之上构建可用的API)以及Web查看器插件(我们前面提到的预构建网页)。出于我们的目的,我们将通过显示API使用核心库;我们将构建我们自己的查看器。
显示API导出一个名为PDFJS的库对象,它允许我们设置一些配置变量并使用PDFJS.getDocument(url)加载我们的文档。该API是完全异步的——它向Web Worker发送和接收消息,因此它大量依赖于JavaScript Promise。我们将主要使用从PDFJS.getDocument()方法异步返回的PDFDocumentProxy对象和从PDFDocumentProxy.getPage()异步返回的PDFPageProxy对象。
尽管文档有点稀疏,但PDF.js有一些创建基本查看器的示例,这里和这里。我们将以此为基础构建我们的自定义组件。
Web Worker集成
PDF.js使用Web Worker来卸载其渲染任务。由于Web Worker在浏览器环境中的运行方式(它们实际上是沙箱化的),我们被迫使用JavaScript文件的直接文件路径来加载Web Worker,而不是通常的模块加载器。幸运的是,Aurelia提供了一个加载器抽象,因此我们不必引用静态文件路径(当我们捆绑应用程序时,这可能会发生变化)。
如果您正在关注我们版本的仓库,您可能已经安装了pdfjs-dist包,否则,您现在需要这样做(例如,使用jspm jspm install npm:pdfjs-dist@^1.5.391)。然后,我们将使用Aurelia的依赖注入模块注入Aurelia的加载器抽象,并使用加载器在我们的构造函数中加载Web Worker文件,如下所示:
加载页面
PDF.js库处理PDF文档的加载、解析和显示。它具有对部分下载和身份验证的内置支持。我们所要做的就是提供相关文档的URI,PDF.js将返回一个Promise对象,该对象解析为表示PDF文档及其元数据的JavaScript对象。
PDF的加载和显示将由我们的可绑定属性驱动;在这种情况下,它将是url属性。基本上,当URL更改时,自定义元素应该要求PDF.js发出对文件的请求。我们将在urlChanged处理程序中执行此操作,并对我们的构造函数进行一些更改以初始化一些属性,并对我们的detached方法进行一些更改以进行清理。
对于文档的每一页,我们将在DOM中创建一个<canvas></canvas>
元素,该元素位于具有固定高度的可滚动容器内。为此,我们将使用Aurelia的基本模板功能,使用一个repeater。因为每个PDF页面都可以有自己的大小和方向,所以我们将根据PDF页面视口设置每个canvas元素的宽度和高度。
渲染页面
现在我们已经加载了页面,我们需要能够将它们渲染到DOM元素。为此,我们将依赖于PDF.js的渲染功能。PDF.js查看器库有一个专门用于渲染页面的异步API;他们的网站上有一个很好的示例,展示了如何创建一个renderContext对象并将其传递给PDF.js渲染方法。我们将这段代码从示例中提取出来,并将其包装在一个render函数中:
实现滚动
为了提供熟悉且无缝的体验,我们的组件应该将页面显示为完全可滚动文档的各个部分。我们可以通过使我们的容器具有具有滚动溢出的固定高度来实现这一点,这可以通过CSS来实现。
为了最大限度地提高大型文档的性能,我们将执行以下几件事。首先,我们将利用Aurelia的TaskQueue来批量更改DOM。其次,我们将跟踪PDF.js已经渲染的页面,这样它就不必重做它已经完成的工作。最后,我们将只在滚动停止后渲染可见页面,方法是使用Aurelia的debounce绑定行为。这是我们在滚动时将运行的方法:
实现缩放
当我们缩放时,我们希望更新当前缩放级别。我们将在scaleChanged属性处理程序中执行此操作。基本上,我们将调整所有canvas元素的大小以反映给定比例的每一页的新视口大小。然后,我们将重新渲染当前视口中显示的内容,重新启动循环。
最终结果
让我们回顾一下我们的目标:
最终代码可以在我们的GitHub仓库中找到,以及此处完成代码的演示。虽然仍有改进的空间,但我们已经达到了目标!
(以下内容与原文基本一致,只是对部分语句进行了细微的调整,以保持流畅性和可读性,并避免重复。)
总有改进的空间,进行项目后分析并确定未来迭代中需要解决的领域始终是一个好习惯。以下是一些我想在PDF查看器实现方面进行升级的内容:
Aurelia提供了一个插件系统。将这个概念验证转换为Aurelia插件将使其成为任何Aurelia应用程序的即用型资源。Aurelia Github仓库提供了一个插件骨架项目,这将是一个良好的起点。这样,其他人就可以使用此功能,而无需重新构建它!
在Web应用程序中处理PDF文件一直以来都非常棘手。但是,凭借当今可用的资源,我们可以通过组合库及其功能来实现比以往更多的事情。今天,我们已经看到一个基本PDF查看器的示例——一个可以通过自定义功能扩展的查看器,因为我们可以完全控制它。可能性是无限的!你准备好构建一些东西了吗?请在下面的评论中告诉我。
(以下内容与原文基本一致,只是对部分语句进行了细微的调整,以保持流畅性和可读性,并避免重复。)
以上是Aurelia的冒险:创建自定义PDF查看器的详细内容。更多信息请关注PHP中文网其他相关文章!