客戶端 PDF 生成的隱藏複雜性

產生 PDF 常被視為一項微不足道的任務——簡單的「列印成 PDF」或呼叫一個函式庫。然而,當需求轉向在客戶端完全生成這些文件,同時保持高保真度、可選取文字的內容時,開發者便踏入了一個令人驚訝的複雜世界。構建 SDocs 的過程凸顯了網頁開發中的一個根本張力:我們為螢幕渲染內容(HTML/CSS)與為固定版面格式(如 PDF)定義文件之間的差距。

文字可選取性的挑戰

使用者最常見的挫折之一是「死」PDF——外觀正確卻像圖像一樣的文件,文字無法被選取、搜尋或複製。要在客戶端生成的 PDF 中實現真正的文字可選取性,僅僅把字元放在頁面上是不夠的;必須精確地將字形映射到座標,並加入 PDF 檢視器能解讀的文字層。

正如社群開發者所指出的,這是網頁開發的「潘多拉盒子」之一,類似於製作跨客戶端電子郵件範本的惡夢。困難在於 PDF 並非響應式;它們是固定版面的文件。將網頁布局的流動特性轉換為靜態座標系統,同時保留視覺字形與底層字元碼之間的關係,是一項非平凡的工程挑戰。

「PDF 地獄」:為何如此困難

除了最初的生成之外,PDF 格式還帶來了多個系統性問題,使其成為現代資料交換中脆弱的工具:

1. 複製貼上噩夢

即使文字可選取,體驗仍常常受損。由於 PDF 著重於視覺定位而非語意結構,複製文字時常會出現句子斷裂、空格遺失,或字元順序錯亂的情況。

"你展示了僅僅為了讓文字可選取而需要的瘋狂工作,包括字形映射、版面、連結、程式碼區塊、渲染樣式等。但一旦從該 PDF 複製,絕大多數檢視器仍只會顯示原始文字,而且往往是破碎的原始文字……"

2. 版面僵硬

軟體工程師常低估 GUI 與版面引擎的複雜度。例如,渲染跨多頁的表格需要複雜的邏輯來處理表頭、頁腳與列分割——這些問題 paged.js 專案嘗試透過將開放網路標準套用於分頁媒體來解決。

3. 編輯差距

編輯現有的 PDF 向來非常困難。簡單的任務,例如勾選核取方塊,可能會靜默失敗,或需要複雜的腳本來操作底層 PDF 結構,因為視覺呈現常常與實際資料層不一致。

替代方法與解決方案

鑑於原生 PDF 生成的複雜性,已出現多種替代策略:

  • WASM-based Compilers: 有些開發者建議使用像 Typst 這樣的工具,將其編譯成 WebAssembly (WASM) 以在瀏覽器本地執行,提供更穩健的管線來產生高品質、可選取的 PDF。
  • Intermediate Formats: 將 Markdown 轉換為穩定的中間格式,如 LaTeX,或使用 Pandoc,可以提供更可靠的渲染管線,儘管這些通常會帶來較重的相依性。
  • HTML-to-PDF Bridges: 能將富文字渲染到 2D canvas 上下文,然後將該上下文代理給 PDF 函式庫的程式庫,可簡化維持視覺保真度的流程。
  • The "Vibecoding" Approach: 有些使用者透過完全繞過 PDF 於內部工具,將 PDF 轉換為 HTML 包,並使用腳本注入變數資料,成功地將 PDF 視為視覺模板而非文件格式。

結論:PDF 是正確的工具嗎?

為了產生完美 PDF 的奮鬥常會引發更廣泛的哲學問題:我們是否應該在以數位為先的內容中使用 PDF?雖然 PDF 在列印與法律存檔上不可或缺,但它本質上不適合現代網路的響應式、可搜尋與可存取特性。對於書籍、學術論文與目錄等,HTML 與 EPUB 等標準提供了遠超的彈性與可存取性,前提是閱讀器的生態系統持續演進。

Sources