Local Font Access API は、ユーザーのローカルにインストールされているフォントデータにアクセスするためのメカニズムを提供します。これには、名前、スタイル、ファミリーなどの上位レベルの詳細情報や、基になるフォント ファイルの未加工のバイト数も含まれます。SVG 編集アプリの Boxy SVG で、この API がどのように利用されるかを説明します。
はじめに
(この記事は動画でもご覧いただけます)。
Boxy SVG は、ベクター グラフィック エディタです。主なユースケースは、SVG ファイル形式の図面を編集して、イラスト、ロゴ、アイコンなどのグラフィック デザインの要素を作成することです。ポーランドのデベロッパー Jarosław Foksa によって開発され、2013 年 3 月 15 日に初めてリリースされました。Jarosław は Boxy SVG ブログを運営し、このアプリに追加された新機能を発表しています。デベロッパーは Chromium の Project Fugu の強力なサポーターで、アプリのアイデア トラッカーには Fugu タグも載っています。
Boxy SVG の Local Font Access API
Jarosław がブログで投稿している機能の一つに、Local Font Access API があります。Local Font Access API を使用すると、ユーザーはローカルにインストールされたフォントにアクセスできます。これには、名前、スタイル、ファミリーなどの上位レベルの詳細情報や、基になるフォント ファイルの未加工のバイト数も含まれます。次のスクリーンショットは、MacBook にローカルにインストールされたフォントへのアクセスをアプリに付与し、テキストにマーカー フェルト フォントを選択した様子です。
基盤となるコードは非常にシンプルです。ユーザーが初めてフォント ファミリー選択ツールを開くと、アプリケーションはまず、ウェブブラウザが Local Font Access API をサポートしているかどうかを確認します。
また、古い試験運用版の API がないかチェックし、存在する場合はそのバージョンを使用します。2023 年現在、試験運用版の Chrome フラグを通じて短期間しか利用できなかったため、古い API を無視しても問題ありませんが、一部の Chromium 派生物では引き続き使用する可能性があります。
let isLocalFontsApiEnabled = (
// Local Font Access API, Chrome >= 102
window.queryLocalFonts !== undefined ||
// Experimental Local Font Access API, Chrome < 102
navigator.fonts?.query !== undefined
);
Local Font Access API を使用できない場合は、フォント ファミリー選択ツールがグレーになります。フォントリストの代わりに、プレースホルダ テキストがユーザーに表示されます。
if (isLocalFontsApiEnabled === false) {
showPlaceholder("no-local-fonts-api");
return;
}
それ以外の場合は、Local Font Access API を使用してオペレーティング システムからすべてのフォントのリストを取得します。権限エラーを適切に処理するために必要な try…catch
ブロックに注目してください。
let localFonts;
if (isLocalFontsApiEnabled === true) {
try {
// Local Font Access API, Chrome >= 102
if (window.queryLocalFonts) {
localFonts = await window.queryLocalFonts();
}
// Experimental Local Font Access API, Chrome < 102
else if (navigator.fonts?.query) {
localFonts = await navigator.fonts.query({
persistentAccess: true,
});
}
} catch (error) {
showError(error.message, error.name);
}
}
ローカル フォントのリストが取得されると、簡素化されて正規化された fontsIndex
がそこから作成されます。
let fontsIndex = [];
for (let localFont of localFonts) {
let face = "400";
// Determine the face name
{
let subfamily = localFont.style.toLowerCase();
subfamily = subfamily.replaceAll(" ", "");
subfamily = subfamily.replaceAll("-", "");
subfamily = subfamily.replaceAll("_", "");
if (subfamily.includes("thin")) {
face = "100";
} else if (subfamily.includes("extralight")) {
face = "200";
} else if (subfamily.includes("light")) {
face = "300";
} else if (subfamily.includes("medium")) {
face = "500";
} else if (subfamily.includes("semibold")) {
face = "600";
} else if (subfamily.includes("extrabold")) {
face = "800";
} else if (subfamily.includes("ultrabold")) {
face = "900";
} else if (subfamily.includes("bold")) {
face = "700";
}
if (subfamily.includes("italic")) {
face += "i";
}
}
let descriptor = fontsIndex.find((descriptor) => {
return descriptor.family === localFont.family);
});
if (descriptor) {
if (descriptor.faces.includes(face) === false) {
descriptor.faces.push(face);
}
} else {
let descriptor = {
family: localFont.family,
faces: [face],
};
fontsIndex.push(descriptor);
}
}
for (let descriptor of fontsIndex) {
descriptor.faces.sort();
}
その後、正規化されたフォント インデックスが IndexedDB データベースに保存されるため、簡単にクエリを行ったり、アプリ インスタンス間で共有したり、セッション間で保持したりできます。Boxy SVG は、Dexie.js を使用してデータベースを管理します。
let database = new Dexie("LocalFontsManager");
database.version(1).stores({cache: "family"}).
await database.cache.clear();
await database.cache.bulkPut(fontsIndex);
データベースが入力されると、フォント選択ツール ウィジェットがクエリを実行し、結果を画面に表示できます。
Boxy SVG は、<bx-fontfamilypicker>
というカスタム要素でリストをレンダリングし、各フォント リストアイテムが特定のフォント ファミリーで表示されるようにスタイルを設定します。ページの他の部分から分離するために、Boxy SVG はこの要素とその他のカスタム要素で Shadow DOM を使用します。
まとめ
ローカル フォント機能は非常に好評で、ユーザーはデザインや創作でローカル フォントを利用することができます。API の形状が変更され、機能が短時間使用できなくなると、ユーザーはすぐにそのことに気付きました。Jarosław はコードを、最新の Chrome や、最新バージョンに切り替えられていない他の Chromium の派生物で機能する防御パターンにすばやく変更しました。上記のスニペットをご覧ください。Boxy SVG を実際に使ってみて、ローカルにインストールしたフォントを確認します。Zapf Dingbats や Webdings など、長く忘れられてきた名作が見つかることもあります。