にせねこメモ

はてなダイアリーがUTF-8じゃないので移ってきました。

PPEM・ポイントサイズを表示するフォント

TrueType命令で遊ぶシリーズ。
前回までの記事はこちら:

何をするのか

ラスタライザのTrueType命令回りの挙動を見るのに便利かなあと思って、TrueType命令から取得できるPPEM(EMあたりのピクセル数)やポイント数を可視化できるようなフォントを作ってみる。

完成品

PPEM

f:id:nixeneko:20170129005803p:plain

Point Size

f:id:nixeneko:20170129005837p:plain

ダウンロード

尚、この記事の末尾にTrueType命令のソースコードをまとめて記載した。

追記(2017-02-01 4時ころ)

gitlab.com
同様のものがすでにあったみたいですね。しかもこっちのが高機能だし……。

というか、PPEMは縦・横で異なる可能性があるのですね*1。長体・平体がかかった時は、確かに縦横でppemが異なりそう。

今回作成したものでは特にprojection vectorの指定をしてないので、MPPEMの前にSTVCA命令などでppemを計る軸を指定した方が良さそう。
(追記終)

方針

伝統ある7セグメント数字を使う。

一つの桁について、初めは8の字が表示されるように7つのセグメントを用意しておく。各セグメントには下図のように番号を割り振っておく。
f:id:nixeneko:20170129010346p:plain

このセグメントについて、その桁の数字が何であるかを見て、各セグメントを必要であれば適切に消去する。これによってその数字を構成するセグメントのみが残り、数字が表示できるという訳である。

セグメント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

実装

次のようなグリフを用意した。
f:id:nixeneko:20170129011619p:plain
各セグメントは4つの制御点からなり、セグメントの並び順は上図の丸囲み数字の番号が若い順に並ぶようにした。
丸囲みの数字をセグメント番号sとすると、セグメントsは制御点4s~4s+3からなる。

また、四角で囲んだ数字(mとする)は、 10^mの位の数字を表す。各桁に対応する数字位置におけるセグメントn ( 0 \le n \le 6)を考えると、
 s = 7m+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番目( 0 \le m \le 3)の数字のセグメントn ( 0 \le m \le 6)について s = 7m+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番目( 0 \le m \le 3)の数字の値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を考える。

剰余の計算は、
 a \div b = c \ \mbox{余り} \ d
のとき、
 a = bc + d
が成り立つから、
 d = a -bc
を計算すればいいことがわかる。cは結果が整数になる様な割り算  c = \mathrm{int}(a/b) で求められるので、実際は
 d = a -b \cdot \mathrm{int}(a/b)
といった感じの計算になる。

さて、関数を実装するが、ここで、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:
f:id:nixeneko:20170129005803p:plain

Point Size:
f:id:nixeneko:20170129005837p:plain
冒頭でも上げたが、PPEMとPoint Sizeが別の値を指している。PPEMの計算式から、DPIが96であることがわかる(要するに、72ptの時のPPEMがDPIである)。
なお、PPEMの値はディスプレイのDPI設定を変えると変わる。
まともな実装だと思う。

Ubuntu フォントビューア

gnome-tweak-toolでヒンティングを有効にしてある。

PPEM:
f:id:nixeneko:20170129164627p:plain

Point Size:
f:id:nixeneko:20170129164707p:plain

PPEMとポイントサイズが全く同じ値を示している。dpi=72固定ということだろうか。
システム設定→ディスプレイ→「メニューとタイトルバーの拡大縮小」をいじってみたけど変化なし。

Adobe ソフト

なんとAdobe IllustratorでもTrueType命令は動く。Photoshopでも動く。以下のスクリーンショットWindows版のCS6のバージョンのものである。

Photoshop

PPEM:
f:id:nixeneko:20170129020643p:plain
謎の挙動を示す。上に挙げたppemの計算式に対し4掛けしたものが400以下かどうかで場合分けされるらしい。

if (4 * pointSize * dpi / 72 <= 400)
     ppem = 4 * pointSize * dpi / 72;
else
     ppem =     pointSize * dpi / 72;

みたいな感じか。

Point Size:
f:id:nixeneko:20170129020925p:plain
72固定っぽい。

Illustrator


Photoshopと似たような挙動を示す。

  • 基本的に最終的に画面に表示されるサイズによってppemは計算される。ズームイン・ズームアウトでも値が変わる。
  • ドラッグで拡縮してるときはppemは(pointSize * dpi / 72)になり、マウスボタン離すと上のPhotoshopと同様の場合分けされた挙動をする。
  • ポイントサイズは、Shift+ドラッグによる拡縮時(つまり、長体あるいは平体がかかってない状態)では、72未満は反映されるが、マウスボタンを離すと72固定になる。
  • アウトライン化するときは、ppemは、100%表示にしたときのppemあるいは1024のどちらか大きい方になるっぽい。ポイントサイズは72固定。
InDesign

f:id:nixeneko:20170129163439p:plain
InDesignも、Illustratorと似た挙動をするっぽい。

  • 基本的に最終的に画面に表示されるサイズによってppemは計算される。ズームイン・ズームアウトでも値が変わる。
  • ppemはPhotoshop同様に場合分けになるっぽい。
  • point sizeは72固定
After Effects

ppemは1024固定っぽい。
f:id:nixeneko:20170128105149p:plain
同様にPoint Sizeも72固定。
f:id:nixeneko:20170128112211p:plain

ソース

例によって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

*1:MPPEMはprojection vectorを利用しているので。