WebサービスでのTrueTypeフォント描画問題の解決方法
はじめに
こんにちは。私は辞書アプリの設計を担当しています。
今回はカシオが提供している辞書サービスで発生していた「一部の端末で文字が読みづらい」という問題を解決した時の内容です。読み進める上で幾つかの[補足]を挟みました。本題とはズレますが理解を深めるのに役立てて貰えたらと思います。
一部の環境でフォントの見栄えが悪い問題
Web上で使える辞書サービスを開発していたところ、キレイに見えるはずのフォントが特定の端末で粗く表示され、読みづらいという問題に直面しました。
フォントが読みづらい画面例
具体的なNGの症状は次の通りでした。
文字の線の太さが一定でない(例:アルファベットの上部が太くて下部が細い)
ジャギーが目立つ(例:ひらがなの斜めの線などで角張っている箇所がある)
文字としては判別できますが、太さの不均一や滑らかさの不足はストレスに感じられます。
[補足] 本記事におけるWebサービスの構成
学校教育に導入されている学習ツールClassPad.netで、その中の辞書機能です。
PC/スマホ/タブレットなど様々な端末で利用できます。
専用アプリではなくWebブラウザ(Google Chrome、Safari等)で利用します。
このサービスでは独自のアウトラインフォント(.woff2形式)で描画します。
原因はフォントヒンティング
アウトラインフォントにはフォントヒンティング(font hinting)と呼ばれる処理が適用されることがあり、今回はこれが悪さをしていました。以下、単にヒンティングと呼びます。
このヒンティングというのは、アウトラインフォントを描画する際に「文字サイズが小さい」とラスタライザーが判断した場合に、該当の字形の点や線を調整して視認性を確保する処理です。
点や線の位置・太さを調整してくっきり表示させるだけでなく、線の間引きが行われる場合もあります。上記の例「書」では、9pxサイズで横棒の数が少ない字形に変化しています。このような字形の変化を含む調整を実現するために、フォントデータ1文字1文字に設定された「ヒント情報」を利用しています。
具体的な処理内容として、液晶のピクセルに合わせてエッジを強調したり、線の太さや本数を部分的に調整したりしています。デメリットとしては、滑らかさが失われてギザギザに見える、本来の字形から変形することで全体のバランスが崩れるといったことが挙げられます。
[補足] アウトラインフォントの仕組み
アウトラインフォントは、複数の「制御点(座標)」で文字の輪郭を定義しているため輪郭の曲線を数学的に計算できます。そのため、あらゆる文字サイズ・・・例えどこまで拡大しても滑らかな輪郭が計算により描画可能です。
一方で、小さい文字サイズでは字形が潰れてしまうことがあります。その場合にヒンティングを行い、制御点の位置を調節することで線の太さや字形自体を変更して可読性を維持します。
アウトラインフォント以外の形式としては、ビットマップフォントがあります。これは文字サイズごとに白黒のパターン情報を持つことで高速に描画できますが、表示はデータが存在する文字サイズに限られ、無段階の拡大/縮小はできません。解像度の低い液晶や弱いCPUなどを持つ昔のデジタル端末で多く利用されていました。
ヒンティングが逆効果になる場合がある
ヒンティングは文字サイズが「小さい」と判断された場合に適用されますが、文字が小さいかどうかの判定基準は環境によって異なります。
具体的には、Webブラウザ(ラスタライザー)の種類、フォントの種類、液晶の解像度など、様々な条件によって「何px以下でヒンティングを適用する」のかが変わってきます。
今回NGが発生していた端末では、ページ倍率:100%で表示しているにもかかわらずヒンティングが適用されてしまい、特定の画だけが太くなったり細くなったり、あるいはエッジが強調されたりして視認性を落としていました。
これが本当に「小さい」文字サイズであったならば、ヒンティングが適用されることで視認性が高まりますが、「普通の」文字サイズで適用された場合は、ただ単にギザギザして汚く見えてしまい、逆効果となります。
ヒンティングを適用しないことで解決
結論から言えば「ヒンティングが一切適用されないようにする」という対応で文字を滑らかにして解決しました。
今回の問題点は次のとおりです:
デフォルトの表示倍率でヒンティングが適用されていた(一部の端末)
ヒント情報の補正品質が低く、見栄えが悪くなっていた(逆効果)
上記は15pxほどの文字サイズにヒンティングを適用した場合・適用しない場合の例です。
左側は線がくっきりしていますが、例えば「き」の文字には1本の線の幅に1pxと2pxの部分があるためギザギザに見えます。一方、右側は1pxと2pxの部分があるものの灰色のグラデーションが残っていて、全体が滑らかに見えます。
実際のところ、ヒンティングが適用されなくて困るケースは殆ど発生しないため、ヒンティングのデメリット(文字がギザギザになる)の防止策として、そもそもヒンティングを適用しないという選択を行いました。
ヒンティングは表示エリアが限られた解像度の低い端末や、自由に拡大/縮小ができないマシンスペック・UIで有効な処理です。しかし、昨今のスマホ・タブレット、PCモニタには十分な解像度があり、また、ページの拡大/縮小が自在にできるため「文字が潰れていて読めない、困る」といった場面は殆どなく、ヒンティングが必要な状況はかなり減ってきていると言えます。
本記事の想定環境下では、ヒンティングを不要な処理として扱い、以下に回避方法を記します。
ヒンティングの回避方法1
描画時に微妙に回転させて描画することでヒンティングの適用を回避することができます。この方法はCSSでテキストの表示エリアを回転させることで簡単に実現することができます。
これは文字が完全に水平であるときにしかヒンティングが実行できないという制約を利用した回避手段になります。
上記の画面例は説明のため大げさに回転させていますが、実際にやる場合は人の目で見ても分からないほど「微妙に傾ける」というのがポイントになります。
CSSで回転が反映される最小値(しきい値)は環境に依存するようですが今回は0.03度の傾きを設定して検証しました。
今回の検証環境では0.02度では回転が反映されませんでした。本記事では、0.03度を表示が改善される最小値として話を進めます。
この0.03度は人間には変化が分かりにくく、かつ、ヒンティングは適用されなくなるので解消したかに思われましたが・・・。
[補足] ローカル環境でのCSS書き換え(微小回転)の試し方
PCでGoogle ChromeやFirefoxをお使いであれば「stylus」というアドオンで自分の環境だけCSSを上書きすることができます。
アドオン導入後、見た目を変更したいWebページを開き、アドオンの「次のスタイルを書く」から書き換えたいディレクトリを選択します。
描画エリア<div>のidやclassを指定する必要があるので、F12キーで出てくるデベロッパーツール等で特定すると良いです。(左上のアイコンが便利です)
stylusのエディタでは書き換えたい<div>のidやclassについて、次のように回転量を指定すればOKです。
/* 検索結果リストのテキストエリアを0.03度傾ける */
.dictionary-side-list {
transform:rotate(-0.03deg);
}
/* データ表示のテキストエリアを0.03度傾ける */
.dictionary-detail-content-wrap {
transform:rotate(-0.03deg);
}
この対応の問題点
表示エリアの微妙な回転によって様々な問題が発生したため、結局この対応は見送りました。代表的な問題点は以下の通りです。
より大きな回転角を与えなければ改善効果が得られない場合がある
CSSのscale()関数を使っている場合は縮小率に応じて回転角が必要になるようでした。例えばscale(0.5)の箇所は回転角0.03度の2倍の0.06度を指定しないと改善しませんでした。
なるべく小さな回転角度を設定したいところですが、このように一部の表示がボトルネックになることがあります。コンテンツごとに最低値が異なることから、必ずこれで十分という回転角度が決められませんでした。
回転が無効になる場合がある
今回のWebサービスではスクロール処理の高速化のためVirtualScrollを利用していますが、これとの相性が良くなく、スクロール(再読み込み)した場合に回転の効果が無くなる、表示エリアの幅を変更したときに回転の効果が無くなる、といった問題が見つかりました。
スクロールしていくと徐々にズレていく
本文が長い見出しを表示したとき、最初は良いのですが読み進めていく(スクロールしていく)につれて徐々にズレが大きくなっていきました。
何となく傾いていると感じる
人の目で分からないほど「わずかに」という話で、0.03度と聞くと見た目では分からないだろうと感じますが、実際にフルHDのモニタの両端で何pxずれるか計算しますと、sin(0.03*π/180)は約0.0005236であり、横幅1920pxを掛けると落差約1.005pxとなります。
PC画面の両端でたった1pxの傾きですが、なんとなくの違和感や気持ち悪さがありました。
ヒンティングの回避方法2
結局、今回はフォントデータからヒント情報を削除する対応を実施しました。この方法であれば調整する情報がそもそも無いため、ヒンティングが適用されることはありません。
HTML/CSSではヒンティングの設定を指定できない
調べた限り、HTML/CSS内で「ヒンティングのON/OFF」、「文字サイズは幾つ以下からヒンティングを適用」といった直接的な指定はできず、HTML/CSS上では前項のように回転処理を利用して無理やりヒンティングを回避するしか方法はなさそうでした。
ヒンティングを適用するかどうかはWebブラウザごとの判断になりますのでHTML/CSSで指定できないのであればサーバー側でできる対応はヒント情報の削除しかありません。
フォントデータからヒント情報を削除する
ヒンティングはブラウザ判断で適用されてしまうため制御できませんが、そもそもフォントデータ内にヒント情報が無ければ実施されません。今回はヒント情報を削除して対応しました。
ヒント情報を削除して保存できるソフトFont Forge
フォントからヒント情報を削除するには「FontForge」というソフトが便利です。
該当のアウトラインフォントを開き、出力のオプションで「TrueTypeヒント」の項目をOFFにするだけでOKです。
改善結果・まとめ
今回のフォント描画問題の解決前後の様子を以下に貼り付けます。(できれば等倍で表示いただきたいです。)
線の太さの均一化がされたことやジャギーがなくなったことで文字が読みやすくなったかと思います。
改善前の様子(Windows / Google Chrome / 100%表示)
改善後の様子(Windows / Google Chrome / 100%表示)
今回はヒンティングという機能が逆効果となり、フォントの描画品質を落としていました。拡大/縮小が自在にできる昨今のデジタル機器ではそこまで必要とされていない機能でしたので、これを無効化することで問題を解決しました。
当開発チームではユーザーが不満なく学習を進められるように今後もUX改善を続けていきたいと思います。
【関連記事】
フォントを0→1で開発した際のプロセスについて紹介しています。
【前回のテックブログはコチラ】
当社が開発したスタンプラリーアプリ「MEGURUWAY」での謎解きコンテンツ制作について紹介しています。
【カシオのソフトウェア採用についてはこちら】