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的方式,直接引用如下對應文件即可:

如果使用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的強大功能。

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM