最近項目中需要用到js庫來渲染pdf文件,調研後發現無論是reach-pdf.js或者是svelte-pdf.js都是在pdf.js基礎上做了些許精簡,反而功能還不如原始的pdf.js來得全面。但是原始的庫幾乎沒有像樣的代碼示例,而能搜索到的大多數代碼不少都是十幾年前的了,在這個過程中踩了不少坑,做 ...
最近項目中需要用到js庫來渲染pdf文件,調研後發現無論是reach-pdf.js或者是svelte-pdf.js都是在pdf.js基礎上做了些許精簡,反而功能還不如原始的pdf.js來得全面。但是原始的庫幾乎沒有像樣的代碼示例,而能搜索到的大多數代碼不少都是十幾年前的了,在這個過程中踩了不少坑,做個記錄,希望對看到的人有所幫助。
使用npm安裝pdfjs-dist庫(也可以直接下載源碼並引入)
npm install pdfjs-dist
導入庫
// 網上很多代碼都是import xxx from 'pdfjs-dist';
// 而xxx一般都是過期或者不存在的,直接把所有導出為pdfjslib即可
import * as pdfjslib from 'pdfjs-dist';
// 註意需要設置這個參數
pdfjslib.GlobalWorkerOptions.workerSrc = 'node_modules/pdfjs-dist/build/pdf.worker.js';
單頁渲染,多頁渲染在下麵代碼基礎上直接添加一個迴圈即可
let src = 'xxx.pdf';
let pageNum = 1;
let scale_ratio = 1.5;
async function renderPage() {
const pdf = await pdfjsLib.getDocument(src).promise;
const page = await pdf.getPage(pageNum);
const viewport = page.getViewport({ scale: scale_ratio });
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
canvas.height = viewport.height;
canvas.width = viewport.width;
const renderContext = {
canvasContext: context,
viewport: viewport
};
await page.render(renderContext);
}
註意渲染完的pdf只有圖片形式,使用開發者工具看網頁的結構只有canvas組件,想要實現文字的選擇和複製還需要在上面渲染一層文字層。
// 需要引入樣式文件,不然文字不會懸浮在cavas組件上
import 'pdfjs-dist/web/pdf_viewer.css';
async function renderFullPage(){
const pdf = await pdfjsLib.getDocument(src).promise;
const pdfContainer = document.createElement('div');
pdfContainer.style.setProperty('--scale-factor', scale_ratio);
for (let i=1; i<=pdf.numPages; i++){
const pageNumber = i;
const page = await pdf.getPage(pageNumber);
const viewport = page.getViewport({scale: scale_ratio});
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
canvas.height = viewport.height ;
canvas.width = viewport.width ;
const renderContext = {
canvasContext: context,
viewport: viewport
};
await page.render(renderContext);
// canvasWrapper 可加可不加
const canvasWrapper = document.createElement('div');
canvasWrapper.className = 'canvasWrapper';
canvasWrapper.appendChild(canvas);
const textContent = await page.getTextContent();
const textLayerDiv = document.createElement('div');
// 類名嚴格為:textLayer
textLayerDiv.className = `textLayer`;
pdfjsLib.renderTextLayer({
textContentSource: textContent,
container: textLayerDiv,
viewport: viewport,
textDivs: []
});
const pageDiv = document.createElement('div');
pageDiv.className = 'page';
// 需要設置 position: relative
// 否則全部文字可能都擠在第一頁
pageDiv.style = "position: relative; margin-bottom:10px";
pageDiv.appendChild(canvasWrapper);
pageDiv.appendChild(textLayerDiv);
pdfContainer.appendChild(pageDiv);
}
}
簡單來說就是在渲染完canvas代碼之後,再渲染出文字層。有幾個註意點:
- 需要在開頭引入樣式表,不然文字層會實際顯示在頁面中,不會懸浮不會透明;
- 需要在外面的組件中設置參數--scale-factor,用於保證圖片和文字的位置對應,否則調整了scale_ratio後圖片尺寸改變,但是文字層的大小還是不變;
- 文字層的類名需要嚴格設置為textLayer,從開頭引入的樣式表中可以看到;
- 包含canvas和文字層的父組件需要設置style為position: relative,否則多頁的文字都會渲染到第一頁中。