🔧 阿川の電商水電行
Shopify 顧問、維護與客製化
💡
小任務 / 單次支援方案
單次處理 Shopify 修正/微調
⭐️
維護方案
每月 Shopify 技術支援 + 小修改 + 諮詢
🚀
專案建置
Shopify 功能導入、培訓 + 分階段交付

前端一行程式碼生成數千頁PDF,dompdf.js新增分頁功能

前端生成 PDF 不清晰?文字無法搜尋選中編輯?體積太大?分頁切割不精準?生成頁數太少?dompdf.js V1.1.0 版本更新後,這些都不再是問題,只需要一行程式碼,就可以將 html 頁面生成數千頁 PDF 檔案,這可能是前端首個實現這一功能的 js 函式庫。

dompdf.js 是作者開源的前端 PDF 生成庫。可以純前端將 html 生成功能非圖片式的,可編輯,小體積的 PDF 檔案。上一篇文章有做相關介紹。之前的版本只能生成單頁 PDF,很多開發者反饋有生成多頁 PDF 的需求,作者花了點時間在 V1.1.0 版本新增了分頁功能,可以將 html 頁面生成功能多頁 PDF 檔案

程式碼示例

import dompdf from "dompdf.js";

dompdf(document.querySelector("#capture"), { pagination: true }).then(function (blob) {
  //檔案操作
});

實現效果如下(數千頁PDF):

總頁數.png

當然,生成數千頁的 PDF 是非常極端的情況才能達成,決定能生成多少頁 PDF 的,要看檔案大小,如果是純文字 PDF,就能生成更多的頁數,反之如果 PDF 中有圖片,生成的頁數就會少

1. 在線體驗

dompdfjs.lisky.com.cn

2. Git 倉庫地址 (歡迎 Star⭐⭐⭐)

github.com/lmn1919/dom…

gitee.com/liu-facai/d…

3. 版本新功能特性

1. 分頁功能-精準的頁面分割

圖片分頁效果

圖片分割效果.png

左右佈局分頁效果

左右佈局分割.png

文字分頁效果

文本分割效果.png

複雜表格分頁效果

複雜表格分割.png

2. 自訂頁眉頁腳

可以設定頁眉頁腳的高度、內容、字體大小、字體顏色等。

image.png

3. PDF 密碼、操作權限設定,PDF 壓縮

4. 分頁實現原理

dompdf.js 是如何將 html 頁面生成功能多頁 PDF 檔案的?

  1. 先將 html 解析為 DOM 樹。DOM 樹主要包含 DOM 元素的位置信息(bounds: width|height|left|top)、樣式數據、文本節點數據等。大致的結構為:

    {
     bounds: {height: 1303.6875, left: 8, top: -298.5625, width: 1273},
     elements: [
       {
         bounds: {left: 8, top: -298.5625, width: 1273, height: 1303.6875},
         elements: [
           {
             bounds: {left: 8, top: -298.5625, width: 1273, height: 1303.6875},
             elements: [
               {styles: CSSParsedDeclaration, textNodes: Array(1), elements: Array(0), bounds: Bounds, flags: 0},
               {styles: CSSParsedDeclaration, textNodes: Array(1), elements: Array(0), bounds: Bounds, flags: 0},
               ...
             ],
             flags: 0,
             styles: {backgroundClip: Array(1), backgroundColor: 0, backgroundImage: Array(0), backgroundOrigin: Array(1), backgroundPosition: Array(1), …},
             textNodes: []
           }
         ],
         flags: 0,
         styles: CSSParsedDeclaration {backgroundClip: Array(1), backgroundColor: 0, backgroundImage: Array(0), backgroundOrigin: Array(1), backgroundPosition: Array(1), …},
         textNodes: []
       }
     ],
     flags: 4,
     styles: CSSParsedDeclaration {backgroundClip: Array(1), backgroundColor: 0, backgroundImage: Array(0), backgroundOrigin: Array(1), backgroundPosition: Array(1), …},
     textNodes: []
    }
  2. 迴圈 DOM 樹的 elements 以及 textNodes,通過 bounds.top+bounds.height 判斷是否需要分頁,將 DOM 樹按照頁面分割成多個分頁 DOM 樹,並重新計算 elements 和 textNodes 的 bounds。比如一個 textNodes 的位置信息是這樣的:

    {
     bounds: {left: 8, top: 1115, width: 850, height: 24},
     text: "這是一個文本節點"
    }

    如果我們要按照 a4 紙張的高度(1123px)來分頁,那麼當文本節點的 top 加上 height 大於 1123px 時,而 top 的值 1115px 小於 1123px,那麼這個文本節點就會分割到第二頁。並且 top 值會重置為 0,後續項的 top 值也會減少相應的值,確保元素位置的準確性。

  3. 循環 DOM 樹分頁數組,調用 jspdf.addPage() 方法添加新頁面,並繪製頁眉頁腳。根據不同的元素類型,以及元素的位置信息,調用不同的 jspdf 方法生成 pdf。比如繪製文字:

    jspdf.text("這是一個文本節點", 1115, 8);

    繪製圖片:

    jspdf.image(img, x, y, width, height);

5. 使用文檔

安裝:

npm install dompdf.js --save

CDN 引入:

<script src="https://cdn.jsdelivr.net/npm/dompdf.js@latest/dist/dompdf.js"></script>

基礎用法

import dompdf from "dompdf.js";

dompdf(document.querySelector("#capture"), options)
  .then((blob) => {
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url;
    a.download = "example.pdf";
    document.body.appendChild(a);
    a.click();
  })
  .catch((err) => {
    console.error(err);
  });

PDF 分頁渲染

預設情況下,dompdf 會將整個文檔渲染到單頁中。

您可以通過設定 pagination 選項為 true 來開啟分頁渲染。通過 pageConfig 欄位自訂頁眉頁腳的尺寸、內容、字體顏色/大小、位置等信息。

非常重要!!!!非常重要!!!!非常重要!!!!非常重要!!!!非常重要!!!!

需要將要生成PDF的dom節點設定為對應的頁面寬度(px),比如 a4 要設定寬度為794px,這裡是頁面尺寸對照表,請點擊查看

<div id="capture" style="width:794px"></div>
import dompdf from "dompdf.js";

dompdf(document.querySelector("#capture"), {
  pagination: true,
  format: "a4",
  pageConfig: {
    header: {
      content: "這是頁眉",
      height: 50,
      contentColor: "#333333",
      contentFontSize: 12,
      contentPosition: "center",
      padding: [0, 0, 0, 0],
    },
    footer: {
      content: "第${currentPage}頁/共${totalPages}頁",
      height: 50,
      contentColor: "#333333",
      contentFontSize: 12,
      contentPosition: "center",
      padding: [0, 0, 0, 0],
    },
  },
})
  .then((blob) => {
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url;
    a.download = "example.pdf";
    document.body.appendChild(a);
    a.click();
  })
  .catch((err) => {
    console.error(err);
  });
更精準的分頁控制-divisionDisable 屬性

如果您不希望某個容器在分頁時被拆分時,為該元素添加 divisionDisable 屬性,跨頁時它會整體移至下一頁。

<img src="" alt="PDF Generation Example" crossorigin="anonymous" divisionDisable />

右邊為使用 divisionDisable 屬性的效果

不分割的效果.png

options 參數

參數名 必傳 預設值 型別 說明
useCORS false boolean 允許跨域資源(需服務端 CORS 配置)
backgroundColor 自動解析/白色 string | null 覆蓋頁面背景色;傳 null 生成透明背景
fontConfig - object 非英文字體配置,見下表
encryption 空配置 object PDF 加密配置,屬性 userPassword 用於給定權限列表下使用者的密碼;屬性 ownerPassword 需要設定 userPassword 和 ownerPassword 以進行正確的身份驗證;屬性 userPermissions 用於指定使用者權限,可選值為 ['print', 'modify', 'copy', 'annot-forms']
precision 16 number 元素位置的精度
compress false boolean 是否壓縮 PDF
putOnlyUsedFonts false boolean 僅將實際使用的字體嵌入 PDF
pagination false boolean 開啟分頁渲染
format 'a4' string 頁面規格,支持 a0–a10b0–b10c0–c10letter
pageConfig 參見下表 object 頁眉頁腳配置
pageConfig 欄位:
參數名 預設值 型別 說明
header 參見下表 pageConfigOptions object 頁眉設定
footer 參見下表 pageConfigOptions object 頁腳設定
pageConfigOptions 欄位:
參數名 預設值 型別 說明
content 頁眉預設值為空,頁腳預設值為${currentPage}/${totalPages} string 文本內容,支持 ${currentPage}${totalPages}${currentPage}為當前頁碼,${totalPages}為總頁碼
height 50 number 區域高度(px)
contentPosition 'center' string | [number, number] 文本位置枚舉 centercenterLeftcenterRightcenterTopcenterBottomleftTopleftBottomrightToprightBottom或座標 [x,y]
contentColor '#333333' string 文本顏色
contentFontSize 16 number 文本字號(px)
padding [0,24,0,24] [number, number, number, number] 上/右/下/左內邊距(px)
字體配置(fontConfig)欄位:
欄位 必傳 預設值 型別 說明
fontFamily 是(啟用自訂字體時) '' string 字體家族名(與注入的 .ttf 同名)
fontBase64 是(啟用自訂字體時) '' string .ttf 的 Base64 字串內容
亂碼問題-字體導入支持

由於 jspdf 只支持英文,所以其他語言會出現亂碼的問題,需要導入對應的字體檔案來解決,如果需要自訂字體,在這裡將字體 tff 檔案轉化成 base64 格式的 js 檔案,中文字體推薦使用思源黑體,體積較小。
在程式碼中引入該檔案即可。

<script type="text/javascript" src="./SourceHanSansSC-Normal-Min-normal.js"></script>
dompdf(document.querySelector('#capture'), {
  useCORS: true,
  fontConfig: {
    fontFamily: 'SourceHanSansSC-Normal-Min',
    fontBase64: window.fontBase64
  }
})
  .then(function (blob) {
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = 'example.pdf';
    document.body.appendChild(a);
    a.click();
  })
  .catch(function (err) {
    console.error(err);
  });
繪製漸變色、陰影等複雜樣式-foreignObjectRendering 使用

在 DOM 十分複雜,或者 PDF 無法繪製的情況(比如:複雜的表格,邊框陰影,漸變等),可以考慮使用 foreignObjectRendering。
給要渲染的元素添加 foreignObjectRendering 屬性,就可以通過 svg 的 foreignObject 將它渲染成一張背景圖插入到 pdf 檔案中。

但是,由於 foreignObject 元素的渲染依賴於瀏覽器的實現,因此在不同的瀏覽器中可能會有不同的表現。
所以,在使用 foreignObjectRendering 時,需要注意以下事項:

  1. foreignObject 元素的渲染依賴於瀏覽器的實現,因此在不同的瀏覽器中可能會有不同的表現。
  2. IE 瀏覽器完全不支持,推薦在 chrome 和 firefox,edge 中使用。
  3. 生成的圖片會導致 pdf 檔案體積變大。

示例

<div style="width: 100px;height: 100px;" foreignObjectRendering>
  <div style="width: 50px;height: 50px;border: 1px solid #000;box-shadow: 2px 2px 5px rgba(0,0,0,0.3);background: linear-gradient(45deg, #ff6b6b, #4ecdc4);">
    這是一個div元素
  </div>
</div>

瀏覽器相容性

該庫應該可以在以下瀏覽器上正常工作(需要 Promise polyfill):

  • Firefox 3.5+
  • Google Chrome
  • Opera 12+
  • IE9+
  • Safari 6+

原文出處:https://juejin.cn/post/7583912637470769203


精選技術文章翻譯,幫助開發者持續吸收新知。

共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。
🏆 本月排行榜
🥇
站長阿川
📝13   💬9   ❤️4
346
🥈
我愛JS
📝1   💬7   ❤️2
56
評分標準:發文×10 + 留言×3 + 獲讚×5 + 點讚×1 + 瀏覽數÷10
本數據每小時更新一次
🔧 阿川の電商水電行
Shopify 顧問、維護與客製化
💡
小任務 / 單次支援方案
單次處理 Shopify 修正/微調
⭐️
維護方案
每月 Shopify 技術支援 + 小修改 + 諮詢
🚀
專案建置
Shopify 功能導入、培訓 + 分階段交付