フォント TrueType 構造解析 新しいページはコチラ

提供: yonewiki
移動: 案内, 検索
(cmap)
(loca)
 
2,176行: 2,176行:
  
  
 サブテーブルには定義されているNumTableの数だけ以下のようなデータが続きます。
+
 サブテーブルには定義されているNumTableの数だけ以下のようなデータが続きます。その形式は、PlatformIDとEncodingIDの組み合わせで有効なものが使われますが、その形式についてはサブテーブル自身が宣言するものです。0~14の中から0,2,4,6,8,10,12,13,14の9種類のフォーマットがあります。cmap formatと呼びます。IMPACT.ttfは1番目のテーブルがPlatformID=1, EncodingID=0(固定)でformat=0(1バイト文字に適したフォーマット)と2番目のテーブルがPlatformID=3, EncodingID=1(Unicode BMP)でformat=4(疎に分布された2バイトフォント向け)となっていました。
 +
 
 +
 
 +
 まずは、IMPACT.ttfが使用している。format0,4についてテキスト化したものをリンクにしておきます。
 +
 
 +
 
 +
[[メディア:IMPACT TAG cmap TebleInfo.txt|IMPACT_TAG_cmap_TebleInfo.txt]]
 +
 
 +
 
 +
 
 +
 以下にすべてのフォーマットについて記述していきますが、特に使われやすいものから記述していくつもりです。まずは0と4と12(Unicodeフル)と14(Unicode異体字制御)の4テーブルについて記述したいと思います。
 +
 
 +
 
 +
 
 +
===== ■Format0 =====
 +
<table style = "width: 100 ; text-align: left; border-collapse: collapse; border-spacing: 0; ">
 +
<tr style = " background: #778ca3; border-right: solid 1px #778ca3; color: #ffffff; ">
 +
  <th style = "width: 200px; ">型</th>
 +
  <th style = "width: 200px; ">Name</th>
 +
  <th>説明</th>
 +
</tr>
 +
<tr>
 +
  <td>unsigned int</td>
 +
  <td>usFormat</td>
 +
  <td>0 フォーマット番号</td>
 +
</tr>
 +
<tr  style = " background: #eeeeee;" >
 +
  <td>unsigned int</td>
 +
  <td>usLength</td>
 +
  <td>サブテーブルの長さ</td>
 +
</tr>
 +
<tr>
 +
  <td>unsigned int</td>
 +
  <td>usLanguage</td>
 +
  <td>言語コード番号</td>
 +
</tr>
 +
<tr  style = " background: #eeeeee;" >
 +
  <td>unsigned char</td>
 +
  <td>ucGlyphIndex[256]</td>
 +
  <td>配列番号が文字コードで、その値がグリフ番号となる形式。</td>
 +
</tr>
 +
</table>
 +
 
 +
 
 +
 
 +
===== ■Format4 =====
 +
<table style = "width: 100 ; text-align: left; border-collapse: collapse; border-spacing: 0; ">
 +
<tr style = " background: #778ca3; border-right: solid 1px #778ca3; color: #ffffff; ">
 +
  <th style = "width: 200px; ">型</th>
 +
  <th style = "width: 200px; ">Name</th>
 +
  <th>説明</th>
 +
</tr>
 +
<tr>
 +
  <td>unsigned int</td>
 +
  <td>usFormat</td>
 +
  <td>4 フォーマット番号</td>
 +
</tr>
 +
<tr  style = " background: #eeeeee;" >
 +
  <td>unsigned int</td>
 +
  <td>usLength</td>
 +
  <td>サブテーブルの長さ</td>
 +
</tr>
 +
<tr>
 +
  <td>unsigned int</td>
 +
  <td>usLanguage</td>
 +
  <td>言語コード番号</td>
 +
</tr>
 +
<tr  style = " background: #eeeeee;" >
 +
  <td>unsigned short</td>
 +
  <td>usSegCountX2</td>
 +
  <td>セグメント数の2倍の値</td>
 +
</tr>
 +
<tr>
 +
  <td>unsigned int</td>
 +
  <td>usSearchRage</td>
 +
  <td></td>
 +
</tr>
 +
<tr  style = " background: #eeeeee;" >
 +
  <td>unsigned short</td>
 +
  <td>usEntrySelector</td>
 +
  <td></td>
 +
</tr>
 +
<tr>
 +
  <td>unsigned int</td>
 +
  <td>usRangeShift</td>
 +
  <td></td>
 +
</tr>
 +
<tr  style = " background: #eeeeee;" >
 +
  <td>unsigned short</td>
 +
  <td>usEndCode[unSegCountX2/2]</td>
 +
  <td></td>
 +
</tr>
 +
<tr>
 +
  <td>unsigned int</td>
 +
  <td>usReservePad</td>
 +
  <td>予約=0</td>
 +
</tr>
 +
<tr  style = " background: #eeeeee;" >
 +
  <td>unsigned short</td>
 +
  <td>usStartCode[unSegCountX2/2]</td>
 +
  <td></td>
 +
</tr>
 +
<tr>
 +
  <td>unsigned int</td>
 +
  <td>usIdDelta[unSegCountX2/2]</td>
 +
  <td></td>
 +
</tr>
 +
<tr  style = " background: #eeeeee;" >
 +
  <td>unsigned short</td>
 +
  <td>usIdRangeOffset[unSegCountX2/2]</td>
 +
  <td></td>
 +
</tr>
 +
<tr>
 +
  <td>unsigned int</td>
 +
  <td>usGlyphIndexArray[Variables]</td>
 +
  <td>要素数はテーブルの長さのあまり分</td>
 +
</tr>
 +
</table>
 +
 
 +
 
 +
 Format4は疎な文字コード領域毎にグリフIDを付与する方式です。例えばUnicode文字コード0x20~0x7fは連番でGlyphIDの3から156に対応して、次の区画の…と繰り返して定義していくような感じです。但し、文字コードに対応するGIDの算出方法はもう少し複雑な内容になります。SearchRangeとかEntrySelectorとかrangeShiftは、header部分でもやったような処理になっています。
 +
 
 +
 
 +
 例えばセグメントが以下のように定義されていて
 +
 
 +
<Syntaxhighlight2 lang="text">
 +
      Element  EndCode      StartCode        idDelta  idRangeOffset
 +
====================================================================
 +
0x0000(    0)    0x007e        0x0020        0xffe3        0x0000
 +
0x0001(    1)    0x017f        0x00a0        0x0000        0x00c2
 +
0x0002(    2)    0x0192        0x0192        0xff14        0x0000
 +
 +
 +
0x0061(  97)    0xffff        0xffff        0x0001        0x0000
 +
 
 +
GlyphIndexArray                        Value
 +
====================================================
 +
GlyphIndexArray[0x0000(00000)]:0x00ac(  172)
 +
 +
GlyphIndexArray[0x002c(00044)]:0x00cf(  207)
 +
 +
 
 +
</Syntaxhighlight2>
 +
 
 +
 文字コードGlyphIDの変換には大きく2とおりの決定方法があります。まず一つ目の方法でみつける方式の例として、文字コード0x20について見てみます。
 +
 
 +
 0x20がEndcodeの値より小さいことが成立する最大のエレメント番号をみつけます。今回の場合は7eより小さいということでElement番号 0 が対象になります。そして、そのときのStartCodeは0x20で0x20がStartCodeの値より同じ、あるいは、大きいかを比較して成立しない場合は、GIDは 0 になります。今回は成立するので、次の手順に進みます。次は以下の演算を行います。(算出したい対象の文字コード:0x20)-(StartCode:0x20) + (idDelta:0xffe3) & 0xFFFFの値を算出します。今回の場合0x0003がその結果になるのでGID3に文字コード0x20の文字の形が格納されていることを意味します。
 +
 
 +
 もうひとつの計算方法はidDeltaが00となっているのエレメントが計算対象になる文字コードです例えば 0xa0の文字コードだとidDelta=00が対象になります。最初の計算方法と同じように、0xa0がEndcodeの値より小さいことが成立する最大のエレメント番号をみつけます。今回の場合は17fより小さいということでElement番号 1の行が対象になります。そして、StartCodeとくらべて同じか大きいなら、次の演算をします。(算出したい対象の文字コード:0xa0)-(StartCode:0xa0)= 0 + (idRangeOffset:c2を計算します。idRangeOffsetがc2のところからc2だけ進んだところにある番号がGIDになります。セグメントになっているエレメントは全部で0x61まであるので、エレメント総数:0x97 -  idRageOffsetのエレメント番号位置:0x1=0x96。そうすると(idRangeOffset:c2)-(算出した値:0x96)=2C。GlyphIndexArrayの配列番号2Cの値が0x00cf(207)がGlyphIDになります。
 +
 
 +
 
 +
 ちょっと複雑ですが、cmapのformt4はこのように利用する形式です。
 +
 
 +
 次は12と14ですね。やだな。もう疲れたよ。後回しにするか…
 +
 
 +
 
 +
 
 +
===== ■Format12 =====
 +
<table style = "width: 100 ; text-align: left; border-collapse: collapse; border-spacing: 0; ">
 +
<tr style = " background: #778ca3; border-right: solid 1px #778ca3; color: #ffffff; ">
 +
  <th style = "width: 200px; ">型</th>
 +
  <th style = "width: 200px; ">Name</th>
 +
  <th>説明</th>
 +
</tr>
 +
<tr>
 +
  <td>unsigned short</td>
 +
  <td>usFormat</td>
 +
  <td>12 フォーマット番号</td>
 +
</tr>
 +
<tr  style = " background: #eeeeee;" >
 +
  <td>unsigned short</td>
 +
  <td>usReserved</td>
 +
  <td>予約 0</td>
 +
</tr>
 +
<tr>
 +
  <td>unsigned int</td>
 +
  <td>uiLength</td>
 +
  <td>長さ</td>
 +
</tr>
 +
<tr  style = " background: #eeeeee;" >
 +
  <td>unsigned int</td>
 +
  <td>usLanguage</td>
 +
  <td>言語コード</td>
 +
</tr>
 +
<tr>
 +
  <td>unsigned int</td>
 +
  <td>uiNGroups</td>
 +
  <td>グループ数</td>
 +
</tr>
 +
</table>
 +
 
 +
 
 +
 以下のテーブルがuiNGroupsの数だけ続きます。
 +
 
 +
<table style = "width: 100 ; text-align: left; border-collapse: collapse; border-spacing: 0; ">
 +
<tr style = " background: #778ca3; border-right: solid 1px #778ca3; color: #ffffff; ">
 +
  <th style = "width: 200px; ">型</th>
 +
  <th style = "width: 200px; ">Name</th>
 +
  <th>説明</th>
 +
</tr>
 +
<tr>
 +
  <td>unsigned int</td>
 +
  <td>uiStartCharCode</td>
 +
  <td></td>
 +
</tr>
 +
<tr  style = " background: #eeeeee;" >
 +
  <td>unsigned int</td>
 +
  <td>uiEndCharCode</td>
 +
  <td></td>
 +
</tr>
 +
<tr>
 +
  <td>unsigned int</td>
 +
  <td>uiStartGlyphCode</td>
 +
  <td></td>
 +
</tr>
 +
</table>
 +
 
 +
 
 +
 
 +
===== ■Format14 =====
 +
 UVSはUnicode Varidation Sequences Unicode選択値制御=異体字セレクタの略です。Format14は異体字用コードを扱うGlyphID検出方式です。
 +
<table style = "width: 100 ; text-align: left; border-collapse: collapse; border-spacing: 0; ">
 +
<tr style = " background: #778ca3; border-right: solid 1px #778ca3; color: #ffffff; ">
 +
  <th style = "width: 200px; ">型</th>
 +
  <th style = "width: 200px; ">Name</th>
 +
  <th>説明</th>
 +
</tr>
 +
<tr>
 +
  <td>unsigned short</td>
 +
  <td>usFormat</td>
 +
  <td>14 フォーマット番号</td>
 +
</tr>
 +
<tr  style = " background: #eeeeee;" >
 +
  <td>unsigned int</td>
 +
  <td>uiLength</td>
 +
  <td></td>
 +
</tr>
 +
<tr>
 +
  <td>unsigned int</td>
 +
  <td>uiNumVarSelectorRecords</td>
 +
  <td></td>
 +
</tr>
 +
</table>
 +
 
 +
 
 +
 
 +
 以下のテーブルがuiNumVarSelectorRecordsの数だけ続きます。
 +
 
 +
<table style = "width: 100 ; text-align: left; border-collapse: collapse; border-spacing: 0; ">
 +
<tr style = " background: #778ca3; border-right: solid 1px #778ca3; color: #ffffff; ">
 +
  <th style = "width: 200px; ">型</th>
 +
  <th style = "width: 200px; ">Name</th>
 +
  <th>説明</th>
 +
</tr>
 +
<tr>
 +
  <td>unsigned int</td>
 +
  <td>uiVarSelector</td>
 +
  <td></td>
 +
</tr>
 +
<tr  style = " background: #eeeeee;" >
 +
  <td>unsigned int</td>
 +
  <td>uiDefaultUVSOffset</td>
 +
  <td></td>
 +
</tr>
 +
<tr>
 +
  <td>unsigned int</td>
 +
  <td>uiNonDefaultUVSOffset</td>
 +
  <td></td>
 +
</tr>
 +
</table>
 +
 
 +
 
 +
■Default UVS Table
 +
 
 +
■Default UVS Table header
 +
<table style = "width: 100 ; text-align: left; border-collapse: collapse; border-spacing: 0; ">
 +
<tr style = " background: #778ca3; border-right: solid 1px #778ca3; color: #ffffff; ">
 +
  <th style = "width: 200px; ">型</th>
 +
  <th style = "width: 200px; ">Name</th>
 +
  <th>説明</th>
 +
</tr>
 +
<tr>
 +
  <td>unsigned int</td>
 +
  <td>uiNumUnicodeValueRanges</td>
 +
  <td></td>
 +
</tr>
 +
</table>
 +
 
 +
 
 +
 
 +
■Unicode Value Range
 +
<table style = "width: 100 ; text-align: left; border-collapse: collapse; border-spacing: 0; ">
 +
<tr style = " background: #778ca3; border-right: solid 1px #778ca3; color: #ffffff; ">
 +
  <th style = "width: 200px; ">型</th>
 +
  <th style = "width: 200px; ">Name</th>
 +
  <th>説明</th>
 +
</tr>
 +
<tr>
 +
  <td>Uint 3byte</td>
 +
  <td>Ui3StartUnicodeValue</td>
 +
  <td></td>
 +
</tr>
 +
<tr  style = " background: #eeeeee;" >
 +
  <td>Unsigned char</td>
 +
  <td>ucAdditionalCount</td>
 +
  <td></td>
 +
</tr>
 +
</table>
 +
 
 +
 
 +
■NonDefault UVS Table
 +
 
 +
■Default UVS Table header
 +
<table style = "width: 100 ; text-align: left; border-collapse: collapse; border-spacing: 0; ">
 +
<tr style = " background: #778ca3; border-right: solid 1px #778ca3; color: #ffffff; ">
 +
  <th style = "width: 200px; ">型</th>
 +
  <th style = "width: 200px; ">Name</th>
 +
  <th>説明</th>
 +
</tr>
 +
<tr>
 +
  <td>unsigned int</td>
 +
  <td>uiNumUVSMappings</td>
 +
  <td></td>
 +
</tr>
 +
</table>
 +
 
 +
 
 +
 
 +
■Unicode Value Range
 +
<table style = "width: 100 ; text-align: left; border-collapse: collapse; border-spacing: 0; ">
 +
<tr style = " background: #778ca3; border-right: solid 1px #778ca3; color: #ffffff; ">
 +
  <th style = "width: 200px; ">型</th>
 +
  <th style = "width: 200px; ">Name</th>
 +
  <th>説明</th>
 +
</tr>
 +
<tr>
 +
  <td>Uint 3byte</td>
 +
  <td>Ui3UnicodeValue</td>
 +
  <td></td>
 +
</tr>
 +
<tr  style = " background: #eeeeee;" >
 +
  <td>Unsigned short</td>
 +
  <td>usGlyphID</td>
 +
  <td></td>
 +
</tr>
 +
</table>
 +
 
 +
 
 +
 
 +
==== fpgm ヒント系 ====
 +
 Font '''p'''ro'''g'''ra'''m'''の略です。中身はヘッダ定義された位置から、その長さまでが1バイトで表されたフォントプログラム命令(TrueType命令とかOpenType命令、TrueType Instruction ... というワードで検索すると対象の文献にたどり着ける)。多くの異なるプログラムから参照されるプログラムになっています。この後に記述するprepやグリフの中に埋め込まれるモノも同じような仕組みを使います。fpgmは関数化されたプログラムを配置するのが主な用途です。最初に関数番号がプッシュされ、関数定義されるたびにスタックからポップされていきます。関数の中でスタックにプッシュした値はポップで使い切らないと関数番号がおかしなことになります。
 +
 
 +
 
 +
 プログラムの動作の流れは、文字を表示する前にprepに格納されたプログラムで初期化、各グリフに埋め込まれたFont Programが制御点の総数を管理しつつも、fpgmテーブルに格納された関数を番号を指定して呼び出す。各プログラムはcvtテーブルの内容に対して数値を書き出したり、読み込んだりという処理をするという流れです。
 +
 
 +
 
 +
 工夫をすればわりといろいろなことが出来るプログラムですが、基本的にはフォントの形を微調整するのに使われることが多いです。極端な話これでフォントが使われている環境次第で動作を大きく変えるようなフォントを作ることで、その環境を調べることもできます。各アプリケーションがどういうふうにフォントを扱おうとしているかということも調べれるという感じです。世界には意外とフォントプログラムでそういうことをやっている人がいて、感心させられました。
 +
 
 +
 
 +
 自分は仕様書のバイトコードと命令文の対応表から逆アセンブルしてどういうプログラムかを見渡すことくらいしかできませんでした。命令以外にも命令を先頭として、命令につづく数バイトをデータとして指定するようなこともあります。
 +
 
 +
 
 +
 IMPACT.ttfでは以下のようなFont programを保持していました。
 +
 
 +
 
 +
[[メディア:IMPACT TAG fpgm TebleInfo.txt|IMPACT_TAG_fpgm_TebleInfo.txt]]
 +
 
 +
 
 +
 前半が単純なバイトコードの羅列で後半に逆アセンブルした情報が記載されています。関数が60ほど用意されていて、最初のスタックへのプッシュで関数番号のデータを積み上げています。関数番号は関数定義をするFEDFが登場する都度スタックから関数番号を取り出すポップ処理を行います。関数毎の処理については、いまのところでは理解しきれないので、解説しません。
 +
 
 +
 
 +
 環境に対応するべくフォントの見栄えを変更するものですが、これはほとんどの場合、フォントヒンティングという動作のためにあります。文字が小さくなると、ドット境界をうまく利用するような文字の形へと極端に変更することで視認性を上げています。Windowsでは現代においても画素のあらいディスプレイが使われている可能性が高く、いわゆる低DPIのディスプレイが使われる可能性を考慮しています。MacOSはハードウェアをアップルが独占して制作している関係で、現代において低DPIがほとんど排除されているため、OSのバージョンによって、Fontprogramそのものを無視していることもあるそうです。
 +
 
 +
 
 +
 つまりFontProgramが全く動作しない環境も多いということです。動作しないからといって、Fontに関してだけで言えば行き詰まることはないです。ちょっと字がみにくいから字を大きくして対処しようとするはずです。あるいはフォントを変えるとかです。フォントによってはヒンティング要素であるFont programがないために低DPIでは見づらいということもあり得ます。
 +
 
 +
 
 +
 FontForgeでもFont programの動作を確認する処理部分はあります。フォント一覧画面からヒントのfpgmテーブルを確認とすると自分も上記に貼り付けたような逆アセンブル情報が表示されますし、特定のフォントを閲覧するビューにはデバッグ画面もついています。ヒントのデバッグを選択すると画面のDPIを選択してヒントのためのプログラムが動く様子を確認できます。選択したDPIによってフォントの形が変わるという面白い動作があることも確認できます。FontForgeすげぇ。デバッグできる機能まであるとは。点が移動していくプログラム。複雑だぜ。
 +
 
 +
 
 +
 仕様書は公式にあります。現時点では以下のようなアドレスになっていました。
 +
 
 +
 
 +
[https://developer.apple.com/fonts/TrueType-Reference-Manual/RM05/Chap5.html https://developer.apple.com/fonts/TrueType-Reference-Manual/RM05/Chap5.html]
 +
 
 +
[https://docs.microsoft.com/en-us/typography/opentype/spec/tt_instructions https://docs.microsoft.com/en-us/typography/opentype/spec/tt_instructions]
 +
 
 +
 
 +
 公式のフォント関連の情報の多さをみるだけでも複雑すぎることがうかがい知れる。これに少しでも対峙しようとした私たちは偉いのかもしれない。それを亡き者にしようとするアップル RetinaDisplay 高DPIが、低DPIの高度な技術を消し去るとはものすごいな。せっかく考えた技術なので無くなりはしないのだろうけど。これだけでもフォント専門学校作れるくらいの技術だな。全部理解するのに3年とかかかりそう。そしてさほど役に立たないかもしれない未来。そりゃ伝統技術も息絶えるわけだな。覚えるのにものすごく時間かかるのに、さほど必要とされていない未来。よく似てる。でも確かに存在する現代の職人。君の事だ。お金にはならないが、この知識。有意義だと思う。
 +
 
 +
 
 +
 [https://nixeneko.hatenablog.com/entry/2020/02/04/000000 https://nixeneko.hatenablog.com/entry/2020/02/04/000000]
 +
 
 +
 
 +
 上記サイトの管理人はtruetype命令で、回転、pixel/em、pointSize、ランダム輪郭といったことをするフォントを作ってしまっています。font programを使いこなしてる。フォントヒンティングのためにあるから、出来ないこともあるという感じのことが伺えます。すごいね。
 +
 
 +
 
 +
 
 +
==== prep ヒント系 ====
 +
 '''preP'''rogramの略です。一度だけ実行されるプログラムです。fprgの命令の前に実行されるもののようです。
 +
 
 +
 
 +
 IMPACT.ttfの結果は以下とおりです。
 +
 
 +
 
 +
[[メディア:IMPACT TAG prep TebleInfo.txt|IMPACT_TAG_prep_TebleInfo.txt]]
 +
 
 +
 
 +
 
 +
==== cvt  ヒント系 ====
 +
 Control Value Table は 制御値テーブルで prep や fpgm や グリフの中のFont Programで使われる制御値をまとめたテーブルです。このテーブルは Font Programで追加・削除・編集される可能性があるため、初期値のようなものです。
 +
 
 +
 
 +
 公式では4バイトの数値となっていますが、実際は2バイトの数値のタグテーブル部で記述されたサイズの配列となっているように見えます。FontForgeも2バイトで扱っているようです。
 +
 
 +
 
 +
 2バイトで扱った場合のIMPACT.ttfの結果が以下のとおりです。
 +
 
 +
 
 +
[[メディア:IMPACT TAG cvt TebleInfo.txt|IMPACT_TAG_cvt_TebleInfo.txt]]
 +
 
 +
 
 +
 
 +
==== loca ====
 +
 glyfタグテーブルのそれぞれのGlyph ID毎のglyfタグテーブル開始位置からのOffset値を順に格納しています。次のOffset値との差分が、そのGlyph IDの長さになります。Glyph ID [0] ~ Glyph [最後のID番号-1]の数のOffsetがあります。Glyph [最後のID番号]にはこのテーブルの終端、長さとも言えるOffset番号になっていて、この番号とGlyph [最後のID番号-1]の差分で最後のIDの長さも取得できるようになっています。Offset値の記述形式となる型は16bit整数のunsigned short と 32bit整数のunsigned intの2種類があり、どちらが使われるかはheadタグテーブルのindexToLocFormat値が0なら16bit、1なら32bitとなります。
 +
 
 +
 
 +
 IMPACT.ttfの結果は以下のようになっています。
 +
 
 +
 
 +
[[メディア:IMPACT TAG loca TebleInfo.txt|IMPACT_TAG_loca_TebleInfo.txt]]
 +
 
 +
 
 +
 
 +
==== glyf ====
 +
 グリフの形状を記述した核となるテーブルがglyfタグテーブルです。glyfはGlyphのことです。以下のような構造の可変長のテーブルになっています。
 +
 
 +
 
  
 
== '''関連記事''' ==
 
== '''関連記事''' ==

2022年9月17日 (土) 00:00時点における最新版



個人用ツール
名前空間

変種
操作
案内
ツールボックス