改善備用字型

Katie Hempenius
Katie Hempenius

摘要

本文將深入探討字型備用方案,以及 size-adjustascent-overridedescent-overrideline-gap-override API。這些 API 可讓您使用本機字型,建立與網頁字型尺寸相近或完全相符的備用字型。這麼做可減少或消除因字型交換造成的版面配置位移。

如果您不想閱讀本文,可以使用下列工具立即開始使用這些 API:

架構工具:

  • @next/font:從 Next 13 開始,next/font 會自動使用字型指標覆寫值和 size-adjust,提供相符的備用字型。
  • @nuxtjs/fontaine:從 Nuxt 3 開始,您可以使用 nuxt/fontaine 自動產生並插入相符的字型備用方案,並插入 Nuxt 應用程式使用的樣式表。

非架構工具:

  • Fontaine:Fontaine 是可自動產生及插入使用字型指標覆寫值的字型備用方案的程式庫。
  • 這個repo包含 Google Fonts 代管的所有字型,以及這些字型的字型指標覆寫值。您可以將這些值複製並貼到樣式表中。

背景

備用字型是指在尚未載入主要字型或缺少用於轉譯網頁內容的字元時,所使用的字型樣式。舉例來說,下方的 CSS 指出應將 sans-serif 字型系列用作 "Roboto" 的字型備用方案。

font-family: "Roboto" , sans-serif;

備用字型可用於更快速地轉譯文字 (也就是使用 font-display: swap),因此網頁內容可更早讀取並提供使用。不過,以往這項做法會導致版面配置不穩定:當備用字型換成網頁字型時,通常會發生版面配置偏移。不過,下文所述的新 API 可讓您建立備用字體,並讓其占用的空間與對應的網路字體相同,藉此減少或消除這個問題。

改善字型備用機制

產生「改善版」字型備用檔案的方法有兩種。較簡單的方法是只使用字型指標覆寫 API。較複雜 (但更強大) 的方法是同時使用字型指標覆寫 API 和 size-adjust。本文將說明這兩種做法。

字型指標覆寫值的運作方式

簡介

字型指標覆寫值可用於覆寫字型的上升、下降和行距:

  • 上昇是指字型字符延伸至基準線上方的最遠距離。
  • Descent 會測量字型字符向下延伸至基準線的最大距離。
  • 行間距 (也稱為「行間距") 是指連續兩行文字之間的距離。

圖表顯示字型上升、下降和行距。

您可以使用字型指標覆寫值,覆寫備用字型的上升、下降和行距,以便與網頁字型的上升、下降和行距相符。因此,網頁字型和調整過的備用字型一律會具有相同的垂直尺寸。

字型指標覆寫值會用於樣式表,如下所示:

body {
    font-family: Poppins, "fallback for poppins";
}

@font-face {
    font-family: "fallback for poppins";
    src: local("Times New Roman");
    ascent-override: 105%;
    descent-override: 35%;
    line-gap-override: 10%;
}

本文開頭列出的工具可產生正確的字型指標覆寫值。不過,您也可以自行計算這些值。

計算字型指標覆寫值

下列等式會產生特定網頁字型的字型指標覆寫值。字型指標覆寫值應以百分比 (例如 105%) 表示,而非小數。

ascent-override = ascent/unitsPerEm
descent-override = descent/unitsPerEm
line-gap-override = line-gap/unitsPerEm

舉例來說,以下是 Poppins 字型的字型指標覆寫值:

/*
Poppins font metrics:
ascent = 1050
descent = 350
line-gap = 100
UPM: 1000
*/

ascent-override: 105%;  /* = 1050/1000 */
descent-override: 35%;  /* = 350/1000 */
line-gap-override: 10%; /* = 100/1000 */

ascentdescentline-gapunitsPerEm 的值皆來自網路字型的中繼資料。本文的下一節將說明如何取得這些值。

讀取字型資料表

字型的中繼資料 (具體來說是字型表) 包含計算字型指標覆寫值所需的所有資訊。

FontForge 中的「Font Information」對話方塊螢幕截圖。對話方塊會顯示字型指標,例如「Typo Ascent」、「Typo Descent」和「Typo Line Gap」。
使用 FontForge 查看字型中繼資料

以下是可用來讀取字型中繼資料的工具:

  • fontkit 是專為 Node.js 建構的字型引擎。下列程式碼片段說明如何使用 FontKit 計算字型指標覆寫值。
  • Capsize 是字型大小和版面配置程式庫。Capsize 提供 API,可取得各種字型規格資訊。
  • fontdrop.info 是一個網站,可讓您在瀏覽器中查看字型表和其他字型相關資訊。
  • Font Forge 是熱門的電腦字型編輯器。如要查看 ascentdescentline-gap:請開啟 Font Info 對話方塊,選取 OS/2 選單,然後選取 Metrics 分頁標籤。如要查看 UPM:請開啟 Font Info 對話方塊,然後選取 General 選單。

瞭解字型表

您可能會注意到,「上升」等概念會被多個指標引用,例如 hheaAscenttypoAscentwinAscent 指標。這是因為不同作業系統採用不同的字型算繪方法:OSX 裝置上的程式通常會使用 hhea* 字型指標,而 Windows 裝置上的程式通常會使用 typo* (也稱為 sTypo*) 或 win* 字型指標。

視字型、瀏覽器和作業系統而定,字型會使用 hheatypowin 指標算繪。

Mac Windows
Chromium 使用「hhea」表格中的指標。 如果已設定「USE_TYPO_METRICS」,則會使用「typo」資料表的指標;否則會使用「win」資料表的指標。
Firefox 如果已設定「USE_TYPO_METRICS」,則會使用「typo」資料表的指標;否則會使用「hhea」資料表的指標。 如果已設定「USE_TYPO_METRICS」,則會使用「typo」資料表的指標,否則會使用「win」資料表的指標。
Safari 使用「hhea」表格中的指標。 如果已設定「USE_TYPO_METRICS」,則會使用「typo」資料表的指標,否則會使用「win」資料表的指標。

如要進一步瞭解字型指標在不同作業系統上的運作方式,請參閱這篇關於垂直指標的文章

跨裝置相容性

對於大多數字型 (例如 Google 字體中約 90% 的字型),即使不知道使用者的作業系統,也能安全地使用字型指標覆寫值。換句話說,對於這些字型,無論是否套用 hheatypowin 指標,ascent-overridedescent-overridelinegap-override 的值都會保持不變。這個repo提供哪些字型適用於此,以及哪些字型不適用於此。

如果您使用的是需要為 OSX 和 Windows 裝置使用不同字型規格覆寫值的字型,建議您在能夠根據使用者的作業系統變更樣式表時,才使用字型規格覆寫值和 size-adjust

使用字型指標覆寫值

由於字型指標覆寫值的計算方式是使用來自網頁字型的中繼資料 (而非備用字型) 的測量值,因此無論使用哪種字型做為備用字型,這些值都會保持不變。例如:

body {
  font-family: "Poppins", "fallback for Poppins", "another fallback for Poppins";
}

@font-face {
  font-family: "fallback for Poppins";
  src: local("Arial");
  ascent-override: 105%;
  descent-override: 35%;
  line-gap-override: 10%;
}

@font-face {
  font-family: "another fallback for Poppins";
  src: local("Roboto");
  ascent-override: 105%;
  descent-override: 35%;
  line-gap-override: 10%;
}

尺寸調整功能的運作方式

簡介

size-adjust CSS 描述元會按比例縮放字型符號的寬度和高度。舉例來說,size-adjust: 200% 會將字型符號放大至原始大小的兩倍;size-adjust: 50% 則會將字型符號縮小至原始大小的一半。

圖表顯示使用「size-adjust: 50%」和「size-adjust: 200%」的結果。

size-adjust 本身的應用範圍有限,無法改善字型備用方案:在大多數情況下,備用字型需要稍微縮小或放大 (而非按比例縮放),才能與網路字型相符。不過,結合 size-adjust 和字型指標覆寫值,即可讓任何兩種字型在水平和垂直方向上彼此相符。

以下是 size-adjust 在樣式表中使用的情形:

@font-face {
  font-family: "fallback for poppins";
  src: local("Arial");
  size-adjust: 60.85099821%;
  ascent-override: 164.3358416%;
  descent-override: 57.51754455%;
  line-gap-override: 16.43358416%;
}

由於 size-adjust 的計算方式 (詳見下一節),size-adjust 的值 (以及對應的字型指標覆寫值) 會因所使用的備用字型而異動:

body {
  font-family: "Poppins", "fallback for Poppins", "another fallback for Poppins";
}

@font-face {
  font-family: poppins-fallback;
  src: local("Arial");
  size-adjust: 60.85099821%;
  ascent-override: 164.3358416%;
  descent-override: 57.51754455%;
  line-gap-override: 16.43358416%;
}

@font-face {
  font-family: poppins-fallback-android;
  src: local("Roboto");
  size-adjust: 55.5193474%:
  ascent-override: 180.1173909%;
  descent-override: 63.04108683%;
  line-gap-override: 18.01173909%;
}

計算大小調整和字型指標覆寫值

以下是計算 size-adjust 和字型指標覆寫值的公式:

size-adjust = avgCharacterWidth of web font / avgCharacterWidth of fallback font
ascent-override = web font ascent / (web font UPM * size-adjust)
descent-override = web font descent / (web font UPM * size-adjust)
line-gap-override = web font line-gap / (web font UPM * size-adjust)

大多數的這些輸入內容 (即上升、下降和行距) 可直接從網路字型的中繼資料讀取。不過,avgCharacterWidth 需要近似值。

近似平均字元寬度

一般來說,平均字元寬度只能估算,但在某些情況下,可以精確計算平均字元寬度,例如使用固定寬度字型,或是事先知道文字字串內容的情況。

舉例來說,計算 avgCharacterWidth 的簡單方法是取得所有 [a-z\s] 字元的平均寬度。

 比較個別 Roboto [a-zs] 字形寬度的圖表。
Roboto 字形寬度

不過,如果所有字元都採用相同的權重,則常用字母 (例如 e) 的寬度可能會不足,而較少使用的字母 (例如 z) 的寬度則可能會過大。

另一種更複雜的做法是考量字母頻率,並計算 [a-z\s] 字元的頻率加權平均寬度,藉此提高準確度。這篇文章是英文文字的字母頻率和平均字詞長度的好參考資料。

圖表顯示英文的字母頻率。
英文字母頻率

選擇方法

本文討論的兩種方法各有優缺點:

  • 如果您想開始最佳化字型備用字型,建議您單獨使用字型指標覆寫值。雖然這兩種方法中,這項方法較為簡單,但通常效能足以明顯減少與字型相關的版面配置位移。

  • 另一方面,如果您希望提高精確度,並願意多做一些工作和測試,整合 size-adjust 是一個不錯的做法。正確實作後,這種做法可有效消除與字型相關的版面配置位移。

選擇備用字型

本文所述的技術會使用字型指標覆寫值和 size-adjust 來轉換廣泛可用的本機字型,而非嘗試找出與網路字型相近的本機字型。選擇本機字型時,請務必記住,只有少數字型可在多數裝置上使用,而且沒有任何字型可在所有裝置上使用。

Arial 是 Sans Serif 字型的建議備用字型,Times New Roman 則是 Serif 字型的建議備用字型。不過,這兩種字型都不適用於 Android (Roboto 是 Android 上唯一的系統字型)。

以下範例使用三種備用字型,確保廣泛的裝置涵蓋率:指定 Windows/Mac 裝置的備用字型、指定 Android 裝置的備用字型,以及使用通用字型系列的備用字型。

body {
  font-family: "Poppins", poppins-fallback, poppins-fallback-android, sans-serif;
}

/*
Poppins font metrics:
- ascent = 1050
- descent = 350
- line-gap = 100
- UPM: 1000
AvgCharWidth:
- Poppins: 538.0103768
- Arial: 884.1438804
- Roboto: 969.0502537
*/

@font-face {
  font-family: poppins-fallback;
  src: local("Arial");
  size-adjust: 60.85099821%;
  ascent-override: 164.3358416%;
  descent-override: 57.51754455%;
  line-gap-override: 16.43358416%;
}

@font-face {
  font-family: poppins-fallback-android;
  src: local("Roboto");
  size-adjust: 55.5193474%:
  ascent-override: 180.1173909%;
  descent-override: 63.04108683%;
  line-gap-override: 18.01173909%;
}

要求提供意見回饋

如果您對使用字型指標覆寫值和 size-adjust 的使用體驗有任何意見,歡迎與我們聯絡。