TrueType命令で遊ぶシリーズ。
前回までの記事はこちら:
何をするのか
ラスタライザのTrueType命令回りの挙動を見るのに便利かなあと思って、TrueType命令から取得できるPPEM(EMあたりのピクセル数)やポイント数を可視化できるようなフォントを作ってみる。
完成品
PPEM
Point Size
追記(2017-02-01 4時ころ)
.@nixeneko こんなの見つけました。ご参考まで… ⁑ showcase · master · sev / f000 · GitLab https://t.co/BpKl9mmOvK
— mashabow (@mashabow) 2017年1月31日
gitlab.com
同様のものがすでにあったみたいですね。しかもこっちのが高機能だし……。
というか、PPEMは縦・横で異なる可能性があるのですね*1。長体・平体がかかった時は、確かに縦横でppemが異なりそう。
今回作成したものでは特にprojection vectorの指定をしてないので、MPPEMの前にSTVCA命令などでppemを計る軸を指定した方が良さそう。
(追記終)
方針
伝統ある7セグメント数字を使う。
一つの桁について、初めは8の字が表示されるように7つのセグメントを用意しておく。各セグメントには下図のように番号を割り振っておく。
このセグメントについて、その桁の数字が何であるかを見て、各セグメントを必要であれば適切に消去する。これによってその数字を構成するセグメントのみが残り、数字が表示できるという訳である。
セグメントNo. | セグメントあり | セグメントなし | |
---|---|---|---|
↑ | 0 | 0,2,3,5,6,7,8,9 | 1,4 |
↖ | 1 | 0,4,5,6,8,9 | 1,2,3,7 |
↗ | 2 | 0,1,2,3,4,7,8,9 | 5,6 |
- | 3 | 2,3,4,5,6,8,9 | 0,1,7 |
↙ | 4 | 0,2,6,8 | 1,3,4,5,7,9 |
↘ | 5 | 0,1,3,4,5,6,7,8,9 | 2 |
↓ | 6 | 0,2,3,5,6,8,9 | 1,4,7 |
実装
次のようなグリフを用意した。
各セグメントは4つの制御点からなり、セグメントの並び順は上図の丸囲み数字の番号が若い順に並ぶようにした。
丸囲みの数字をセグメント番号sとすると、セグメントsは制御点4s~4s+3からなる。
また、四角で囲んだ数字(mとする)は、の位の数字を表す。各桁に対応する数字位置におけるセグメントn ()を考えると、
となるようになっている。
なお、セグメントの並べ替えは、.ttfに書き出し→ttxで.ttxに変換→テキストエディタで並べ替え→.ttfに変換という風にして行った。
関数0: 指定した制御点を原点に移動
次にTrueType命令を考えていく。
さて、まずは、以前作った、指定した番号の制御点を原点に移動する関数0をもってきて使うことにする。
PUSHB_1 0 FDEF STVCA[1] DUP PUSHW_1 0x0000 SCFS STVCA[0] PUSHW_1 0x0000 SCFS ENDF
関数1: 指定したセグメントを消去
次に、指定した番号のセグメントを消去する関数1を考える。
入力は消去したいセグメント番号sとする。
セグメント番号sは、右からm番目()の数字のセグメントn ()についてで計算したものとする。
このとき、関数1は、制御点番号4s, 4s+1, 4s+2, 4s+3を原点に移動する。
/* func1: removes segment s */ /* initial stack ...|s| */ /* final stack ...| */ PUSHB_1 1 FDEF /* ...|s| */ PUSHW_1 256 /* ...|s|4.0| */ MUL /* ...|4*s| */ DUP /* ...|4*s|4*s| */ PUSHB_1 1 /* ...|4*s|4*s|1| */ ADD /* ...|4*s|4*s+1| */ DUP /* ...|4*s|4*s+1|4*s+1| */ PUSHB_1 1 /* ...|4*s|4*s+1|4*s+1|1| */ ADD /* ...|4*s|4*s+1|4*s+2| */ DUP /* ...|4*s|4*s+1|4*s+2|4*s+2| */ PUSHB_1 1 /* ...|4*s|4*s+1|4*s+2|4*s+2|1| */ ADD /* ...|4*s|4*s+1|4*s+2|4*s+3| */ PUSHB_1 0 /* ...|4*s|4*s+1|4*s+2|4*s+3|0| */ CALL /* ...|4*s|4*s+1|4*s+2| */ /*call func0 with 4*s+3*/ PUSHB_1 0 /* ...|4*s|4*s+1|4*s+2|0| */ CALL /* ...|4*s|4*s+1| */ /*call func0 with 4*s+2*/ PUSHB_1 0 /* ...|4*s|4*s+1|0| */ CALL /* ...|4*s| */ /*call func0 with 4*s+1*/ PUSHB_1 0 /* ...|4*s|0| */ CALL /* ...| */ /*call func0 with 4*s */ ENDF
関数2: 各桁についての数字を表示
さて、次に、右からm番目()の数字の値dについて適切にセグメントを消去する関数2を考える。これはまあ大体気合で。
/* func2: removes some segments of the m-th digit to show the number d */ /* initial stack ...|d|m| */ /* final stack ...| */ PUSHB_1 2 FDEF /* ...|d|m| */ PUSHW_1 448 /* ...|d|m|7.0| */ MUL /* ...|d|7*m| */ SWAP /* ...|7*m|d| */ DUP /* ...|7*m|d|d| */ PUSHB_1 0 /* ...|7*m|d|d|0| */ EQ /* ...|7*m|d|d==0| */ IF /* ...|7*m|d| */ /* if d==0 */ /* 消去: 3 */ SWAP /* ...|d|7*m| */ DUP /* ...|d|7*m|7*m| */ PUSHB_1 3 /* ...|d|7*m|7*m|3| */ ADD /* ...|d|7*m|7*m+3| */ PUSHB_1 1 /* ...|d|7*m|7*m+3|1| */ CALL /* ...|d|7*m| */ /* call func1 with 7*m+3 */ SWAP /* ...|7*m|d| */ EIF /* ...|7*m|d| */ DUP /* ...|7*m|d|d| */ PUSHB_1 1 /* ...|7*m|d|d|1| */ EQ /* ...|7*m|d|d==1| */ IF /* ...|7*m|d| */ /* if d==1 */ /* 消去: 0,1,3,4,6 */ SWAP /* ...|d|7*m| */ DUP /* ...|d|7*m|7*m| */ PUSHB_1 1 /* ...|d|7*m|7*m|1| */ CALL /* ...|d|7*m| */ /* call func1 with 7*m */ DUP /* ...|d|7*m|7*m| */ PUSHB_1 1 /* ...|d|7*m|7*m|1| */ ADD /* ...|d|7*m|7*m+1| */ PUSHB_1 1 /* ...|d|7*m|7*m+1|1| */ CALL /* ...|d|7*m| */ /* call func1 with 7*m+1 */ DUP /* ...|d|7*m|7*m| */ PUSHB_1 3 /* ...|d|7*m|7*m|3| */ ADD /* ...|d|7*m|7*m+3| */ PUSHB_1 1 /* ...|d|7*m|7*m+3|1| */ CALL /* ...|d|7*m| */ /* call func1 with 7*m+3 */ DUP /* ...|d|7*m|7*m| */ PUSHB_1 4 /* ...|d|7*m|7*m|4| */ ADD /* ...|d|7*m|7*m+4| */ PUSHB_1 1 /* ...|d|7*m|7*m+4|1| */ CALL /* ...|d|7*m| */ /* call func1 with 7*m+4 */ DUP /* ...|d|7*m|7*m| */ PUSHB_1 6 /* ...|d|7*m|7*m|6| */ ADD /* ...|d|7*m|7*m+6| */ PUSHB_1 1 /* ...|d|7*m|7*m+6|1| */ CALL /* ...|d|7*m| */ /* call func1 with 7*m+6 */ SWAP /* ...|7*m|d| */ EIF /* ...|7*m|d| */ DUP /* ...|7*m|d|d| */ PUSHB_1 2 /* ...|7*m|d|d|2| */ EQ /* ...|7*m|d|d==2| */ IF /* ...|7*m|d| */ /* if d==2 */ /* 消去: 1,5 */ SWAP /* ...|d|7*m| */ DUP /* ...|d|7*m|7*m| */ PUSHB_1 1 /* ...|d|7*m|7*m|1| */ ADD /* ...|d|7*m|7*m+1| */ PUSHB_1 1 /* ...|d|7*m|7*m+1|1| */ CALL /* ...|d|7*m| */ /* call func1 with 7*m+1 */ DUP /* ...|d|7*m|7*m| */ PUSHB_1 5 /* ...|d|7*m|7*m|5| */ ADD /* ...|d|7*m|7*m+5| */ PUSHB_1 1 /* ...|d|7*m|7*m+5|1| */ CALL /* ...|d|7*m| */ /* call func1 with 7*m+5 */ SWAP /* ...|7*m|d| */ EIF /* ...|7*m|d| */ DUP /* ...|7*m|d|d| */ PUSHB_1 3 /* ...|7*m|d|d|3| */ EQ /* ...|7*m|d|d==3| */ IF /* ...|7*m|d| */ /* if d==3 */ /* 消去: 1,4 */ SWAP /* ...|d|7*m| */ DUP /* ...|d|7*m|7*m| */ PUSHB_1 1 /* ...|d|7*m|7*m|1| */ ADD /* ...|d|7*m|7*m+1| */ PUSHB_1 1 /* ...|d|7*m|7*m+1|1| */ CALL /* ...|d|7*m| */ /* call func1 with 7*m+1 */ DUP /* ...|d|7*m|7*m| */ PUSHB_1 4 /* ...|d|7*m|7*m|4| */ ADD /* ...|d|7*m|7*m+4| */ PUSHB_1 1 /* ...|d|7*m|7*m+4|1| */ CALL /* ...|d|7*m| */ /* call func1 with 7*m+4 */ SWAP /* ...|7*m|d| */ EIF /* ...|7*m|d| */ DUP /* ...|7*m|d|d| */ PUSHB_1 4 /* ...|7*m|d|d|4| */ EQ /* ...|7*m|d|d==4| */ IF /* ...|7*m|d| */ /* if d==4 */ /* 消去: 0,4,6 */ SWAP /* ...|d|7*m| */ DUP /* ...|d|7*m|7*m| */ PUSHB_1 1 /* ...|d|7*m|7*m|1| */ CALL /* ...|d|7*m| */ /* call func1 with 7*m */ DUP /* ...|d|7*m|7*m| */ PUSHB_1 4 /* ...|d|7*m|7*m|4| */ ADD /* ...|d|7*m|7*m+4| */ PUSHB_1 1 /* ...|d|7*m|7*m+4|1| */ CALL /* ...|d|7*m| */ /* call func1 with 7*m+4 */ DUP /* ...|d|7*m|7*m| */ PUSHB_1 6 /* ...|d|7*m|7*m|6| */ ADD /* ...|d|7*m|7*m+6| */ PUSHB_1 1 /* ...|d|7*m|7*m+6|1| */ CALL /* ...|d|7*m| */ /* call func1 with 7*m+6 */ SWAP /* ...|7*m|d| */ EIF /* ...|7*m|d| */ DUP /* ...|7*m|d|d| */ PUSHB_1 5 /* ...|7*m|d|d|5| */ EQ /* ...|7*m|d|d==5| */ IF /* ...|7*m|d| */ /* if d==5 */ /* 消去: 2,4 */ SWAP /* ...|d|7*m| */ DUP /* ...|d|7*m|7*m| */ PUSHB_1 2 /* ...|d|7*m|7*m|2| */ ADD /* ...|d|7*m|7*m+2| */ PUSHB_1 1 /* ...|d|7*m|7*m+2|1| */ CALL /* ...|d|7*m| */ /* call func1 with 7*m+2 */ DUP /* ...|d|7*m|7*m| */ PUSHB_1 4 /* ...|d|7*m|7*m|4| */ ADD /* ...|d|7*m|7*m+4| */ PUSHB_1 1 /* ...|d|7*m|7*m+4|1| */ CALL /* ...|d|7*m| */ /* call func1 with 7*m+4 */ SWAP /* ...|7*m|d| */ EIF /* ...|7*m|d| */ DUP /* ...|7*m|d|d| */ PUSHB_1 6 /* ...|7*m|d|d|6| */ EQ /* ...|7*m|d|d==6| */ IF /* ...|7*m|d| */ /* if d==6 */ /* 消去: 2 */ SWAP /* ...|d|7*m| */ DUP /* ...|d|7*m|7*m| */ PUSHB_1 2 /* ...|d|7*m|7*m|2| */ ADD /* ...|d|7*m|7*m+2| */ PUSHB_1 1 /* ...|d|7*m|7*m+2|1| */ CALL /* ...|d|7*m| */ /* call func1 with 7*m+2 */ SWAP /* ...|7*m|d| */ EIF /* ...|7*m|d| */ DUP /* ...|7*m|d|d| */ PUSHB_1 7 /* ...|7*m|d|d|7| */ EQ /* ...|7*m|d|d==7| */ IF /* ...|7*m|d| */ /* if d==7 */ /*消去: 1,3,4,6 */ SWAP /* ...|d|7*m| */ DUP /* ...|d|7*m|7*m| */ PUSHB_1 1 /* ...|d|7*m|7*m|1| */ ADD /* ...|d|7*m|7*m+1| */ PUSHB_1 1 /* ...|d|7*m|7*m+1|1| */ CALL /* ...|d|7*m| */ /* call func1 with 7*m+1 */ DUP /* ...|d|7*m|7*m| */ PUSHB_1 3 /* ...|d|7*m|7*m|3| */ ADD /* ...|d|7*m|7*m+3| */ PUSHB_1 1 /* ...|d|7*m|7*m+3|1| */ CALL /* ...|d|7*m| */ /* call func1 with 7*m+3 */ DUP /* ...|d|7*m|7*m| */ PUSHB_1 4 /* ...|d|7*m|7*m|4| */ ADD /* ...|d|7*m|7*m+4| */ PUSHB_1 1 /* ...|d|7*m|7*m+4|1| */ CALL /* ...|d|7*m| */ /* call func1 with 7*m+4 */ DUP /* ...|d|7*m|7*m| */ PUSHB_1 6 /* ...|d|7*m|7*m|6| */ ADD /* ...|d|7*m|7*m+6| */ PUSHB_1 1 /* ...|d|7*m|7*m+6|1| */ CALL /* ...|d|7*m| */ /* call func1 with 7*m+6 */ SWAP /* ...|7*m|d| */ EIF /* ...|7*m|d| */ /*(if d==8)*/ /* 消去:なし */ DUP /* ...|7*m|d|d| */ PUSHB_1 9 /* ...|7*m|d|d|9| */ EQ /* ...|7*m|d|d==9| */ IF /* ...|7*m|d| */ /* if d==9 */ /* 消去: 4 */ SWAP /* ...|d|7*m| */ DUP /* ...|d|7*m|7*m| */ PUSHB_1 4 /* ...|d|7*m|7*m|4| */ ADD /* ...|d|7*m|7*m+4| */ PUSHB_1 1 /* ...|d|7*m|7*m+4|1| */ CALL /* ...|d|7*m| */ /* call func1 with 7*m+4 */ SWAP /* ...|7*m|d| */ EIF /* ...|7*m|d| */ POP /* ...|7*m| */ POP /* ...| */ ENDF
数字で場合分けして、セグメントを消去する関数を呼び出してるというだけだが、全部スタックをつかって値を扱ってるので妙に長くなる。うーん、気合感。
関数3: 剰余の計算
次に、各桁の数値を計算して関数2を呼び出すような関数を作りたいところだが、先に剰余を計算する関数3を考える。
剰余の計算は、
のとき、
が成り立つから、
を計算すればいいことがわかる。cは結果が整数になる様な割り算 で求められるので、実際は
といった感じの計算になる。
さて、関数を実装するが、ここで、MULやDIVは整数ではなくF26Dot6に対して定義されている命令であり、整数a, bに対して
MUL: a * b / 64 DIV: a * 64 / b
という風な計算になることに注意する。
/* func3: calculates remainder a % b */ /* initial stack: ...|dividend a(int)|divisor b(int)| */ /* final stack: ...|remainder(int) = dividend a % divisor b| */ PUSHB_1 3 FDEF /* ...|a|b| */ PUSHB_1 /* ...|a|b|2| */ 2 CINDEX /* ...|a|b|a| */ PUSHB_1 /* ...|a|b|2| */ 2 CINDEX /* ...|a|b|a|b| */ PUSHW_1 /* ...|a|b|a|b|64.0| */ 4096 MUL /* ...|a|b|a|b*64| */ DIV /* ...|a|b|int(a/b)| */ /* DIV: (a)*64 / (b*64) */ PUSHB_1 /* ...|a|b|int(a/b)|1.0/64| */ 1 DIV /* ...|a|b|int(a/b)*64| */ MUL /* ...|a|b*int(a/b)| */ /* MUL: (b) * (int(a/b)*64) / 64 */ SUB /* ...|a - b*int(a/b)| */ ENDF
関数4: 数値を表示する
さて、ここで、各桁の数値を計算して関数2を呼び出し、数値を表示する関数4を作る。
要するに、数値xについて、
x%10, x/10 % 10, x/100 % 10, x/1000 % 10
を計算して、それらの数字が各桁に表示されるように関数2を適用する。 / は整数の除算、 % は剰余を表す。
/* func4: shows the given number x */ /* initial stack: ...|x| */ /* final stack: ...| */ PUSHB_1 4 FDEF /* ...|x| */ DUP /* ...|x|x| */ PUSHB_1 10 3 /* ...|x|x|10|3| */ CALL /* ...|x|x%10| */ /*call func3 with x, 10 */ PUSHB_2 0 2 /* ...|x|x%10|0|2| */ CALL /* ...|x| */ /*call func2 with x%10, 0 */ PUSHW_1 640 /* ...|x|10.0| */ DIV /* ...|x/10| */ DUP /* ...|x/10|x/10| */ PUSHB_2 10 3 /* ...|x/10|x/10|10|3| */ CALL /* ...|x/10|(x/10)%10| */ /*call func3 with x/10, 10 */ PUSHB_2 1 2 /* ...|x/10|(x/10)%10|1|2| */ CALL /* ...|x/10| */ /*call func2 with (x/10)%10, 1 */ PUSHW_1 640 /* ...|x/10|10.0| */ DIV /* ...|x/100| */ DUP /* ...|x/100|x/100| */ PUSHB_2 10 3 /* ...|x/100|x/100|10|3| */ CALL /* ...|x/100|(x/100)%10| */ /*call func3 with x/100, 10 */ PUSHB_2 2 2 /* ...|x/100|(x/100)%10|2|2| */ CALL /* ...|x/100| */ /*call func2 with (x/100)%10, 2 */ PUSHW_1 640 /* ...|x/100|10.0| */ DIV /* ...|x/1000| */ DUP /* ...|x/1000|x/1000| */ PUSHB_2 10 3 /* ...|x/1000|x/1000|10|3| */ CALL /* ...|x/1000|(x/1000)%10| */ /*call func3 with x/1000, 10 */ PUSHB_2 3 2 /* ...|x/1000|(x/1000)%10|3|2| */ CALL /* ...|x/1000| */ /*call func2 with (x/1000)%10, 3*/ POP /* ...| */ ENDF
グリフのプログラム
さて、この関数4をグリフから呼び出す。
引数の数値としては、PPEMやポイントサイズを与えてみようと思う。
PPEM
PPEMとは、Pixels/EMのことで、EMのサイズが何ピクセルで描画されるかということを示した値である。
TrueType fundamentals - Typography | Microsoft Docsによると、ppemは、
ppem = pointSize * dpi / 72
によって計算される。
PPEMを測定するのはMPPEM命令を使う。PPEMを表示するようなプログラムは次のようになる。
MPPEM /* |PPEM| */ PUSHB_1 4 /* |PPEM|4| */ CALL /* | */ /* call func4 with PPEM */
ポイントサイズ
ポイントサイズを取得するのはMPS命令を使う。
MPS /* |PS| */ /* Point Size */ PUSHB_1 4 /* |PS|4| */ CALL /* | */ /* call func4 with PS */
いろんなソフトの挙動をみる
以下で挙げた他にも、Windows 10上のFirefox, Google Chromeでも動作するのを確認した。
Windows 10 フォントビューア
PPEM:
Point Size:
冒頭でも上げたが、PPEMとPoint Sizeが別の値を指している。PPEMの計算式から、DPIが96であることがわかる(要するに、72ptの時のPPEMがDPIである)。
なお、PPEMの値はディスプレイのDPI設定を変えると変わる。
まともな実装だと思う。
Ubuntu フォントビューア
gnome-tweak-toolでヒンティングを有効にしてある。
PPEM:
Point Size:
PPEMとポイントサイズが全く同じ値を示している。dpi=72固定ということだろうか。
システム設定→ディスプレイ→「メニューとタイトルバーの拡大縮小」をいじってみたけど変化なし。
Adobe ソフト
なんとAdobe IllustratorでもTrueType命令は動く。Photoshopでも動く。以下のスクリーンショットはWindows版のCS6のバージョンのものである。
Photoshop
PPEM:
謎の挙動を示す。上に挙げたppemの計算式に対し4掛けしたものが400以下かどうかで場合分けされるらしい。
if (4 * pointSize * dpi / 72 <= 400) ppem = 4 * pointSize * dpi / 72; else ppem = pointSize * dpi / 72;
みたいな感じか。
Point Size:
72固定っぽい。
Illustrator
Adobe Illustrator CS6 (Windows版)のPPEMの扱ひがキモい。一方Point sizeは72固定。 pic.twitter.com/EbB8NBPOwf
— にせねこ (@nixeneko) 2017年1月29日
Photoshopと似たような挙動を示す。
- 基本的に最終的に画面に表示されるサイズによってppemは計算される。ズームイン・ズームアウトでも値が変わる。
- ドラッグで拡縮してるときはppemは(pointSize * dpi / 72)になり、マウスボタン離すと上のPhotoshopと同様の場合分けされた挙動をする。
- ポイントサイズは、Shift+ドラッグによる拡縮時(つまり、長体あるいは平体がかかってない状態)では、72未満は反映されるが、マウスボタンを離すと72固定になる。
- アウトライン化するときは、ppemは、100%表示にしたときのppemあるいは1024のどちらか大きい方になるっぽい。ポイントサイズは72固定。
InDesign
InDesignも、Illustratorと似た挙動をするっぽい。
- 基本的に最終的に画面に表示されるサイズによってppemは計算される。ズームイン・ズームアウトでも値が変わる。
- ppemはPhotoshop同様に場合分けになるっぽい。
- point sizeは72固定
After Effects
ppemは1024固定っぽい。
同様にPoint Sizeも72固定。
ソース
例によってFontforge向けである。'prep'と'cvt 'は利用していない。
'fpgm'
PUSHB_1 0 FDEF SVTCA[x-axis] DUP PUSHB_1 0 SCFS SVTCA[y-axis] PUSHB_1 0 SCFS ENDF PUSHB_1 1 FDEF PUSHW_1 256 MUL DUP PUSHB_1 1 ADD DUP PUSHB_1 1 ADD DUP PUSHB_1 1 ADD PUSHB_1 0 CALL PUSHB_1 0 CALL PUSHB_1 0 CALL PUSHB_1 0 CALL ENDF PUSHB_1 2 FDEF PUSHW_1 448 MUL SWAP DUP PUSHB_1 0 EQ IF SWAP DUP PUSHB_1 3 ADD PUSHB_1 1 CALL SWAP EIF DUP PUSHB_1 1 EQ IF SWAP DUP PUSHB_1 1 CALL DUP PUSHB_1 1 ADD PUSHB_1 1 CALL DUP PUSHB_1 3 ADD PUSHB_1 1 CALL DUP PUSHB_1 4 ADD PUSHB_1 1 CALL DUP PUSHB_1 6 ADD PUSHB_1 1 CALL SWAP EIF DUP PUSHB_1 2 EQ IF SWAP DUP PUSHB_1 1 ADD PUSHB_1 1 CALL DUP PUSHB_1 5 ADD PUSHB_1 1 CALL SWAP EIF DUP PUSHB_1 3 EQ IF SWAP DUP PUSHB_1 1 ADD PUSHB_1 1 CALL DUP PUSHB_1 4 ADD PUSHB_1 1 CALL SWAP EIF DUP PUSHB_1 4 EQ IF SWAP DUP PUSHB_1 1 CALL DUP PUSHB_1 4 ADD PUSHB_1 1 CALL DUP PUSHB_1 6 ADD PUSHB_1 1 CALL SWAP EIF DUP PUSHB_1 5 EQ IF SWAP DUP PUSHB_1 2 ADD PUSHB_1 1 CALL DUP PUSHB_1 4 ADD PUSHB_1 1 CALL SWAP EIF DUP PUSHB_1 6 EQ IF SWAP DUP PUSHB_1 2 ADD PUSHB_1 1 CALL SWAP EIF DUP PUSHB_1 7 EQ IF SWAP DUP PUSHB_1 1 ADD PUSHB_1 1 CALL DUP PUSHB_1 3 ADD PUSHB_1 1 CALL DUP PUSHB_1 4 ADD PUSHB_1 1 CALL DUP PUSHB_1 6 ADD PUSHB_1 1 CALL SWAP EIF DUP PUSHB_1 9 EQ IF SWAP DUP PUSHB_1 4 ADD PUSHB_1 1 CALL SWAP EIF POP POP ENDF PUSHB_1 3 FDEF PUSHB_1 2 CINDEX PUSHB_1 2 CINDEX PUSHW_1 4096 MUL DIV PUSHB_1 1 DIV MUL SUB ENDF PUSHB_1 4 FDEF DUP PUSHB_2 10 3 CALL PUSHB_2 0 2 CALL PUSHW_1 640 DIV DUP PUSHB_2 10 3 CALL PUSHB_2 1 2 CALL PUSHW_1 640 DIV DUP PUSHB_2 10 3 CALL PUSHB_2 2 2 CALL PUSHW_1 640 DIV DUP PUSHB_2 10 3 CALL PUSHB_2 3 2 CALL POP ENDF
グリフ固有('glyf'内)
PPEM表示フォントの場合
MPPEM PUSHB_1 4 CALL
Point size表示フォントの場合
MPS PUSHB_1 4 CALL