前端生成 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):

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




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

dompdf.js 是如何將 html 頁面生成功能多頁 PDF 檔案的?
先將 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: []
}
迴圈 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 值也會減少相應的值,確保元素位置的準確性。
循環 DOM 樹分頁數組,調用 jspdf.addPage() 方法添加新頁面,並繪製頁眉頁腳。根據不同的元素類型,以及元素的位置信息,調用不同的 jspdf 方法生成 pdf。比如繪製文字:
jspdf.text("這是一個文本節點", 1115, 8);
繪製圖片:
jspdf.image(img, x, y, width, height);
安裝:
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);
});
預設情況下,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 屬性的效果

| 參數名 | 必傳 | 預設值 | 型別 | 說明 |
|---|---|---|---|---|
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–a10、b0–b10、c0–c10、letter 等 |
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] |
文本位置枚舉 center、centerLeft 、 centerRight、centerTop、 centerBottom、leftTop、 leftBottom、rightTop、rightBottom或座標 [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);
});
在 DOM 十分複雜,或者 PDF 無法繪製的情況(比如:複雜的表格,邊框陰影,漸變等),可以考慮使用 foreignObjectRendering。
給要渲染的元素添加 foreignObjectRendering 屬性,就可以通過 svg 的 foreignObject 將它渲染成一張背景圖插入到 pdf 檔案中。
但是,由於 foreignObject 元素的渲染依賴於瀏覽器的實現,因此在不同的瀏覽器中可能會有不同的表現。
所以,在使用 foreignObjectRendering 時,需要注意以下事項:
示例
<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):