PDF 无处不在,无论您构建的是哪种 Web 应用,您都必然需要一款方便的 PDF 查看器。但如果您曾尝试在 Web 上处理 PDF,您就会知道,这就像与过时的插件、iframe 解决方法搏斗,或者忍受尴尬的弹出窗口。
动态图片

这就是 PDF.js 的作用所在——它是一个开源 JavaScript 库,可直接在浏览器中呈现 PDF。现在,如果您像我一样使用 Vue.js 构建应用程序,那么弄清楚如何在项目中使用 PDF.js 一开始可能会很棘手。
要充分利用 PDF.js 的功能,了解其分层架构非常重要。如果您曾经想过“如何启用文本选择?”或“如何处理 PDF 中的链接?”甚至“如何构建注释等高级功能?”,答案通常在于了解这些层。
在本文中,我将向您展示 PDF.js 如何创建图层、每个图层负责什么以及如何将它们应用到您的下一个 Vue 项目中。让我们开始吧!
动态图片

Vue PDF 查看器:灵活而强大的 Vue.js PDF 组件
快速了解一下我一直在研究的东西:Vue PDF Viewer,这是一款方便的 PDF 查看器,可直接在您的 Vue 或 Nuxt 应用程序中呈现 PDF。它包含 20 多项功能,包括开箱即用的默认工具栏、自定义和响应式设计,因此您的用户无需离开您的网站即可与您的文档进行交互。
如果这听起来很有趣,我希望你尝试一下Vue PDF Viewer。你的支持帮助我继续创建像这样的精彩工具和教程。❤️
PDF.js 层概述
在深入研究之前,我们先来快速了解一下 PDF.js 中的四个主要层。每个层处理渲染或用户交互的特定方面:
画布层:渲染 PDF 的静态视觉内容(形状、图像、文本作为图形)。它是 PDF 查看器的基础。
文本层:位于画布顶部,确保文本可选择和可搜索。
注释层:处理交互元素(链接、表格、突出显示),以便用户可以单击、键入或导航。
结构层:管理整体布局,包括所有其他层的对齐和缩放。
通过将不同的功能分成不同的层,PDF.js 仍然保持模块化、高效,并且非常容易调整。
让我们首先创建一个新的 Vue.js 项目来集成 PDF.js。
设置 Vue.js 项目
在本文中,我使用 Codepen 作为代码编辑器。如果你想跟着我一起做,你可以Pen在 Codepen 上创建一个新项目,然后按照以下步骤操作。
步骤 1:选择 Vue Pen 模板
最简单的入门方法是使用 CodePen 的内置 Vue 模板。从左侧菜单中,单击菜单Pen以打开选项,然后选择Vue Pen。
这为您提供了一个具有默认 Vue 设置的预配置环境。在此界面中,您可以试验 Vue 代码并立即在预览区域中查看结果。
第 2 步:配置项目并安装 PDF.js
接下来,让我们配置您的 Vue 项目以使用该pdfjs-dist库。
打开设置:单击笔顶部的“设置”按钮。
选择 JS 选项:在弹出窗口中,切换到 JS 选项卡。
选择 Vue 版本:在 Vue 版本下,选择 Vue 3(或任何适合您需要的版本)。
添加包:在添加包框中,搜索并添加
pdfjs-dist。保存并关闭:单击“保存并关闭”以应用您的更改。
CodePen 会自动将 import 语句注入pdfjs-dist到您的代码中。您通常需要将该语句移到您的<script>部分下方,以保持内容整洁有序。
就这样!现在,您已准备好在 Vue 环境中开始尝试使用 PDF.js 层。让我们开始了解这些层。
1. 画布层
目的
画布层是基础 — PDF.js 会在此绘制图像、形状和文本(作为图形)等视觉元素。从本质上讲,您在屏幕上看到的内容都来自此层。
工作原理
PDF.js 使用 HTML
<canvas>元素显示 PDF 的可视内容它利用浏览器的2D 画布 API进行高性能渲染。
这意味着您的 PDF 内容在各种屏幕尺寸和设备上看起来都将一致且准确。
使用案例
非常适合非交互式或静态PDF 内容。
它将文档呈现为图像,确保 PDF 的字体、颜色和布局在所有设备上保持一致。
使用 Vue 的 Canvas 层代码示例
这是一个快速的 CodePen 演示,展示了 Vue.js 中的最小 Canvas Layer 设置。
代码如何工作
下面,我将介绍将 PDF 页面渲染到画布上的主要步骤。
(1)设置 PDF Worker
在加载PDF之前,我们需要配置PDF worker,该worker负责在单独的线程中处理PDF,以提高性能。
import * as PDFJS from "https://esm.sh/pdfjs-dist";
import * as PDFWorker from "https://esm.sh/pdfjs-dist/build/pdf.worker.min";
try {
PDFJS.GlobalWorkerOptions.workerSrc = PDFWorker;
} catch (e) {
window.pdfjsWorker = PDFWorker;
}
解释:
这里我们导入
pdf.worker.min.js并设置其为工作者源。工作者在单独的线程中处理PDF,因此我们的UI不会被阻塞。
如果发生错误,则将工作人员分配
window.pdfjsWorker为后备
(2)获取并加载 PDF
const PDF_SRC = "https://pdfobject.com/pdf/pdf_open_parameters_acro8.pdf";
export default {
...
methods: {
processLoadingTask(source) {
const loadingTask = PDFJS.getDocument(source);
loadingTask.promise
.then(docProxy => ...)
.then(page => {
...
const renderContext = {
canvasContext: context,
viewport: viewport
};
return page.render(renderContext);
})
}
},
// Start the process when the component is mounted
mounted() {
this.processLoadingTask(PDF_SRC);
}
}
解释:
该方法通过from
processLoadingTask初始化 PDF 加载。PDFJS.getDocument(source)pdfjs-dist一旦获取 PDF(来自
PDF_SRC),我们就会存储文档代理(docProxy)。总页数已确定并存储在 中
totalPages。
(3)渲染 Canvas 层
解释:
PDF 加载后,我们有一个页面对象,getPage(1)然后使用 .PDF.js 提取第一页:
缩放画布以匹配 PDF 页面尺寸。
元素
<canvas>(canvasLayer)用于将 PDF 页面呈现为图像。渲染过程由 处理
page.render(renderContext),其中canvasContext将页面内容绘制到画布上。
这可确保最终输出与原始 PDF 的外观准确匹配。
2. 文本层
目的
文本层可确保 PDF 中的文本可选择、可搜索和可访问。虽然画布层承担了渲染视觉效果的重任,但文本层可让您突出显示和复制实际文本。
工作原理
PDF.js从 PDF 中单独提取文本内容。
<div>它使用绝对定位元素将每段文本精确地定位在底层画布上。尽管您可能无法
<div>直观地看到这些文本(它们通常是透明的),但它们的存在可以支持选择和搜索。
使用案例
启用从 PDF 中选择和复制文本。
文本层对于可访问性至关重要,因为它使屏幕阅读器能够解释内容。
在 PDF 查看器中提供文本搜索和突出显示。
保持文本与呈现的 PDF 页面对齐
文本层代码示例
这是一个 Vue.js CodePen 示例,演示了文本层的实际操作:
代码如何工作
(1)构建层
<div ref="pdfLayersWrapper" class="pdf__layers">
<div class="pdf__canvas-layer">
<canvas ref="canvasLayer" />
</div>
<div ref="textLayer" class="pdf__text-layer"></div>
</div>
解释:
是
pdfLayersWrapper一个包含画布层和文本层的容器。画布层()
canvasLayer以可视化方式显示 PDF 页面。文本层()
textLayer位于画布顶部,可以选择文本。
(2)获取并加载 PDF
(与画布层中的步骤相同——我们首先加载 PDF 文档。)
(3)渲染文本层
renderText(pdfPageProxy, textLayerContainer, viewport) {
...
pdfPageProxy
.getTextContent()
.then((content) => {
const renderTask = new PDFJS.TextLayer({
container: textLayerContainer,
textContentSource: content,
viewport: viewport.clone({ dontFlip: true })
});
return renderTask.render();
});
},
processLoadingTask (source) {
...
this.renderText(...)
}
解释:
渲染文本层(
this.renderText())。pdfPageProxy.getTextContent()从 PDF 页面检索文本数据。此数据被传递到新的
TextLayer,指定:container:呈现文本的容器。textContentSource:从 PDF 中提取文本数据。viewport:定义文本定位和缩放。
renderTask.render()将不可见但可选择的文本放置<div>在画布上。
3.注释层
目的
注释层处理超链接、突出显示、表单字段和注释等交互元素。如果您需要 PDF 查看器支持内部链接(跳转到不同页面),或者您的 PDF 包含可填写的表单,则此层非常适合您。
工作原理
PDF.js 从 PDF 中提取注释数据(链接、表单字段等)。
它将这些数据覆盖为交互式 HTML 元素(例如
<a>,,<input>)<textarea>,位于使用 CSS 定位的画布层和文本层之上。该层确保用户可以单击、键入和交互,而不会改变 PDF 的静态视觉效果。
使用案例
单击 PDF 内的链接可在页面之间导航。
从 PDF 注释中呈现高亮、下划线和注释。
在 PDF 中显示交互式表单域。
注释层代码示例
查看有关Vue.js注释层的 CodePen 示例:
您可以单击 Codepen 内的链接来更改 PDF 的页面。
代码如何工作
(1)构建层
<div ref="pdfLayersWrapper" class="pdf__layers">
<div class="pdf__canvas-layer">
<canvas ref="canvasLayer" />
</div>
<div ref="textLayer" class="pdf__text-layer"></div>
<div ref="annotationLayer" class="pdf__annotation-layer"></div>
</div>
解释:
我们
<div>为其中的注释层pdfLayersWrapper添加另一个。注释层位于画布层和文本层的上方。
(2)获取并加载 PDF
(同样,初始 PDF 加载步骤与之前相同。)
(3)渲染注释层
async getAnnotations(pageProxy) {
const annotations = await pageProxy.getAnnotations({ intent: "display" });
return annotations;
},
async renderAnnotations(pdfPageProxy, annotationLayerContainer, viewport) {
const annotations = await this.getAnnotations(pdfPageProxy);
...
const annotationLayer = new PDFJS.AnnotationLayer({
div: annotationLayerContainer,
viewport: clonedViewport,
page: pdfPageProxy
});
await annotationLayer.render({
...,
annotations,
linkService: new SimpleLinkService(),
...
})
}
解释:
getAnnotations从 PDF 页面检索所有注释信息(链接、表单字段等)。我们克隆视口来匹配页面的尺寸,从而实现正确定位。
renderAnnotations然后调用 来替换现有元素,annotationLayerContainer然后再渲染新元素。它通过以下方式实现:创建一个新的
AnnotationLayer实例来管理和呈现注释元素。通过显示注释层
annotationLayer.render({...}),传入注释并SimpleLinkService()处理内部PDF链接。
(4)处理内部链接点击
...
annotationLayerContainer.addEventListener("click", async (event) => {
...
const annotationLinkId = annotations.find((ele) => ele.id === id);
...
const pageIndex = await this.pdfDocProxy.getPageIndex(
annotationLinkId.dest[0]
);
this.currentPage = pageIndex + 1;
});
解释:
当用户点击 PDF 内的链接时,事件会检查它是否是注释。
如果是,我们就查找目标页面,
getPageIndex()看看它是否指向 PDF 中的其他页面。然后我们通过更新导航到该页面
this.currentPage。
(5)处理页面导航
简单的导航栏让用户可以在页面之间切换。
<div class="page-navigation">
<button :disabled="currentPage <= 1" @click="--currentPage">
←
</button>
<span>{{ currentPage }}/{{ totalPages }}</span>
<button :disabled="currentPage >= totalPages" @click="++currentPage">
→
</button>
</div>
解释:
currentPage单击导航按钮时,该值会更新。watch: { currentPage(newValue) { ... } }确保页面发生变化时重新渲染所有图层。这可确保所有图层(画布、文本和注释)保持同步。
4. 结构层
目的
将结构层视为主布局管理器。当用户放大或缩小或调整窗口大小时,它可使画布、文本和注释层保持正确对齐、缩放和定位。
创建PDF 查看器时必须有此层,因为它是将所有其他层粘合在一起的基础。
工作原理
结构层通常由
<div>包裹所有其他层的容器元素()实现。保持各层之间的一致定位,以便它们在缩放或滚动时不会漂移。
充当将所有内容联系在一起的基础,以便您的 PDF 查看器保持凝聚力。
使用案例
确保在不同屏幕尺寸和分辨率下获得一致的用户体验。
通过按比例缩放所有图层实现平滑缩放。
集中导航和布局逻辑(例如控制查看器如何从一个页面滚动到另一个页面。)
在我们的代码示例中,<div ref="pdfLayersWrapper" class="pdf__layers">元素作为结构层,包装其他层:
<div ref="pdfLayersWrapper" class="pdf__layers">
<div class="pdf__canvas-layer">
<canvas ref="canvasLayer" />
</div>
<div ref="textLayer" class="pdf__text-layer"></div>
<div ref="annotationLayer" class="pdf__annotation-layer"></div>
</div>
为什么这很重要?
结构层包装器可确保画布、文本和注释层保持同步,即使您缩放或翻转页面也是如此。
您可以自定义包装器来处理特殊行为,如延迟加载页面、滚动捕捉和其他性能优化。
各层如何协同工作
PDF.js 中的所有四个层如何协调协作:
渲染过程:
每个页面首先绘制在画布层(可视内容)上。接下来,文本层被置于其上,以便进行选择和搜索。最后,注释层被置于最顶层,提供链接和表单字段等交互元素。交互性:
注释层处理用户事件(例如单击链接或在表单字段中输入),而文本层确保可以突出显示、复制或搜索文本。PDF 查看器:结构层
将所有内容打包起来,确保缩放、调整大小和跨页面导航保持一致。它使所有层保持一致且响应迅速,因此用户可以获得完美的观看体验。
结论
我在各种 Vue.js 项目中使用过 PDF.js,从简单的 PDF 预览到完全交互式的文档系统,分层架构始终是核心。
这对你来说为什么重要?
您可以优化每一层以提高性能或用户体验。
您可以自定义查看器处理交互、文本搜索或表单的方式。
您会发现维护或扩展 PDF 查看器代码变得更加容易。
通过了解这些层,您将能够优化 PDF.js 实现、自定义功能并提供更好的用户体验。无论您是构建简单的 Vue.js PDF 查看器还是复杂的文档管理系统,有效利用这些层都将为您的项目取得成功奠定基础。

