フォント TrueType 構造解析
フォントに戻る。
概要
仕様書は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行を超えるため、テキストへのリンクを貼り付けておきます。これだけの値を手作業でまとめることは無謀すぎます。ようやっとでプログラムで動かしてるんだなって思ってもらえると思います。
つかっているプログラムは、もし完成することがあるならば有用なものになるかもしれないので、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行の文字長さを推測できるもだそうです。
字 | 係数 | 字 | 係数 | 字 | 係数 | 字 | 係数 | 字 | 係数 |
---|---|---|---|---|---|---|---|---|---|
a | 64 | b | 14 | c | 27 | d | 35 | e | 100 |
f | 20 | g | 14 | h | 42 | i | 63 | j | 3 |
k | 6 | l | 35 | m | 20 | n | 56 | o | 56 |
p | 17 | q | 4 | r | 49 | s | 56 | t | 71 |
u | 31 | v | 10 | w | 18 | x | 3 | y | 18 |
z | 2 | sp | 166 |
※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 AdbeVenderID | 名前 |
---|---|
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の設定内容は以下の通りでした。グリフの数だけある線形スケーリング垂直骨高さなので、テキストファイルのリンクを示します。
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の場合は以下のようになっていました。ものすごい細かく設定されてるんですね。フォントファイル制作ってやっぱスゴイな。
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の内容は以下のファイルリンクの通りになります。割かし長いテーブル内容です。
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についてテキスト化したものをリンクにしておきます。
以下にすべてのフォーマットについて記述していきますが、特に使われやすいものから記述していくつもりです。まずは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を保持していました。
前半が単純なバイトコードの羅列で後半に逆アセンブルした情報が記載されています。関数が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の結果は以下とおりです。
cvt ヒント系
Control Value Table は 制御値テーブルで prep や fpgm や グリフの中のFont Programで使われる制御値をまとめたテーブルです。このテーブルは Font Programで追加・削除・編集される可能性があるため、初期値のようなものです。
公式では4バイトの数値となっていますが、実際は2バイトの数値のタグテーブル部で記述されたサイズの配列となっているように見えます。FontForgeも2バイトで扱っているようです。
2バイトで扱った場合のIMPACT.ttfの結果が以下のとおりです。
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の結果は以下のようになっています。
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 |
argument1 | 参照グリフ配置x座標 |
unsigned short short |
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万行以上にわたる膨大な情報です。プログラムを使わずしては読み取れないレベル。作った人もスゴイ。グリフ毎に付随しているプログラムとか確認できてんのかすげぇな。
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テーブルの内容です。
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テーブルは以下のようになっていました。
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では以下のようになりました。
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では以下のようになっていました。
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は以下のようになっています。
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だけ存在して中身が無い感じ。ゴミが残ったのかな。
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 ■Pair Adjustment
このテーブルもフォーマットが2つあります。
以下はフォーマット1の内容です。
型 | Name | 説明 |
---|---|---|
unsinged short | posFormat | フォーマット番号 1 |
unsigned short | coverageOffset | coverageテーブルへのオフセット位置 |
unsinged short | valueFormat1 | ValueRecordのタイプ |
unsinged short | valueFormat2 | ValueRecordのタイプ |
unsinged short | PairSetCount | PairSetテーブルの数 |
unsinged short | PairSetCount | PairSetテーブル |
PairValueテーブルは以下のとおり
型 | Name | 説明 |
---|---|---|
unsinged short | PairValueCount | PairValueRecordの数 |
PairValueRecord | PairValueRecord[PairValueCount] | PairValueRecord配列 |
PairValueRecordは以下のとおり
型 | Name | 説明 |
---|---|---|
unsinged short | secondGlyph | 2番目のグリフのグリフID |
ValueRecord | ValueRecord1 | ValueRecord1配列は少し手前に使ったValueRecordと同じ。 |
ValueRecord | ValueRecord2 | ValueRecord2配列は少し手前に使ったValueRecordと同じ。 |
フォーマット2は以下のとおり。
型 | Name | 説明 |
---|---|---|
unsinged short | posFormat | フォーマット番号 2 |
unsigned short | coverageOffset | coverageテーブルへのオフセット位置 |
unsinged short | valueFormat1 | ValueRecordのタイプ |
unsinged short | valueFormat2 | ValueRecordのタイプ |
unsinged short | ClassDef1Offset | ClassDefテーブルへのオフセット |
unsinged short | ClassDef2Offset | ClassDefテーブルへのオフセット |
unsinged short | Class1Count | ClassDef1テーブルのClassの数 |
unsinged short | Class2Count | ClassDef2テーブルのClassの数 |
Class1Record | Class1Record[Class1Count] | Class1Recordテーブル |
以下がClass1Recordの内容です。
型 | Name | 説明 |
---|---|---|
Class2Record | Class2Record[Class2Count] | Class2Recordテーブル |
以下がClass1Recordの内容です。
型 | Name | 説明 |
---|---|---|
ValueRecord | ValueRecord1 | ValueRecord1テーブル |
ValueRecord | ValueRecord2 | ValueRecord2テーブル |
LookupSubTable 3 ■Cursive Attachment
筆記体の位置の付け合わせのためにある、このテーブルはフォーマットが1つです。ですが、これにぶら下がっているAnchorテーブルというものにフォーマットが3つあります。
以下はフォーマット1の内容です。
型 | Name | 説明 |
---|---|---|
unsinged short | posFormat | フォーマット番号 1 |
unsigned short | coverageOffset | coverageテーブルへのオフセット位置 |
unsinged short | entryExitCount | EntryExitレコードの数 |
EntryExitRecord | EntryExitRecord[EntryExitRecord] | EntryExitRecord配列 |
EntryExitRecordは以下のとおり
型 | Name | 説明 |
---|---|---|
unsinged short | EntryAnchorOffset | EntryAnchorテーブルへのオフセット |
unsigned short | ExitAnchorOffset | ExitAnchorテーブルへのオフセット |
Anchorテーブルのフォーマット1は以下のとおりです。
型 | Name | 説明 |
---|---|---|
unsinged short | anchorFormat | アンカーフォーマット番号 1 |
unsigned short | xCoordinate | x座標値 |
unsinged short | xCoordinate | x座標値 |
Anchorテーブルのフォーマット2は以下のとおりです。
型 | Name | 説明 |
---|---|---|
unsinged short | anchorFormat | アンカーフォーマット番号 2 |
unsigned short | xCoordinate | x座標値 |
unsinged short | xCoordinate | x座標値 |
unsigned short | anchorPoint | グリフの輪郭点への管理番号 |
Anchorテーブルのフォーマット3は以下のとおりです。
型 | Name | 説明 |
---|---|---|
unsinged short | anchorFormat | アンカーフォーマット番号 3 |
unsigned short | xCoordinate | x座標値 |
unsinged short | xCoordinate | x座標値 |
unsigned short | xDeviceOffset | x座標値デバイステーブルへのオフセット |
unsinged short | yDeviceOffset | y座標値デバイステーブルへのオフセット |
LookupSubTable 4 ■Mark-to-Mark Attachment
このテーブルはフォーマットが1つです。
以下はフォーマット1の内容です。
型 | Name | 説明 |
---|---|---|
unsinged short | posFormat | フォーマット番号 1 |
unsinged short | markCoverageOffset | markCoverageテーブルへのオフセット位置 |
unsinged short | baseCoverageOffset | baseCoverageテーブルへのオフセット位置 |
unsigned short | markClassCount | マークのために定義されているクラスの数 |
unsinged short | markArrayOffset | markArrayテーブルへのオフセット位置 |
unsigned short | baseArrayOffset | baseArrayテーブルへのオフセット位置 |
baseArrayテーブルは以下の通りです。
型 | Name | 説明 |
---|---|---|
unsinged short | baseCount | BaseRecordの数 |
BaseRecord | BaseRecord[baseCount] | BaseRecord配列 |
BaseRecord配列は以下のようなものです。
型 | Name | 説明 |
---|---|---|
unsinged short | baseAnchorOffsets[markClassCount] | Anchorテーブルへのオフセットの配列 |
LookupSubTable 5 ■Mark-to-Ligature Attachment
このテーブルはフォーマットが1つです。
以下はフォーマット1の内容です。
型 | Name | 説明 |
---|---|---|
unsinged short | posFormat | フォーマット番号 1 |
unsinged short | markCoverageOffset | markCoverageテーブルへのオフセット位置 |
unsinged short | ligatureCoverageOffset | ligatureCoverageテーブルへのオフセット位置 |
unsigned short | markClassCount | マークのために定義されているクラスの数 |
unsinged short | markArrayOffset | markArrayテーブルへのオフセット位置 |
unsigned short | ligatureArrayOffset | ligatureArrayテーブルへのオフセット位置 |
ligatureArrayテーブルは以下の通りです。
型 | Name | 説明 |
---|---|---|
unsinged short | ligatureCount | ligature配列の数 |
LigatureAttachOffset | LigatureAttach[ligatureCount] | LigatureAttach配列へのオフセット位置 |
ligatureAttach配列は以下のとおり
型 | Name | 説明 |
---|---|---|
unsinged short | componentCount | LigatureのComponentRecordsの数 |
ComponentRecord | ComponentRecord[ComponentCount] | ComponentRecord配列 |
型 | Name | 説明 |
---|---|---|
unsinged short | LigatureAnchorOffsets[markClassCount] | Anchorテーブルへのオフセット位置の配列 |
LookupSubTable 6 ■Mark-to-Mark Attachment
このテーブルはフォーマットが1つです。
以下はフォーマット1の内容です。
型 | Name | 説明 |
---|---|---|
unsinged short | posFormat | フォーマット番号 1 |
unsinged short | mark1CoverageOffset | mark1Coverageテーブルへのオフセット位置 |
unsinged short | mark1CoverageOffset | mark1Coverageテーブルへのオフセット位置 |
unsigned short | markClassCount | マークのために定義されているクラスの数 |
unsinged short | mark1ArrayOffset | mark1Arrayテーブルへのオフセット位置 |
unsigned short | mark1ArrayOffset | mark1Arrayテーブルへのオフセット位置 |
Mark2Arrayテーブルは以下の通りです。
型 | Name | 説明 |
---|---|---|
unsinged short | Mark2Count | Mark2レコードの数 |
Mark2Records | Mark2Records[Mark2Count] | Mark2Records配列 |
Mark2Record配列は以下のとおり
型 | Name | 説明 |
---|---|---|
unsinged short | mark2AnchorOffset[markClassCount] | Anchorテーブルへのオフセットの配列 |
LookupSubTable 7 ■Context positioning
GPOS LookupListテーブルに準ずるらしい。各シーケンス位置+ネストされたLookupの組み合わせはaSequenceLookupRecordで指定されている。
LookupSubTable 8 ■Chained Context positioning
シーケンスコンテキストの単体グリフコンテキストに準ずるらしい。
フォーマット1もhttps://learn.microsoft.com/en-us/typography/opentype/spec/chapter2#chseqctxt1
フォーマット2はhttps://learn.microsoft.com/en-us/typography/opentype/spec/chapter2#chseqctxt2
フォーマット3はhttps://learn.microsoft.com/en-us/typography/opentype/spec/chapter2#chseqctxt3
LookupSubTable 9 ■Extension positioning
このテーブルはフォーマットが1つです。
以下はフォーマット1の内容です。
型 | Name | 説明 |
---|---|---|
unsinged short | posFormat | フォーマット番号 1 |
unsinged short | extensionLookupType | サブテーブルを参照する検索タイプ |
unsinged int | extensionOffset | 拡張サブテーブルへのオフセット |
FeatureVariation テーブルは以下のとおりです。
型 | Name | 説明 |
---|---|---|
unsinged short | majorVersion | メジャーバージョン番号 1 |
unsinged short | minorVersion | マイナーバージョン番号 0 |
unsinged int | featureVariationRecordCount | featureVariationRecordの数 |
featureVariationRecord | featureVariationRecord[featureVariationRecordCount] | featureVariationレコード |
featureVariationレコードは以下のとおりです。
型 | Name | 説明 |
---|---|---|
unsinged int | conditionSetOffset | ConditionSetへのOffset |
unsinged int | featureTableSubstitutionOffset | featureTableSubstitutionテーブルへのオフセット |
GSUB
Glyph Substitution Table はグリフ置換を行うためのもので、言語によっては特定の文字位置や順序。特定の発音に切り替わるといった場合にはグリフの形そのものを変更する必要があり、このような処理をおこなうためのテーブルです。日本語の場合は横書きから縦書きに変更されたときにグリフが置換されることがあります。例えば括弧の向きなどが良い例です。
GSUBはGPOSとよく似た構造を持っていますが、GPOSが位置の調整だったのに対して、GSUBは最終的に調整するものが、グリフの置換であるということに注意が必要です。
SPOS同様、大きく分けて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 Substitution
フォーマットが2つあります。
フォーマット1は以下のとおり。
型 | Name | 説明 |
---|---|---|
unsinged short | substFormat | フォーマット番号 1 |
unsigned short | coverageOffset | coverageテーブルへのオフセット位置 |
unsinged short | deltaGlyphID | 元のGlyphIDに追加する代替GlyphIDを得るための数値 |
フォーマット2は以下のとおり。
型 | Name | 説明 |
---|---|---|
unsinged short | substFormat | フォーマット番号 2 |
unsigned short | coverageOffset | coverageテーブルへのオフセット位置 |
unsinged short | glyphCount | グリフの数 |
unsinged short | substituteGlyphIDs[glyphCount] | グリフ置換の配列 |
共通レコードのCoverage Talbe と substituteGlyphIDs Tableが使われます。Coverage Tableはちょっと前にも出てきました。さっそく再利用されてます。GPOSテーブルでも共通レコードが使われてましたね。
LookupSubTable 2 ■Multi Substitution
このテーブルもフォーマットが2つあります。
以下はフォーマット1の内容です。
型 | Name | 説明 |
---|---|---|
unsinged short | substFormat | フォーマット番号 1 |
unsigned short | coverageOffset | coverageテーブルへのオフセット位置 |
unsinged short | sequenceCount | sequenceテーブルへのオフセットの配列大きさの数 |
unsinged short | sequenceOffsets[sequenceCount] | sequenceテーブル |
sequenceテーブルは以下のとおり
型 | Name | 説明 |
---|---|---|
unsinged short | glyphCount | 置き換えるGlyphの数 |
unsinged short | substituteGlyphIDs[glyphCount] | 置換GliphID |
フォーマット2は以下のとおり。
型 | Name | 説明 |
---|---|---|
unsinged short | substFormat | フォーマット番号 2 |
unsigned short | coverageOffset | coverageテーブルへのオフセット位置 |
unsinged short | valueFormat1 | ValueRecordのタイプ |
unsinged short | valueFormat2 | ValueRecordのタイプ |
unsinged short | ClassDef1Offset | ClassDefテーブルへのオフセット |
unsinged short | ClassDef2Offset | ClassDefテーブルへのオフセット |
unsinged short | Class1Count | ClassDef1テーブルのClassの数 |
unsinged short | Class2Count | ClassDef2テーブルのClassの数 |
Class1Record | Class1Record[Class1Count] | Class1Recordテーブル |
以下がClass1Recordの内容です。
型 | Name | 説明 |
---|---|---|
Class2Record | Class2Record[Class2Count] | Class2Recordテーブル |
以下がClass1Recordの内容です。
型 | Name | 説明 |
---|---|---|
ValueRecord | ValueRecord1 | ValueRecord1テーブル |
ValueRecord | ValueRecord2 | ValueRecord2テーブル |
LookupSubTable 3 ■Alternate Substitute
このテーブルはフォーマットが1つです。ですが、これにぶら下がっているAnchorテーブルというものにフォーマットが3つあります。
以下はフォーマット1の内容です。
型 | Name | 説明 |
---|---|---|
unsinged short | substFormat | フォーマット番号 1 |
unsigned short | coverageOffset | coverageテーブルへのオフセット位置 |
unsinged short | alternateSetCount | AlternateSetテーブルの数 |
unsinged short | AlternateSetOffsets[alternateSetCount] | AlternateSetOffsetsテーブル |
EntryExitRecordは以下のとおり
型 | Name | 説明 |
---|---|---|
unsinged short | glyphCount | グリフの数 |
unsigned short | alternateGlyphIDs[glyphCount] | alternateGlyphIDsレコード値 |
LookupSubTable 4 ■Ligature Substitution
このテーブルはフォーマットが1つです。
以下はフォーマット1の内容です。
型 | Name | 説明 |
---|---|---|
unsinged short | substFormat | フォーマット番号 1 |
unsinged short | CoverageOffset | Coverageテーブルへのオフセット位置 |
unsinged short | ligatureSetCount | ligatureSetテーブルの数 |
unsigned short | ligatureSetOffset[ligatureSetCount] | ligatureSetテーブルへのオフセットの配列 |
ligatureSetテーブルは以下の通りです。
型 | Name | 説明 |
---|---|---|
unsinged short | ligatureCount | Ligatureテーブルの数 |
unsigned short | ligatureOffset[ligatureCount] | ligatureテーブルへのオフセット |
ligatureテーブルは以下のようなものです。
型 | Name | 説明 |
---|---|---|
unsinged short | ligatureGlyph | 置換する部品のGlyphID |
unsigned short | componentCount | ligatureテーブルへのオフセット |
unsinged short | componentGlyphIDs[componentCount - 1] |
LookupSubTable 5 ■Contextual Substitution
このテーブルはフォーマットが3つです。
具体的なテーブル構造は公式では、リンク先で紹介しているようなので、後ほど、探し回りたいと思います。
LookupSubTable 6 ■Chain Contexts Substitution
このテーブルはフォーマットが1つです。
具体的なテーブル構造は公式では、リンク先で紹介しているようなので、後ほど、探し回りたいと思います。
LookupSubTable 7 ■Context positioning
GPOS LookupListテーブルに準ずるらしい。各シーケンス位置+ネストされたLookupの組み合わせはaSequenceLookupRecordで指定されている。
LookupSubTable 8 ■Chained Context positioning
シーケンスコンテキストの単体グリフコンテキストに準ずるらしい。
フォーマット1もhttps://learn.microsoft.com/en-us/typography/opentype/spec/chapter2#chseqctxt1
フォーマット2はhttps://learn.microsoft.com/en-us/typography/opentype/spec/chapter2#chseqctxt2
フォーマット3はhttps://learn.microsoft.com/en-us/typography/opentype/spec/chapter2#chseqctxt3
LookupSubTable 9 ■Extension positioning
このテーブルはフォーマットが1つです。
以下はフォーマット1の内容です。
型 | Name | 説明 |
---|---|---|
unsinged short | substFormat | フォーマット番号 1 |
unsinged short | extensionLookupType | サブテーブルを参照する検索タイプ |
unsinged int | extensionOffset | 拡張サブテーブルへのオフセット |
FeatureVariation テーブルは以下のとおりです。
型 | Name | 説明 |
---|---|---|
unsinged short | majorVersion | メジャーバージョン番号 1 |
unsinged short | minorVersion | マイナーバージョン番号 0 |
unsinged int | featureVariationRecordCount | featureVariationRecordの数 |
featureVariationRecord | featureVariationRecord[featureVariationRecordCount] | featureVariationレコード |
featureVariationレコードは以下のとおりです。
型 | Name | 説明 |
---|---|---|
unsinged int | conditionSetOffset | ConditionSetへのOffset |
unsinged int | featureTableSubstitutionOffset | featureTableSubstitutionテーブルへのオフセット |
meta
関連記事
フォントに戻る。