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

我肥来啦😁。看到 Redux教程突破3w的浏览量,小窃喜,很高兴本身的文章可以帮助到你们。

此次重返,依然带给你们一个小指南,也是最近工做中遇到的一个小case。javascript

前不久,产品经理提出要在界面上优雅地展现PDF文档,立即就有了两种实现方式:css

实现方式一
使用embed标记来使用浏览器自带的pdf工具。html

这种实现方式优缺点都很明显:
优势:自带“打印”,“搜索”,“翻页”等功能,强大且实现方便。
缺点:不一样浏览器的pdf工具样式不一,且没法知足个性化需求,好比:禁止打印,下载等。java

咱们的产品经理是挑剔的😒,因而...git

实现方式二
使用Mozilla的PDF.js,自定义展现PDF。github

下面咱们就细致讲述一下使用 PDF.js过程当中遇到的问题。主要包括:
  • 基础功能集成
  • 使用Text-Layers渲染

什么是PDF.JS

PDF.js是基于HTML5技术构建的,用于展现可移植文档格式的文件(PDF),它能够在现代浏览器中使用且无需安装任何第三方插件。web

基础功能集成

1️⃣引用npm

首先,引用PDF.js就遇到了问题,官网中提到经过CDN引用或者下载源码至本地。
而咱们并不想污染咱们的index.html而且但愿能够对每个引用的框架有统一的版本管理。因而,咱们搜寻到一个包:pdfjs-distcanvas

经过npm install pdfjs-dist,咱们引入了PDF.js。segmentfault

基础功能有两个必须引用的文件:

  • pdf.js
  • pdf.worker.js

若是使用CDN的方式,直接引用以下对应文件便可:

若是使用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.jstext_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的强大功能。

若是这篇文章有帮助到您,记得点赞咯👍!

相关文章
相关标签/搜索