OpenType フォントのカーニング情報をぶっこぬく

[OpenType](https://ja.wikipedia.org/wiki/OpenType) フォーマットのフォントファイルからカーニング情報を取り出したい事情があっていろいろ調べた作業ログ。

- フォントファイルをいじるときにつかう定番ライブラリ [freetype 2.6](http://www.freetype.org/) では OpenType のカーニング情報がひきだせなかったので（フォントにもよると思うけど）、[libotf (0.9.13)](http://www.nongnu.org/m17n/) で OTF ファイルを解析して取り出す。
- OTF のファイルフォーマットは [Microsoft Typography - OpenType Specification] (https://www.microsoft.com/typography/otspec/) にまとまってる。
- [The TrueType Font File](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6.html) OpenType は TrueType をベースにしてるので↑にないテーブル定義とかはこのへんも見る。
- libotf の example に含まれてる `otfdump` コマンドを使うと、OTF ファイルの構造をツリー上にテキストでダンプしてくれるので↓、仕様書とくらべながら見ると理解しやすい。

```
(OTF
  (OffsetTable
    (sfnt-version 20308.21583)
    (numTables 25)
    (searchRange 256)
    (enterSelector 4)
    (rangeShift 384))
  (Table 0 (tag "BASE" #x42415345)
    (checkSum 6962C672) (offset #x00000430) (length: #x000001C8))
  (Table 1 (tag "CFF " #x43464620)
    (checkSum 0BDFCA60) (offset #x0034700C) (length: #x0080F295))
  (Table 2 (tag "EBDT" #x45424454)
    (checkSum E109E7DA) (offset #x00213B60) (length: #x001334AA))
  (Table 3 (tag "EBLC" #x45424C43)
    (checkSum 81D5AE36) (offset #x00002A58) (length: #x0001064C))
  (Table 4 (tag "GPOS" #x47504F53)
    (checkSum D6E4B8CC) (offset #x00036E04) (length: #x00015098))
  (Table 5 (tag "GSUB" #x47535542)
...
```

- カーニング情報は↓らへんにはいってる。
    1. [`kern` テーブル](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6kern.html)
    2. [`kerx` テーブル](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6kerx.html)
    3. [`GPOS` テーブル](https://www.microsoft.com/typography/otspec/GPOS.htm)の [`kern` feature](https://www.microsoft.com/typography/otspec/features_ko.htm#kern)
- freetype で取り出せるのはおそらく 1 の `kern` テーブルのみ？ しかも Apple 拡張フォーマットには対応してない。[TTX](https://github.com/behdad/fonttools/) は Apple 拡張対応してる。


## libotf でカーニング情報を実際にとりだす

GPOS テーブルから取り出すのが正攻法っぽいのでそっからいく。（`/Library/Fonts/ヒラギノ明朝 Pro W6.otf` には `kerx` テーブルもあったけど、後方互換てきなこと？）

1. Unicode から Glyph IDに変換。
2. `GPOS` テーブルの ScriptList から処理対象 Glyph に対応する Script を選ぶ（←ここがよくわからんので、とりあえずリストのいちばん最初の Script にしとく。）
3. Script のなかから対応する LangSys を選ぶ。（←これもよくわからんけど基本 1 個しかないのでそれで。）
4. LangSys の下に、対応してる Feature の FeatureList 内における index を含んだリストがあるので、それをなめてく。
5. `kern` feature の lookup table index から lookup table 取得。
6. lookup のなかの sub table のなかの coverage 構造体のなかに対象の Glyph があればカーニング値がふくまれてる。
7. coverage 内の glyph の index が pair set の index と対応してる。
8. pair set 内の pair value record をだーっとみていって、second glyph がカーニングペアの id だったらそれが実際のカーニング値。

って、書いててわけわかんなくなってきたけど、コードに直すと↓

```c
#include <iostream>
#include "otf.h"
#include <ft2build.h>
#include FT_FREETYPE_H


int glyph_contains(OTF_Coverage *coverage, OTF_GlyphID glyph_id) {
  if (coverage->CoverageFormat == 1) {
    for (int i = 0; i < coverage->Count; i++) {
      if (coverage->table.GlyphArray[i] == glyph_id) {
        return i;
      }
    }
  } else if (coverage->CoverageFormat == 2) {
    for (int i = 0; i < coverage->Count; i++) {
      OTF_RangeRecord *range = coverage->table.RangeRecord + i;
      for (int j = range->Start; j <= range->End; j++) {
        if (j == glyph_id) {
          return j;
        }
      }
    }
  }
  // ふくまれてない
  return -1;
}


OTF_PairValueRecord *get_pair_set(OTF_LookupSubTableGPOS *sub_table, OTF_GlyphID left, OTF_GlyphID right) {
  //  printf("Coverage Format: %d, Count: %d, left: %d, right: %d\n", sub_table->Coverage.CoverageFormat,
  //  sub_table->Coverage.Count, glyph_contains(&sub_table->Coverage, left), glyph_contains(&sub_table->Coverage,
  //  right));
  int pair_set_index = glyph_contains(&sub_table->Coverage, left);
  if (pair_set_index < 0) {
    // この sub table には left の情報無い
    return NULL;
  }

  OTF_PairSet *pair_set = &sub_table->u.pair1.PairSet[pair_set_index];
  for (int i = 0; i < pair_set->PairValueCount; i++) {
    if (pair_set->PairValueRecord[i].SecondGlyph == right) {
      return &pair_set->PairValueRecord[i];
    }
  }

  // right との情報無い
  return NULL;
}


int get_kerning_value(OTF *otf, OTF_GlyphString *gstring) {
  char name[5];
  OTF_Tag kern = OTF_tag("kern");
  //  OTF_Tag palt = OTF_tag("palt");

  // よくわからんので最初の Script
  OTF_LangSys *lang_sys = &otf->gpos->ScriptList.Script[0].DefaultLangSys;
  for (int i = 0; i < lang_sys->FeatureCount; i++) {
    int featureIndex = lang_sys->FeatureIndex[i];
    OTF_Feature *feature = &otf->gpos->FeatureList.Feature[featureIndex];
    OTF_tag_name(feature->FeatureTag, name);
    //    printf("%d: %d, %s\n", i, featureIndex, name);
    if (feature->FeatureTag != kern) continue;

    // LookupListIndex ながさ 1 なので最初のやつ。複数はいってることってあるの？
    unsigned int lookupIndex = feature->LookupListIndex[0];
    //    printf("%i: %s, %d\n", i, name, lookupIndex);
    // このへんもリストの最初だけ
    OTF_PairValueRecord *value = get_pair_set(&otf->gpos->LookupList.Lookup[lookupIndex].SubTable.gpos[0], gstring->glyphs[0].glyph_id, gstring->glyphs[1].glyph_id);
    if (value) {
      // XAdvance 以外に値はいってることあるの？
      return value->Value1.XAdvance;
    }
  }
  return 0;
}


int main(int argc, const char *argv[]) {
  OTF *otf = OTF_open("/Library/Fonts/ヒラギノ明朝 Pro W6.otf");
  OTF_get_table(otf, "GPOS");

  OTF_GlyphString gstring;
  gstring.size = 2;
  gstring.used = 2;
  gstring.glyphs = (OTF_Glyph *)calloc(gstring.size, sizeof(OTF_Glyph));
  gstring.glyphs[0].c = 0x3042;  // あ
  gstring.glyphs[1].c = 0x3062;  // ぢ
  OTF_drive_cmap(otf, &gstring);
  printf("Left glyph=0x%04x, Right glyph=0x%04x\n", gstring.glyphs[0].glyph_id, gstring.glyphs[1].glyph_id);

  int kerning_value = get_kerning_value(otf, &gstring);
  printf("Kerning value=%d\n", kerning_value);

  free(gstring.glyphs);
  OTF_close(otf);

  return 0;
}
```

んで、これではまだ正確にポジショニングできなくて、[`kern` feature の解説のとこ](https://www.microsoft.com/typography/otspec/features_ko.htm#kern)に、

> Feature interaction: If kern is activated, palt must also be activated if it exists. If palt is activated, there is no requirement that kern must also be activated.

ってあるように、`palt` があるならそれもちゃんと処理しないといけない。OTF 複雑すぎ、、、