しろもじメモランダム

文字についてあれこれと。

ブラウザからローカルフォントにアクセスする:Font Enumeration API と Font Table Access API

最近の Web アプリはどんどんリッチになっていっていますが、ネイティブアプリとは違って、「ローカルフォント*1を列挙してユーザーに選択させ、それを表示に使う」といったことは仕様上不可能です。

例えば、画像に文字を入れる Web アプリを考えてみます。文字の描画自体は canvas の ctx.fillText() が使えますが、問題はフォントの指定です。ctx.font にローカルフォントを指定するためには、そのローカルフォントのフォント名を知っている必要があります。OS にプリインストールされているフォントでなどであれば、「Web アプリ側であらかじめフォント名のリストを用意しておき、実際にインストールされているかどうかをチェック*2してからユーザーに選択させる」という手段がとれますが、「自分でインストールしたこのフォントが使いたい!」という要求に応えるのは現実的ではありません。

提案中の2つの API

現在、Font Enumeration API・Font Table Access API という API が提案されています。まだまだ検討中のようですが、もし標準化までたどり着けば、この状態が解消されそうです。

Font Enumeration API が、ローカルフォントを列挙するための API です。先ほどの例でやりたかったことは、これで実現できそうです。一方の Font Table Access API は、ローカルフォントのテーブルのバイナリにアクセスするための API です。どちらも単機能で、かなりシンプルな構成になっています。

GitHub リポジトリの README によると、提案の動機としては以下のような感じです。

  1. プロ品質のデザインツールを Web 上に作ろうとすると、以下のような障害がある
    • Web アプリからはローカルフォントの列挙ができない
    • ブラウザは OS のフォントエンジンを使って描画するので、OS 間で差が生じる
    • (ネイティブアプリを Web アプリに移植する場合)自前のフォントエンジンはたいてい生のフォントデータを要求するものだが、Web フォントの仕様ではフォントのバイナリにアクセスできない
  2. Font Enumeration API と Font Table Access API を提供して、このような状況を解消したい

この記事では API の詳細には触れませんが、HarfBuzz/FreeType を WASM に乗せて、それにフォントデータを食わせて描画する、というようなユースケース挙げられています

ちなみにこれらの APIChromeChromium)では8月下旬に "Intent to Implement" になったようです。

ローカルフォントとプライバシー

しかしながら、ローカルフォントの情報は、個人を識別・同定しようとするフィンガープリンティングへの利用が懸念されます。従来では、例えばこんなふうに「フォント名のリストを決め打ちしておき、そのフォントがインストールされているか順次試す」というようなことしかできませんでした。ところが、Font Enumeration API と Font Table Access API が利用可能になると、情報をまるまる吸い出すことが可能になってしまいます。インストールの有無だけでなく、head テーブルや name テーブルから、フォントのバージョンを取得することもできます*3。フォントの販売・サブスクリプション方式によっては、ユーザー情報や固有識別子をフォントファイルに埋め込むものもあるでしょう。そうすると、ほとんど確実に個人を識別できてしまいます。

フィンガープリンティングへの懸念は、提案されている仕様書にも記述があります。また、「会社の PC にコーポレートフォントをインストールしていると、それだけで会社が特定できちゃうよね」というような例も挙げられています。

これらのプライバシー対策として、Font Enumeration API と Font Table Access API は、Permissions API でユーザーの許可を得てからローカルフォントにアクセスする設計になっています。本当に「プロ品質のデザインツール」だけが使うのであれば、これで落ち着くかもしれません。しかし万が一、いろんなサイトでやたらめったら濫用されるようになってしまったとしたら、許可を求めるダイアログがポコポコ出てきて、なかなかうるさい未来になるかもしれません。

Flash 時代のローカルフォント

ところで、過去はどうだったんでしょう? 実は、ActionScript には Font Enumeration API のようなものがありました。Font.enumerateFonts(true) を実行すると、ローカルフォントも含めてフォントを列挙することができました。このおかげで Flash 時代の画像加工ツールは、ローカルフォントによる文字入れが可能だったのですが、Flash の衰退・サポート終了によって不可能になり、表現力が落ちてしまいました。もちろん、プライバシー面では向上したのですが。

ちなみに、ActionScriptExternalInterface を使うと、「ActionScript で取得したフォント名をJavaScript に渡して要素のスタイルに指定する」というようなこともできました。ActionScript 側はこれと同じ感じのコードになります。実際に自分自身、内製ツールの開発で使ったこともあります。

ちなみにちなみに、当時は事実上、Flash がどこでも勝手に実行されるような状況だったので、「ページを踏んだ人のローカルフォントを列挙してこっそりサーバーに送信」というようなことが可能でした。フォント名を送信するだけでなく、「ローカルフォントで描画して送信」までしてしまえばさらに強烈ですね。フォント制作においては、制作中のフォントを自分の PC にインストールしてテストすることが多々あります。そのような状態で、悪意をもったページを踏んだ場合、制作中のフォントの情報が漏れてしまいます。言うまでもなく、わたしはやったことありませんが。

Font Enumeration API と Font Table Access API があれば、過去に Flash で実現されていた表現力を取り戻し、さらに高度な文字組みなどを発展させることができます。アクセスにはユーザーの許可が必要になるので、プライバシー面もある程度担保されます。ただし、「このフォントは OK だけどこれはダメ」のようなアクセス制御は今のところできそうにないので、フォント制作者はうかつに [許可する] を押せなくなるかもしれません…笑

その他雑感

  • Font Enumeration API で取得したフォントのリストを元に、ユーザーにフォントを選択させる場合、並べ方が大変そう
    • 各 OS のネイティブのフォント選択ダイアログがそもそもバラバラでわりと混乱している
    • Adobe 製品もまた独自だったり
  • 前節で書いたように、フォントごとのアクセス制御ができない
    • いっそのこと OpenType 仕様の方に「このフォントは Web API からアクセスしちゃダメ」フラグを新設するとか? :P
  • OS 間のレンダリングの差がなくなったとしても、互換性のためには全く同じバージョンのフォントが各端末にインストールされている必要がある。となると、ローカルフォントだけでなく、Web フォントとの連携も考えた方がよさそう?
    • 現状の Font Table Access API getTables()This method is only usable for local fonts, and will throw an exception if called for a web font.書かれている
  • プライバシーまわりの懸念点がどう決着つくのか気になる

実現すればおもしろいことはいろいろできそうですが、どうなるんでしょうね。

*1:ユーザーの端末にインストールされているフォント。デバイスフォントと呼ばれることも。

*2:適当な文字列のダミー要素を不可視状態で用意 → 要素にチェックしたいフォントを指定 → 指定前後で要素の幅が変わったかチェック、というのが常套手段でしょう。後述のフィンガープリンティングでも使われる手法です。

*3:フィンガープリンティングが目的であるなら、そんな丁寧なことをしなくても、バイナリのハッシュをまるごと取ってしまえば十分そうですが。