フォントカーニングの実装について学ぶ

フォントカーニングとは

フォントカーニングとは一般的に文字間のスペースのことを表します。カーニングは OTF(Open Type Font) や TTF(True Type Font) のようなフォントファイルによって自動で設定されています。

例えば日本語の文字を描画した場合、等幅で文字が表示されます。しかし等幅で表示されてしまうと、のような文字幅が小さい文字を描画したときに、余分なスペースが生まれてしまいます。このスペースがあるとデザイン的に違和感のあるものになったり、読みにくいという問題が生まれてしまいます。
この問題を解消するために、一般的に文字詰めという手法が取られます。Adobe Photoshop のようなデザインツールでは文字間を自分で細かく調整できたり、自動で設定できたりします。

GUI でどのようにカーニングを調整するか

デザインツールであれば手動で自由に設定できます。しかし、GUI において 1 文字 1 文字の文字間を調整していてはパフォーマンスや工数の関係で難しいです。
そのためある程度自動で文字詰めをして、CSS のletter-spacingのようなプロパティで一括を使って調整するのが現実的です。

自動で調整するには以下のようにいくつかの方法があります。(名前はそれぞれ Adobe から取ってきています。)

  • メトリクスカーニング
  • オプティカルカーニング
  • プロポーショナルメトリクスカーニング

メトリクスカーニングとは、フォントファイルに含まれているペアカーニング情報というものを取り出し、カーニングを調整する方法です。
ペアカーニングとはその名の通り、ある文字とある文字の組み合わせの時にどのくらいの文字間を開けるかを示すものです。ペアカーニングを持っていない場合は文字詰がされないので適切に設定されているフォントを選ぶ必要があります。

オプティカルカーニングとは、ソフトウェア側が文字の形状に合わせて設定する方法です。フォントファイルに含まれている情報ではなくコード側でフォントの形状にピッタリ合うように調整します。

プロポーショナルメトリクスカーニングとは、OTF に設定されているpaltという機能を使うことで文字間をフォントデザイナーが設定した理想の幅に字詰めする方法です。CSS ではfont-feature-settings: "palt"というプロパティがあります。

これらの方法を使うことで GUI でも自動で字詰めを行うことができます。

どのように実装するか

メトリクスカーニング

メトリクスカーニングはペアカーニングを取得し、その値を元に文字間を設定すれば良いです。 大抵のフォントライブラリにはカーニングを取得するためのメソッドが公開されています。 例えば Rust のフォントライブラリである ab_glyph にはkern メソッドが公開されています。

以下のように使用します。

let font = FontRef::try_from_slice(include_bytes!("../fonts/sample.otf"))?;
let scaled_font = font.as_scaled(PxScale::from(45.0));
let kern = scaled_font.kern(font.glyph_id('あ'), font.glyph_id('い'));

オプティカルカーニング

オプティカルカーニングは glyph の width を取得できるのでその width をそのまま文字幅として使用します。

通常は advanced width を使います。advanced width を使うことで日本語フォントの場合、等幅になります。

Glyph の Positioning 周りの話はfreetype2 の Managing Glyphsで説明されています。

プロポーショナルメトリクスカーニング

Rust の既存のライブラリでは抽象化された API が見つからなかったです。なんとなくできそうな方法としては、ttf_parser という parser から受け取る方法があります。また、rustybuzz というライブラリが FeatureList から値を受け取れそうですが、palt が受け取れるかは微妙です。palt が日本語などの等幅フォントでしか使えない機能のため、あまりサポートされていなそう。

仕組みとしては OTF ファイルの中に FeatureList というリストの中にpaltなどの機能のタグとメトリクスをどのように調整するかの情報が入っています。 この情報を Lookup Table と照らし合わせ、該当の Glyph 情報を取得します。ここで取得した情報をカーニングとして設定すれば晴れてpaltを適用できるはずです。

詳しいテーブルの構造は「OpenType フォントのプロポーショナルメトリクス情報をぶっこぬく」という記事かOTF の GPOS(glyph の配置などに関するテーブル)の仕様を確認してください。

まとめ

フォント周りはあまり知識がなくまだあまりわかっていないところが多く辛いです。詳しい方いたら教えてください。

オプティカルカーニングを試してみたリポジトリを作ったのでよかったらみて見てください。