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的強大功能。

若是這篇文章有幫助到您,記得點贊咯👍!

相關文章
相關標籤/搜索