Skip to main content

前端PDF下載 | pdfmake自訂中文字型(使用NotoSansTC)

· 9 min read
Adam You

工作上有需要在前端將內容生成PDF下載的功能,在同事的推薦下使用pdfmake這個套件。

不過這個套件有個問題,他的預設字型是英文字型,而且似乎也沒有提供api能夠直接替換成自己的字型檔。如果需要更換字型需要在source code做一些修改,然後重新打包出編譯過的字型檔;雖然網路上也有很多教學,但在我研究過程中還是踩到一些坑,所以就把我自己的實作步驟、以及使用方式整理了一下跟大家分享。

凖備字型檔

在解說步驟之前呢,我先把我所遇到的坑說明一下。

因為擔心會有版權的問題,我是使用Google提供的開源字體「思源黑體」(NotoSansTc)。而我套用上去之後(在我文章後面所有步驟做完之後)PDF下載的結果如下:

結果是慘不忍睹(因部份內容不方便展示所以有馬賽克,看起來也不太像是缺字。爬文之後懷疑是字體格式的問題。

Google提供的字體檔副檔名是.otf。於是我又上網找了副檔名是woff2(至於為何是用woff2而不是woff,是因為前者的size小一些)檔之後果然就正常了!

以下是我所找到的CDN檔案連結,請依所需下載,或者自行使用自己所需的字體檔案。

打包字型檔

由於pdfmake不能直接載入字型檔,還需透過source code裡用gulp寫好的腳本來編譯成js檔。依照你是用npm安裝或者是直接到官網下載整包source code,做法會有些微差異。

至於要用哪種方式好呢?我是覺得其實都沒差,前者(方法1)的話是可以直接在開發專案裡就可以直接處理(到node_modules裡可找到source code),後者(方法2)的話是可以在獨立的環境中處理,建好字型檔後就可以把source code刪掉了。

所以下分開說明:

方法1 - npm安裝pdfmake:

開發專案使用npm安裝pdfmake之後,到node_modules裡找到pdfmake資料夾,開啟gulpfile.js。

然後找到如下圖的這一行。這個路徑是要放置字型檔的資料夾,但是npm安裝到node_modules裡面的檔案並沒有包含原始的字型檔、以及這個資料夾路徑,所以需要手動建立一個資料夾並放入要使用的字型(可放多個字型檔)到該資料夾裡,然後再把這個路徑改寫成自己建立的資料夾。

接著在terminal中將路徑移動到pdfmake的根目錄(也就是和gulpfile.js所在的資料夾中)

cd node_modules/pdfmake

source code裡面包含package.json檔,所以直接下指令將所需套件安裝即可。

npm install

最後一步,用gulp打包字型檔。

gulp buildFonts

完成之後就會在「build」資料夾裡面打包出vfs_fonts.js,這個檔案就是pdfmake所要用的字型檔了。

最後一件要注意的事情,為方便維護、以及避免在node_modules更新的過程當中被覆蓋掉,我不建議僅將該檔案取代掉原始的程式,我會建議把vfs_fonts.js複製出來放到我們開發的專案資料夾當中,以我自己為例我是使用vue-cli開發的專案,我放在src/assets/fonts/資料夾當中。

方法2 - 下載整包source code:

方法2和方法1幾乎是一樣了。我自己就是這樣做的,因為我不想在我目前開發中的專案去修改source code(雖然其實也不會有什麼影響),所以就到 Github上將整包source code下載下來,然後在獨立的環境裡操作。

這包檔案比起方法1中用npm安裝,還會多了一個叫「examples」的資料夾。這個資料夾裡還有一個叫「fonts」的資料夾,裡面包含了pdfmake預設使用的字型檔,你應該會看到這些:

  • Roboto-Italic.ttf

  • Roboto-Medium.ttf

  • Roboto-MediumItalic.ttf

  • Roboto-Regular.ttf

由於打包時會把這個資料夾的字型檔全部一起打包。字型檔愈多、檔案就愈大,檔案愈大、前端網頁所要下載js chunk就愈大,如果用不到,全部刪掉即可!刪刪刪

然後把自己要用的字型檔放進去(可放多個字型檔)。

接下來步驟就和方法1當中所說的都一樣了:

將所需套件安裝。

npm install

最後一步,用gulp打包字型檔。

gulp buildFonts

完成之後就會在「build」資料夾裡面打包出vfs_fonts.js,這個檔案就是pdfmake所要用的字型檔了。

為方便維護、以及避免在node_modules更新的過程當中被覆蓋掉,我不建議僅將該檔案取代掉原始的程式,我會建議把vfs_fonts.js複製出來放到我們開發的專案資料夾當中,以我自己為例我是使用vue-cli開發的專案,我放在src/assets/fonts/資料夾當中。

使用方式

以下是我覺得比較需要注意的地方:

1. 設定預設字體

除非你修改source code(這就不太建議了,這樣用npm維護套件版本會有些問題),否則的話需要在參數上設定你所建立的預設字體。

2. 獨立載入字體檔

在前述的步驟中已經有提到了,我建議將所打包的字體檔vfs_fonts.js放到在開發專案的資料夾當中。一來是為方便維護、也避免在node_modules更新的過程當中被覆蓋掉。

3. 字體檔(vfs_fonts.js)可使用動態載入

如果是在js module當中直接import的話,webpack會把vfs_fonts.js和你的頁面程式打包成同一個js chunk,檔案size會非常驚人,會影響到載入頁面的效能。所以我會設計成在要開始下載pdf的時候才去下載vfs_fonts.js

根據上述三點,以下是程式範例:

import pdfMake from 'pdfmake/build/pdfmake'

let data = {
content: [
// pdf內容及格式
],
styles: {
// 樣式設定
},
defaultStyle: {
font: 'NotoSansTC' // 預設的字體格式
}
}

// 動態載入字體檔
pdfMake.vfs = await import('@/assets/fonts/vfs_fonts.js')

pdfMake.fonts = {
NotoSansTC: {
normal: 'NotoSansTC-Regular.woff2',
bold: 'NotoSansTC-Bold.woff2',
}
}

pdfMake.createPdf(data).download('檔案名稱')