「ね」
前に読みだした「ね」のcharstringを解析すると、次のようになっている。
-6 71 142 65 252 83 -26 70 hstemhm 269 72 -57 67 -57 71 106 69 255 76
hintmask 0xD980 641 65 rmoveto -56 -45 22 51 40 45 29 51 47 45 -13 -23 43 hvcurveto
-65 -22 -39 -41 -69 hhcurveto hintmask 0xD380 -271 576 rmoveto -81 callsubr
hintmask 0xE380 -11 -2 -36 -3 -46 vhcurveto -51 -9 -58 -6 -32 -2 -24 -1 -19 -1 -22
1 8 -82 rcurveline hintmask 0xE580 -90 callsubr hintmask 0xD980 -103 callsubr 38 1
44 3 45 vhcurveto 93 87 128 95 92 hhcurveto 82 61 -76 -124 -50 -2 -47 -6 -42
hvcurveto 19 -43 -48 11 -53 hhcurveto -101 -71 -58 -79 -100 80 -41 95 100 59 48
84 33 hvcurveto 29 -24 30 -29 29 -33 42 65 rcurveline -34 36 -36 33 -40 28
rrcurveto 9 48 4 55 62 vvcurveto 164 -71 120 -134 vhcurveto hintmask 0xD580
-108 -123 -88 -75 -84 hvcurveto 1 17 2 16 1 16 rrcurveto hintmask 0xD380 45
callsubr
少しずつ読んでいく。今回、ヒントは無視する。
グリフ幅はdefaultWidthX (1000)と同じであるので、最初にWidthが来ず、ヒントステムの指定がくる。
-6 71 142 65 252 83 -26 70 hstemhm
269 72 -57 67 -57 71 106 69 255 76
hintmask 0xD980
hstemhmはヒントの水平ステムを指定する。hstemhmに先行する数字列が指定された水平ステムである。この命令の後、hintmask命令がくるまでの値の列は垂直ステムvstemの指定であるらしい。
一つのステムは2つの値のペアで指定するため、水平ステム4個、垂直ステム5個である。hintmask命令は有効にするヒントステムを指定する命令で、マスクが後続し、ステムの数×1ビットのマスク用のフラグがつくため、ステムの数によって命令長が変わる*4。このグリフはステム9個なのでマスクが2バイトである。
以下、#以降はコメントとする。
641 65 rmoveto #(641, 65)
rmovetoによって現在位置を(0, 0)から(641, 65)に移動し、パスの描画を始める。このように、(hintmask, cntrmask命令を除いて)逆ポーランド記法的に引数が前に来るようになっている。数値がくると引数スタックに積まれて行く。命令によっては(特にパス定義命令は)引数が可変であり、引数の個数によって動作が変わることがある。
特にパス定義の命令などは実行されると引数スタックを空にする。スタックを空にする命令は仕様書の定義において
|- dx1 {dya dxb}* hlineto (6) |-
のように“|-”(スタックの底を表す記号)で終わっており、命令が引数スタックを空にすることを示す。また、先頭の“|-”はスタックの底から順番に引数にとっていくことを示している。
CFFフォントにおいては、アウトラインを直線と三次ベジエ曲線によって定義する。
一つの(三次)ベジエ曲線は4つの制御点で定義されるが、charstringにおいては基本的には座標の差分を表す6つの数値dxa, dya, dxb, dyb, dxc, dycで表現される。Type 2 Charstringには特別な条件でバイト数を節約できるようにいくつものオペレータが用意されている。
各オペレータがどのようにパスを定義するかは次掲のページの最後に図次されているものがわかりやすい。
これに従ってパスの定義をみていく。
-56 -45 22 51 40 45 29 51 47 45 -13 -23 43 hvcurveto
hvcurveto命令は、最初の端点の接ベクトルが水平で最後の端点の接ベクトルが垂直であるベジエ曲線(と、最初の端点の接ベクトルが垂直で最後の端点の接ベクトルが水平であるベジエ曲線とが交互に並ぶ場合)を定義する。次のパスが得られる。
- 曲線: (641, 65)-(585, 65)-(540, 87)-(540, 138)
- 曲線: (540, 138)-(540, 178)-(585, 207)-(636, 207)
- 曲線: (636, 207)-(683, 207)-(728, 194)-(771, 171)
-65 -22 -39 -41 -69 hhcurveto
hhcurveto命令は端点の接ベクトルが水平のベジエ曲線(の並び)を定義する。次のパスを得る。
- 曲線: (771, 171)-(749, 106)-(710, 65)-(641, 65)
最初の地点に戻ってきた。
hintmask 0xD380 #無視
-271 576 rmoveto #(370, 641)
-81 callsubr
rmovetoで次のパスへ移動。続いてcallsubrでローカルサブルーチンの呼び出しを行っている。
callsubrの引数である-81はbiased indexであり、CFFの仕様書にバイアスの詳細があり、Local Subr INDEXに含まれるサブルーチンの個数によって場合分けが行われる。ここではLocal Subr INDEXの個数(count)が180 (<1240)なのでバイアス107を加える。よって、-81は26番目のローカルサブルーチンを表す。
26番目のローカルサブルーチンは、0xEFCAB5+26*2=0xEFCAE9番地から0x0513, 0x0526と書かれていることから、0xEFCAB5+(180+1)*2+0x0513-1=0xEFD131番地から0xEFCAB5+(180+1)*2+0x0526-1=0xEFD144番地までが26番目のサブルーチンである。次に挙げるが、charstringと同じ形式である。
86 89 92 D0 93 C3 90 A3 19 2C 8E 05 90 72 8A 71 74 1A 0B
先掲のPythonスクリプトを使ってType 2 Charstringを訳すと、
-5 -2 7 69 8 56 5 24 rlinecurve -95 3 rlineto 5 -25 -1 -26 -23 vvcurveto return
となる。これを読んでいく。
-5 -2 7 69 8 56 5 24 rlinecurve
rlinecurveは一つ以上の直線のあとに曲線がくる場合に使われる。
- 直線: (370, 641)-(365, 639)
- 曲線: (365, 639)-(372, 708)-(380, 764)-(385, 788)
-95 3 rlineto
rlinetoは直線を表現する。
- 直線: (385, 788)-(290, 791)
5 -25 -1 -26 -23 vvcurveto return
vvcurvetoは、端点の接ベクトルが垂直なベジエ曲線(が並ぶ)場合を表現するのに適している。
- 曲線: (290, 791)-(295, 766)-(294, 740)-(294, 717)
return命令によってサブルーチンを終了している。
charstringに戻って、
hintmask 0xE380 #無視
-11 -2 -36 -3 -46 vhcurveto
- 曲線: (294, 717)-(294, 706)-(292, 670)-(289, 624)
-51 -9 -58 -6 -32 -2 -24 -1 -19 -1 -22 1 8 -82 rcurveline
rcurvelineは一つ以上の曲線の後に直線が続く場合を表現する。
- 曲線: (289, 624)-(238, 615)-(180, 609)-(148, 607)
- 曲線: (148, 607)-(124, 606)-(105, 605)-(83, 606)
- 直線: (83, 606)-(91, 524)
hintmask 0xE580 #無視
-90 callsubr
callsubrで、-90+107=17番目のサブルーチンを呼び出す。
0xEFCAB5+17*2=0xEFCAD7番地から0x040B, 0x0423と書かれているので、0xEFCAB5+(180+1)*2+0x040B-1=0xEFD029番地から0xEFCAB5+(180+1)*2+0x0423-1=0xEFD041までを読み出すと
C9 93 E1 97 B8 90 89 6C 89 6B 89 6B 59 3D FB 06 FB 2E 54 46 BD 46 18 0B
となっている。これを解読すると
62 8 86 12 45 5 -2 -31 -2 -32 -2 -32 -50 -78 -114 -154 -55 -69 50 -69 rcurveline
return
となり、次のパスが得られる
- 曲線: (91, 524)-(153, 532)-(239, 544)-(284, 549)
- 曲線: (284, 549)-(282, 518)-(280, 486)-(278, 454)
- 曲線: (278, 454)-(228, 376)-(114, 222)-(59, 153)
- 直線: (59, 153)-(109, 84)
続いてcharstringに戻り、
hintmask 0xD980 #無視
-103 callsubr
再度callsubrによるサブルーチン呼び出し。-103+107=4番目のサブルーチンを呼び出す。
0xEFCAB5+4*2=0xEFCABD番地から0x021D, 0x0247と書かれているので、0xEFCAB5+(180+1)*2+0x021D-1=0xEFCE3B番地から0xEFCAB5+(180+1)*2+0x0247-1=0xEFCE65番地までを読み出すと、
BB CD CC EA BB D4 08 7C 8A 7E 81 1A 89 FB 02 8B
5A 8A 2C 08 7B 89 6E 8A 7D 1E E2 06 89 9D 89 A4
8A 9C 08 87 E4 8B C7 E5 1A 0B
となっている。これを解読すると、
48 66 65 95 48 73 rrcurveto -15 -1 -13 -10 vvcurveto -2 -110 0 -49 -1 -95 rrcurveto
-16 -2 -29 -1 -14 vhcurveto 87 hlineto -2 18 -2 25 -1 17 rrcurveto -4 89 0 60 90
vvcurveto return
である。
順に読んでいくと、
48 66 65 95 48 73 rrcurveto
- 曲線: (109, 84)-(157, 150)-(222, 245)-(270, 318)
-15 -1 -13 -10 vvcurveto
- 曲線: (270, 318)-(270, 303)-(269, 290)-(269, 280)
-2 -110 0 -49 -1 -95 rrcurveto
- 曲線: (269, 280)-(267, 170)-(267, 121)-(266, 26)
-16 -2 -29 -1 -14 vhcurveto
- 曲線: (266, 26)-(266, 10)-(264, -19)-(263, -33)
87 hlineto
- 直線: (263, -33)-(350, -33)
-2 18 -2 25 -1 17 rrcurveto
- 曲線: (350, -33)-(348, -15)-(346, 10)-(345, 27)
-4 89 0 60 90 vvcurveto return
- 曲線: (345, 27)-(341, 116)-(341, 176)-(341, 266)
サブルーチンは終了。
charstringに戻って、
38 1 44 3 45 vhcurveto
- 曲線: (341, 266)-(341, 304)-(342, 348)-(345, 393)
93 87 128 95 92 hhcurveto
- 曲線: (345, 393)-(432, 486)-(560, 581)-(652, 581)
82 61 -76 -124 -50 -2 -47 -6 -42 hvcurveto
- 曲線: (652, 581)-(734, 581)-(795, 505)-(795, 381)
- 曲線: (795, 381)-(795, 331)-(793, 284)-(787, 242)
19 -43 -48 11 -53 hhcurveto
- 曲線: (787, 242)-(744, 261)-(696, 272)-(643, 272)
-101 -71 -58 -79 -100 80 -41 95 100 59 48 84 33 hvcurveto
- 曲線: (643, 272)-(542, 272)-(471, 214)-(471, 135)
- 曲線: (471, 135)-(471, 35)-(551, -6)-(646, -6)
- 曲線: (646, -6)-(746, -6)-(805, 42)-(838, 126)
29 -24 30 -29 29 -33 42 65 rcurveline
- 曲線: (838, 126)-(867, 102)-(897, 73)-(926, 40)
- 直線: (926, 40)-(968, 105)
-34 36 -36 33 -40 28 rrcurveto
- 曲線: (968, 105)-(934, 141)-(898, 174)-(858, 202)
9 48 4 55 62 vvcurveto
- 曲線: (858, 202)-(867, 250)-(871, 305)-(871, 367)
164 -71 120 -134 vhcurveto
- 曲線: (871, 367)-(871, 531)-(800, 651)-(666, 651)
hintmask 0xD580 #無視
-108 -123 -88 -75 -84 hvcurveto
- 曲線: (666, 651)-(558, 651)-(435, 563)-(351, 488)
1 17 2 16 1 16 rrcurveto
- 曲線: (351, 488)-(352, 505)-(354, 521)-(355, 537)
hintmask 0xD380 #無視
45 callsubr
45+107=152番目のサブルーチンを呼び出す。
0xEFCAB5+152*2=0xEFCBE5番地から0x0B6E, 0x0B76と書かれているので、0xEFCAB5+(180+1)*2+0x0B6E-1=0xEFD78C番地から0xEFCAB5+(180+1)*2+0x0B76-1=0xEFD794番地までを読み出すと、
9A A3 9C A6 97 9D 08 0E
となっている。解読すると、
15 24 17 27 12 18 rrcurveto endchar
- 曲線: (355, 537)-(370, 561)-(387, 588)-(399, 606)
となり、endcharで一つの文字のパスの定義が終了する。ここで、このパスの輪郭が(370, 641)から始まったことから、パスが閉じられていない。パスを閉じるためには次の直線が必要である。
- 直線: (399, 606)-(370, 641)
以上で「ね」のアウトラインが得られた。
「こ」
続いて「こ」のcharstringは先掲のPythonスクリプトで解読すると次になる。
-20 82 546 80 hstem 176 82 vstem 237 619 rmoveto -7 79 84 -4 99 hhcurveto 91 109
7 5 67 hvcurveto 82 vlineto -7 -71 -101 -47 callsubr -41 -310 rmoveto -9 -41
64 callsubr -66 201 141 126 15 19 71 vhcurveto -1 86 rlineto -23 -43 callsubr 52
74 37 8 36 12 40 hvcurveto -82 8 rlineto endchar
順に読んでいく。
-20 82 546 80 hstem #横stem
176 82 vstem #縦stem定義。stemは合計6個
237 619 rmoveto #(237, 619)
ヒントステムの合計は6個なので、hintmask, cntrmaskのマスクのバイト数は1バイトである。
rmovetoで(0, 0)から(237, 619)に移動、パスの定義をスタートさせる。
-7 79 84 -4 99 hhcurveto
- 曲線: (237, 619)-(316, 612)-(400, 608)-(499, 608)
91 109 7 5 67 hvcurveto
- 曲線: (499, 608)-(590, 608)-(699, 615)-(766, 620)
82 vlineto
vlinetoは垂直な線を示す。
- 直線: (766, 620)-(766, 702)
-7 -71 -101 -47 callsubr
callsubrで-47+107=60番目のサブルーチンの呼び出し。
0xEFCAB5+60*2=0xEFCB2D番地から0x0776, 0x0782と書かれているので、0xEFCAB5+(180+1)*2+0x0776-1=0xEFD394番地から0xEFCAB5+(180+1)*2+0x0782-1=0xEFD3A0番地までを読み出すと、
84 2C 1B 28 30 8F 94 43 1F 39 07 0B
となっている。解読すると、
-7 -95 hhcurveto -99 -91 4 9 -72 hvcurveto -82 vlineto return
となる。
これを-7 -71 -101の引数をつけて呼び出すので、最初の命令は-7 -71 -101 -7 -95 hhcurveto ...となる。ここでは引数を[~]で囲んで表現しておく。
[-7 -71 -101] -7 -95 hhcurveto
- 曲線: (766, 702)-(695, 695)-(594, 688)-(499, 688)
-99 -91 4 9 -72 hvcurveto
- 曲線: (499, 688)-(400, 688)-(309, 692)-(237, 701)
-82 vlineto return
- 直線: (237, 701)-(237, 619)
サブルーチンは終了。パスが始まった点に戻ってきた。
charstringに戻る。
-41 -310 rmoveto #次のパスへ (196, 309)
-9 -41 64 callsubr
callsubrで64+107=171番目のサブルーチンの呼び出し。
0xEFCAB5+171*2=0xEFCC0B番地から0x0C12, 0x0C1Bと書かれているので、0xEFCAB5+(180+1)*2+0x0C12-1=0xEFD830番地から0xEFCAB5+(180+1)*2+0x0C1B-1=0xEFD839番地までを読み出すと、
80 5D 57 1A FB 10 F7 09 0B
となっている。解読すると、
-11 -46 -52 vvcurveto -124 117 return
である。
引数を合わせて読んでいくと、
[-9 -41] -11 -46 -52 vvcurveto
- 曲線: (196, 309)-(187, 268)-(176, 222)-(176, 170)
-124 117 return
最後に数値が残った状態でサブルーチンがreturnされるので、戻った先でその値が使われることになる。
charstringに戻って、返された-124 117も併せて続きを読んでいく。
[-124 117] -66 201 141 126 15 19 71 vhcurveto
- 曲線: (176, 170)-(176, 46)-(293, -20)-(494, -20)
- 曲線: (494, -20)-(635, -20)-(761, -5)-(832, 14)
-1 86 rlineto
-23 -43 callsubr
callsubrで、-43+107=64番目のサブルーチンの呼び出し。
0xEFCAB5+64*2=0xEFCB35番地から0x0797, 0x07A2と書かれているので、0xEFCAB5+(180+1)*2+0x0797-1=0xEFD3B5番地から0xEFCAB5+(180+1)*2+0x07A2-1=0xEFD3C0番地までを読み出すと、
40 FB 13 7C FB 1D 1B FB 32 3F 0B
となっている。解読すると、
-75 -127 -15 -137 hhcurveto -158 -76 return
である。引数-23を合わせて読んでいくと、
[-23] -75 -127 -15 -137 hhcurveto
- 曲線: (831, 100)-(756, 77)-(629, 62)-(492, 62)
-158 -76 return
サブルーチンは終了。使われなかった-158 -76が返される。
charstringに戻って、返された-158 -76も併せて読んでいく。
[-158 -76] 52 74 37 8 36 12 40 hvcurveto
- 曲線: (492, 62)-(334, 62)-(258, 114)-(258, 188)
- 曲線: (258, 188)-(258, 225)-(266, 261)-(278, 301)
-82 8 rlineto endchar
- 直線: (278, 301)-(196, 309)
endcharが来たので終了。パスの開始点へともどってきている。