omuronの備忘録

個人的な備忘録

「Nu みちしるべ」フォントの中身を見る

kokagem.sakura.ne.jp

「Nu みちしるべ」は、日本の道路標識に基づいて作ったフォントです。標識の数字に興味を持ったのが切っ掛けで作りましたが、数字だけだとフォントとしてあまり面白くないので、周りの赤い円を含めて自動的に組み立てられるように作り込んでみました。道路標識の全種に対応しているわけではないです。用途は…何かあるでしょうか。

道路標識を表示できるフォント「Nu みちしるべ」が非常に話題になってます。
さゆぬ氏が作成したOpenTypeフォントで、Boothにて公開されており、無料で入手できます。
非常に面白い動きをしているのですが、どうやって実現しているか気になりました。

標識を自動的に組み立てるには、アプリケーションが OpenType‐フォントの高度な機能に対応している必要があります。非対応のアプリしか持っていない場合、手動で画像編集をして部品を組み立てる事になりますが、その部品としてフォントを利用できます。

OpenType フォントの機能を使っているとのことなので、フォントの中身を見てみたいと思います。

ライセンスの確認

フォントの改変と頒布
このフォントに由来するデータを使って新たなフォントを作成し、それを私的な環境にインストールできます。それを頒布してはいけません。ここで言う「新たなフォント」とは、一字づつ切り出した画像など、フォントと同然に使用できるデータを含みます。

フォントの私的な改変は許諾されております。
改変ができるのであれば、中身をみるという行為は問題なさそうに思われますので、実装を見てみたいと思います。
(問題ありそうであれば削除します)

XML ファイルに変換する

fonttools に付属する ttx コマンドを利用して、XML ファイルにして覗いてみたいと思います。
Python 3 系をいれており、 pip が使えるなら以下のコマンドでインストールできます。

$ pip install fonttools

fonttools を入れたら、 ttx コマンドでフォントを変換します。

$ ttx NuMichishirube-Regular-v1.0.0.otf 

これで NuMichishirube-Regular-v1.0.0.ttx という xml 形式のファイルが出来上がりました。
テキストファイルなので、適当なエディタで開いて眺めていきたいと思います。

XML ファイル見る

全体で、25,000行ぐらいのファイルが出力されました。
テーブル単位で構造化されているので気になるところを見ていきます。

GlyphOrder

フォントに入っているグリフがGID 順に表示され、人が読む用の名前も付与されて出力されてます。

  <GlyphOrder>
    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
    <GlyphID id="0" name=".notdef"/>
    <GlyphID id="1" name="space"/>
...
    <GlyphID id="174" name="alignEnd"/>
    <GlyphID id="175" name="alignEndSpace"/>
  </GlyphOrder>

フォントに必ず必要な .notdef を含めて176文字のみ収録されているようです。

また、数字ごとに4種類のグリフパターンが収録されております。

    <GlyphID id="66" name="zero"/>
...
    <GlyphID id="76" name="zero.narrow"/>
...
    <GlyphID id="86" name="zero.small"/>
...
    <GlyphID id="96" name="zero.small.narrow"/>

GPOS

GPOS より(DeepL 翻訳)

Glyph Positioning table (GPOS) は、フォントがサポートする各スクリプトや言語システムにおいて、洗練されたテキストレイアウトやレンダリングを実現するためのグリフ配置を正確に制御します。

ここがとにかく大きい。20,000行近くあります。
このフォントの肝はここにありそうです。

  <GPOS>
...
      <Lookup index="0">
        <LookupType value="8"/>
        <LookupFlag value="0"/>
        <!-- SubTableCount=265 -->
        <ChainContextPos index="0" Format="3">
...

LookupType 8 のサブテーブルが265個あり、とても沢山のパターンを処理していることが見受けられます。
この LookupType 8 がどういう制御をしているか 仕様 を確認します。

LookupType 8: Chaining Contextual Positioning Subtable A Chaining Contextual Positioning subtable

文脈に応じてテキストの位置調整を行うことができます。
言葉で説明するより、仕様書の表を見るほうがわかりやすいと思うのですが、前後の文字に応じていい感じに調整できる機能ですね。

f:id:omron:20201107135631p:plain

この機能を用いて特殊な並びになったときに位置調整を行っているようです。

標識として組み立てるには全部品を丸括弧 ( ) か波括弧 { } で囲み、例えば「(○40)」のように書きます。

公式より引用しましたが、「丸括弧」などを利用して判断している様です。

GSUB

GSUB より(DeepL 翻訳)

グリフ置換 (GSUB) テーブルは、アラビア文字のカーシブに連結された形などのスクリプトの適切なレンダリングや、合字などの高度なタイポグラフィ効果のためにグリフを置換するためのデータを提供しています。

グリフを置換するテーブルを利用して、丸括弧を消したり、文字の種類を切り替えたりしてそうです。

  <GSUB>
...
      <Lookup index="3">
        <LookupType value="6"/>
        <LookupFlag value="0"/>
        <!-- SubTableCount=72 -->
        <ChainContextSubst index="0" Format="3">

GSUB は LookupType 6 のサブテーブルが72個あり、一番大きい処理になってます。
GSUB LookupType 6 も GPOS LookupType 8 と同様に文脈に応じて文字を置換する機能です。

試してみる

FontGoggles を利用して、「(○40)」の入力を可視化してみます。

特殊処理のきっかけとなる丸括弧を入力せず「○40」と入力した場合、入力した文字がそのまま表示されました。

f:id:omron:20201107141329p:plain

「(○40)」と入力した場合は、丸括弧が空文字に置換され○の幅が変更されていることがわかります。

f:id:omron:20201107141526p:plain

「(○100)」と入力した場合は、数字も狭いものに置換されてますね。

f:id:omron:20201107141749p:plain

まとめ

OpenType の高度な組版機能 GPOS / GSUB を利用し、文脈に応じた処理を用いて表現していることがわかりました。
本来この機能は、アラビックとかタイ語とか文脈に応じてグリフが変化する言語を表現するためのものです。
これを利用してこんな面白い表現をするなんて素晴らしいアイデアだと感じました。
柔軟なアイデアを思いついて形にできる力が私も欲しい!