乐趣区

PDF.js实现个性化PDF渲染(文本复制)

我肥来啦????。看到 Redux 教程突破 3w 的浏览量,小窃喜,很高兴自己的文章能够帮助到大家。
这次重返,依然带给大家一个小指南,也是最近工作中遇到的一个小 case。
前不久,产品经理提出要在界面上优雅地展示 PDF 文档,当即就有了两种实现方式:
实现方式一使用 embed 标记来使用浏览器自带的 pdf 工具。
这种实现方式优缺点都很明显:优点:自带“打印”,“搜索”,“翻页”等功能,强大且实现方便。缺点:不同浏览器的 pdf 工具样式不一,且无法满足个性化需求,比如:禁止打印,下载等。
我们的产品经理是挑剔的????,于是 …
实现方式二使用 Mozilla 的 PDF.js,自定义展示 PDF。
下面我们就细致讲述一下使用 PDF.js 过程中遇到的问题。主要包括:

基础功能集成
使用 Text-Layers 渲染

什么是 PDF.JS
PDF.js 是基于 HTML5 技术构建的,用于展示可移植文档格式的文件 (PDF),它可以在现代浏览器中使用且无需安装任何第三方插件。
基础功能集成
1️⃣引用
首先,引用 PDF.js 就遇到了问题,官网中提到通过 CDN 引用或者下载源码至本地。而我们并不想污染我们的 index.html 并且希望可以对每一个引用的框架有统一的版本管理。于是,我们搜寻到一个包:pdfjs-dist。
通过 npm install pdfjs-dist,我们引入了 PDF.js。
基础功能有两个必须引用的文件:

pdf.js
pdf.worker.js

如果使用 CDN 的方式,直接引用如下对应文件即可:

https://mozilla.github.io/pdf…
https://mozilla.github.io/pdf…

如果使用 npm 的方式,则在需要使用 PDF.js 的文件中如下引用:
import PDFJS from ‘pdfjs-dist’;

PDFJS.GlobalWorkerOptions.workerSrc = ‘pdfjs-dist/build/pdf.worker.js’;
这两个文件包含了获取、解析和展示 PDF 文档的方法,但是解析和渲染 PDF 需要较长的时间,可能会阻塞其它 JS 代码的运行。
为解决该问题,pdf.js 依赖了 HTML5 引入的 Web Workers——通过从主线程中移除大量 CPU 操作(如解析和渲染)来提升性能。
PDF.js 的 API 都会返回一个 Promise,使得我们可以优雅的处理异步操作。
2️⃣使用
首先,我们需要在 HTML 中添加 <canvas> 元素以渲染 PDF:
<canvas id=”pdf-canvas”></canvas>
然后添加渲染 PDF 的 js 代码:
var url = ‘Helloworld.pdf’;

PDFJS.getDocument(url).then((pdf) => {
return pdf.getPage(1);
}).then((page) => {
// 设置展示比例
var scale = 1.5;
// 获取 pdf 尺寸
var viewport = page.getViewport(scale);
// 获取需要渲染的元素
var canvas = document.getElementById(‘pdf-canvas’);
var context = canvas.getContext(‘2d’);
canvas.height = viewport.height;
canvas.width = viewport.width;

var renderContext = {
canvasContext: context,
viewport: viewport
};

page.render(renderContext);
});
现在,PDF 已经成功渲染在界面上了。我们来分析一下使用到的函数:
getDocument():用于异步获取 PDf 文档,发送多个 Ajax 请求以块的形式下载文档。它返回一个 Promise,该 Promise 的成功回调传递一个对象,该对象包含 PDF 文档的信息,该回调中的代码将在完成 PDf 文档获取时执行。
getPage():用于获取 PDF 文档中的各个页面。
getViewport():针对提供的展示比例,返回 PDf 文档的页面尺寸。
render():渲染 PDF。
到这里,基本功能告一段落了。满心欢喜准备上线的时候,产品经理提出了另一个需求:文本复制。然鹅。。。翻了好几遍官方文档,也没有找到文本复制的方法,并且 stackoverflow 上有很多类似的问题。在不断的尝试下,我们发现了 Text-Layer。
使用 Text-Layers 渲染
PDF.js 支持在使用 Canvas 渲染的 PDF 页面上渲染文本图层。然而,这个功能需要用到额外的两个文件:text_layer_builder.js 和 text_layer_builder.css。我们可以在 GitHub 的 repo 中获取到。
如果是使用 npm,则需要做如下引用:
import {TextLayerBuilder} from ‘pdfjs-dist/web/pdf_viewer’;
import ‘pdfjs-dist/web/pdf_viewer.css’;
现在,我们开始实现文本复制功能。
首先,创建渲染需要用到 DOM 节点:
<div id=”container”></div>
div#container 为最外层节点,在该 div 中,我们会为 PDF 的每个页面创建自己的 div,在每个页面的 div 中,都会有 Canvas 元素。
接着,我们修改 JS 代码:
var container, pageDiv;

function getPDF(url) {
PDFJS.getDocument(url).then((pdf) => {
pdfDoc = pdf;
container = document.getElementById(‘container’);
for (var i = 1; i<= pdf.numPages; i++) {
renderPDF(i);
}
})
}

function renderPDF(num) {
pdf.getPage(num).then((page) => {
var scale = 1.5;
var viewport = page.getViewport(scale);
pageDiv = document.createElement(‘div’);
pageDiv.setAttribute(‘id’, ‘page-‘ + (page.pageIndex + 1));
pageDiv.setAttribute(‘style’, ‘position: relative’);
container.appendChild(pageDiv);
var canvas = document.createElement(‘canvas’);
pageDiv.appendChild(canvas);
var context = canvas.getContext(‘2d’);
canvas.height = viewport.height;
canvas.width = view.width;

var renderContext = {
canvasContext: context,
viewport: viewport
};

page.render(renderContext);
});
}
以上代码只是实现了多页渲染,接下来,开始渲染文本图层。我们需要将 page.render(renderContext) 修改为以下代码:
page.render(renderContext).then(() => {
return page.getTextContent();
}).then((textContent) => {
// 创建文本图层 div
const textLayerDiv = document.createElement(‘div’);
textLayerDiv.setAttribute(‘class’, ‘textLayer’);
// 将文本图层 div 添加至每页 pdf 的 div 中
pageDiv.appendChild(textLayerDiv);

// 创建新的 TextLayerBuilder 实例
var textLayer = new TextLayerBuilder({
textLayerDiv: textLayerDiv,
pageIndex: page.pageIndex,
viewport: viewport
});

textLayer.setTextContent(textContent);

textLayer.render();
});
我们依旧来讲解以下用到的几个关键函数:
page.render():该函数返回一个当 PDF 页面成功渲染到界面上时解析的 promise,我们可以使用成功回调来渲染文本图层。
page.getTextContent():该函数的成功回调会返回 PDF 页面上的文本片段。
TextLayerBuilder:该类的实例有两个重要的方法。setTextContent() 用于设置 page.getTextContent() 函数返回的文本片段;render() 用于渲染文本图层。
Bingo????!通过以上改造,文本复制功能就实现了。官方文档上可没有这个小技巧哦。
PDF.js 是一个很棒的工具,但无奈文档写的较为精简,需要开发人员不断探索 PDF.js 的强大功能。
如果这篇文章有帮助到您,记得点赞咯????!

退出移动版