前言
最近公司有個行銷活動需要做一個簡單的 SPA,基本上只有簡單的三個頁面,完全可以利用 Gatsby 或 Nextjs 來製作靜態頁面,然後部署到 CDN 上頭,效能上來說理應足夠好了,但是我們的設計師在頁面上混用了多種的字體,尤其是日文部分,除了一般瀏覽器內建的字體外,某些元件採用額外的免費字體,例如 Corporate Logo Font,這代表我們需要額外去下載這些字型,但為了頁面上的幾個字,去下載一整個字型檔案(ttf, 2.6MB)實在很浪費,因此只好來研究一下如何客製化字型檔案,只載入我們需要的字。
雖然這感覺是個很容易遇到的需求,但我還真的是第一次實際需要處理,感謝同事 Carlos 提供解法,透過這篇文章筆記一下,希望對他人也有點幫助。
開始前先稍稍複習一下,什麼是 ttf?還有什麼其他字型格式呢?
TTF(TrueType Font)是由蘋果和微軟共同開發的一種電腦輪廓字型類型標準,是 Mac 與 Windows 上最常見的格式,基本上所有主流瀏覽器都支援,也是免費或便宜的第三方字體最常提供的格式。缺點是檔案未經過壓縮,文件大小較大。
另一個主流格式為 OTF(OpenType Font),是一種可縮放字型(scalable font)電腦字型類型,由 TrueType 延伸而來,採用 PostScript 格式,是微軟與 Adobe 聯合開發,用來替代 TrueType 字型的新字型。
WOFF(Web Open Font Format) 則是完全為了 Web 而設計的格式,由 Mozilla、Microsoft 與 Opera 合作推出。WOFF 的字型都經由 WOFF 的編碼工具壓縮,體積能比 tff 小 40%,現在已經是網頁字體的推薦標準。WOFF2 則是 WOFF 的升級版,體積可以壓得更小。
最後,當然大家熟悉的 SVG 也可以算是一種。
在主流的作業系統與瀏覽器上,這幾種格式的支援度都很高,而其主要的細節差異,因為非本篇重點,就不多著墨,有興趣的讀者可以到 wiki 上查看。
今天要減少的字型檔案為最常見的 tff 格式。
順帶一提,當我在撰寫文章的時候,對於字型、字體等名詞的差異很模糊,好在 JustFont 在多年前的一篇文章解釋得蠻清楚的,推薦大家理解一下!
Setup
要針對字型進行處理的話,首先我們需要下載 FontForge,FontForge 是一個很有名的軟體,可以用來設計、創建字體,或是進行各種字型相關的操作,可以從 https://fontforge.org/en-US/downloads/mac-dl/ 下載 Mac 版本(也有 Linux 與 Windows 的版本)。
在官網上你可以找到許多文件,甚至是一整個 ebook 來教你如何用 FontForge 來設計字體。
用 FontForge 開啟原始字型檔
載好 FontForge 後,我們先打開原始的字型檔,這邊以前面提到的 Corporate Logo Font 為例(註: 在 MacOS Catalina 或 Big Sur,直接點選載好在 Apps 內的 FontForge app 可能會被 OS 擋下來,快一點的方式是按右鍵 -> "Show Package Contents" -> "Contents" -> "MacOS"
,然後點選 FontForg.app,接者就會打開 terminal 並執行 FontForge。):
開啟後可以看到所有的字圖,接著其實你就可以選取你不要的字圖,然後 clear
掉它們:
但照這樣處理,弄到天荒地老六親不認都弄不完。工程師要用更聰明的解法。
正確的姿勢
打從一開始,我們就是因為原始字檔裡面太多我們不要的東西,我們需要的很少,才想要從原始檔案中擷取需要的部分,既然如此,就應該從我們想要的字圖下手,而不是慢慢刪掉我們不要的字圖。
FontForge 其實有提供一個很方便的功能,叫做 Invert Selection
,能夠選取所有你沒有選取到的東西,直接看個動圖範例:
這樣一來,就很簡單了,只要選取住你想要的字圖,然後點選 Edit -> Select -> Invert Selection
,就完成了,接著就把 Fontforge 自動幫你選取的字圖 clear
掉即可。
但這樣還是有個問題。
字型檔內容這麼多,我要手動在 FontForge 中找到自己想要的字圖不也是找到山窮水盡嗎?
正確姿勢二
FontForge 是個蠻強大的工具,除了 GUI 以外,也提供 interpreters,讓你能撰寫 scripts 來修改字型檔。
一個 interpreter 是 Python,另一個則是其內建的 scripting language。詳細的範例、語法等可以從官網查看,文件很完整。
有了 scripting 的功能,我們就不用自己手動選取字圖啦。
在 FontForge UI 上,你可以點選 File -> Execute Script
叫出 Dialog,可以選擇直接貼上 Python 程式碼,也可以選擇 FF -> Call
,來載入使用另一個內建 interpreter 的 script file。
因為我們要處理的動作很簡單,只有三個動作(選取字型、反轉選取、刪除),所以直接用內建的 script language 其實比較簡單,可以利用 NodeJS 來產生執行檔。
需要的 API
三個動作,選取字圖、反轉選取、刪除,分別對應的 API 為 SelectMore()
、SelectInvert()
、DetachAndRemoveGlyphs()
。
我們要匯入進 FontForge 的執行檔,就只需要這三個 API 即可。
SelectMore()
用法是傳入字型的 unicode 作為參數,即可選取該字圖,不過執行一次只能選取一個字圖。SelectInvert()
與 DetachAndRemoveGlyphs()
則不需要參數。
範例程式
知道了需要的 API,我們就可以來寫程式產生執行檔 subset-font.pe
,.pe
是 FontForge 可以接受的格式,.ff
也行:
// 挑選出你頁面上需要用到的字
const characters = '123招待コード';
const stream = fs.createWriteStream('./subset-font.pe');
stream.once('open', function (fd) {
characters.split('').forEach(char => {
// 轉換成 16 進位
let hex = char.charCodeAt(0).toString(16);
// 補零,以符合 \u 格式
if (hex.length < 4) {
hex = hex.padStart(4, '0');
}
// 然後執行檔內寫入 SelectMore
stream.write(`SelectMore("u${hex}")\n`);
});
// 反轉選擇,選取所有其他不要的字
stream.write('SelectInvert()\n');
// 最後移除字型
stream.write('DetachAndRemoveGlyphs()\n');
stream.end();
});
利用 FontForge API 搭配上面程式後,會產生以下內容:
sh subset-font.pe
SelectMore("u0031")
SelectMore("u0032")
SelectMore("u0033")
SelectMore("u62db")
SelectMore("u5f85")
SelectMore("u30b3")
SelectMore("u30fc")
SelectMore("u30c9")
SelectInvert()
DetachAndRemoveGlyphs()
接著依照正確姿勢二的方式,匯入此執行檔,FontForge 就會產生只包含我們想要的字圖的字型檔了!:
不過因為刪掉的字圖很多,我們可以進一步透過 FontForge 的壓縮功能來輔助我們檢視成品:
剛執行完 script 後,畫面會停留在 select 所有其他你不要的字圖的狀態,你可以先隨便點選空白處 deselect 所有字圖,然後選擇 Encoding -> Compact
:
就能清楚看到整個檔案的確只剩下我們所選的字圖(以上面範例 script 來說就是 123招待コード
)。
最後步驟就是產生字型檔案,點選 File -> Generate Fonts...
,然後看你要 export 成什麼格式,如果是網頁上要用,當然就推薦使用 woff
:
按下 Generate
後可能會出現 Error,可以不用理他,繼續 generate:
這樣就大功告成了!
(註:關掉 FontForge 時,記得選 Don't Save
,不然會蓋掉原始的檔案喔!)
結論
這個方式可以用在各種字體檔案,非常方便,對於靜態頁面上內容文字不太會變動的狀況下,利用這個技巧可以大幅降低需要載入的檔案大小,以我公司專案的例子來說,從原本 2.6MB 的 tff 檔案,最後可以變成 8KB 的 woff 檔案,省下的大小很可觀的。
簡單的筆記,希望對大家有幫助!