TrueType命令で遊ぶシリーズ。
- 初めてのTrueType命令: Windowsでは見えないフォントをつくる - にせねこメモ
- フォントサイズに合わせて回転するフォントを作る(1) - にせねこメモ
- PPEM・ポイントサイズを表示するフォント - にせねこメモ
- TrueType命令で三角関数(sin, cos)を計算する - にせねこメモ
- フォントサイズに合わせて回転するフォントを作る(2) - にせねこメモ
- TrueType命令で擬似乱数: 線形合同法 - にせねこメモ
- TrueType命令でビット演算 - にせねこメモ
- アウトラインがぶれるフォント - にせねこメモ
概要
文字が回転するvariable fontを作った。variable fontの変化軸の値をTrueType命令で読み取り、その値に応じてTrueType命令を使ってグリフを回転させている。テストページはこちら。たぶんMacでは動かない。
実行例
序論
OpenType font formatのversion 1.8からvariable font機能が追加された。2016年9月のことである。これにより、変化軸を設定して、それに沿って変化するフォントを作成できるようになった。基本の形の情報と変化した形の差分情報を持たせていて、レンダリング時に変化軸の値を変化させると、形が補間される。3DCGを知っているのであれば、モーフターゲット等と呼ばれる機能を想像するとよい。例えば、セリフ体とサンセリフ体を設定してそれらを切り替えたりその中間にするだとか、文字の太さ(ウェイト)やエックスハイトを自在に変化させることが(ちゃんと設定すれば)可能になる。
このvariable fontへのソフトウェアの対応が最近進んできたので、これを利用して文字の回転を指定できるフォントを作ってみたい。
フォント作成時に設定した変化軸の値は、レンダリング時にユーザー側で変化させることができるが、この値を新規に追加されたTrueType命令GETVARIATION
を利用して読み出すことができる。
このため、以前作成した、TrueType命令を利用してフォントサイズに応じて回転するフォントを改変することによって、文字が回転するvariable fontを作ることができる。
Variable fontについて
variable fontについての概要をつかむには次の記事がよい。
これを読みながらメモしたツイートのまとめが次である。
twitter.com
TrueTypeベースのvariable fontに必須のものは次の通り。
'STAT'
テーブル…フォントスタイルをattribute valuesの集合で表現するもの'fvar'
テーブル…フォント内で使われる軸を定義。named instanceの情報も含む。'gvar'
テーブル…バリエーションの変化差分
編集
TTX/FontTools
フォントの編集には、OpenTypeフォントバイナリと編集に便利なXMLとを相互変換できるユーティリティであるTTX/FontToolsを用いる。これは、pythonとpipが入っている環境で
pip install fonttools
とすれば簡単に導入できる。
使用方法としては、
ttx font.ttf
とするとフォントファイルfont.ttf
の内容をXML形式でダンプしたfont.ttx
が出力される。反対に
ttx font.ttx
とするとfont.ttx
をフォントバイナリに変換する。名前が被る場合は#1などの連番suffixがつく。
尚、今回はTTXのバージョンは4.2.5を利用した。.ttx
ファイルはTTXのバージョンによって書式が変更されていたりするので、別のバージョンでダンプした.ttx
ファイルを.ttf
に再変換するのは避けた方がよさそうに思う。
作成
さて、回転するvariableフォントの作成に入る。
のページで作成したフォント
- Arotate.ttf (1.74 MB)
をダウンロードしてきて、TTXで.ttx
ファイルに変換する。
ttx Arotate.ttf
すると、Arotate.ttx
ファイルが出来上がるので、これをテキストエディタで開いて編集していく。
まず、<name>
の下に、変化軸やインスタンスの名前に使う文字列データを最低限追加しておく。WindowsとMac用に2回同じ値を定義した。
nameIDは、0~255は用途が決まっている(または予約されている)ので、256~32767を使う*1。
<namerecord nameID="259" platformID="1" platEncID="0" langID="0x0" unicode="True"> Rotation </namerecord> <namerecord nameID="260" platformID="1" platEncID="0" langID="0x0" unicode="True"> Regular </namerecord> <namerecord nameID="261" platformID="1" platEncID="0" langID="0x0" unicode="True"> Arotate-Regular </namerecord> <namerecord nameID="259" platformID="3" platEncID="1" langID="0x409"> Rotation </namerecord> <namerecord nameID="260" platformID="3" platEncID="1" langID="0x409"> Regular </namerecord> <namerecord nameID="261" platformID="3" platEncID="1" langID="0x409"> Arotate-Regular </namerecord>
そして、variable fontの体をなすための次のコードを<ttFont>
~</ttFont>
の下に追加する。
<STAT> <Version value="0x00010001"/> <DesignAxisRecordSize value="8"/> <!-- DesignAxisCount=1 --> <DesignAxisRecord> <Axis index="0"> <AxisTag value="ROTT"/> <AxisNameID value="259"/> <!-- Rotation --> <AxisOrdering value="0"/> </Axis> </DesignAxisRecord> <!-- AxisValueCount=1 --> <AxisValueArray> <AxisValue index="0" Format="2"> <AxisIndex value="0"/> <Flags value="2"/> <ValueNameID value="19"/> <!-- Regular --> <NominalValue value="400.0"/> <RangeMinValue value="350.0"/> <RangeMaxValue value="450.0"/> </AxisValue> </AxisValueArray> <ElidedFallbackNameID value="0"/> <!-- Regular --> </STAT> <fvar> <!-- Rotate --> <Axis> <AxisTag>ROTT</AxisTag> <Flags>0x0</Flags> <MinValue>-180.0</MinValue> <DefaultValue>0.0</DefaultValue> <MaxValue>180.0</MaxValue> <AxisNameID>259</AxisNameID> </Axis> <!-- Regular --> <!-- PostScript: Arotate-Regular --> <NamedInstance flags="0x0" postscriptNameID="261" subfamilyNameID="260"> <coord axis="ROTT" value="0.0"/> </NamedInstance> </fvar> <gvar> <version value="1"/> <reserved value="0"/> </gvar>
STATはフォントスタイルの定義であり、fvarはフォントで使われる変化軸の定義である。それっぽくなるように適当に設定した。
fvarでは変化軸を一つ(ROTT)定義し、あとはそれっぽい値にしている。変化軸はASCII文字4文字からなるタグで指定する。タグはイタリック、幅、ウェイトなどのためにいくつか登録されたものもあるが、自分で定義することもできる。フォント作者が勝手に作ったタグはアルファベット大文字で始まりアルファベット大文字と数字のみからなると決められている*2。
また、回転角を指定する軸なので、-180°~180°を指せるように軸の設定をした。
gvarは変化する部分を定義するテーブルだが、ほぼ空っぽである。これでは変化軸に沿って値を変えてもグリフは変形しないはずであるが、変形はTrueType命令に任せることにするのでこうなっている。
次にTrueType命令の改変を行う。
TrueType instructionで変化軸の値の取得するために、GETVARIATION命令が追加された。OpCodeは0x91。
GETVARIATION命令を実行すると、変化軸の数だけ現在のノーマライズされた座標の値(-1.0~1.0ということだろう)がスタックに積まれる(形式は2.14の固定小数点数)。積まれる順番は'fvar'に定義されているものと同順で、'fvar'の最初に定義されているものが最初にpushされる。
新規に追加されたこの命令であるが、もちろん対応していない処理系では扱うことができず、未定義命令としてエラーになってしまう。なので、互換性のため、'fpgm'あるいは'prep'においてIDEF命令によって互換の命令を割り当てる必要がある(未定義命令でないものはIDEFによって上書きされない)。つまり、今回は変化軸が1個なので、GETVARIATION命令と同じ個数pushする次のような命令を追加する必要がある。
PUSHB[ ] 145 /* OpCode 0x91, GetVariation */ IDEF[ ] PUSHB[ ] 0 /* Number of axes in this font = 1*/ ENDF[ ]
…と仕様書にはあるのだが、現在Windows 10において、これを書くとGETVARIATIONがうまく動かないみたいなので書かない。代わりにGETINFOでバージョンを取得し、GETVARIATIONに対応した42以上ならばGETVARIATIONを実行、それ以外なら0を返すようにすることにする。
PUSHB[ ] 1 GETINFO[ ] /* Get version */ PUSHB[ ] 42 GTEQ[ ] /* version >= 42 */ IF[ ] GETVARIATION[ ] /* returns a 2.14 fixed number corresponding to the axis */ ELSE[ ] PUSHB[ ] 0 EIF[ ]
GETVARIATIONによって得られる変化軸の値はF18Dot14で-1.0~1.0に正規化される。今回は入力軸は回転角度で-180~180が-1.0~1.0に正規化されている。
TrueType命令で三角関数(sin, cos)を計算する - にせねこメモで作成した三角関数は入力としてスタックトップからF26Dot6の角度[°]を受け取るので、-1.0~1.0(F18Dot14)→-180.0~180.0(F26Dot6)への変換が必要。つまり、180.0を掛けた後、256.0で割ってやればよい(あるいは、180/256=0.703125を掛ける)。
PUSHB[ ] 45 /* 180/256 */ MUL[ ]
変化軸の値を変更する度に'prep'は呼び出されるはずなので、以前の回転フォントにおいて回転の角度をStorage Areaに書きこんでいた部分を、この命令で置き換える。
これらをまとめると、<prep>
は次のようになる。
<prep> <assembly> PUSHB[ ] 1 GETINFO[ ] /* Get version */ PUSHB[ ] 42 GTEQ[ ] /* version >= 42 */ IF[ ] GETVARIATION[ ] /* GetVariation: returns a 2.14 fixed number corresponding to the axis */ ELSE[ ] PUSHB[ ] 0 EIF[ ] PUSHB[ ] 45 /* 180/256 */ MUL[ ] PUSHB[ ] /* 1 value pushed */ 4 CALL[ ] /* CallFunction */ PUSHB[ ] /* 2 values pushed */ 2 3 ROLL[ ] /* RollTopThreeStack */ WS[ ] /* WriteStore */ SWAP[ ] /* SwapTopStack */ WS[ ] /* WriteStore */ </assembly> </prep>
これをTTXで.ttfファイルに変換すると出来上がりである。
サンプル
回転を確認できるようにテストページを作成した:
Windows 10のFirefox, Chrome, Edgeで動作を確認している。MacはTrueType命令を無視するはずなのでたぶん動かない。
*1:Name IDs 256 to 32767, inclusive, are reserved for font-specific names such as those referenced by a font’s layout features. https://docs.microsoft.com/en-us/typography/opentype/spec/name
*2:Foundry-defined tags must begin with an uppercase letter (0x41 to 0x5A), and must use only uppercase letters or digits. https://docs.microsoft.com/en-us/typography/opentype/spec/dvaraxisreg