搭配本機字型使用進階字體排版

瞭解如何運用 Local Font Access API 存取使用者本機安裝的字型,並取得關於這些字型的低階詳細資料

網頁安全字型

如果你做網頁開發的時間夠長,可能還記得所謂的 網頁安全字型。 這些字型幾乎適用於所有最常用的作業系統執行個體 (即 Windows、macOS 為最常見的 Linux 發行版、Android 和 iOS)。2000 年代初期 Microsoft 甚至引領了 計畫 TrueType 是網路的 TrueType 核心字型,都可透過網路免費下載這些字型。 目的是:每次您造訪有指定這些目標的網站時, 您所看到的網頁 都會與 "是,包含設定在 Comic Sans MS:以下是 經典的網路安全字型堆疊 (其最終能備用 sans-serif敬上 字型) 看起來會像這樣:

body {
  font-family: Helvetica, Arial, sans-serif;
}

網頁字型

網路安全字型如此重大的時代早已浮現。目前我們有 網路字型,其中包含 甚至是可變字型,只要變更 包括各個曝露軸如要使用網路字型,只需宣告 CSS 開頭的 @font-face 區塊 ,指定要下載的字型檔案:

@font-face {
  font-family: 'FlamboyantSansSerif';
  src: url('flamboyant.woff2');
}

之後,只要指定 font-family,照常:

body {
  font-family: 'FlamboyantSansSerif';
}

將本機字型當做指紋向量

大多數網頁字型都來自網路,但有趣的是 @font-face 中的 src 資源 除了 url() 函式,也接受 local() 函式。這允許在本機載入自訂字型 (驚喜!)。如果使用者正好會發生 在他們的作業系統上安裝 FlamboyantSansSerif,而非使用本機副本 它正在下載:

@font-face {
  font-family: 'FlamboyantSansSerif';
  src: local('FlamboyantSansSerif'), url('flamboyant.woff2');
}

這種做法可以提供或許節省頻寬的好備用機制。在網際網路上 可惜的是,我們不能擁有美好的東西。local() 函式的問題在於 濫用瀏覽器數位指紋採集。事實上,使用者已安裝的字型清單可能很複雜 識別個人身分許多公司都有自己的公司字型,並安裝在員工上 筆電。舉例來說,Google 採用 Google Sans 這個企業字型。

顯示 Google Sans 字型預覽畫面的 macOS Font Book 應用程式。
Google 員工筆電上安裝的 Google Sans 字型。

攻擊者可以藉由測試是否存在 許多已知的企業字型,例如 Google Sans。攻擊者會嘗試轉譯文字 顯示在畫布的字型中 並測量字符如果字符與已知形狀 字型,攻擊者就很成功了。如果字符不相符,攻擊者知道 因為沒有安裝公司字型,所以是使用預設替代字型。如需詳細資料,請前往 以及其他瀏覽器數位指紋採集攻擊 問卷調查報告,作者: Laperdix 等人

公司字型與公司字型不同,即使只有已安裝的字型清單也能辨識。請注意, 使得這個攻擊媒介變得非常糟糕,最近 WebKit 團隊 決定「只包括在清單中提供的字型」 而非使用者安裝在本機的字型」(我在這裡寫了一篇文章 轉換成本機字型)。

本機字型存取權 API

這篇文章開頭可能讓您有負面的心情。我們能否 什麼?別煩我。我們願意 一切都不是天生。 不過,我們先讓我回答你可能想知道的問題。

有網路字型時,為什麼需要 Local Font Access API?

專業質感設計與圖像工具一直難以實現 網頁。有個難以進入的專業領域 設計及微調的字型,這些字型都是由設計人員在本機安裝。網路字型可讓您進行部分發布 用途,但無法允許程式存取 利用光柵器轉譯字符輪廓。同樣地,無法存取網路字型的二進位檔 資料。

  • 設計工具需要存取字型位元組才能執行自己的 OpenType 版面配置,並允許 設計工具,在較低層級建立可吸引人的操作,例如執行向量濾鏡或動作 字符形狀上的轉換
  • 開發人員可能會將原有的應用程式字型堆疊納入網路, 使用這些堆疊時,通常需要直接存取字型資料,網路字型則不需要 因此,使用 Kubernetes Engine 即可。
  • 部分字型可能未取得透過網路傳遞的授權。例如,Linotype 已擁有 部分僅包含電腦版使用的字型。

Local Font Access API 會嘗試解決這些挑戰。其中包含兩個部分:

  • 字型列舉 API,可讓使用者授予整個可用系統的存取權 。
  • 在每個列舉結果中,能要求低階 (位元組導向) SFNT 容器 內含完整字型資料的存取權

瀏覽器支援

瀏覽器支援

  • Chrome:103。
  • Edge:103。
  • Firefox:不支援。
  • Safari:不支援。

資料來源

如何使用 Local Font Access API

特徵偵測

如要檢查是否支援 Local Font Access API,請使用:

if ('queryLocalFonts' in window) {
  // The Local Font Access API is supported
}
敬上

列舉本機字型

如要取得本機安裝的字型清單,您需要呼叫 window.queryLocalFonts()。 首次觸發後,系統會觸發權限提示,讓使用者核准或拒絕。如果使用者 允許查詢本機字型,瀏覽器會傳回包含字型資料的陣列 可以循環使用每個字型都會以 FontData 物件表示,並具有 family 屬性 (例如 "Comic Sans MS")、fullName (例如 "Comic Sans MS")、postscriptName ( 例如 "ComicSansMS") 和 style (例如 "Regular")。

// Query for all available fonts and log metadata.
try {
  const availableFonts = await window.queryLocalFonts();
  for (const fontData of availableFonts) {
    console.log(fontData.postscriptName);
    console.log(fontData.fullName);
    console.log(fontData.family);
    console.log(fontData.style);
  }
} catch (err) {
  console.error(err.name, err.message);
}

如果您只想瞭解部分字型,也可以根據 PostScript 篩選 加入 postscriptNames 參數即可。

const availableFonts = await window.queryLocalFonts({
  postscriptNames: ['Verdana', 'Verdana-Bold', 'Verdana-Italic'],
});

存取 SFNT 資料

如需完整的 SFNT 存取權,請使用以下項目的 blob() 方法: FontData 物件。SFNT 是一種字型檔案格式,可包含 PostScript 等其他字型, TrueType、OpenType、Web Open Font Format (WOFF) 字型等等。

try {
  const availableFonts = await window.queryLocalFonts({
    postscriptNames: ['ComicSansMS'],
  });
  for (const fontData of availableFonts) {
    // `blob()` returns a Blob containing valid and complete
    // SFNT-wrapped font data.
    const sfnt = await fontData.blob();
    // Slice out only the bytes we need: the first 4 bytes are the SFNT
    // version info.
    // Spec: https://docs.microsoft.com/en-us/typography/opentype/spec/otff#organization-of-an-opentype-font
    const sfntVersion = await sfnt.slice(0, 4).text();

    let outlineFormat = 'UNKNOWN';
    switch (sfntVersion) {
      case '\x00\x01\x00\x00':
      case 'true':
      case 'typ1':
        outlineFormat = 'truetype';
        break;
      case 'OTTO':
        outlineFormat = 'cff';
        break;
    }
    console.log('Outline format:', outlineFormat);
  }
} catch (err) {
  console.error(err.name, err.message);
}

示範

如要瞭解 Local Font Access API 的實際運作情形,請參閱 示範。另外,也別忘了查看 原始碼。示範 會顯示名為 <font-select> 的自訂元素,且 實作本機字型挑選器。

隱私權注意事項

"local-fonts" 權限似乎可以提供高度指紋的介面。不過 瀏覽器可以免費傳回任何物件舉例來說,以匿名身分為主的瀏覽器可能會 只提供瀏覽器內建的一組預設字型。同樣地,瀏覽器也不需要 根據磁碟中顯示的資料,提供完全相同的資料表資料。

Local Font Access API 會盡可能隻公開精確的資訊 執行上述用途所需的資訊系統 API 可能會產生已安裝字型清單,而非 隨機或排序,但按照字型安裝順序。傳回確切的清單 這類系統 API 提供的安裝字型,可能會揭露可用於 請注意,保留這個順序無法協助啟用指紋辨識功能,以及我們啟用的用途。身為 結果,這個 API 需要先排序傳回的資料,然後才能傳回。

安全性和權限

Chrome 團隊根據核心原則設計並實作 Local Font Access API 定義,包括控管強大的 Web Platform 功能存取權,包括使用者 掌控權、透明度和人體工學

使用者控制項

使用者的字型完全由使用者掌控,不得存取,除非 "local-fonts" 權限,如 權限註冊資料庫

透明度

網站是否已取得使用者的本機字型存取權,都會顯示在 網站資訊工作表

權限持續性

重新載入網頁之間會保留 "local-fonts" 權限。可以透過以下方式撤銷: 網站資訊工作表。

意見回饋

Chrome 團隊想瞭解您使用 Local Font Access API 的體驗。

請與我們分享 API 設計

您覺得這個 API 有任何不如預期的運作方式嗎?或者缺少某些方法 需要實現什麼構想?對安全性有任何疑問或意見 以及模型在對應的 GitHub 存放區上提出規格問題,或將您的想法新增至 現有的問題。

回報導入問題

您發現 Chrome 實作錯誤嗎?還是採用與規格不同? 前往 new.crbug.com 回報錯誤。請務必盡量提供詳細資料 重現問題的簡單操作說明,然後在「Components」(元件) 方塊中輸入 Blink>Storage>FontAccess Glitch 有便捷的報復工具,

顯示對 API 的支援

您是否打算使用 Local Font Access API?您的公開支援可幫助 Chrome 團隊: 優先開發功能,並向其他瀏覽器廠商說明支援這些功能的重要性。

使用主題標記將推文傳送至 @ChromiumDev #LocalFontAccess,然後 請告訴我們您使用應用程式的位置和方式。

特別銘謝

Local Font Access API 規格是由以下者編輯: Emil A.EklundAlex RussellJoshua Bell 以及 Olivier Yiptong。本文評論者: Joe Medley Dominik Röttsches 以及 Olivier Yiptong。主頁橫幅製作者: Brett Jordan 已啟用 Unsplash