GSUB テーブル (1) - ScriptList

GSUB テーブル
GSUB テーブル」には、縦書きグリフへの変換や、合字への変換など、単一または複数のグリフを、別のグリフに置き換えるためのデータが格納されています。

グリフは、「グリフ ID からグリフ ID」への変換となります。

文字を描画する際は、以下の順番で処理を行っていきます。

  1. cmap で文字コードからグリフ ID を取得
  2. GSUB でグリフを置き換え
  3. GPOS で位置を調整

GSUB テーブルは、それぞれのデータが細かいテーブルに分かれているため、目的のデータを取得するためには、少々手間が掛かります。

まずは、GSUB テーブルのフォーマットと、文字体系/言語の情報である「ScriptList テーブル」を説明します。
テーブルデータ
uint16 majorVersionメジャーバージョン = 1
uint16 minorVersionマイナーバージョン = 0 or 1
Offset16 scriptListOffsetScriptList テーブルへのオフセット位置。
(GSUB の先頭を 0 とする)
Offset16 featureListOffsetFeatureList テーブルへのオフセット位置
Offset16 lookupListOffsetLookupList テーブルへのオフセット位置
ver 1.1 の場合、以下が続く
Offset32 featureVariationsOffsetFeatureVariations テーブルへのオフセット位置

各テーブルの説明
ScriptList文字体系や言語の情報がある。
各言語ごとに、必要となる FeatureList のインデックス番号を取得するためのテーブル。
FeatureList縦書きや合字など、置き換えの種類ごとに、必要な LookupList のインデックス値が格納されている
LookupList対象のグリフを検索して、置き換えるためのデータ
FeatureVariations可変フォント用
ScriptList テーブル
ここには、フォントに含まれる文字体系や言語の情報が格納されています。

言語によって置き換えるグリフが異なる場合があるため、まずはここで文字体系や言語を検索し、選択します。

ただ、複数の言語の情報が格納されていたとしても、実際にはすべての言語で同じデータが得られる場合も多いです。
(CJK フォントなど、複数の言語のグリフが含まれているようなフォントは除く)

ScriptList
uint16 scriptCount配列の数
ScriptRecord scriptRecords[scriptCount]ScriptRecord の配列。
scriptTag のアルファベット順に並んでいる。

ScriptRecord
Tag scriptTag文字体系の識別子。

タグ一覧は、OpenType 仕様書の
「OpenType Layout Tag Registry」>「Script tags」にあります。

'DFLT' = デフォルト
'latn' = ローマ字
'hani' = CJK (中国語/日本語/韓国語)
'kana' = ひらがな/カタカナ
'cyrl' = キリル文字
'grek' = ギリシャ文字
Offset16 scriptOffsetScript テーブルへのオフセット位置。
(ScriptList の先頭を 0 とする)

Script
Offset16 defaultLangSysデフォルトの LangSys テーブルへのオフセット位置。
(Script の先頭を 0 とする)
0 の場合あり。

LangSysRecord に当てはまる言語がない場合や、言語による動作の違いがない場合に使う。
uint16 langSysCount配列の数 (defaultLangSys は除く)。
言語固有の動作がない場合は、0。
LangSysRecord langSysRecords[langSysCount]LangSysRecord の配列。
langSysTag のアルファベット順に並んでいる。

LangSysRecord
Tag langSysTag言語の識別子。

タグ一覧は、仕様書の「Language tags」にあります。
「3文字+スペース」の形となっています。

'JAN ' = 日本語
Offset16 langSysOffsetLangSys テーブルへのオフセット位置。
(Script の先頭を 0 とする)

LangSys
Offset16 lookupOrder0 (予約)
uint16 requiredFeatureIndexこの言語システムに必須な FeatureList のインデックス。
必要なければ 0xFFFF。
uint16 featureIndexCount配列の数 (requiredFeatureIndex は除く)
uint16 featureIndices[featureIndexCount]FeatureList テーブルのインデックス番号 (0〜) の配列。
任意の順番で並んでいる。

最終的に必要となるのは、「FeatureList のインデックス番号」です。
プログラム
>> 17_gsub_script.c

GSUB の ScriptList の一覧を表示するプログラムです。

---- GSUB ----

version: 1.0
scriptListOffset: 10
featureListOffset: 1746
lookupListOffset: 9824

---- Script List ----

scriptCount: 7

== ScriptRecord [0] ==

scriptTag: 'DFLT'
scriptOffset: 44

** Script **

defaultLangSys: 4
langSysCount: 0

<LangSys> [default]
 requiredFeatureIndex: 65535
 featureIndexCount: 16
 featureIndices: 0,37,74,111,148,185,222,259,296,333,370,442,479,516,555,594,
...

日本語フォント
日本語を対象としたい場合は、ScriptRecord の scriptTag が「kana」または「hani」のデータを使います。
(いずれもなければ「DFLT」)

いくつかの日本語フォントを見てみたところ、基本的に上記の2つが存在する場合が多いですが、「kana」しかない場合や、「DFLT」がないものもありました。

実際の使い方
日本語を扱いたい場合の手順は、以下のようになります。

[1] 使用したい文字体系を元に検索

ScriptRecord から、scriptTag が「'kana' か 'hani'」、なければ「DFLT」のデータを探します。
「DFLT」もない場合は、「latn」か、先頭のデータを選択します。

[2] LangSys テーブルを選択

LangSysRecord の langSysTag が「'JAN '」のデータを選択します。

LangSysRecord が存在しない、または目的のタグが存在しない場合は、defaultLangSys の位置のデータを使います。

[3] FeatureList のインデックス値を取得

LangSys テーブルにある、FeatureList のインデックス値を取得します。
この値を使って、FeatureList テーブル内を参照することになります。