PDF.js 是基於 HTML5 解析與渲染 PDF 的 JavaScript 庫,由 Mozilla 主導開源。
本文旨在介紹 PDF.js 於 Electron 裡如何開始使用,實際嘗試了用其 API 或嵌入 HTML 的幾種方式。
從零準備專案
專案採用 Electron React Antd PDF.js 來實現,以下是從零準備專案的過程。
Electron React
這裡用 electron-react-boilerplate 模板開始 Electron React 專案。
# 獲取模板
git clone --depth=1 \
https://github.com/electron-react-boilerplate/electron-react-boilerplate \
electron-pdf-viewer
cd electron-pdf-viewer
# 設定倉庫
git remote set-url origin git@github.com:ikuokuo/electron-pdf-viewer.git
# 如果想合併成一個初始提交
# https://stackoverflow.com/a/23486788
git config --global alias.squash-all '!f(){ git reset $(git commit-tree HEAD^{tree} -m "${1:-A new start}");};f'
git squash-all "first commit"
git push -u origin main
# 依賴
npm install
# 執行
npm start
# 打包
npm run package
準備編輯器(VSCode):
code --install-extension dbaeumer.vscode-eslint
code --install-extension dzannotti.vscode-babel-coloring
code --install-extension EditorConfig.EditorConfig
其他編輯器,可見 Editor Configuration。
Ant Design
新增 antd
依賴:
npm install antd
之後,就可以快速佈局頁面了,如下:
PDF.js
新增 pdfjs
依賴:
npm install pdfjs-dist
npm install -D worker-loader
此外,準備 PDF 樣例進 static/
,簡單用 Python 提供 HTTP 訪問:
npm run static
用於開發執行,正式執行可用 file://
地址。
PDF.js 渲染
使用 API
用 API 渲染頁面,可見官方 Examples。
1. 匯入包
import * as pdfjsLib from 'pdfjs-dist/webpack';
2. 渲染頁面
(async () => {
// 獲取 doc
const loadingTask = pdfjsLib.getDocument(url);
const pdf = await loadingTask.promise;
console.log(`PDF loaded, n=${pdf.numPages}`);
setNumPages(pdf.numPages);
// 獲取 page
const page = await pdf.getPage(1);
// 獲取 canvas
const scale = 1.5;
const viewport = page.getViewport({ scale });
// Support HiDPI-screens.
const outputScale = window.devicePixelRatio || 1;
const canvas = canvasRef.current;
if (canvas == null) return;
const context = canvas.getContext('2d');
canvas.width = Math.floor(viewport.width * outputScale);
canvas.height = Math.floor(viewport.height * outputScale);
canvas.style.width = `${Math.floor(viewport.width)}px`;
canvas.style.height = `${Math.floor(viewport.height)}px`;
const transform =
outputScale !== 1 ? [outputScale, 0, 0, outputScale, 0, 0] : null;
// 渲染 page
const renderContext = {
canvasContext: context,
transform,
viewport,
};
await page.render(renderContext);
console.log('Page rendered!');
})();
完整程式碼,見 Pdfjs/index.tsx。效果如下:
使用 Viewer API
用 Viewer API 渲染,其在 pdfjs-dist/web/pdf_viewer
路徑下。
1. 匯入包
import * as pdfjsLib from 'pdfjs-dist/webpack';
import { PDFViewer, EventBus } from 'pdfjs-dist/web/pdf_viewer';
import 'pdfjs-dist/web/pdf_viewer.css';
2. 佈局頁面
<div className="viewer">
<div>url={url}</div>
<div>numPages={numPages}</div>
<div ref={hrRef} />
<div ref={containerRef} className="container">
<div className="pdfViewer" />
</div>
</div>
要求 absolute
定位:
.viewer {
position: relative;
.container {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
overflow: scroll;
}
}
3. 渲染 PDF
const container = containerRef.current;
if (container == null) return;
if (hrRef.current) {
container.style.top = `${hrRef.current.offsetTop}px`;
}
// 監聽事件,必須傳參 PDFViewer 為例項
const eventBus = new EventBus(null);
eventBus.on('pagesinit', () => {
console.log('pagesinit');
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any
eventBus.on('pagesloaded', (e: any) => {
console.log('pagesloaded');
console.log(e);
setNumPages(e.pagesCount);
});
eventBus.on('pagerendered', () => {
console.log('pagerendered');
});
// 建立 PDFViewer
const pdfViewer = new PDFViewer({
container,
eventBus,
linkService: null,
renderer: 'canvas',
l10n: null,
});
// 匯入 Document
(async () => {
const loadingTask = pdfjsLib.getDocument(url);
const pdf = await loadingTask.promise;
pdfViewer.setDocument(pdf);
})();
完整程式碼,見 PdfViewer/index.tsx。效果如下:
使用 Viewer HTML
PDF.js 提供了線上演示的 viewer.html
,不過 pdfjs-dist
裡沒有,要自己編譯其原始碼。
編譯結果已放進 static/pdfjs/
,可用 Electron Window 開啟 web/viewer.html?file=x.pdf
或用 iframe
嵌入。
如果自己重新編譯,過程如下:
git clone -b master --depth=1 https://github.com/mozilla/pdf.js.git
cd pdf.js
# 安裝依賴
npm install -g gulp-cli
npm install
# 開發執行
gulp server
# http://localhost:8888/web/viewer.html
# 編譯釋出
gulp generic
# build/generic/
iframe
嵌入的話,也是開啟 web/viewer.html?file=x.pdf
:
<div className="viewerHTML">
<div>pdfUrl={pdfUrl}</div>
<div>pdfWebViewerUrl={pdfWebViewerUrl}</div>
<iframe
className="pdfViewer"
title="PdfViewerHTML"
src={`${pdfWebViewerUrl}?file=${pdfUrl}`}
/>
</div>
.viewerHTML {
.pdfViewer {
border: none;
width: 100%;
height: 100%;
}
}
這裡開啟的 npm run static
提供的 HTTP 地址,效果如下:
iframe
要開啟本地 HTML 試了下沒成,如果想在 Electron + React 下這麼用,還要研究下怎麼弄。
最後
PDF.js 可以說是 Web 渲染 PDF 的不二選擇,很多 PDF Web Viewer 庫都是基於它實現的。
GoCoding 個人實踐的經驗分享,可關注公眾號!