Local Font Access API を使用してユーザーのローカルにインストールされているフォントにアクセスし、フォントに関する下位レベルの詳細情報を取得する方法について学習します
ウェブセーフ フォント
ウェブ開発に長年携わってきた方なら、いわゆるウェブセーフ フォントを覚えているかもしれません。これらのフォントは、最もよく使用されているオペレーティング システムのほぼすべてのインスタンス(Windows、macOS、最も一般的な Linux ディストリビューション、Android、iOS)で使用できることが確認されています。2000 年代初頭、Microsoft は「ウェブ向けの TrueType コアフォント」という取り組みを先導しました。この取り組みでは、「フォントを指定したウェブサイトにアクセスするたびに、サイト設計者の意図とまったく同じページを表示する」という目標のもと、これらのフォントを無料で提供しています。はい。これには Comic Sans MS で設定されたサイトも含まれます。従来のウェブセーフ フォント スタックは次のようになります(最終的には sans-serif
フォントをフォールバックします)。
body {
font-family: Helvetica, Arial, sans-serif;
}
ウェブフォント
ウェブセーフなフォントが本当に重要な時代は終わりました。現在、Google はウェブフォントを提供しています。その一部は可変フォントで、露出しているさまざまな軸の値を変更することでさらに微調整できます。ウェブフォントを使用するには、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 などの既知の企業用フォントが多数存在するかどうかをテストすることで、どの企業で働いているかを判断しようとする可能性があります。攻撃者は、これらのフォントで設定されたテキストをキャンバスにレンダリングし、グリフを測定します。グリフが会社のフォントの既知の形状と一致すると、攻撃者にヒットします。グリフが一致しない場合、攻撃者は企業フォントがインストールされていないため、デフォルトの代替フォントが使用されていることがわかります。このようなブラウザ フィンガープリント攻撃の詳細については、Laperdix 他によるアンケート調査をご覧ください。
会社のフォントがばらばらになっているため、インストールされているフォントのリストだけでも識別できます。この攻撃ベクトルの状況は非常に悪くなっており、WebKit チームは最近、オペレーティング システムに付属するウェブフォントとフォントを [利用可能なフォントのリストに記載し、ローカルのユーザーがインストールしたフォントのみを含める] ことを決定しました。(ローカルフォントへのアクセスの許可に関する記事を 示します)。
Local Font Access API
この記事の冒頭でネガティブな気分に陥っていたかもしれません。本当に素晴らしいものが ないのでしょうか?ご安心ください。可能であり、すべてが絶望的なわけではないと考えています。まずは、あなたが自分自身に問いかけていることにお答えします。
ウェブフォントがあるのに Local Font Access API が必要なのはなぜですか?
従来、ウェブでプロフェッショナルな品質のデザイン ツールやグラフィック ツールを提供するのは困難でした。そのひとつに、デザイナーがローカルにインストールしたプロが作成したフォントやヒント付きのフォントをすべて利用できず、それを利用することができないという問題がありました。ウェブフォントを使用すると、公開のユースケースの一部が可能になりますが、ラスタライザがグリフのアウトラインをレンダリングするために使用されるベクター グリフのシェイプとフォント テーブルにプログラムでアクセスすることはできません。同様に、ウェブフォントのバイナリデータにアクセスする方法もありません。
- デザインツールでは、独自の OpenType レイアウトの実装を行うためにフォントバイトにアクセスする必要があります。また、デザインツールを下位レベルでフックして、ベクター フィルタやグリフ形状の変換などのアクションを実行できるようにします。
- デベロッパーは、ウェブに取り込むアプリケーションの以前のフォント スタックを所有している場合があります。これらのスタックを使用するには、通常、ウェブフォントでは提供されないフォントデータに直接アクセスする必要があります。
- 一部のフォントは、ウェブでの配信が許可されていない場合があります。たとえば Linotype には、デスクトップでの使用のみを含む一部のフォントのライセンスがあります。
Local Font Access API は、こうした課題を解決するための試みです。次の 2 つの部分で構成されます。
- フォント列挙 API。ユーザーが利用可能なシステム フォント一式へのアクセス権を付与できます。
- 各列挙結果から、完全なフォントデータを含む低レベル(バイト指向)の SFNT コンテナ アクセスをリクエストする機能。
ブラウザ サポート
Local Font 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 データへのアクセス
完全な SFNT アクセスは、FontData
オブジェクトの blob()
メソッドを介して利用できます。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 でバグを報告します。できる限り詳細な情報と再現手順を記載し、[Components] ボックスに「Blink>Storage>FontAccess
」と入力します。Glitch を使えば、再現をすばやく簡単に共有できます。
API のサポートを表示する
Local Font Access API を使用する予定はありますか?公開サポートにより、Chrome チームは機能の優先順位付けを行い、他のブラウザ ベンダーはサポートがいかに重要であるかを示します。
ハッシュタグ #LocalFontAccess
を使用して @ChromiumDev 宛てにツイートを送信し、使用場所と使用方法をお知らせください。
関連情報
- 説明
- 仕様のドラフト
- フォントの列挙に関する Chromium のバグ
- フォント テーブルへのアクセスに関する Chromium のバグ
- ChromeStatus エントリ
- GitHub リポジトリ
- TAG の審査
- Mozilla 標準の位置
謝辞
Local Font Access API の仕様は Emil A. Eklund、Alex Russell、Joshua Bell、Olivier Yiptong。この記事は、Joe Medley、Dominik Röttsches、Olivier Yiptong によってレビューされました。ヒーロー画像(作成者: Brett Jordan、Unsplash)