GSUB テーブル (3) - LookupList [1]

LookupList
次に、LookupList テーブルです。
ここには、対象のグリフを検索して、グリフの置き換えなどを行うためのデータが格納されています。

FeatureList で取得したインデックス値を使って参照します。

なお、ScriptList/FeatureList/LookupList は、「GPOS テーブル」でも同じように使われます。

ただし、この先で説明する LookupType の値やサブテーブルのフォーマットは、GSUB・GPOS でそれぞれ異なります。
テーブルデータ
LookupList
uint16 lookupCount以下の配列数
Offset16 lookups[lookupCount]Lookup テーブルのオフセット位置。
(LookupList の先頭を 0 とする)

FeatureList テーブルで取得したインデックス値は、この配列の添字となる。

Lookup
uint16 lookupTypeLookup Type の番号
uint16 lookupFlagフラグ
uint16 subTableCountサブテーブルの数
Offset16 subtableOffsets[subTableCount]各サブテーブルへのオフセット位置。
(Lookup の先頭を 0 とする)
uint16 markFilteringSetlookupFlag で useMarkFilteringSet が ON の場合、
GDEF の MarkGlyphSets のインデックス (0〜)。

▼ lookupFlag
0x0001rightToLeftGPOS lookup type 3 時でのみ使われる
0x0002ignoreBaseGlyphsGDEF で定義された class = 1 (単一文字) のグリフを無視
0x0004ignoreLigaturesGDEF で定義された class = 2 (合字) のグリフを無視
0x0008ignoreMarksGDEF で定義された class = 3 (マーク) のグリフを無視
0x0010useMarkFilteringSetON で、markFilteringSet が存在する
0x00E0reserved予約 (0)
0xFF00markAttachmentType0 でない場合、GDEF の MarkAttachClassDef で定義されたマークグリフのクラスと比較し、グリフのクラス値が異なる場合は、無視する
Lookup Type
Lookup テーブルの lookupType は、「どのようなグリフをどのように処理するか」という値となります。
GSUB と GPOS で値の意味が異なります。

GSUB の場合は、以下のように定義されています。

1Single Substitution単一のグリフを、別の一つのグリフに置き換えます。
一番単純な置き換えです。
2Multiple Substitution単一のグリフを、別の複数のグリフに置き換えます。
合字分解などで使います。
3Alternate Substitution関連する複数のグリフの中から、任意で一つを選択して置き換えます。
4Ligature Substitution複数のグリフを、単一のグリフに置き換えます。
合字などで使われます。
5Contexual Substitution1つ以上のグリフを、文脈に応じて置き換えます。
6Chaining Contexual Substitution-
7Extension SubstitutionOffset32 の位置にサブテーブルを格納するために使われます。
8Reverse Chaining Contexual Single Substitution-
Lookup のサブテーブル
Lookup テーブルには、サブテーブルが複数個格納されています。
そのサブテーブルのデータを使って、グリフの検索と置き換えを行います。

サブテーブルでは、Lookup Type ごとに、複数のフォーマットが定義されています。
(LookupType = 1 : format = 1,2.. など)

例えば、GSUB の「LookupType = 1」では、2種類のフォーマットが定義されており、format 1 or 2 のサブテーブルが複数個格納されています。

このデータの中から、対象となるグリフに適合するものを探し、そのフォーマットに応じた変換方法で、別のグリフへと変換します。

ここではまず、一番単純な LookupType 1 と 7 のサブテーブルを解説します。
LookupType 1 のサブテーブル
GSUB の「LookupType = 1」は、「単一グリフから単一グリフ」への変換を行います。
2種類のフォーマットが定義されています。

Coverage テーブルは、元となるグリフを検索するためのデータです。
このテーブル内で指定グリフが見つかった場合、そのフォーマットに従って、グリフを変換します。

format 1
元のグリフ ID から指定値を増減させて、グリフ ID を取得します。

uint16 substFormatフォーマット番号 = 1
Offset16 coverageOffsetCoverage テーブルへのオフセット位置。
(このサブテーブルの先頭を 0 とする)
int16 deltaGlyphID元のグリフ ID から変換後のグリフ ID への増減値。
加算した後、0xFFFF で AND する必要があります。

format 2
置き換え後のグリフ ID は、配列から取得します。

uint16 substFormatフォーマット番号 = 2
Offset16 coverageOffsetCoverage テーブルへのオフセット位置 (format 1 と同じ)
uint16 glyphCount配列の数
uint16 substituteGlyphIDs[GlyphCount]変換後のグリフ ID の配列。
Coverage テーブルで示されるグリフ数と同じ数が必要。

Coverage format = 1 の場合は、グリフが見つかった位置の配列インデックス値を使う。
Coverage format = 2 の場合は、Coverage で算出されたインデックス値を使う。
LookupType 7 のサブテーブル
「LookupType 7」は、サブテーブルの合計サイズが 16 bit の範囲を超える場合に、Offset32 のオフセット位置にサブテーブルを格納するために使います。

uint16 substFormatフォーマット番号 = 1
uint16 extensionLookupType実体の LookupType。7 以外。
すべてのサブテーブルで、この LookupType が適用される。
Offset32 extensionOffset実体のサブテーブルへのオフセット位置。
(このサブテーブルの先頭を 0 とする)
Coverage テーブル
対象となるグリフ (GSUB では、置き換え元のグリフ) を検索するためのデータです。
2種類のフォーマットがあります。

format 1
グリフ ID の配列から検索します。

uint16 coverageFormatフォーマット番号 = 1
uint16 glyphCount配列の数
uint16 glyphArray[glyphCount]グリフ ID の配列。数値の小さい順に並んでいる。

format 2
グリフ ID の範囲から検索します。

uint16 coverageFormatフォーマット番号 = 2
uint16 rangeCountRangeRecord の数
RangeRecord rangeRecords[rangeCount]RangeRecord の配列。
グリフ ID の値は小さい順で、値の重複はしていない。

▼ RangeRecord
uint16 startGlyphID範囲の最初のグリフ ID
uint16 endGlyphID範囲の最後のグリフ ID
uint16 startCoverageIndexstartGlyphID に対応した Coverage インデックス値。

Coverage Index = glyphID - startGlyphID + startCoverageIndex
プログラム (1) - LookupList 一覧表示
>> 19a_gsub_lookuplist.c

LookupList テーブルデータの一覧を表示するプログラムです。

---- LookupList ----

==== [0] offset:110 | 'aalt' ====

{Lookup} lookupType <1> | lookupFlag:0x0000 | subTableCount:1

# {SubTable} offset:22912 | format <2>

==== [1] offset:118 | 'aalt' ====

{Lookup} lookupType <3> | lookupFlag:0x0000 | subTableCount:1

# {SubTable} offset:24352 | format <1>

==== [2] offset:126 | 'ccmp' ====

{Lookup} lookupType <4> | lookupFlag:0x0000 | subTableCount:1

# {SubTable} offset:950 | format <1>

FeatureList を読み込んで、各 LookupList インデックスを参照している FeatureList の featureTag も一緒に表示しています ('aalt' 'ccmp' など)

ただし、同じ LookupList インデックスが複数のタグで参照される場合があるため ('vert' 'vrt2' など)、最初に参照された時のタグのみ表示しています。

また、FeatureList から参照されない LookupList インデックスが存在する場合もあります。
それらは、LookupType 5・6 で参照されます。
プログラム (2) - LookupType 1 の置き換え情報表示
>> 19b_gsub_type1.c

LookupList の全データから、LookupType 1 のデータを読み込み、グリフの置き換え情報を表示するプログラムです。

output.txt」に出力されます。

==== [47] ====

{Lookup} lookupType <1> | lookupFlag:0x0000 | subTableCount:1

# {SubTable} foramt <2> / {Coverage} format <2>

[716] (―) U+2015 => [59887] (︱) U+FE31
[727] (‥) U+2025 => [59886] (︰) U+FE30
[728] (⋯) U+22EF => [59885] (︙) U+FE19
[890] (⎰) U+23B0 => [64408] (none)
[891] (⎱) U+23B1 => [64409] (none)
[1397] (、) U+3001 => [59877] (︑) U+FE11
[1398] (。) U+3002 => [59878] (︒) U+FE12
[1402] (〆) U+3006 => [64412] (none)
[1404] (〈) U+3008 => [59901] (︿) U+FE3F
[1405] (〉) U+3009 => [59902] (﹀) U+FE40
[1406] (《) U+300A => [59899] (︽) U+FE3D
[1407] (》) U+300B => [59900] (︾) U+FE3E
...

[] 内がグリフ ID で、() 内が Unicode 文字です。
グリフ ID に対応する Unicode がない場合は、(none) となっています。

LookupType 1 では、「substFormat = 1, 2」&「CoverageFormat = 1, 2」の4通りのフォーマットがあることになりますが、「源ノ角ゴシック」「源ノ明朝」では、この4通りすべてのフォーマットが存在していました。
プログラム (3) - グリフ画像
「プログラム (2)」では、テキストで表示できないグリフも多数あるため、このままではすべてのグリフを確認できません。

そこで、FreeType を使って、グリフを描画します。
(ここでは、FreeType の使い方の解説は行いません)

>> 19c_gsub_glyph.c

fontfile.cimage.cftlib.c も併せてコンパイルしてください。
また、FreeType のライブラリをリンクしてください。

LookupList の全データの中から、LookupType 1 のデータを全て処理し、左に置き換え前グリフ、右に置き換え後のグリフを描画します。

page<番号>.bmp」のファイル名で、BMP 画像で出力します。