身為新手工程師,在實作前端網頁設計與互動功能時,經常會需要處理 PDF 檔案的各種操作,像是網頁內嵌預覽、動態列印或資料匯出。若沒有適當的處理工具,這些常見需求往往會耗費大量開發時間。在 JavaScript PDF 的技術領域中,有一個由社群與大廠廣泛使用的開源 PDF 庫——PDF.js,可以讓你在網頁上輕鬆整合 PDF 相關功能,實現流暢的無外掛閱讀體驗!
⚡ 快速跳轉目錄:
PDF.js 是什麼?
PDF.js 是由 Mozilla 團隊打造的 HTML5 JavaScript 庫,旨在提供能夠在網頁環境中呈現和操作 PDF 檔案的能力。它的運作原理是利用 HTML5 的 Canvas 將 PDF 內容精準呈現在網頁上,並提供多種 API 可供開發者呼叫,實現例如非同步頁面載入、動態縮放、列印等基本操作。
使用 PDF.js 的最大好處就是可以完全在瀏覽器端處理 PDF,免去使用者安裝額外瀏覽器外掛程式的麻煩。對於現代網頁應用來說,無縫整合 PDF 瀏覽體驗將變得輕而易舉。
官方連結:https://mozilla.github.io/pdf.js/
官方 Demo:https://mozilla.github.io/pdf.js/web/viewer.html
怎麼使用 PDF.js?
1. 引入庫檔案
首先,你需要在 HTML 引入 PDF.js 提供的 JS 檔案,官網上有 CDN 和 npm 安裝兩種選擇。舉例來說,可以直接在 <head> 引入 CDN 版本:
<script src="https://mozilla.github.io/pdf.js/build/pdf.js"></script>
如果是在現代前端應用開發中使用 PDF.js,建議透過 npm 套件管理器安裝:
首先確認已安裝 Node.js 和 npm,然後在專案根目錄執行:
npm install pdfjs-dist
安裝完成後,即可在 JavaScript 檔案中進行引用與 Worker 設定:
import * as pdfjsLib from 'pdfjs-dist/legacy/build/pdf';
pdfjsLib.GlobalWorkerOptions.workerSrc = 'pdfjs-dist/build/pdf.worker.min.js';
2. 準備容器元素
接著在 HTML 中準備一個 <canvas> 容器,等一下 PDF 檔案的指定頁面內容就會被渲染在這個畫布上:
<canvas id="pdf-viewer"></canvas>
3. 撰寫 JavaScript 程式碼
最後就是撰寫 JavaScript 程式碼了。利用 PDF.js 提供的 API,從指定 PDF 檔案路徑開始載入並執行 Canvas 渲染:
const url = 'path/to/pdf.pdf';
pdfjsLib.getDocument(url).promise.then(doc => {
return doc.getPage(1);
}).then(page => {
const scale = 1.5;
const viewport = page.getViewport({ scale: scale });
const canvas = document.getElementById('pdf-viewer');
const context = canvas.getContext('2d');
canvas.height = viewport.height;
canvas.width = viewport.width;
const renderContext = {
canvasContext: context,
viewport: viewport
};
return page.render(renderContext).promise;
}).then(() => {
console.log('PDF 頁面渲染完成!');
}).catch(error => {
console.error('載入或渲染 PDF 時發生錯誤:', error);
});
上述範例完整示範瞭如何使用 PDF.js 庫載入 PDF 檔案的第一頁,並透過 Promise 鏈結確保非同步渲染流程正確執行,將內容呈現在畫布中。你可依實際業務需求調整縮放比例(scale)、頁數索引等參數。
4. Retina 螢幕高解析度與 Worker 跨域排錯
在實際將程式碼部署到生產環境時,開發者常會遇到兩個經典痛點:一是高畫素密度(Retina)螢幕上文字模糊,二是 Worker 載入失敗導致程式完全停擺。以下為對應的解決思路與修正寫法:
Retina 螢幕高解析度模糊修正
在高解析度螢幕(如 MacBook Retina 顯示器)上,直接賦值 viewport.height 與 viewport.width 會導致 Canvas 畫布以低畫素張開,渲染出的 PDF 文字與線條會出現鋸齒狀模糊。透過裝置像素比(window.devicePixelRatio)進行縮放修正,可讓 PDF 呈現極為清晰的向量字型效果:
const scale = 1.5;
const viewport = page.getViewport({ scale: scale });
const canvas = document.getElementById('pdf-viewer');
const context = canvas.getContext('2d');
// 取得裝置像素比,若無則預設為 1
const outputScale = window.devicePixelRatio || 1;
// 放大 Canvas 實際像素大小以適應 Retina 螢幕
canvas.width = Math.floor(viewport.width * outputScale);
canvas.height = Math.floor(viewport.height * outputScale);
// 透過 CSS 限制畫布在網頁上的顯示大小
canvas.style.width = Math.floor(viewport.width) + 'px';
canvas.style.height = Math.floor(viewport.height) + 'px';
// 縮放 Context 座標系確保渲染比例正確
const transform = outputScale !== 1 ? [outputScale, 0, 0, outputScale, 0, 0] : null;
const renderContext = {
canvasContext: context,
viewport: viewport,
transform: transform
};
page.render(renderContext);
Worker 載入失敗與 CORS 跨域排錯提示
PDF.js 為了避免繁重的解析運算阻塞網頁主執行緒(Main Thread),會透過 Web Worker 在背景執行緒解析 PDF。如果在實際打包部署時發生 PDF 無法載入或控制台報錯,請檢查以下兩點:
- Worker 檔案路徑與跨域(CORS)限制:若將
pdf.worker.min.js放置於外部 CDN,而該 CDN 未正確設定允許跨域存取(CORS Header),瀏覽器基於安全性考量會拒絕載入該 Worker。建議將 worker 檔案放置於同網域下,或確保 CDN 伺服器支援跨域回應。 - 現代前端打包工具(Webpack / Vite)設定:在使用 npm 套件時,若直接指向 node_modules 中的 worker 路徑,生產環境打包常會發生找不到檔案的 404 錯誤。建議透過靜態資源複製外掛,或是直接引入對應版本的 CDN 遠端 Worker 網址,確保路徑在編譯後依然正確有效。
除了基本渲染功能外,PDF.js 還提供了豐富的 API,可實現縮放、文字搜尋、列印、文字選取等多元操作。這些功能將大幅強化你的網頁應用,提供更佳的檔案整合體驗。
5. 實務開發常見踩坑與排錯防雷
在前端實務專案中,處理 PDF 檔案除了上述基礎渲染與 Retina 調整外,開發者還經常會遇到記憶體流失、中文字型亂碼或跨網域存取失敗等挑戰。以下整理三個最常遇到的真實踩坑情境與解法:
1. 記憶體流失與多頁切換時的 canvas 殘影(RenderingCancelledException)
- 情境說明:當使用者在網頁上點擊「上一頁/下一頁」按鈕快速切換 PDF 頁數時,若未妥善處理上一個尚未完成的非同步渲染任務(Render Task),畫布會同時疊印好幾頁的內容,或直接噴出
RenderingCancelledException: Rendering has been cancelled錯誤。 - 解決思路:在每次呼叫
page.render()之前,必須將上一次的renderTask實例保存起來,並在切換頁面或元件銷毀時主動呼叫.cancel():
let currentRenderTask = null;
function renderPage(pageNumber) {
pdfDoc.getPage(pageNumber).then(page => {
// 若有正在執行的渲染任務,先取消它以避免殘影
if (currentRenderTask) {
currentRenderTask.cancel();
}
const viewport = page.getViewport({ scale: 1.5 });
const canvas = document.getElementById('pdf-viewer');
const context = canvas.getContext('2d');
canvas.width = viewport.width;
canvas.height = viewport.height;
// 建立新的渲染任務並保存參照
currentRenderTask = page.render({
canvasContext: context,
viewport: viewport
});
currentRenderTask.promise.then(() => {
currentRenderTask = null; // 渲染完成後清空參照
}).catch(error => {
if (error.name === 'RenderingCancelledException') {
console.log('前一次的渲染任務已被安全取消');
} else {
console.error('渲染失敗:', error);
}
});
});
}
2. 中文字型渲染變成亂碼、方框(豆腐字)或無法選取
- 情境說明:部分由公司內部系統或特定中文軟體輸出的 PDF 檔案,若其內嵌字型未被 PDF.js 標準支援,或者在 Canvas 模式下預設無法選取文字,使用者在前端會看到整片空白或亂碼方框。
- 解決思路:
- 字型遺失處理:確保在初始化
getDocument時,有正確引入官方的 CMap 檔案路徑(cMapUrl與cMapPacked),否則特殊中文語系字型會無法正確解析:const loadingTask = pdfjsLib.getDocument({ url: 'path/to/pdf.pdf', cMapUrl: 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/{VERSION}/cmaps/', cMapPacked: true, }); - 文字選取需求:若業務需求要求使用者必須能在畫布上用滑鼠選取、複製 PDF 內的文字,單純靠
<canvas>是辦不到的。必須搭配 PDF.js 提供的<div id="text-layer">容器與TextLayerBuilder,在 Canvas 上方疊加一層透明的 DOM 文字選取層。
- 字型遺失處理:確保在初始化
3. 跨網域(CORS)載入 PDF 檔案導致 Missing PDF 錯誤
- 情境說明:前端網頁部署在網域 A,而 PDF 檔案存放在 AWS S3、GCP Storage 或另一台檔案伺服器網域 B。直接將
url傳入pdfjsLib.getDocument(url)時,瀏覽器會直接攔截並噴出 CORS 錯誤,導致 PDF.js 誤判為檔案遺失(Missing PDF)。 - 解決思路:
- 確保檔案伺服器(如 S3 Bucket 的 CORS 設定)有正確開放
Access-Control-Allow-Origin: *(或指定前端網域)。 - 若無法修改遠端伺服器的 Header,則必須透過後端(Node.js / Go / Python)寫一支 Proxy API,由後端向遠端抓取 PDF 檔案後,再將資料流(Stream)回傳給前端給 PDF.js 讀取。
- 確保檔案伺服器(如 S3 Bucket 的 CORS 設定)有正確開放
總結
在網頁前端工程中,處理 PDF 檔案是相當常見的實際需求。有了 PDF.js 這個強大的 JavaScript PDF 工具,開發者便能擺脫對第三方外掛的依賴,在瀏覽器端完成高效能的渲染與互動。對於新手來說,熟悉這個工具的非同步載入機制、Canvas 繪製流程、Retina 螢幕的高解析度調整以及常見的踩坑排錯技巧,將是提升前端實作能力的一大利器。現在就動手測試看看,為你的專案打造流暢的 PDF 檢視功能吧!
發佈留言