網頁轉 PDF 如何實作?談談列印、整頁截圖與瀏覽器自動化
從瀏覽器列印、HTML 轉 PDF 和整頁截圖談起,比較常見實作方式,說明 Webpage to PDF 的分段截圖、PDF 產生流程,以及擴充功能 debugger 提示的原因。

網頁轉 PDF 理論上很簡單:開啟網址,等頁面載入完成,儲存一下就好了。瀏覽器本身也有列印功能,理論上只要呼叫現成的介面,應該沒什麼難度。
但是這裡面可能有很多細節上的問題:印出來的頁面為什麼和螢幕上不一樣?截圖明明有文字,放進 PDF 後為什麼不能複製?網頁很長的時候,為什麼後半段會不見?這些情況不一定會一起出現,不同的情境下,可能會有不同的結果。
網頁轉 PDF 究竟有多少種實作方式?下面先簡單介紹市面上常見方法的基本原理,再介紹 Webpage to PDF 目前的處理流程。至於擴充功能為什麼會在瀏覽器頂端顯示 debugger 提示,以及為什麼不建議直接關閉它,後面也會說明。
1. 直接使用瀏覽器列印
這是最容易想到的方法。開啟網頁,按下列印快捷鍵,將目的地改成「另存為 PDF」,瀏覽器就會產生檔案。一般文字通常可以選取,連結也可能保留下來,偶爾儲存一篇文章,用這個方法就夠了。

問題在於,網站的樣式是否支援列印模式。有些網站會為列印模式套用特殊樣式,導致印出來的頁面和螢幕上不一樣。例如隱藏導覽列和廣告,把深色背景換成白色,或重新調整正文寬度。這些修改可能比較適合閱讀,但列印後的版面和內容,也可能與原本的頁面不同。
還有一種情況容易誤判:網站的主要樣式只對 screen 生效,切換為 print 後,大部分樣式都不再符合條件,看起來像 CSS 沒載入。其實檔案可能已經下載了,只是目前不套用這些規則。詳細說明可以參考 MDN 的列印樣式指南。
當然,也可以讓 PDF 輸出使用 screen 樣式,不過紙張寬度、邊界和分頁仍然存在。連續的網頁放到 A4 紙上,總要決定在哪裡換頁,不能指望換個媒體類型就解決所有排版問題。
2. HTML 轉圖片,再將圖片寫入 PDF
這類方法經常會用到 html2canvas。名稱裡帶著 canvas,很容易讓人以為它就是替瀏覽器截個圖,實際上並不是。
它讀取 DOM 和樣式,再依照自己支援的繪製規則重建畫面。換句話說,瀏覽器已經畫過一次了,這個函式庫還要再畫一次。自己控制的元件和簡單報表比較容易處理,換成任意網站,CSS 支援範圍、跨來源圖片和 Canvas 尺寸限制都需要考慮。官方文件也說明了它和真正截圖的差異。
另一種做法是直接讓瀏覽器截圖,再將截圖寫入 PDF。這樣取得的是瀏覽器實際繪製的畫面,要保留網頁外觀會比較直接。不過這兩種方法產生的都是圖片型 PDF,其中的文字無法選取或複製,這一點要注意。
目前 Webpage to PDF 的 Quick 模式,都是透過瀏覽器截圖,再將截圖寫入 PDF。這種方法能保留網頁實際呈現的外觀,並連續顯示整個頁面。不過長網頁需要分段擷取,否則單張圖片的尺寸和記憶體用量都會成為問題。
3. 瀏覽器自動化和專用排版引擎
如果要做線上網址轉 PDF 服務,不能每次都讓人開啟列印視窗。因此可以透過 Puppeteer 這類工具控制瀏覽器,設定頁面尺寸、等待載入,再呼叫截圖或 PDF 介面。Webpage to PDF 的雲端轉換也需要這樣的瀏覽器處理過程。Puppeteer 的 PDF 介面本質上仍然使用瀏覽器的列印功能。
自動化解決了重複操作的問題,但什麼時候算載入完成,還是需要自己決定。有些網站一直傳送統計請求,有些圖片要捲動到附近才載入,還有些彈出視窗要過一陣子才出現。一直等,工作可能永遠不結束;完全不等,內容又可能不完整。
另外也有專門的 HTML 轉 PDF 或文件排版引擎。固定範本的發票、帳單、報表很適合這類方法,因為輸入內容和樣式都可以控制。不過不同引擎對 CSS 和 JavaScript 的支援有差異,不能直接認為它能完整執行任意網站。
前面幾種方法,大致可以這樣比較:
| 方法 | 優點 | 需要注意的地方 | 適用情境 |
|---|---|---|---|
| 瀏覽器列印 | 不需額外工具,通常保留文字和連結 | 列印樣式和分頁可能改變版面 | 偶爾儲存文章、列印網頁 |
| DOM 轉 Canvas | 可在網頁程式碼中執行,方便匯出自己的元件 | 需要重建樣式,受跨來源資源和畫布尺寸限制 | 簡單報表、固定元件 |
| 瀏覽器整頁截圖轉 PDF | 保留實際繪製的外觀,可以連續顯示 | 文字是圖片,長網頁需要分段擷取 | 設計參考、網頁存檔 |
| 自動化瀏覽器輸出 PDF | 可重複執行,可以控制列印參數 | 需要處理載入、登入狀態和瀏覽器資源釋放 | 線上網址轉 PDF 服務 |
| 專用排版引擎 | 固定範本和分頁容易控制 | 不一定支援任意網站的 CSS、JavaScript | 發票、帳單、範本文件 |
這些方法可以搭配使用。自動化瀏覽器既可以列印,也可以截圖;瀏覽器擴充功能同樣能做這兩件事。最後產生的是圖片 PDF 還是文字文件,要看實際使用哪個流程。
4. Webpage to PDF 的核心
目前 Webpage to PDF 有雲端瀏覽器和擴充功能本機瀏覽器兩種網頁處理方式。
雲端收到網址後,由網站後端驗證請求、協調工作,再交給瀏覽器服務開啟頁面。這個瀏覽器不在你的電腦上,也不會自動繼承你目前的登入狀態。所以同一個網址,你這裡看得到內容,雲端卻顯示登入頁面,並不奇怪。
擴充功能本機轉換使用你選取的分頁,在權限和頁面類型允許的範圍內處理目前的網頁。頁面已經登入、已經展開某些內容,這些狀態就可以作為轉換的起點,不需要遠端瀏覽器重新開啟一次。本機繪製不需要將頁面交給雲端瀏覽器,不過帳戶等功能仍會各自發出網路請求,不能把它理解成整個擴充功能完全離線。
擴充功能透過 chrome.debugger 傳送 Chrome DevTools Protocol,也就是 CDP 命令,控制可視區域(viewport)、screen 或 print 模式、截圖和 PDF 輸出。頂端的 debugger 提示就是這樣來的。

5. Quick 模式為什麼要分段截圖?
Quick 的目標是盡量保留整個網頁的外觀,最後產生一頁連續的 PDF。短網頁截一張圖即可,但網頁夠長時,單張圖片的尺寸和記憶體用量都會成為問題。繼續強行截圖,可能出現缺漏、重複或空白,不能只看介面有沒有回報成功。
目前的做法是先測量文件,再依區域擷取多張圖片。每張圖片除了內容,還帶著它在頁面中的位置和尺寸。產生 PDF 時,直接將這些片段放到對應位置,不再合成一張超長圖片,否則繞了一圈,又回到了原來的限制。
例如,假設一個網頁分成三段,第二段在原網頁中從某個縱座標開始,放進 PDF 後也應該接在第一段後面。分成三張圖片,不代表要產生三頁 PDF。
PDF 頁面本身也要考慮尺寸。目前實作採用 14,400 點的頁面尺寸上限;寬或高超過這個上限時,整頁等比例縮小,所有圖片的位置和尺寸一起縮放。只縮高度、不縮寬度,頁面就會被壓扁。這裡說的是目前實作選擇的相容性上限,不是所有 PDF 規範和閱讀器都有相同限制。
分段擷取也不是萬能的。網站如果使用虛擬清單,移除了畫面外的 DOM,或截圖期間內容持續變動,仍然可能影響結果。它解決的是單張截圖過大的問題,不是讓網頁和記憶體都變得無限大。
6. Custom 和 Visual 如何產生 PDF?
Custom 同樣會截圖,但截圖只是預覽。最終檔案由瀏覽器列印產生:擴充功能使用 Page.printToPDF,雲端透過 Puppeteer 呼叫瀏覽器的 PDF 輸出,之後再處理支援的文件選項。
這點很容易混淆。Custom 的長網頁預覽可以拆成多張圖片,不代表最終 PDF 也要改成圖片拼接。一般網頁文字通常可以保留為文字,紙張和分頁交給瀏覽器處理;原本就在圖片或 Canvas 裡的字,當然不會自動變成可以複製的文字。
Visual 則在匯出前多了編輯過程。先套用轉換需要的 viewport 和呈現模式,再隱藏節點、修改樣式,最後將編輯後的網頁輸出為 PDF。本機擴充功能直接編輯實際頁面,網站編輯器則操作遠端網頁,透過截圖和 DOM 資訊顯示結果。
網站上的預覽即使由多張圖片拼接,DOM 座標仍然屬於整個網頁。點擊選取元素時,要將滑鼠位置配合容器的縮放和捲動換算回頁面座標,不能到了第二張圖片就把縱座標重新從零開始計算。
擴充功能還支援匯出選取的節點。這裡會將編輯後的選取內容當作獨立文件的根來準備,而不只是從截圖裡裁切一塊。脫離原來的父元素後,部分樣式和寬度可能改變,所以這種結果需要當作獨立頁面檢查。
7. 從開啟網頁到下載 PDF 檔案,中間執行了哪些步驟?
前面講的是原理,實際執行還要把載入和還原串起來。大致流程如下:
- 開啟或取得網頁。雲端導覽和資源等待共用逾時時間:頁面沒有開啟就回報錯誤;頁面已開啟,只是資源遲遲未穩定下來,達到時間上限後可以繼續。使用者設定的額外等待時間另計。
- 套用頁面設定。包括 viewport、呈現模式,以及設定要求的配色、動畫、等待、延遲內容預先載入和頁面清理。清理按照規則辨識,無法保證涵蓋所有網站的彈出視窗。
- 擷取預覽。測量處理後的網頁,傳回帶座標的圖片片段。Quick 也用這些圖片產生最終檔案。
- 產生來源 PDF。Custom 和 Visual 呼叫瀏覽器列印,取得後續處理需要的 PDF。
- 還原和釋放。擴充功能嘗試還原暫時修改並中斷偵錯連線,雲端則釋放遠端瀏覽器資源。
- 完成 PDF 處理。Quick 將圖片放在同一頁,其他模式處理來源 PDF 的相關設定,再顯示結果供下載。
如果中途出錯,正常流程會停止,並顯示錯誤提示。內部則會嘗試清理,不能因為截圖失敗,就把改過的顯示設定留給使用者。異常清理和正常流程是兩回事,不能清理成功了,就把後面尚未執行的步驟也顯示為完成。
8. 為什麼不要直接關閉頂端的 debugger 提示?
使用擴充功能本機轉換時,瀏覽器頂端會出現正在偵錯的提示。這是 Chrome 告知你,擴充功能正在透過偵錯連線操作目前的分頁,不需要你再開啟開發人員工具。介面說明可以看 Chrome debugger 文件。
問題出在橫幅上的 Cancel。點擊它,瀏覽器會直接中斷連線。如果此時正在截圖或修改顯示參數,命令可能執行到一半就被打斷,擴充功能也就無法按照原來的順序完成還原。
測試中確實出現過這種情況:截圖中斷後,頁面尺寸異常,或捲軸消失,重新整理同一個分頁也沒有恢復。至於是否每次都會發生,還與瀏覽器版本和中斷時機有關,不能簡單地說按取消就一定會出問題。
如果需要停止轉換,請使用側邊欄裡的「取消」,讓擴充功能有機會停止工作並執行清理。瀏覽器還在處理時,先保留頂端提示。加上這個提醒不表示底層問題已經完全修好,也不能保證所有異常都能自動還原。
如果已經遇到顯示異常,可以在擴充功能出現「Restore page display(還原頁面顯示)」時點擊還原。仍然無效的話,先在新分頁開啟同一個網址,確認顯示正常後再關閉舊分頁。新分頁有自己的顯示狀態,不過舊頁面未儲存的表單輸入不會跟過去,這一點要注意。
只是要將網頁儲存為 PDF,不必把這些介面都研究一遍。保留原始外觀用 Quick,需要紙張排版和文字選取用 Custom,要先修改內容再用 Visual。如果結果不符合預期,先分清楚是網頁沒有載入完整、截圖出了問題,還是列印樣式改變了版面,再調整對應設定,會比反覆按轉換更有用。