ローカル フォントを使用して高度なタイポグラフィを使用する

Local Font Access API を使用して、ユーザーがローカルにインストールしたフォントへのアクセスと、フォントに関する詳細な情報を取得する方法について学びます。

ウェブセーフ フォント

ウェブ開発を長く行っている場合は、いわゆるウェブセーフ フォントを覚えているかもしれません。これらのフォントは、最も使用されているオペレーティング システム(Windows、macOS、最も一般的な Linux ディストリビューション、Android、iOS)のほぼすべてのインスタンスで利用可能であることが確認されています。2000 年代初頭、Microsoft は「ウェブ向け 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 のフォントブック アプリ。
Google 社員のノートパソコンにインストールされている Google Sans フォント。

攻撃者は、Google Sans などの既知の企業フォントが大量に存在するかどうかをテストすることで、ユーザーが勤務している企業を特定しようとする可能性があります。攻撃者は、これらのフォントに設定されたテキストをキャンバスにレンダリングし、グリフを測定します。グリフが企業フォントの既知の形状と一致する場合、攻撃者はヒットします。グリフが一致しない場合、攻撃者は企業フォントがインストールされていないため、デフォルトの代替フォントが使用されていることを認識します。この攻撃やその他のブラウザ フィンガープリント攻撃の詳細については、Laperdix 調査論文をご覧ください。

会社のフォントは異なっており、インストールされているフォントのリストだけでも識別できます。この攻撃ベクトルの状況は非常に悪化したため、最近 WebKit チームは「[利用可能なフォントのリストに] ウェブフォントとフォントのみを含めるが、ローカルにユーザーがインストールしたフォントではなく、オペレーティング システムに付属するフォントのみを含める」ことを決定しました。(そして、私はローカル フォントへのアクセス権を付与する方法に関する記事を書いています)。

Local Fonts Access API

この記事の冒頭は気分が良くなかったかもしれません。本当にいいものは手に入らないのですか?ご安心ください。すべてが絶望的ではないと考えています。ですが、まずお客様が抱いているであろう疑問にお答えいたします。

ウェブフォントがあるのに、Local Font Access API が必要な理由

従来、プロ品質の設計ツールやグラフィック ツールをウェブ上で提供することは困難でした。1 つの障害は、デザイナーがローカルにインストールした、専門的に作成されヒンティングされたさまざまなフォントへのアクセスと使用ができなかったことです。ウェブフォントでは一部の公開用ユースケースを有効にできますが、ラスタライザがグリフの輪郭をレンダリングするために使用するベクター グリフの形状とフォント テーブルへのプログラムによるアクセスを有効にすることはできません。同様に、ウェブフォントのバイナリ データにアクセスする方法もありません。

  • デザインツールが独自の OpenType レイアウトを実装するには、フォント バイトにアクセスする必要があります。また、デザインツールが低レベルでフックできるようにして、グリフシェイプのベクトル フィルタや変換などのアクションを実行できるようにする必要があります。
  • デベロッパーは、ウェブに移行するアプリに以前のフォントスタックを使用している場合があります。これらのスタックを使用するには、通常はフォントデータに直接アクセスする必要がありますが、これはウェブフォントでは提供されません。
  • 一部のフォントは、ウェブ経由での配信のライセンスがない場合があります。たとえば、Linotype には、デスクトップでの使用のみが許可されているフォントのライセンスがあります。

Local Font Access API は、これらの課題を解決するための試みです。次の 2 つの部分で構成されます。

  • フォント列挙 API。ユーザーが利用可能なシステム フォント全体へのアクセス権を付与できます。
  • 各列挙結果から、完全なフォントデータを含む低レベル(バイト指向)の SFNT コンテナ アクセスをリクエストする機能。

ブラウザ サポート

対応ブラウザ

  • Chrome: 103。
  • Edge: 103.
  • Firefox: サポートされていません。
  • Safari: サポートされていません。

ソース

Local Fonts Access API の使用方法

機能検出

Local Font Access API がサポートされているかどうかを確認するには、次のコマンドを使用します。

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

ローカル フォントの列挙

ローカルにインストールされているフォントのリストを取得するには、window.queryLocalFonts() を呼び出す必要があります。初めてアクセスすると、権限プロンプトが表示され、ユーザーが承認または拒否できます。ユーザーがローカル フォントのクエリを承認すると、ブラウザはループ処理可能なフォントデータを含む配列を返します。各フォントは、family(例: "Comic Sans MS")、fullName(例: "Comic Sans MS")、postscriptName(例: "ComicSansMS")、style(例: "Regular")のプロパティを持つ FontData オブジェクトとして表されます。

// 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);
}

フォントのサブセットのみを対象とする場合は、postscriptNames パラメータを追加して PostScript 名に基づいてフィルタすることもできます。

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

SFNT データへのアクセス

FontData オブジェクトの blob() メソッドを使用して、SFNT に完全にアクセスできます。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 を設計し、実装しました。

ユーザー コントロール

ユーザーのフォントへのアクセスはユーザーが完全に管理しており、権限レジストリに記載されている "local-fonts" 権限が付与されていない限り許可されません。

透明性

サイトにユーザーのローカル フォントへのアクセス権が付与されているかどうかは、サイト情報シートに表示されます。

権限の保持

"local-fonts" 権限はページの再読み込み間で保持されます。サイト情報シートから取り消すことができます。

フィードバック

Chrome チームは、Local Font Access API の使用感について、皆様のご意見をお聞かせいただきたいと考えています。

API 設計について

API が想定どおりに動作しない点はありますか?または、アイデアを実装するために必要なメソッドやプロパティが不足している場合はどうすればよいですか?セキュリティ モデルに関するご質問やご意見がございましたら、対応する GitHub リポジトリで仕様に関する問題を報告するか、既存の問題にコメントを追加します。

実装に関する問題を報告する

Chrome の実装にバグが見つかりましたか?それとも、実装が仕様と異なるのでしょうか?new.crbug.com でバグを報告します。できるだけ詳細な情報を含め、再現手順を簡単に説明してください。[コンポーネント] ボックスに Blink>Storage>FontAccess を入力します。Glitch は、簡単な再現手順をすばやく共有するのに適しています。

API のサポートを表示する

Local Font Access API を使用する予定ですか?公開サポートは、Chrome チームが機能の優先度を判断し、そのサポートの重要性を他のブラウザ ベンダーに伝えるのに役立ちます。

ハッシュタグ #LocalFontAccess を使用して @ChromiumDev にツイートし、どこでどのように使用しているかをお知らせください。

謝辞

Local Font Access API の仕様は、Emil A. EklundAlex RussellJoshua BellOlivier Yiptong にお問い合わせください。この記事は、Joe MedleyDominik RöttschesOlivier Yiptong が確認しました。UnsplashBrett Jordan によるヒーロー画像。