フォント TrueType 構造解析

提供:yonewiki

フォントに戻る。

概要

 仕様書はAppleのホームページに掲載されています。この記事で頑張って読みこみたいと思います。訂正:TrueTypeの構造解析をやってるつもりでしたが、OpenTypeの構造解析でした。サンプルに選んだImpact.ttfがttfって拡張子なのにOpenTypeだった。でもKernテーブルまではほとんど違いがなかったってことも証明したな。

https://developer.apple.com/fonts/TrueType-Reference-Manual/

 オープンタイプはこちら https://docs.microsoft.com/en-us/typography/opentype/spec/

 OpenTypeとTrueTypeの違いってあるんですけど、ほとんどは同じです。何にも変わらないことだらけ。でも侮っていると、全然違う部分に迷い込んでたりする。注意は必要。ずっと解析してると。どっち見ても同じなので、普通にOpenTypeの仕様書を読みながらTrueTypeを解析してたりする。あぶねぇわな。


 手動で読み解いてもすぐ忘れてしまうし、プログラムを使ってフォント構造をなんらかの方法で処理して理解を助けるようなものを作りたいと思います。できればPDFでグリフ番号も取得しつつ、フォントが埋め込めるくらいまではいきたい。


 まずは、FreeTypeが動かせる環境を構築します。そうすると、カラー絵文字処理用のlibpngもいるんだそうな。ということはその圧縮メカニズムを司るzlibもいるんだって。というわけで

 FreeTypeをコンパイルにその道中どんなこと作業が必要なのかをまとめます。


 FreeTypeを使うで基本的な操作から、ちょっとした使い方くらいをやってみます。


 だいぶと逸れましたが、目的を達成しようと思うと、やっぱりFontファイルの構造を理解しないと駄目ですね。アップルのページを見ながら考えてみます。せっかくなので、C++でフォントのImpact.ttfのバイナリファイルを読み込んで、プログラムによって分解しながら解析してみようと思います。まずはヘッダー情報とテーブルの配置情報が上の方に詰まってるみたいなので、読み込んでみようと思います。理解した情報をテキストに吐き出すということをやってみます。途中、簡易freetypeみたいなことをして遊ぶことを目的にやっていけば、自分でフォントを描画するsvgファイルを作ってみたりできて楽しそう。サブセットを作るための礎を築くことが出来そうな気がしています。ちょっと時間かかりそうだな。もっと若い時に頑張ってみればよかったかも。


 力技のプログラムで分解します。カッコいいクラスとかは作る気が無いです。よっぽど繰り返し同じようなプログラムを書いてるなこれって思ったら、なんかやるかもしれないですけど。例外処理とかやる気なし。エラーが出始めても見返せないくらいの奴にしてやりますよ。

 

構造

 ttfファイルの構造を以下に示します。

 全体としては、ヘッダ部 12byte と TAGと呼ばれる4文字のコードに意味付けがされていて、TAGとその内容という構成が可変長でファイル全体のほとんどを占めます。とくに文字の形に関する情報を持つglyfというタグが容量が大きくなります。ヘッダ部につづいて、それぞれのTAGテーブルがどこに配置されているかを示した情報が1つのTAGあたり16Byteで位置情報を記載しています。これをTAGテーブル部と呼びます。そのあとにそれぞれのタグの値に対応するテーブル部とよばれる大きい容量の領域が続きます。TAGには、フォントファイル内で必ず定義されるべきものと、オプションのものがあります。またフォントファイルの形式毎にも必須となるTAGの違いがあります。OpenTypeフォントのotfやそのコレクションファイルである。otcファイルも一部のTAG情報が独立しているもののほとんど同じような構造になっています。TrueTypeと同じ意味を持っているTAGもあります。TrueTypeのコレクションであるttcもttcだけが扱うTAGがあります。


■ヘッダ部(ファイル先頭から12Byte固定 アドレス0x0000 0000 ~ 0x0000000b)

バイト数 名前 説明
4 unsigned int sfntVersion ttf outline 0x00010000
2 unsigned short numTables TAGテーブル数
2 unsigned short searchRange TAGテーブル数から決定
2 unsigned short entrySelector TAGテーブル数から決定
2 unsigned short rangeShift TAGテーブル数から決定


 entrySelectorはTAG テーブル数が 16(0x10) 以下なら 3。17(0x11) 以上 32(0x20)以下なら4。33(0x21) 以上 64((0x40))以下なら5。の、ように2^n<=TAGテーブル数となるようなnの最大値がentrySelectorです。

 searchRangeは 16 * 2^(entrySelector)という値です。

 rangeShiftは16 * TAGテーブル数 - entrySelector という値です。

 TAGテーブル数から計算できるこれらの値は、よくわからない意味不明の操作をしているように感じますが、TAGテーブルの範囲を計算するために必要となるような値になっています。無くても原理がわかっていれば、再度計算できますけど、また計算するという時間を減らすために備わっていると考えてよいでしょう。なんどもなんども、読み込むかもしれませんので、計算量の節約に役立つのかもしれません。上記の値にヘッダ部自体が使う12byteをふくめると最初のテーブルへのoffsetが求まります。


■TAGテーブル部 (TAG数*16Byteの可変長)

 この部分にフォントファイルで使われるTAGとそのテーブルの位置とテーブルの長さとチェックサムと呼ばれる誤り検知のための符号が記述されます。

バイト数 名前 説明
4 char array[4] tag ASCII code4文字からなるtag名
4 unsigned int checkSum 誤り検出符号
4 unsigned int offset 当該tagの始まるポジション
4 unsigned int length 長さ


 例えばWindowsのImpactというフォントだと以下のようなTAGテーブルになっています。見やすいようにTAGテーブルのポジションの値で昇順にしました。あとCheckSumは実際に計算した値も記載しています。もちろんtableに書かれていた値と計算した値は一致しています。

TAG code Check sum Check sum(calc) Offset Length
head e4e92bf5 e4e92bf5 0000017c 00000036
hhea 125a0ce1 125a0ce1 000001b4 00000024
maxp 0837030e 0837030e 000001d8 00000020
OS/2 785c754a 785c754a 000001f8 00000060
hmtx fea2e775 fea2e775 00000258 00000fec
LTSH f1306fc0 f1306fc0 00001244 000003ff
VDMX 6959818c 6959818c 00001644 00001194
hdmx 1dd7c845 1dd7c845 000027d8 00005408
cmap 089aa4b6 089aa4b6 00007be0 00000672
fpgm c7712c46 c7712c46 00008254 00000764
prep 32e8b0c7 32e8b0c7 000089b8 00000855
cvt d595b7a9 d595b7a9 00009210 00000620
loca 03dc4364 03dc4364 00009830 00000ff0
glyf 550e4c1b 550e4c1b 0000a820 00022720
kern bfcec4b5 bfcec4b5 0002cf40 00000b8e
name 28de000e 28de000e 0002dad0 00000ba1
post ff360066 ff360066 0002e674 00000020
gasp 001d0021 001d0021 0002e694 00000010
DSIG 771dbaee 771dbaee 0002e6a4 00001dc4
GDEF 00260400 00260400 00030468 0000001e
GPOS 662a22e1 662a22e1 00030488 00000f12
GSUB 35d8e622 35d8e622 0003139c 000013da
meta 1aa59251 1aa59251 00032778 00000048


 各tagテーブルの本体テーブルは4の倍数のバイト数の長さになるように調整されていて、実際の長さがLengthですが、Lengthが4の倍数になっていない場合は、そうなるように各byte値が00値で埋められます。

 このような構造なので、フォントファイル自体は4の倍数のバイト数になっています。ファイルの先頭から4byteづつ読み込んで、4byteをunsigned int値として、足し算を繰り返す操作によって、チェックサムが計算できます。フォントファイル自体のチェックサムは全てのフォントファイルで0xB1B0AFBAになります。これは必須tagのheadのadjustment CheckSumという値で0xB1B0AFBAになるように調整されています。adjustment CheckSumはheadの3番目の4byteの値です。したがって(adjustment CheckSumを除いたフォントファイル全てのCheckSum)+(必須tagのheadのadjustment CheckSum) = 0xB1B0AFBA となります。テーブルごとのチェックサムはテーブルのオフセットポジションから、次のテーブルまでを足し算をした値です。チェックサムの計算を繰り返すと、unsigned intの4byteという容量からはなんども桁あふれが発生しますが、どんどん切り捨てて計算を繰り返します。tag名がheadのテーブルだけはadjustment CheckSumを無視したチェックサムを求めます。


 adjustment CheckSumはフォントファイルに変更が発生した場合は計算しなおす必要がある値です。変更が発生したテーブルのチェックサムも変更が必要です。


■テーブル部 (可変長)

head

 headテーブルは固定長(0x36 Byte)です。朱色のアンダーラインは、他のテーブルでも利用する値です。

Name 説明
unsigned short MajorVersion メジャーバージョン
unsigned short MinorVersion マイナーバージョン
FixPoint16.16 FixedfontRevision 16.16固定小数点
unsigned int checkSumAdjustment 前の項目で説明したとおり
unsigned int magicNumber 0x5f0f3cf5で固定
unsigned short flags ※1
unsigned short unitsPerEm 1文字(EM)あたりのピクセル数
unsigned long long CreateDate 作成日
unsigned long long ModifyDate 編集日
unsigned short xMin 水平方向有効最小座標値
unsigned short yMin 垂直方向有効最小座標値
unsigned short xMax 水平方向有効最大座標値
unsigned short yMax 垂直方向有効最大座標値
unsigned short macStyle mac向けスタイルフラグ。OS/2 テーブルの fsSelection ビットと一致させる。※2
unsigned short lowestRecPPEM 文字が可読となる最小の1EMあたりのピクセル表現値
short fontDirectionHint フォント記述方向 0:両方、1 : 左から右、2:左から右と移動無し、-1 : 右から左、-2:右から左と移動無し
unsigned short indexToLocFormat タグ名loca1のテーブルにおけるOffset形式 0 : unsigned short16、 1 : unsigned int32
unsigned short glyphDataFormat グリフ記述形式 現時点では0のみ


 ※1. flagは各ビット毎に以下のような意味になります。

 bit 0:y=0がベースラインなら1

 bit 1 : 左端の黒がLSB(Left Side Bearing)

 bit 2 : スケーリングされたポイントサイズと実際のポイントサイズで字形が異なる。

 bit 3 : 分数スケールが含まれる場合に整数スケールを利用する。

 bit 4 : マイクロソフトが提供するTrueTypeスケーラを利用する。

 bit 5 : 垂直方向にレイアウトされ、x 座標 0 が目的の垂直ベースラインに対応するようにグリフが描画されたフォントに設定する必要がある。

 bit 6 : このビットはゼロで固定。

 bit 7 : フォントが正しい言語レンダリング(アラビア語フォントなど)の合字レイアウトを必要とする場合に設定する。

 bit 8 : 規定値で発生していると指定されている1つ以上の特異効果 Apple Advanced Typographyを持つ場合に設定が必要。

 bit 9 : フォントに右から左への強いグリフが含まれている場合に設定する必要がある。

 bit 10 : フォントにインド風の並べ替え効果が含まれている場合に設定する必要がある。

 bit11~13 : Adobeによって定義されている。

 bit 14 : フォント内のグリフが、 last resort font(豆腐文字の先進的なモノ)など、コードポイント範囲の単なる汎用シンボルがある場合に、このビットを設定する必要がある。


 ※2.macfontStyleは以下のようなフラグです。 

 bit 0 : 太字

 bit 1 : 斜体

 bit 2 : 下線

 bit 3 : アウトライン

 bit 4 : シャドウ

 bit 5 : 凝縮 (狭)

 bit 6 : 拡張

 IMPACT.ttfの例は以下のとおりです。

Name Value
MajorVersion 1
MinorVersion 0
FixedfontRevision 5.11
checkSumAdjustment 0x9932de9a
magicNumber 0x5f0f3cf5
flags 0x001b ※0001_1011 bit4,3,1,0
unitsPerEm 2048
CreateDate 1992/07/22 17:04:10
ModifyDate 2017/03/03 19:59:44
xMin -265
yMin -677
xMax 2894
yMax 2392
macStyle 0
lowestRecPPEM 9
fontDirectionHint 1
indexToLocFormat 1
glyphDataFormat 0


maxp

 maxpテーブルは固定長(0x20 Byte)です。主にグリフ全体における各要素の最大値を示しています。メモリをどれくらい確保していればグリフが確実に読み込めるかといった目安にもなります。基本的には動的にメモリを確保すると思いますが、メモリ不足を事前に察知することができます。総グリフ数は他のテーブルでも利用します。

Name Value
Fixed 16.16 Fixedversion 0b0000000100000000 1.00=TrueTypeアウトラインフォント
0b0000000101010000 0.3125=PostScriptアウトライン
unsigned short numGlyphs グリフ数
unsigned short maxPoints 単一グリフの最大ポイント数
unsigned short maxContours 単一グリフの最大輪郭(りんかく)数
unsigned short maxCompositePoints 複合グリフの最大ポイント数 複合グリフに使う単一グリフのポイント数の足し算なので、どのグリフが複合で使われるかによって決まるため、上記項目の単一グリフの最大値とはあまり関連はないものです。
unsigned short maxCompsiteContours 複合グリフの最大輪郭数。上記に同じく
unsigned short maxZones 1 : Z0命令を使う。2 : Z0命令を使わない。
unsigned short maxTwilightPoints 最大Z0命令使用数。
unsigned short maxStorage ストレージエリア最大数
unsigned short maxFunctionDefs 最大FDEF数
unsigned short maxInstructionDefs 最大IDEF数
unsigned short maxStackElements 最大スタック深さ
unsigned short maxSizeOfInstructions 最大命令数
unsigned short maxComponentElements 最大複合グリフ要素利用数
unsigned short maxComponentDepth 最大複合グリフ再帰深度


グリフ数は以下のテーブルに影響を与えます。

  • グリフ位置テーブル ('loca')
  • グリフデータテーブル ('glyf')
  • 'Zapf' テーブル


さらに、一部のテーブルにはグリフインデックスが含まれており、フォントからグリフを削除した場合に更新が必要になる場合があります。このようなテーブルの部分的なリストは次のとおりです。

  • 文字グリフマッピングテーブル ('cmap')
  • 妥当性テーブル ('just')
  • カーニングテーブル ('kern')
  • 拡張グリフ変態テーブル ('morx')


これらの整合性を保って、編集すれば、不要なグリフを削除するといったサブセット化はできそうですね。


でも、ここに来て、今まで知らなかった技術のいくつかを知る。それが、フォントヒンティング命令スタック再帰トワイライトポイントストレージといったあたり。


いやはや、これは深い。そりゃこんだけ沢山タグがあるわけだな。フォントは深い。知り尽くせぬほど深そうだ。もうちょっと仕様書とか文献を読み込みます。日本でフォントに詳しい文書書いてる人はGoogleでは10人くらいな感じがする。グラフ用紙に「ねこ」という2文字をプロットしてる深いサイトとopnetypeのタグの半分くらいにふれているサイト。凄すぎる。あっちこちいったりきたりして勉強だな。


 フォントヒンティングはwikipediaによると文字が小さくなったときに頂点の位置を変更する場所みたいな説明だった。参考画像をみると確かにフォントヒンティングがあるほうが小さい文字を読み取りやすい。優れた技術だ。ちなみにfontforgeでさえもフォントヒンティングは読み込まないらしい。fontforgeでは編集できないそうな。コピーしたり自動生成?したりするらしい。説明によるとユーザインターフェースを提供しにくいそうだ。あれだけのものを作る人が諦めるってことは、ヒント関連ってむずかしそうだな。


 この時代になってなお、無料ではまだ、フォントの奥深いところは素人には簡単に手出しできない状態になっているようだ。有料版とか企業がつかってるフォントエディタってすごいんかな。気になる。企業だけしか持ててないから、優秀なフォントは生まれにくいのかもしれない。個人でもきっかけさえあればすごいことやってのける時代だけど、環境がなければ、たやすくは生まれないんだな。

 ともかく、

 以下がIMPACT.ttfの例です

Name Value
Fixedversion 1.00
numGlyphs 1019
maxPoints 242
maxContours 60
maxCompositePoints 82
maxCompsiteContours 5
maxZones 2
maxTwilightPoints 16
maxStorage 47
maxFunctionDefs 86
maxInstructionDefs 0
maxStackElements 913
maxSizeOfInstructions 408
maxComponentElements 3
maxComponentDepth 1


 英語フォントの癖にスタックが913とかマジか。トワイライトポイントが16ってことはあんまり沢山せっていするものではないんだな。なんだろこれ。あとストレージエリアの47は多い奴は結構あるんだなっていう印象。なんかわかったら、追記します。追記がないあいだは、こいつ、いろいろ書いてるくせに理解できていないと思って下さい。

 

hhea、hmtx

 hheaテーブルは固定長(0x24 Byte)です。水平レイアウト情報です。hmtxテーブルはグリフ数で決まる可変長です。各グリフの横送り幅と送り幅の左側を起点として、文字の黒色がある一番左の位置の情報をもっています。LSBと呼びます。Left Side Bearingです。このときのBearingは位置認識という意味を持ちます。挙動という意味もありますが、こちらが、軸受けなんかに使われるボールベアリングの意味だと思われです。

 水平方向にフォントを並べていくとき使われる情報です。horizontal headで、hheaだと思われます。

 各グリフの情報をもつという意味でhorizontal matrixで、hmtxだと思われます。確度が高いとは思いますが、推測情報ばかりで申し訳ないです。

■hhea  

Name 説明
unsigned short MajorVersion メジャーバージョン
unsigned short MinorVersion マイナーバージョン
short Ascent 高さ
unsigned short Descender ベースラインの下
short LineGap 行間
unsigned short AdvanceWidthMax 最大文字の送り幅
short MinLeftSideBearing 送り幅枠左基準の最小の左側黒色部の座標
short MinRightSideBearing 送り幅枠右基準の最小の右側黒色部の座標
short XMaxExtent 左下原点から右黒色部の最大値
short CaretSlopeRise 斜体角度垂直ならtan90度でCaretSlopeRise:1/CaretSlopeRun:0。
short CaretSlopeRun 上記説明のとおり
short CaretOffset 斜体分のカーソル移動量
short MetricDataFormat hmtx テーブルのデータフォーマット。現時点で0のみ
unsigned short NumberOfMetrics 各グリフのAdvanceWidth、LSB定義数

 

 IMPACT.ttfでは以下のようになります。

Name Value
MajorVersion 1
MinorVersion 0
Ascent 2066
numGlyphs -432
LineGap 0
AdvanceWidthMax 2974
MinLeftSideBearing -265
MinRightSideBearing -265
XMaxExtent 2894
CaretSlopeRise 1
CaretSlopeRun 0
CaretOffset 0
MetricDataFormat 0
NumberOfMetrics 1019


 hmtx の IMPACTの結果は以下の通りです。各グリフ毎にある値なので1019行を超えるため、テキストへのリンクを貼り付けておきます。これだけの値を手作業でまとめることは無謀すぎます。ようやっとでプログラムで動かしてるんだなって思ってもらえると思います。

IMPACT_TAG_hmtx_TebleInfo.txt

 つかっているプログラムは、もし完成することがあるならば有用なものになるかもしれないので、Qtのライセンスに触れない形式でアップロードするかもしれません。商用の扱いはダメですからね。

■hmtx

Name 説明
longHorMetric hMetrics[GlyphID] グリフID毎のAdvaceWidthとLSB longHorMetric構造体は後述
short leftSideBearing[GlyphID] hMetricsで記述されなかった残りのグリフID毎のLSB

 


※longHorMetric構造体

Name 説明
unsigned short advaceWidth グリフの送り幅
short leftSideBearing グリフのLSB


 hmtxでは、グリフの送り幅とLSBを構造体の形式とみなしてGlyphID順に読み込んでいきます。hheaのNumberOfMetricsで定義された分だけ構造体が配列のように読み取ることができるようになっています。maxpで定義されたNumGlyphsの値よりNumberOfMetricsが少ない場合は、残りのadvanceWidthは配列の最後の値がすべてに適用されます。残りのLSBは別途、グリフ毎に定義することが必要になっています。

 

OS/2

 OS/2テーブルはフォントのコンピュータが取り扱うために必要な基本的な数値情報が格納されています。OS/2はIBMやMicrosoftのオペレーティングシステムの名前でどちらもOS/2の愛称がありました。一番流行っていたオペレーティングシステムとやりとりする主な値なのでこのような後世に残るようなテーブル名をつけてしまったのだと思います。OS/2自体は、みかけなくなりましたが、このワードは今も生きています。OS/2テーブルのバージョン毎に長さが変わります。Version0なら0x4cByte Version1では、0x54Byte。Version2、3および4だと0x60Byteです。Version5なら0x68Byteになります。

Name 説明
unsigned short UsVersion テーブルバージョン番号 0~5
short SXAvgCharWidth フォント平均文字幅
unsigned short UsWeightClass フォント太さ指標 ※1
unsigned short UsWidthClass フォント文字幅指標 ※2
unsigned short usType フォント取り扱い各種フラグ ※3
short SSubscriptXSize 下付き文字の横幅
short SSubscriptYSize 下付き文字の高さ
short SSubscriptXOffset 下付き文字の横位置
short SSubscriptYOffset 下付き文字の縦位置
short SSuperscriptXSize 上付き文字の横幅
short SSuperscriptYSize 上付き文字の縦幅
short SSuperscriptXOffset 上付き文字の横位置
short SSuperscriptYOffset 上付き文字の縦位置
short SStrikeoutSize 取り消し線の太さ
short SStrikeoutPosition 取り消し線のベースラインからの縦位置
short SFamilyClass 上位バイト=ファミリークラス、下位バイト=サブクラス ※4
unsigned char ucPanose[0] 書体特性 ※5
unsigned char ucPanose[1]
unsigned char ucPanose[2]
unsigned char ucPanose[3]
unsigned char ucPanose[4]
unsigned char ucPanose[5]
unsigned char ucPanose[6]
unsigned char ucPanose[7]
unsigned char ucPanose[8]
unsigned char ucPanose[9]
unsigned int UiUnicodeRange1 1~4まで128バイトのフラグ ※6
unsigned int UiUnicodeRange2
unsigned int UiUnicodeRange3
unsigned int UiUnicodeRange4
unsigned char[4] ucAchVendID ASCIIコード4文字のフォントベンダーID
unsigned short FsSelection フォントパターンフラグ ※7
unsigned short UsFirstCharIndex 最小ユニコード番号32
unsigned short UsLastCharIndex 最大ユニコード番号32
unsigned short STypoAscender 印刷するときのグリフ高さ
unsigned short STypoDescender -印刷するときのグリフ深さ
unsigned short STypoLineGap 印刷するときの行間
unsigned short UsWinAscent Windowsでのグリフ高さ
unsigned short UsWinDescent Windowsでのグリフ深さ
unsigned int UiCodePageRange1 1~2の64bitの含まれるコードページ言語のフラグ
unsigned int UiCodePageRange2 UiUnicodeRangeと同じような仕組みです。
short SXHeight ベースラインと小文字xの高さ
short SCapHeight ベースラインと大文字Hの高さ
unsigned short UsDefaultChar フォントが無い場合に参照されるグリフ番号 0 になることが多い。
unsigned short UsBreakChar ブレーク文字の規定値 ほとんどが0x20半角空白スペース
unsigned short UsMaxContext 合字で扱う最大グリフ数
unsigned short UsLowerOpticalPointSize フォントが使われる最小サイズ
unsigned short UsUpperOpticalPointSize フォントが使われる最大サイズ


  • xAvgCharWidth
 小文字26文字すべてがないと0になります。小文字のみの、advanceWidth送り幅の平均ですが、単純な値ではないです。使用頻度に応じた重みづけ係数表を使った計算をします。(a送り幅x係数+…z送り幅 + 空白送り幅x係数)/1000の値です。この値は文字数 x 送り幅で1行の文字長さを推測できるもだそうです。
係数係数係数係数係数
a64b14c27d35e100
f20g14h42i63j3
k6l35m20n56o56
p17q4r49s56t71
u31v10w18x3y18
z2sp166


※1usWeightClassは100~900の100の倍数で指定される指標です。

100 Ultra-light 200 Extra-light 300 Light 400 Semi-light 500 Medium

600 Semi-Bold 700 Bold 800 Extra-Bold 900 Ultra-Bold


※2usWidthClassは文字幅の指標です。

1 Ultra-condensed 50%

2 Extra-condensed 63.5%

3 condensed 75%

4 semi-condensed 87.5%

5 Medium 100%

6 Semi-Expanded 112.5%

7 Expanded 125%

8 Extra-expanded 150%

9 Ultra-expanded 200%


※3 フォント取り扱いフラグ

Bit 0 : 予約 = 0

Bit 1 : Bit2あるいは3が1のときは1であってはならないフラグです。所有者に許可なく埋め込み、編集、ファイル交換してはならないです。

Bit 2 : Bit1あるいは3が1のときは1であってはならないフラグです。そのまま埋め込みしてもよいですが、リモートでフォントが表示されるドキュメントは読み取り専用になっていないといけない。

Bit 3 : フォントファイルを編集しての埋め込みが許されています。

Bit 4-7 : 予約 = 0

Bit 8 : サブセット化しようとする文書の出力の仕組みを許可しないです。

Bit 9 :ビットマップによる埋め込みのみ許可

Bit 10-15 : 予約 = 0


※4 SFamilyClass 文字のデザイン分類をするものです。作ってる人も分類むずかしいでしょうね。

ファミリークラス分類

Bit 0 : 分類なし

Bit 1 : オールドスタイルセリフ 15世紀から17世紀のセリフスタイルです。

Bit 2 : 過度なセリフ 18世紀から19世紀のセリフスタイルです。

Bit 3 : モダンセリフ 20世紀。

Bit 4 : クラレンドンセリフ

Bit 5 : スラブセリフ

Bit 6 : 予約済 = 0

Bit 7 : フリーフォームセリフ。セリフはあるけど自由なものです。

Bit 8 : サンセリフ。セリフがないゴシック体のような文字

Bit 9 : 装飾。豪華な装飾がほどこされた字体です。

Bit 10 : スクリプト。手書きをシミュレートするような設計がされています。

Bit 11 : 予約済 = 0

Bit 12 : シンボリック。記号とか

Bit 13-14 : 予約済 = 0

オールドスタイルセリフのサブクラスといったように上位バイトで決まる下位バイトの意味付け分類が存在します。1、2、3、4、5、7、8、9、10、12の10種類ある。

以下はオールドスタイルの例です。全部の分類を記述するのは大変なので、リンクを貼っておきます。

Bit 0 : 分類無し

Bit 1 : 丸みを帯びている

Bit 2 : ガラルデ

Bit 3 : ベネチアン

Bit 4 : モディファイベネチアン

Bit 5 : 現代オランダ

Bit 6 : 伝統オランダ

Bit 7 : コンテンポラリ

Bit 8 : カリグラフィ

Bit 9-14 : 予約済 = 0

Bit 15 : 種々雑多な

といったように上位バイトに基づいた分類がある。

※5 PANOSE

1バイトの数値で以下0~9の10種類の様々なフラグがあります。

PANOSE[0] : bFamilyType :

0 : Any
1 : No Fit
2 : Text and Display
3 : Script
4 : Decorative
5 : Pictorial

PANOSE[1] : bSerifStyle :

0 : Any
1 : No Fit
2 : Cove
3 : Obtuse Cove
4 : Square Cove
5 : Obutuse Square Cove
6 : Square
7 : Thin
8 : Bone
9 : Exaggerated
10 : Triangle
11 : Normal Sans
12 : Obtuse Sans
13 : Perp Sans
14 : Fiared
15 : Rounded

PANOSE[2] : bWeight :

0 : Any
1 : No Fit
2 : Very Light
3 : Light
4 : Thin
5 : Book
6 : Medium
7 : Demi
8 : Bold
9 : Heavy
10 : Black
11 : Nord

PANOSE[3] : bProportion :

0 : Any
1 : No Fit
2 : Old Style
3 : Modern
4 : Even Width
5 : Expanded
6 : Condensed
7 : Very Expanded
8 : Very Condensed
9 : MonoSpace

PANOSE[4] : bContrast :

0 : Any
1 : No Fit
2 : None
3 : Very Low
4 : Low
5 : Medium Low
6 : Medium
7 : Medium High
8 : High
9 : Very High

PANOSE[5] : bStrokeVariation :

0 : Any
1 : No Fit
2 : Gradual/Diagonal
3 : Gradual/Transitional
4 : Gradual/Vertical
5 : Gradual/Horizontal
6 : Rapid/Vertical
7 : Rapid/Horizontal
8 : Instant/Vertical

PANOSE[6] : bArmStyle :

0 : Any
1 : No Fit
2 : Straight Arm/Horizontal
3 : Straight Arm/Wedge
4 : Straight Arm/Vertical
5 : Straight Arm/Single Serif
6 : Straight Arm/Double Serif
7 : Non-Straight Arm/Horizontal
8 : Non-Straight Arm/Wedge
9 : Non-Straight Arm/Vertical
10 : Non-Straight Arm/Single Serif
11 : Non-Straight Arm/Double Serif

PANOSE[7] : bLetterform :

0 : Any
1 : No Fit
2 : Normal/Contact
3 : Normal/Weighted
4 : Normal/Boxed
5 : Normal/Flattened
6 : Normal/Rounded
7 : Normal/Off Centter
8 : Normal/Square
9 : Obliqui/Contact
10 : Obliqui/Weighted
11 : Obliqui/Boxed
12 : Obliqui/Flattened
13 : Obliqui/Rounded
14 : Obliqui/Off Centter
15 : Obliqui/Square

PANOSE[8] : bSerifStyle :

0 : Any
1 : No Fit
2 : Standard/Trimmed
3 : Standard/Pointed
4 : Standard/Serifed
5 : High/Trimmed
6 : High/Pointed
7 : High/Serifed
8 : Constant/Trimmed
9 : Constant/Pointed
10 : Constant/Serifed
11 : Low/Trimmed
12 : Low/Pointer
13 : Low/Serifed

PANOSE[9] : bXHeight :

0 : Any
1 : No Fit
2 : Constant/Small
3 : Constant/Standard
4 : Constant/Large
5 : Ducking/Small
6 : Ducking/Standart
7 : Ducking/Large

※6 uiUnicodeRange ユニコード使用範囲を指定する部分です。0~127のそれぞれのbitフラグがUnicodeの一定の範囲を指し示します。範囲についてはMicrosoftのOpentype仕様書に記述があります。

achVendID[4] 4文字のベンダーIDをMicrosoftが管理しています。

Adbe Adbe
VenderID 名前
ACG AGFA コンピュグラフィック
Adbe Adobe Systems
Appl Apple
Alts AltSys
B? Bigelow&Holmemes
Bert Berthold
Bits BitStream
DTC ディジタルタイプフェイス Corp.
HP ヒューレットパッカード
IBM IBM
KATF Kingsley/ATF
Lans 有限会社 Lanston タイプ
LETR Letraset
Lino Linotype
MONO モノタイプ
MS マイクロソフト
QMSI QMS/Imagen
URW URW
ZSFT ZSoft

※7 fsSelection フォントスタイルを選択するフラグ部分です。 Bit 0 : ITALIC 斜体 Bit 1 : UNDERSCORE 下線 Bit 2 : NEGATIVE 反転 Bit 3 : OUTLINED 輪郭 Bit 4 : STRIKEOUT 打消し線 Bit 5 : BOLD 太字

IMPACT.ttfの値は以下のとおりでした。

Name Value
UsVersion 3
SXAvgCharWidth 1222
UsWeightClass 400
UsWidthClass 3
sFsType 8
SSubscriptXSize 1184
SSubscriptYSize 1081
SSubscriptXOffset 0
SSubscriptYOffset 0
SSuperscriptXSize 1184
SSuperscriptYSize 1081
SSuperscriptXOffset 0
SSuperscriptYOffset 800
SStrikeoutSize 102
SStrikeoutPosition 690
SFamilyClass 2053
ucPanose[0] 2
ucPanose[1] 11
ucPanose[2] 8
ucPanose[3] 6
ucPanose[4] 3
ucPanose[5] 9
ucPanose[6] 2
ucPanose[7] 5
ucPanose[8] 2
ucPanose[9] 4
UiUnicodeRange1 0x00000287
UiUnicodeRange2 0x00000000
UiUnicodeRange3 0x00000000
UiUnicodeRange4 0x00000000
ucAchVendID MONO
FsSelection 64
UsFirstCharIndex 32
UsLastCharIndex 64260
STypoAscender 1619
STypoDescender -229
STypoLineGap 343
UsWinAscent 2066
UsWinDescent 432
UiCodePageRange1 0x2000009f
UiCodePageRange2 0xdfd70000
SXHeight 1327
SCapHeight 1619
UsDefaultChar 0
UsBreakChar 32
UsMaxContext 5
UsLowerOpticalPointSize 0
UsUpperOpticalPointSize 64565

 

LTSH スケール系

 グリフの数だけ unsigned char 型の配列をもつ可変長のテーブルです。サイドベアリングが無い場合は常に線形であることを意味する1とする必要がある値で、LinearThreSHoldの意です。headテーブルのflag値のbit4:マイクロソフトのスケーラを使うを有効にしたときにだけLTSH指定するのが一般的な使い方です。0~255の値でグリフを線形にスケーリングできる垂直骨の高さを1emあたりの数値として指定します。0~255ピクセルの大きさにスケールしたときに線形しはじめるのはどこからかというテーブルだと思います。小さい文字にはヒントにしたがったリニアスケールではない値という意味です。1だと、とにかくリニアスケール(単純な拡大・縮小)っていう感じなのかな。もうちょっと確信をもてるような文献にであえたら続報あるかもしれません。

Name 説明
unsigned short UsVersion テーブルバージョン番号 0
unsigned short UsNumGlyphs グリフ数
unsigned char UcPels[NumGlyphs] 各グリフのスケール垂直骨高さ


 IMPACT.ttfの設定内容は以下の通りでした。グリフの数だけある線形スケーリング垂直骨高さなので、テキストファイルのリンクを示します。

IMPACT_TAG_LTSH_TebleInfo.txt

 

VDMX スケール系

 VDMXはVertical Device Metric の意味でOS/2テーブルのusWinAscentとusWinDescentの値から任意のサイズのフォントにおける最大の黒色部分高さを決定します。スケーリングによる高さと丸め込み量による高さとで異なるフォント高さになるため、yMaxやyMinから飛び出ることがないように定義されます。VDMXヘッダーにアスペクト比ごとにグループ化したときの総数だけratRange構造体を保有する可変量と総数だけテーブル開始位置からの記載ポジションを設定する可変量を保有しています。ratRange構造体が4バイトです。そしてVDMXレコードが続きます。高さレコードの数だけ1レコード12byteのvTable構造体を持ちます。VDMXタグテーブルは大きな配列になります。アスペクト比ごとのグループ?なんそれって感じでマイクロソフトの仕様書のページにいくと分類が乗ってました。なるほどxとyの比率を検出して、どのテーブルを使うべきかを検出するそうな。それで、ratRagneのxRatio、yStartRatio、yEndRatioが0,0,0になっているのが全ての比率に対応するグループのテーブルなのでこれが見つかると検索を終了するので、0,0,0は配列の最後に配置するべきだそうです。いやー、これは深い。ratRagneのbCharSetの意味はテーブルバージョンによって変わることになっています。Version 0 の bCharset 1のみすべてのグリフに適用せずANSIコードのみに適用されることになります。Version 1の利用が推奨されているようです。bCharset 0 は既存のフォントに追加する場合、1 は全く新しいフォント用に使うことになっています。

■VDMX Header

Name 説明
unsigned short UsVersion テーブルバージョン番号 0 or 1
unsigned short UsNumRecs VDMXグループ数
unsigned short UsNumRatios アスペクト比ごとのグループ数
ratioRange構造体 ratRange[UsNumRatios] numRatioの数の配列のratRage構造体
unsigned int vdmxGroupOffsets[UsNumRatios] numRatioの数の配列のテーブル先頭からのアドレス位置

■ratRagne構造体

Name 説明
unsigned char bCharSet 文字セット
unsigned char xRatio x-Ratioのために使う値
unsigned char yStartRatio y-Ratioの開始値
unsigned char yEndRatio y-Ratioの終了値


■VDMX Record

Name 説明
unsigned short UsRecs 高さレコード数
unsigned char ucStartsz y垂直骨高さ開始値
unsigned char ucEndsz y垂直骨高さ終了値
vTable構造体 vTable_Entry[usRecs] numRatioの数の配列のratRage構造体

■vTable構造体

Name 説明
unsigned short yPelHeight 適用するyPel値
short yMax yPelのための最大値
short yMin yPelのための最小値


 IMPACT.ttfの場合は以下のようになっていました。ものすごい細かく設定されてるんですね。フォントファイル制作ってやっぱスゴイな。

IMPACT_TAG_VDMX_TebleInfo.txt

 

hdmx スケール系

 hdmxはHorizontal Device Metricの意味でVDMXは大文字でしたが、水平方向についての定義です。このテーブルもheadテーブルのflag値のbit 4 が 1 である場合にのみ使われることが推奨されています。


Name 説明
unsigned short UsVersion テーブルバージョン番号 0
unsigned short usNumRecord レコード数
unsigned int UiSizeDeviceRecord DeviceRecordテーブルの大きさ。NumGlyph+2より大きい値
DeviceRecords Record[NumRecord] DeviceRecords構造体

 

■DeviceRecord構造体

Name 説明
unsigned char ucPixelSize ピクセルサイズ
unsigned char ucMaxWidth 最大幅
unsigned char ucWidths[numGlyph] 各グリフごとの送り幅


 全部がツメツメに格納されているわけではなく、1つのRecord番号のDeviceRecord構造体を記述するときにUiSizeDeviceRecordの長さになっていることは注意するべきところで、2+numGlyph数からUiSizeDeviceRecord Byteの分までを0x00でPaddingをしないといけません。


 IMPACT.ttfの内容は以下のファイルリンクの通りになります。割かし長いテーブル内容です。

IMPACT_TAG_hdmx_TebleInfo.txt

 

cmap

 文字コードをうけとって、グリフIDに変換して、必要なグリフ表示を行うための対応表です。最近はUnicodeを受け取る方式のフォントファイルcmapが準備されていることが多いそうです。他の方法もあるのは手ごわい。あんまり使われない方法も知らないといけないんだなぁ。やだな。

 中規模の可変長のテーブルになることが多いです。ややこしさはglypの実体テーブルの次くらいだという感じです。理解できるかどうかわかりませんが、仕様書をひも解いていきます。仕様書読んだらわかるだろ?そうなん?あれだけで瞬時に理解しきれるとか頭良すぎるでしょ。あーた。


 cmapテーブルはテーブルデータ部とサブテーブルで構成されます。


 テーブルデータ部でテーブルデータ数のエンコードレコード構造体が定義されます。まずはテーブルデータ数までの項目について確認すると

Name 説明
unsigned short UsVersion テーブルバージョン番号 0
unsigned short usNumTable レコード数
EncordingRecord EncodingRecord[usNumTable] EncodingRecord構造体をtableの数だけ配列を保持


■EncodingRecord構造体

Name 説明
unsigned short usPlatformID プラットフォームの番号 ※1
unsigned short usEncodingID プラットフォーム番号によって意味が変わるID
unsigned int uiOffset サブテーブルが配置されている位置のcmapテーブル先頭からのオフセット位置


 プラットフォームIDは全部で5種類。ID2は非推奨で4はCustomなのであまり使われないことを考えると、0と1と3がよく使われる。

■PlatformID

ID Name 説明
0 Unicode 文字コード形式Unicode用
1 Macintosh アップルのマッキントッシュOS向け
2 ISO 標準
3 Windows マイクロソフトのWindowsOS向け
4 Custom カスタム

 

■PlatfomrID=0(Unicode)のときのEncodingID

ID Name 説明
0 Unicode1.0 非推奨
1 Unicode1.1 非推奨。IMPACT.ttfは利用
2 ISO/IEC 10646 非推奨。
3 Unicode2.0 BMPのみ cmap format(0,4,6)
4 Unicode2.0 フルレパートリー cmap format(0,4,6,10,12)
5 Unicode 異体字制御 cmap format(14)
6 Unicode フルレパートリー cmap format(0,4,6,10,12,13)

 

■PlatfomrID=1(Macintosh)のときのEncodingIDは0のみ。

 

■PlatfomrID=2(ISO)のときのEncodingIDは0,1,2。 0 は 7bitASCIIエンコード。 1 は ISO10646エンコード。2 は ISO8859-1エンコード

 

■PlatfomrID=3(Windows)のときのEncodingID

ID Name 説明
0 Symbol 記号
1 Unicode BMP
2 ShiftJIS
3 PRC
4 Big5 cmap format(0,4,6,10,12)
5 Wansung
6 Johab cmap format(0,4,6,10,12,13)
7-9 予約
10 Unicode フルレパートリー cmap format(0,4,6,10,12,13)

 

■PlatfomrID=4(Custom)のときのEncodingIDは0~255。 OpenTypeFont WindowsNTの互換性マッピング


 サブテーブルには定義されている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

 

 以下にすべてのフォーマットについて記述していきますが、特に使われやすいものから記述していくつもりです。まずは0と4と12(Unicodeフル)と14(Unicode異体字制御)の4テーブルについて記述したいと思います。

 

■Format0
Name 説明
unsigned int usFormat 0 フォーマット番号
unsigned int usLength サブテーブルの長さ
unsigned int usLanguage 言語コード番号
unsigned char ucGlyphIndex[256] 配列番号が文字コードで、その値がグリフ番号となる形式。

 

■Format4
Name 説明
unsigned int usFormat 4 フォーマット番号
unsigned int usLength サブテーブルの長さ
unsigned int usLanguage 言語コード番号
unsigned short usSegCountX2 セグメント数の2倍の値
unsigned int usSearchRage
unsigned short usEntrySelector
unsigned int usRangeShift
unsigned short usEndCode[unSegCountX2/2]
unsigned int usReservePad 予約=0
unsigned short usStartCode[unSegCountX2/2]
unsigned int usIdDelta[unSegCountX2/2]
unsigned short usIdRangeOffset[unSegCountX2/2]
unsigned int usGlyphIndexArray[Variables] 要素数はテーブルの長さのあまり分


 Format4は疎な文字コード領域毎にグリフIDを付与する方式です。例えばUnicode文字コード0x20~0x7fは連番でGlyphIDの3から156に対応して、次の区画の…と繰り返して定義していくような感じです。但し、文字コードに対応するGIDの算出方法はもう少し複雑な内容になります。SearchRangeとかEntrySelectorとかrangeShiftは、header部分でもやったような処理になっています。


 例えばセグメントが以下のように定義されていて

      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)
…

 文字コード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
Name 説明
unsigned short usFormat 12 フォーマット番号
unsigned short usReserved 予約 0
unsigned int uiLength 長さ
unsigned int usLanguage 言語コード
unsigned int uiNGroups グループ数


 以下のテーブルがuiNGroupsの数だけ続きます。

Name 説明
unsigned int uiStartCharCode
unsigned int uiEndCharCode
unsigned int uiStartGlyphCode

 

■Format14

 UVSはUnicode Varidation Sequences Unicode選択値制御=異体字セレクタの略です。Format14は異体字用コードを扱うGlyphID検出方式です。

Name 説明
unsigned short usFormat 14 フォーマット番号
unsigned int uiLength
unsigned int uiNumVarSelectorRecords

 

 以下のテーブルがuiNumVarSelectorRecordsの数だけ続きます。

Name 説明
unsigned int uiVarSelector
unsigned int uiDefaultUVSOffset
unsigned int uiNonDefaultUVSOffset


■Default UVS Table

■Default UVS Table header

Name 説明
unsigned int uiNumUnicodeValueRanges

 

■Unicode Value Range

Name 説明
Uint 3byte Ui3StartUnicodeValue
Unsigned char ucAdditionalCount


■NonDefault UVS Table

■Default UVS Table header

Name 説明
unsigned int uiNumUVSMappings

 

■Unicode Value Range

Name 説明
Uint 3byte Ui3UnicodeValue
Unsigned short usGlyphID

 

fpgm ヒント系

 Font programの略です。中身はヘッダ定義された位置から、その長さまでが1バイトで表されたフォントプログラム命令(TrueType命令とかOpenType命令、TrueType Instruction ... というワードで検索すると対象の文献にたどり着ける)。多くの異なるプログラムから参照されるプログラムになっています。この後に記述するprepやグリフの中に埋め込まれるモノも同じような仕組みを使います。fpgmは関数化されたプログラムを配置するのが主な用途です。最初に関数番号がプッシュされ、関数定義されるたびにスタックからポップされていきます。関数の中でスタックにプッシュした値はポップで使い切らないと関数番号がおかしなことになります。


 プログラムの動作の流れは、文字を表示する前にprepに格納されたプログラムで初期化、各グリフに埋め込まれたFont Programが制御点の総数を管理しつつも、fpgmテーブルに格納された関数を番号を指定して呼び出す。各プログラムはcvtテーブルの内容に対して数値を書き出したり、読み込んだりという処理をするという流れです。


 工夫をすればわりといろいろなことが出来るプログラムですが、基本的にはフォントの形を微調整するのに使われることが多いです。極端な話これでフォントが使われている環境次第で動作を大きく変えるようなフォントを作ることで、その環境を調べることもできます。各アプリケーションがどういうふうにフォントを扱おうとしているかということも調べれるという感じです。世界には意外とフォントプログラムでそういうことをやっている人がいて、感心させられました。


 自分は仕様書のバイトコードと命令文の対応表から逆アセンブルしてどういうプログラムかを見渡すことくらいしかできませんでした。命令以外にも命令を先頭として、命令につづく数バイトをデータとして指定するようなこともあります。


 IMPACT.ttfでは以下のようなFont programを保持していました。


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://docs.microsoft.com/en-us/typography/opentype/spec/tt_instructions


 公式のフォント関連の情報の多さをみるだけでも複雑すぎることがうかがい知れる。これに少しでも対峙しようとした私たちは偉いのかもしれない。それを亡き者にしようとするアップル RetinaDisplay 高DPIが、低DPIの高度な技術を消し去るとはものすごいな。せっかく考えた技術なので無くなりはしないのだろうけど。これだけでもフォント専門学校作れるくらいの技術だな。全部理解するのに3年とかかかりそう。そしてさほど役に立たないかもしれない未来。そりゃ伝統技術も息絶えるわけだな。覚えるのにものすごく時間かかるのに、さほど必要とされていない未来。よく似てる。でも確かに存在する現代の職人。君の事だ。お金にはならないが、この知識。有意義だと思う。


 https://nixeneko.hatenablog.com/entry/2020/02/04/000000


 上記サイトの管理人はtruetype命令で、回転、pixel/em、pointSize、ランダム輪郭といったことをするフォントを作ってしまっています。font programを使いこなしてる。フォントヒンティングのためにあるから、出来ないこともあるという感じのことが伺えます。すごいね。

 

prep ヒント系

 preProgramの略です。一度だけ実行されるプログラムです。fprgの命令の前に実行されるもののようです。


 IMPACT.ttfの結果は以下とおりです。


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

 

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

 

glyf

 グリフの形状を記述した核となるテーブルがglyfタグテーブルです。glyfはGlyphのことです。以下のような構造の可変長のテーブルになっています。フォントファイルの中でも上位クラスの複雑さのため、プログラムも時間がかかりましたがやっとこさ、こさえることができました。フォントプログラムの一部の意味がまだ理解できていないので、おかしなことになっている。IFが来ないでいきなりELSEが現れるとこがあったり、プログラムの0xfxあたりの意味とかね。複合グリフの読み込み方はなんとか分かった。フラグによってフォントプログラムが登場したりもする。


Name 説明
short numberOfContours 輪郭数 -1だと複合グリフ
short xMin x方向最小値
short yMin y方向最小値
short xMax x方向最大値
short yMax y方向最大値


 グリフ情報の始まりは簡単な情報から始まる。わかりやすい。最初に輪郭数を2バイト符号付整数のshortで記述。輪郭数ならマイナスはなさそうだが、ここの-1と使って、-1なら複合グリフと判断する。-1は0xFF FFですね。実際は負の数なら全て複合グリフと判断する仕組みだけど、基本的には-1で表現される。複合グリフは英語だとComponent Glyphと表現します。輪郭情報をもった普通のグリフを1つ以上で表すグリフです。再利用みたいな感じだな。通常のグリフがあるから複合ができます。ちなみにIMPACT.ttfでも複合グリフは使ってました。英字フォントはこのあたりが強いみたいだね。ラテン文字とかに弱いので、こういう複合文字を作ってみようと思ったことが無いですが便利そうです。必要な地域に住んでいれば、こういう知識にも強いんだろうけど。必要は発明の母なり とはよく言ったものです。輪郭数がプラスだった場合について、先に記述します。以下のような情報が続きます。


Name 説明
unsigned short endPtsOfContours[n] 輪郭nの頂点の総数
unsigned short instructionLength フォントプログラム命令の長さ
unsigned char instructions[instructionLength] フォントプログラム
unsigne char flags[可変長] ビット毎意味付け記号
unsigne char
short
xCoordinates[] x座標[可変長]
unsigne char
short
yCoordinates[] y座標[可変長]


 輪郭数の分だけ、頂点総数が2バイト符号なし整数で記載されています。それにつづいてグリフに1つつけることのできるフォントプログラムの長さ、そしてプログラム本体がつづきます。そして、各頂点毎の属性を指定するようなものとしflagsが続きますが、ここは可変長になります。頂点の総数分だけflagがあるのですが、同じflagが続くことが多いため、同じものが繰り返されるという意味のflagも存在していて、その場合は次のflag値が繰り返す回数が1から256の値で記載されます。繰り返し回数が1の場合は節約になりませんのでどっちで記載されていても同じことです。また、その後に続く座標情報もx座標が変わらないフラグやy座標が変わらないフラグをもっているため、可変長になります。以下がflagsの意味について記した表です。


bit Flag名 説明
0 On Curve 0=制御点、1=輪郭上の点
1 x-Short Vector 0=short、1=unsigned char
2 y-Short Vector 0=short、1=unsigned char
3 Repeat 0=繰り返し無し、1=(次フラグ値分)繰り返し
4 This x is same
(Positive x-Short vector)
bit1が0のとき0=新しい値、1=同じ値で省略
bit1が1のとき0=座標値は負、1=座標値は正
5 This y is same
(Positive y-Short vector)
bit2が0のとき0=新しい値、1=同じ値で省略
bit2が1のとき0=座標値は負、1=座標値は正
6 Reserved 予約済 0
7 Reserved 予約済 0


 Flagsの意味が繰り返しや値省略や同値、といった複雑な意味を持つため読み取り処理も判断分岐が多くなり、複雑になります。たいして節約できないのに、なぜそんなに頑張る?というくらい節約しようとします。いまでこそフォントファイルを読み込むくらいたやすいことですが、昔はちょっとでも節約したかったのかもしれませんね。たしかに、同じになりやすいし繰り返しやすいこともあるなと思ったりもします。手書き文字フォントは垂直や水平に点が移動しないため、そういう場合だとほとんど節約できませんのに。


 そして、最初の言っていたnumberOfContoursが負の時は以下のような情報が続くことになっています。


Name 説明
unsigned short flags ビット毎意味付け記号
unsigned short glyphIndex 参照グリフ番号。指定された番号のグリフを利用。
unsigned short

short
unsigned char

char
argument1 参照グリフ配置x座標
unsigned short

short
unsigned char

char
argument2 参照グリフ配置y座標
transformationOption flags bit3=1のとき拡大縮小ありで指定される。


 複合グリフにもフラグがあって、このフラグによって、参照したグリフに拡大縮小オプションがあるかをbit3で指定したり、フォントプログラムがあるかどうか、これが最後の複合グリフ参照かどうかということや、参照グリフの配置座標の形式が決まります。

bit Flag名 説明
0 ARG_1_AND_2_ARE_WORDS argument1と2が0=char型、1=short型
1 ARGS_ARE_XY_VALUES argument1と2が0=符号有、1=符号なし
2 ROUND_XY_TO_GRID 0=グリッドに丸める、1=グリッドに丸めない
3 WE_HAVE_A_SCALE 0=等倍 オプション読み込みバイト数0、1=拡大縮小xy比維持 オプション読み込みバイト数1
4 This x is same
(Positive x-Short vector)
廃止 0を設定
5 MORE_COMPONENTS
(Positive y-Short vector)
0=複合グリフ終端、1=複合グリフ定義継続
6 WE_HAVE_AN_X_AND_Y_SCALE X、Y別拡大縮小指定 オプション読み込みバイト数2
7 WE_HAVE_A_TWO_BY_TWO 自由変形指定 オプション読み込みバイト数4
8 WE_HAVE_INSTRUCTIONS フォントプログラムが0=無し、1=有り
9 USE_MY_METRICS メトリック(文字間隔などの情報)がこの複合グリフに0=従わない。1=従う
10 OVERLAP_COMPOUND 複合グリフ同士が0=重ならない 1=重なる
11 未使用
12 未使用
13 未使用
14 未使用
15 未使用


上記のような構造で読み取ったIMPACT.ttfは以下のような情報を持っていることになっています。8万行以上にわたる膨大な情報です。プログラムを使わずしては読み取れないレベル。作った人もスゴイ。グリフ毎に付随しているプログラムとか確認できてんのかすげぇな。


IMPACT_TAG_glyf_TebleInfo.txt

 

kern

 文字間の調整をする役割のテーブルです。AとTなら狭めてMとLなら広めたりとするプロポーショナルフォントで使われる技術です。その定義方法が4種類あってFormatとして0から3までがあります。0はグリフIDの左と右とその場合の文字間の値を定義するだけなので簡単かなと思ったのですが、なかなか複雑なことになっています。

 まずはkernテーブルのヘッダとサブヘッダが以下の通り続きます。


Name 説明
unsigned int version バージョン情報 現行は0x00010000らしい。
unsigned int nTables テーブル数


 Impact.ttfではバージョン番号が0x00000001でした。なんか説明書と食い違うことが起こってきてる。これが自分の理解を進められない始まりになっているのかもしれない。いまだによくわかっていないんだな。テーブル数も、kernテーブルの長さである0x0b8aとなっている。あー。これってひょっとして、今まで一つも違いがなかったけど、opentypeの仕様に従っているってことなんじゃないか?OpenTypeの仕様に照らし合わせると、矛盾が少ない。Opentypeではヘッダは以下のようになっています。


Name 説明
unsigned short version バージョン情報 現行は0x0000らしい。
unsigned short nTables テーブル数


 これならテーブル数は1ってことで矛盾がない。いや、拡張子.ttfなのにOpenTypeなんだ。なんでもありだなコレ。次には


Name 説明
unsigned short version バージョン情報 これも0x0000らしい。
unsigned short length kernテーブルの長さ
unsigned short coverage サブテーブルの意味


 Impact.ttfの場合、lengthは0x0b8aで、coverageは0x0001でした。そうするとサブテーブル覆う意味は以下のようなビット符号により意味付けされます。


ビット Name 説明
0 horizontal 水平カーニングを0=持たない 1=持つ
1 minimum 0=最小値 1=カーニング値
2 cross-stream クロスストリーム値としての機能が0=ない 1=ある
3 override 置き換えられう必要が0=ない 1=ある
4~7 予約済 0で予約済
8~15 format 形式0と2の2種類


 カバレッジもtruetypeとかなり違っている。0x0001だと上位二桁でフォーマット0を意味していて、overrideも0、crossstreamも0で水平カーニングが1となっている。次に形式0のヘッダが続きます。

Name 説明
unsigned short nPair ペアの数(Format0は1ペア6バイト Lengthの1/6)
unsigned short SearchRange 1ペアのバイト数×2^EntrySelectorという値
unsigned short EntrySelector 2^EntrySelectorがペア数より小さくなるような最大の値
unsigned short RangeShift kernテーブルの長さ-SearchRangeの値


 これは他のテーブルでも使ってきた検索用速度向上用の値です。検索速度を高める実際の使い方を自分は理解していないので、そのうちに調べてみます。Impact.ttfの場合はSearchRangeが0x0600。EntrySelectorが0x08。RangeShiftが0x057cです。

 そして最後にペアの数分だけ、カーニング対象となるグリフの左側のグリフIDと右側のグリフIDが2バイトペアの4バイトで記述され、実際のカーニング値が2バイトの符号付きで表現されます。


Name 説明
unsigned short Left 左側の文字のグリフID
unsigned short Right 右側の文字のグリフID
unsigned short Value カーニング値


 OpenTypeの仕様書に従えば矛盾がないですね。自分はOpenTypeとTrueTypeの区別の仕方を間違えているのでしょう。以下が読み取ったImpact.ttfのkernテーブルの内容です。


IMPACT_TAG_kern_TebleInfo.txt

 

name

 情報の抜き出しとしては、nameテーブルが一番面白い。わかりやすいテキストデータが入ってますからね。どれどれ見せてごらん。みたいなね。FontForgeが表にしてくれるやつをテキストにして抜き出すっていうね。心地よいよ。

このテーブルにはVersion0とVersion1の2つのフォーマットがあります。


Name 説明
unsigned short version バージョン情報 0x0000 or 0x0001
unsigned short count 名前レコードの数
unsigned short StorageOffset 名前テーブルへのオフセット位置
NameRecord NameRecord[count] 名前レコードの数


 バージョン0だと上記まで、バージョン1だとさらに以下の2つの項目が続きます。

Name 説明
unsigned short langTagCount 言語タグレコードの数
LangTagRecord LangTagRecord[langTagCount] 言語タグレコード


 この後に、実際の文字列値である名前テーブルがつづきます。以下のような名前レコードでEncodingIDがローマ字の場合は1バイト文字。それ以外の場合2バイト文字といった具合に変化も発生します。


Name 説明
unsigned short platformID 0=Unicode、1=Macintosh、3=Windows
unsigned short encodingID 名前レコードがどういうコードで書かれるか
unsigned short languageID 言語ID。ものすごい数ある。
unsigned short NameID 項目名みたいなもの
unsigned short length 文字列の長さ
unsigned short stringOffset 名前レコードが終わったところから文字列が始まる位置


 国際的フォントの場合はいろいろの言語・地域に対応した名前が必要になる場合があり、たいていの場合、名前レコードもわりかし長くなります。EncodingIDとLanguageIDの対応表はものすごい長くなるので、ここには記述しません。公式サイトを見に行って下さい。文字列の長さはバイト長であって、文字数ではないです。Unicode関連の2バイト文字で書かれている場合は、Lengthの半分が文字数になります。


 Impact.ttfはバージョン0でした。なのでバージョン1にの仕組みはまだ確かめていません。また後日ですね。一通り、Impactで使われている形式に絞って、解析しつつ紹介してから、他のフォントをこのプログラムで読み込んでみて、うまくいかない部分を解析しては紹介していくという、なんと素晴らしい企画なのでしょう。企画?まぁまぁ長くなってるよこの作業。日本でこんなことやってる人だいぶと希少やと思う。ふふふ。フォントを知らずして文書を制覇することはできない。画像も動画も3Dも。頑張って解析して、フォントを自由にあやつるひとが日本人が文書プログラムに一石を投じることができるか?ってやつだな。


Impact.ttfのNameテーブルは以下のようになっていました。


IMPACT_TAG_name_TebleInfo.txt


post

 PostScript印刷用のデータについてのテーブルです。Impact.ttfの場合のデータを見てもどういう役割で、どう役に立つのかはわかりませんでした。PostScriptとフォントに関連した部分の勉強が必要だなと思いました。データは以下のようになります。


Name 説明
unsigned short version Version1, 2, 2.5, 3の4パターン
Fixed (2Byte) italicAngle 斜体文字の角度
DWORD (2Byte) underlinePosition 下線の位置
DWORD (2Byte) underlineThickness 下線の細さ
unsigned int isFixedPitch 下線の位置
unsigned int minMemType42 OpenTypeとしての最小メモリ容量
unsigned int maxMemType42 OpenTypeとしての最大メモリ容量
unsigned int minMemType1 OpenTypeをType1としての最小メモリ容量
unsigned int maxMemType1 OpenTypeをType1としての最大メモリ容量


 上記のような値によって印刷時にPostScriptによる操作ができるようになるってことなのかな。Impact.ttfでは以下のようになりました。


IMPACT_TAG_post_TebleInfo.txt

 

gasp

 Grid-fitting And Scan-conversion Procedure table ピクセルのマス目へのフィッティングとスキャン変換処理についてのテーブルです。小さいサイズの振る舞いは点のようなものにどのような濃度を付与するか。ちょっと小さいサイズの振る舞いには、ヒント情報を使った塗りつぶし。大きいサイズの振る舞いのヒントとスケーリングによって処理されるもの。実際にはどのように役割をはたすのかは理解していませんが、以下のようなテーブルになっています。


Name 説明
unsigned short version Version1のみ
unsigned short numRanges レコード数
GaspRange gaspRanges[numRanges] PPEM範囲毎のgaspテーブル配列


 gaspRanges配列は以下のようになっています。


Name 説明
unsigned short rangeMaxPPEM PPEM範囲の最大値に対しての定義
unsigned short rangeGaspBehavior 範囲における振る舞い


 振る舞いは以下のようなビット毎の記号によって意味がつけられています。


ビット Name 説明
0 GASP_GRIDFIT GRIDFITTINGを0=行わない。1=行う。
1 GASP_DOGRAY Grayスケールの塗りつぶしを0=行わない。1=行う。
2 GASP_SYMMETRIC_GRIDFIT ClearType対称フィッティングを0=行わない。1=行う。
3 GASP_SYMMETRIC_SMOOTHING ClearType対称スムーズを0=行わない。1=行う。
4-7 未使用予約済 0で固定


 IMPACT.ttfでは以下のようになっていました。


IMPACT_TAG_gasp_TebleInfo.txt


DSIG

 DSIG=Digital Signature はデジタル署名についてのテーブルです。署名の付け方はややこしいので、署名をつける専用のアプリがあった方が楽だと思います。フォント制作者はきっとそういうツールをもっています。フォントを改変して使うことの多い一般人としてはDSIGを取り除く作業をするくらいだと思います。署名されていないフォントとして改変するのが通常だと思います。それをやっていいかどうかはフォントのライセンス情報に従うことになります。


 署名は複数つけれるので、ヘッダテーブルとか署名本体テーブルといった具合にわかれています。以下のようなテーブルになっています。まずは署名のヘッダ情報的なテーブル構造複数の署名分だけ先に記述されます。署名が1個の場合も多いので、この構造が機能しない場合も多いです。


Name 説明
unsinged int version Versionは1
unsigned short numSignatures 署名の数
unsinged int flags ビット毎の意味付け符号
SignatureRecord signatureRecords[numSignatures] 署名本体のヘッダ情報の配列


 SignatureRecordテーブルは以下のような構造です。


Name 説明
unsinged int format フォーマット番号 1
unsigned int length 長さ
unsinged int signatureBlockOffset シグネチャーテーブルの最初の位置からブロック構造へのオフセット位置


 そして署名本体のテーブルが以下のように続きます。


Name 説明
unsinged short Reserve1 未使用予約済 0
unsigned short Reserve2 未使用予約済 0
unsinged int signatureLength 署名の長さ
unsigned char signature[signatureLength] バイナリー値の配列です。署名は文字列ではない。


 上記のようになっています。フォントファイル全体のMD5ハッシュテーブルに対して、署名化するPKCS#7 署名処理をします。Version 1.5 (https://www.rfc-editor.org/info/rfc2315)。Impact.ttfは以下のようになっています。


IMPACT_TAG_DSIG_TebleInfo.txt


GDEF

 GDEF Grif DEFinition Table グリフ定義テーブルは、GSUB、GPOSテーブルで用いられる定義で、7種類程度の定義が、あります。基本グリフ、合字、マーク、グリフコンポーネントのようなグリフクラスを定義するもの、接続点リストや合字キャレット、マーク接続グリフ、マークグリフセット、品目バリエーションセットといったグリフ毎に保持している特別な要素について定義しておくものです。グリフの中で定義されているよりも早く処理できるようにする工夫です。高速化の要素が不要な場合は定義自体があまりないフォントファイルもあります。


 バージョン毎に異なるテーブルになっています。


 バージョン1.0は以下のようになっています。

Name 説明
unsinged short major version Versionは1
unsigned short minor version Versionは0
unsinged short glyphClassDefOffset グリフクラス定義へのオフセット位置
unsigned short attachListOffset アタッチリスト定義へのオフセット位置
unsinged short ligCaretListOffset 合字キャレットリスト定義へのオフセット位置
unsigned short markAttachClassDefOffset マークアタッチクラス定義へのオフセット位置


 バージョン1.2は以下のようになっています。

Name 説明
unsinged short major version Versionは1
unsigned short minor version Versionは2
unsinged short glyphClassDefOffset グリフクラス定義へのオフセット位置
unsigned short attachListOffset アタッチリスト定義へのオフセット位置
unsinged short ligCaretListOffset 合字キャレットリスト定義へのオフセット位置
unsigned short markAttachClassDefOffset マークアタッチクラス定義へのオフセット位置
unsinged short markGlyphSetsDefOffset マークグリフ設定定義へのオフセット位置


 バージョン1.3は以下のようになっています。

Name 説明
unsinged short major version Versionは1
unsigned short minor version Versionは3
unsinged short glyphClassDefOffset グリフクラス定義へのオフセット位置
unsigned short attachListOffset アタッチリスト定義へのオフセット位置
unsinged short ligCaretListOffset 合字キャレットリスト定義へのオフセット位置
unsigned short markAttachClassDefOffset マークアタッチクラス定義へのオフセット位置
unsinged short markGlyphSetsDefOffset マークグリフ設定定義へのオフセット位置
unsigned short itemVarStoreOffset アイテムバリエーションストア定義へのオフセット位置

 

glyphClassDef

 glyphClassDefOffsetは以下のようなテーブルです。こちらもフォーマット番号毎にテーブル構造が変わります。


Name 説明
unsinged short classFormat 1の場合
unsigned short startGlyphID グリフIDの範囲スタート番号
unsinged short glyphCount グリフの数
unsigned short classValueArray[glyphCount] クラス値。グリフID毎の値がつづく。


 Format番号2の場合はいかのとおりです。


Name 説明
unsinged short classFormat 2の場合
unsigned short classRangeCount クラス範囲の数
unsinged short classRangeRecords[classRangeCount] クラス範囲レコード配列


 続いて、classRangeRecords配列が以下の通り続きます。


Name 説明
unsinged short startGlyphID グリフIDのスタート番号
unsigned short endGlyphID グリフIDのエンド番号
unsinged short class クラス値


 この調子で残り5種類のテーブルが続きます。なかなか大変。

attachList

 付け足し位置に関しての定義情報です。

Name 説明
unsinged short coverageOffset オフセット位置
unsigned short glyphCount グリフの数
unsinged short attachPointOffsets[glyphCount] 付け足し点の位置配列


 attachPointOffsets配列は以下のように続きます。


Name 説明
unsinged short coverageOffset オフセット位置
unsigned short pointIndices[pointCount] グリフの数


Ligature Caret List

 合字のキャレット表示位置についての定義です。


Name 説明
unsinged short coverageOffset オフセット位置
unsigned short ligGlyphCount 合字グリフの数
unsinged short ligGlyphOffsets[ligGlyphCount] 合字キャレットの位置配列


Ligature Glyph Table

 合字グリフについての定義です。


Name 説明
unsinged short caretCount キャレット数
unsigned short caretValueOffsets[caretCount] キャレット位置の値


Caret Value Table

 キャレットについての定義です。テーブルのフォーマットが3つあります。


 フォーマット1の場合は以下のとおり


Name 説明
unsinged short caretValueFormat フォーマット 1の場合
unsigned short coordinate キャレット位置の値


 フォーマット2の場合は以下のとおり


Name 説明
unsinged short caretValueFormat フォーマット 2の場合
unsigned short caretValuePointIndex キャレット値のポイント


 フォーマット3の場合は以下のとおり


Name 説明
unsinged short caretValueFormat フォーマット 3の場合
unsigned short coordinate 座標値
unsinged short deviceOffset X値またはY値の変動指数テーブル(可変フォント)へのオフセット


Mark Attachment Class Definition Table

 クラス定義テーブルと同一の構造です。

Mark Glyph Sets Table

 マークグリフの設定をするテーブルです。

Name 説明
unsinged short format フォーマット 1のみ
unsigned short markGlyphSetCount マークグリフに設定する数。
unsinged int coverageOffsets[markGlyphSetCount] MarkGlyphSet テーブルの先頭から、グリフセットカバレッジテーブルをマークするオフセットの配列


Item Variation Store Table

 このテーブルは理解できませんでした。初めてだな。うんともすんとも意味がわからん公式の説明。あちらに説明がありますっていってみたものの、いやなんか違うんじゃないかと不安になるような感じ。本当の意味で理解しないと読み解けないのかもしれない。Impact.ttfでも使ってない部分なので、また後で考えてみます。

まとめ

 GDEFテーブルは、なかなか複雑。のこり3テーブルくらい説明したら、ひととおりと思ったけど、箱根駅伝みたいに最後が急坂だな。GPOSもGSUBも大変そうだ。METAは簡単そう。

Impact.ttfは以下のような感じだった。なんなんLigCarretListのテーブルはOffsetだけ存在して中身が無い感じ。ゴミが残ったのかな。

IMPACT_TAG_GDEF_TebleInfo.txt

 

GPOS

 GPOS=Glyph POSitioning tableはkernテーブルの上位機能を持っています。TrueTypeのkernでは水平または垂直のどちからの幅や文字の幅の取り決めしかできませんが。GPOSはOpenTypeにのみ存在するテーブルで、斜め接続が必要なウルドゥー語でより正確な接続を表現できます。ベトナム語のアクセント発音記号となり修飾文字の位置調整も可能になり制御が楽になります。日本語や、ラテン文字ではこのような飛躍的な機能は必要ならないかもしれません。必要ない言語圏の人であっても、外国語を習得する人にとっては重要な機能になり得ます。国際的な電子文書保持アプリにも役立ちます。


大きく分けて4つの機能をもっています。

  • ScriptList
配置に使用するスクリプトと言語システムを識別するためリスト。
  • FeatureList
レンダリングするために必要なグリフ配置機能
  • LookupList
グリフ位置決め機能を実装するために必要なすべての文字検索データ含まれます。機能は9種類あります。
  • FeatureVariation
特定の機能に使用する参照テーブルの代替セットを置き換えることができます。現状、可変フォントでのみ使用されます。


 GPOSヘッダバージョン1.0テーブルは以下のようになります。


Name 説明
unsinged short majorVersion メジャーバージョン番号 1
unsigned short minorVersion マイナーバージョン番号 0
unsinged short scriptListOffset scriptListテーブルへのオフセット位置
unsigned short featureListOffset featureListテーブルへのオフセット位置
unsinged short lookupListOffset lookupListテーブルへのオフセット位置


 GPOSヘッダバージョン1.1テーブルは以下のようになります。


Name 説明
unsinged short majorVersion メジャーバージョン番号 1
unsigned short minorVersion マイナーバージョン番号 0
unsinged short scriptListOffset scriptListテーブルへのオフセット位置
unsigned short featureListOffset featureListテーブルへのオフセット位置
unsinged short lookupListOffset lookupListテーブルへのオフセット位置
unsigned short featurevariationsListOffset featurevariationsListテーブルへのオフセット位置


 ScriptListテーブルは以下のようになっています。


Name 説明
unsinged short scriptCount スクリプトレコードの数
ScriptRecord scriptRecords[scriptCount] スクリプトレコード配列


 続いて、スクリプトレコード配列が記載されます。


Name 説明
unsinged int scriptCount 4byteスクリプトタグID
unsigned short scriptOffset スクリプトテーブルへのオフセットScriptList基点


 更にScriptテーブルが続きます。


Name 説明
unsinged short defaultLangSysOffset defaultLangSysへのオフセットScriptテーブル基点
unsigned short langSysCount LangSysRecordsの数
LangSysRecord LangSysRecords[langSysCount] LangSysRecords配列

 更に更に、 LangSysRecord配列が続きます。


Name 説明
unsinged int langSysTag 4ByteのLangSysTagID
unsigned short langSysCount LangSysテーブルへのオフセット


更に、そのさらにさらに、LangSysテーブルが続きます。


Name 説明
unsinged short lookupOrderOffset 4ByteのLangSysTagID
unsigned short requiredFeatureIndex この言語システムの機能要件の目次
unsigned short featureIndexCount featureIndexの数
unsigned short featureIndices[featureIndexCount] 2byte数値によるfeatureIndices配列


 FeatureListテーブルは以下のようになっています。


Name 説明
unsinged short featureCount スクリプトレコードの数
FeatureRecord featureRecords[featureCount] スクリプトレコード配列


 featureRecode配列は以下のようになっています。


Name 説明
unsinged int featureTag 4ByteのFeatureIDTag
unsigned short featureOffset Featureテーブルへのオフセット位置 FeatureList基点


 続いて、Featureテーブルが以下の通りに記述されます。


Name 説明
unsinged short featureParamesOffset FeatureParamsテーブルへのオフセット位置 Featureテーブル基点
unsigned short lookupIndexCount lookupIndexの数
unsinged short lookupListIndices[lookupIndexCount] lookupList目次配列

FeatureParamsテーブルは以下の記事に記載あり。公式のページでも説明されてるページが飛ぶのであってるかは不明。 https://learn.microsoft.com/en-us/typography/opentype/spec/features_ae#cv01-cv99


 LookupListテーブルは以下のようになっています。


Name 説明
unsinged short lookupType GPOSのための列挙子
unsigned short lookupFlag 検索方式
unsinged short subTableCount 検索サブテーブルの数
unsigned short lookupFlag 検索サブテーブルへのオフセット位置
unsinged short markFilteringSet 印グリフとして目次を設定するもの


 GPOS 検索型列挙子は以下の通りになります。


列挙子番号 Type 説明
1 Single adjustment 単体グリフの位置調整用
2 Pair adjustment ペアグリフの位置調整用
3 Cursive attachment 筆記体の付け合わせ
4 MarkToBase attachment 印と基礎の付け合わせ
5 MarkToLigature attachment 印と合字の付け合わせ
6 MarkToMark attachment 印と印の付け合わせ
7 Context positioning 一つあるいは他グリフの内容に対する位置
8 Chained Context positioning 一つあるい多グリフの連鎖内容に対する位置
9 Extension positioning 拡張位置合わせ
10+ Reserved 予約済 0


 検索方式のビット符号フラグは以下のようになっています。


ビット Name 説明
1 RIGHT_TO_LEFT CURSIVE attachmentの検索列挙子のときに関連しています。設定すると筆記体の付け合わせファイル検索が適用されるシーケンスの最後のグリフがベースラインに配置されます。
2 IGNORE_BASE_GLYPHS 基本グリフに対しては除外します。
3 IGNORE_LIGATURES 合字に対しては除外します。
4 IGNORE_MARKS マークに対しては除外されます。
5 USE_MARK_FILTERING_SET 設定されている場合はルックアップテーブルの後にMarkFilteringSet配列が続くことを示唆
6-8 RESERVED 予約済
9-16 MARK_ATTACHMENT_TYPE_MASK 指定したものと異なるATTACHMENT FILEの種類のすべてのマークを除外します。


 ここから先は9種類あるLookupListテーブルのサブテーブルについて記述していきます。大変だこれ。いっぱいあるな。9種類+フォーマット別もあるやつあるんで、項目自体も長くなりそう。書き手、読み手ともに覚悟せよって感じだな。ここまで記事を読みこんで理解を進めている人いないと思うけど…


LookupSubTable 1 ■Single Adjustment

 フォーマットが2つあります。

 フォーマット1は以下のとおり。


Name 説明
unsinged short posFormat フォーマット番号 1
unsigned short coverageOffset coverageテーブルへのオフセット位置
unsinged short valueFormat ValueRecordのタイプ
Value Record Value Record Value Record配列値


 フォーマット2は以下のとおり。


Name 説明
unsinged short posFormat フォーマット番号 2
unsigned short coverageOffset coverageテーブルへのオフセット位置
unsinged short valueFormat ValueRecordのタイプ
unsinged short Value Count Value Recordの数
ValueRecord ValueRecord[ValueCount] ValueRecord配列


 共通レコードのCoverage Talbe と Value Record Tableが使われます。Coverage Tableはちょっと前にも出てきました。さっそく再利用されてます。GSUBテーブルでも共通レコードが使われるそうな。公式の説明書にValueRecord配列についての説明をしたリンクがないのが不満です。わかりづらい。ここの記事は公式を写し取ってるだけじゃんと思う部分もあるかと思いますが、こういった不満が解消されている部分もあるというメリットもあります。思う存分、利用するといいね。

 以下がValueRecordの内容です。

Name 説明
unsinged short xPlacement 設計された単位で配置するための水平調整。
unsigned short yPlacement 設計された単位で配置するための垂直調整。
unsinged short xAdvance 設計された単位で配置するための水平幅調整。
unsigned short yAdvance 設計された単位で配置するための垂直幅調整。
unsinged short xPlaDeviceOffset 水平配置デバイステーブルへのオフセット値
unsinged short yPlaDeviceOffset 垂直配置デバイステーブルへのオフセット値
unsinged short xAdvDeviceOffset 水平送り幅デバイステーブルへのオフセット値
unsinged short yAdvDeviceOffset 垂直送り幅デバイステーブルへのオフセット値

 

LookupSubTable 2

 

LookupSubTable 3

 

LookupSubTable 4

 

LookupSubTable 5

 

LookupSubTable 6

 

LookupSubTable 7

 

LookupSubTable 8

 

LookupSubTable 9

 



 

GSUB

 

meta

 

関連記事

フォント OpenType 構造解析
FreeTypeをコンパイル
FreeTypeを使う

  

フォントに戻る。