序論
OpenType font formatのversion 1.8からvariable font機能が追加された。2016年9月のことである。これにより、変化軸を設定して、それに沿って変化するフォントを作成できるようになった。基本の形の情報と変化した形の差分情報を持たせていて、レンダリング時に変化軸の値を変化させると、形が補間される。3DCGを知っているのであれば、モーフターゲット等と呼ばれる機能を想像するとよい。例えば、セリフ体とサンセリフ体を設定してそれらを切り替えたりその中間にするだとか、文字の太さ(ウェイト)やエックスハイトを自在に変化させることが(ちゃんと設定すれば)可能になる。
このvariable fontへのソフトウェアの対応が最近進んできたので、これを利用して文字の回転を指定できるフォントを作ってみたい。
フォント作成時に設定した変化軸の値は、レンダリング時にユーザー側で変化させることができるが、この値を新規に追加されたTrueType命令GETVARIATION
を利用して読み出すことができる。
このため、以前作成した、TrueType命令を利用してフォントサイズに応じて回転するフォントを改変することによって、文字が回転するvariable fontを作ることができる。
作成
さて、回転するvariableフォントの作成に入る。
のページで作成したフォント
をダウンロードしてきて、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"/>
<DesignAxisRecord>
<Axis index="0">
<AxisTag value="ROTT"/>
<AxisNameID value="259"/>
<AxisOrdering value="0"/>
</Axis>
</DesignAxisRecord>
<AxisValueArray>
<AxisValue index="0" Format="2">
<AxisIndex value="0"/>
<Flags value="2"/>
<ValueNameID value="19"/>
<NominalValue value="400.0"/>
<RangeMinValue value="350.0"/>
<RangeMaxValue value="450.0"/>
</AxisValue>
</AxisValueArray>
<ElidedFallbackNameID value="0"/>
</STAT>
<fvar>
<Axis>
<AxisTag>ROTT</AxisTag>
<Flags>0x0</Flags>
<MinValue>-180.0</MinValue>
<DefaultValue>0.0</DefaultValue>
<MaxValue>180.0</MaxValue>
<AxisNameID>259</AxisNameID>
</Axis>
<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ファイルに変換すると出来上がりである。