フォントの持つGSUBテーブル、cmapテーブルを解析してごにょごにょする


## 目的

InDesignの「書式」メニュー → 「字形」パネルのオプションから「プロポーショナル字形」を選択すると、一部の約物が見た目ぜんぜん違う字形に置き換わってしまう。

![_名称未設定-1___227_.png](https://qiita-image-store.s3.amazonaws.com/0/3956/43759d77-6178-c423-25f2-a43c6906463c.png "_名称未設定-1___227_.png")

これはフォントの持つGSUBテーブルのpwidの情報をもとに、字形を置き換えているらしい。
どの文字が置き換わるのかを把握したくて、フォントのGSUBテーブルを読む方法を調べた。

## フォントからGSUBテーブルを書き出す

Adobeの配布している、[AFDKO (Adobe Font Development Kit for OpenType)](http://www.adobe.com/devnet/opentype/afdko.html)。
これの `ttx` コマンドを使えば、フォントの持つテーブルを .ttx ファイルとして書き出せるらしい。

「download AFDKO」に入って、最新の「FDK.2.5.65463-MAC.zip」を落として解凍。
中の Read_Me_First.html に従ってインストールする。
使い方に関しては、 `ttx -h` で確認する。

GSUBテーブルを書き出す。

```bash:ターミナル画面
% cd /Library/Fonts
% ttx -l KozMinPr6N-Regular.otf
Listing table info for "KozMinPr6N-Regular.otf":
    tag     checksum   length   offset
    ----  ----------  -------  -------
    BASE  0x1B8E18D8      228  6502820
    CFF   0xAE58E4E3  5795669   264704
    DSIG  0xB0EC9DA9     7840  6505008
    GPOS  0x25479C46    58296  6444524
    GSUB  0x38D14556   208878  6235644
    OS/2  0x78B6A189       96      368
    VORG  0x418227EF     1960  6503048
    cmap  0xADA99534   262133     2536
    head  0xFB722B93       54      268
    hhea  0x0A2152AD       36      324
    hmtx  0x1927FD4E    86972  6060376
    maxp  0x5A125000        6      360
    name  0x95DAF0D1     2072      464
    post  0xFFB80032       32   264672
    vhea  0x08EF63B3       36  6147348
    vmtx  0x7C360A5B    88260  6147384

% ttx -t GSUB KozMinPr6N-Regular.otf
Dumping "KozMinPr6N-Regular.otf" to "KozMinPr6N-Regular.ttx"...
Dumping 'GSUB' table...
```

これで、/Library/Fonts/KozMinPr6N-Regular.ttx が書き出された。
KozMinPr6N-Regular.ttx をテキストエディタで開くと、xmlで書かれている。

```xml:KozMinPr6N-Regular.ttx
<?xml version="1.0" encoding="UTF-8"?>
<ttFont sfntVersion="OTTO" ttLibVersion="2.5">

  <GSUB>
    <Version value="1.0"/>
    <ScriptList>
      <!-- ScriptCount=6 -->
      <ScriptRecord index="0">
        <ScriptTag value="DFLT"/>
        <Script>
          <DefaultLangSys>
            <ReqFeatureIndex value="65535"/>
            <!-- FeatureCount=32 -->
            <FeatureIndex index="0" value="0"/>
            <FeatureIndex index="1" value="6"/>
            <FeatureIndex index="2" value="12"/>
            <FeatureIndex index="3" value="18"/>
            <FeatureIndex index="4" value="24"/>
            <FeatureIndex index="5" value="30"/>
            <FeatureIndex index="6" value="36"/>
            <FeatureIndex index="7" value="42"/>
            <FeatureIndex index="8" value="48"/>
...以下略...
```

## GSUBテーブルを解析

KozMinPr6N-Regular.ttx を、 `pwid` で検索すると、1121行めから1162行めまで

```xml:KozMinPr6N-Regular.ttx,line1121~1162
      <FeatureRecord index="120">
        <FeatureTag value="pwid"/>
        <Feature>
          <!-- LookupCount=1 -->
          <LookupListIndex index="0" value="21"/>
        </Feature>
      </FeatureRecord>
      <FeatureRecord index="121">
        <FeatureTag value="pwid"/>
        <Feature>
          <!-- LookupCount=1 -->
          <LookupListIndex index="0" value="21"/>
        </Feature>
      </FeatureRecord>
      <FeatureRecord index="122">
        <FeatureTag value="pwid"/>
        <Feature>
          <!-- LookupCount=1 -->
          <LookupListIndex index="0" value="21"/>
        </Feature>
      </FeatureRecord>
      <FeatureRecord index="123">
        <FeatureTag value="pwid"/>
        <Feature>
          <!-- LookupCount=1 -->
          <LookupListIndex index="0" value="21"/>
        </Feature>
      </FeatureRecord>
      <FeatureRecord index="124">
        <FeatureTag value="pwid"/>
        <Feature>
          <!-- LookupCount=1 -->
          <LookupListIndex index="0" value="21"/>
        </Feature>
      </FeatureRecord>
      <FeatureRecord index="125">
        <FeatureTag value="pwid"/>
        <Feature>
          <!-- LookupCount=1 -->
          <LookupListIndex index="0" value="21"/>
        </Feature>
      </FeatureRecord>
```

という記述がある。ここから pwid のテーブルにリンクしてるっぽい。

![capture-2017-03-18-16.53.52.gif](https://qiita-image-store.s3.amazonaws.com/0/3956/d8bda287-da0d-5c6f-9602-9fab272719be.gif "capture-2017-03-18-16.53.52.gif")

CID:684 が CID:109 に変換されるというのはわかっているので、KozMinPr6N-Regular.ttx の中を `cid00684` で検索して該当部分を探す。

82242行めに、

```xml:KozMinPr6N-Regular.ttx,line82242
<Substitution in="cid00684" out="cid00109"/>
```

という記述があった。上のノードが、

```xml:KozMinPr6N-Regular.ttx,line81890~
      <Lookup index="21">
        <!-- LookupType=1 -->
        <LookupFlag value="0"/>
        <!-- SubTableCount=1 -->
        <SingleSubst index="0">
          <Substitution in="cid00231" out="cid00001"/>
          ... 中略 ...
          <Substitution in="cid00684" out="cid00109"/>
```

となっているので、 index="21" が LookupListIndex の value に当たるので間違いなさそう。
ということは、

1. FeatureRecord -> FeatureTag の value が pwid のものを探す
1. それの Feature -> LookupListIndex の value を収集
1. そのindexを持つ Lookup の中の SingleSubst -> Substitution の in と out を集める

で変更されるグリフを集められそう。

## フォントからcmapテーブルを書き出して解析

グリフがどの unicode に当たっているかを知りたいので、今度は cmap テーブルを書き出す。

```bash:ターミナル画面
% ttx -t cmap KozMinPr6N-Regular.otf
Dumping "KozMinPr6N-Regular.otf" to "KozMinPr6N-Regular#1.ttx"...
Dumping 'cmap' table...
```

新しく書き出した KozMinPr6N-Regular#1.ttx を確認する。
cid00684 が unicode の 300A に当たっている部分を探す。
1582行め、16941行め、55530行め、70889行めに全く同じ記述があった。

```xml:KozMinPr6N-Regular#1.ttx,line1582,16941,55530,70889
<map code="0x300a" name="cid00684"/><!-- LEFT DOUBLE ANGLE BRACKET -->
```

* 6行め `<cmap_format_4 platformID="0" platEncID="3" language="0">`
* 15365行め `<cmap_format_12 platformID="0" platEncID="4" format="12" reserved="0" length="167728" language="0" nGroups="13976">`
* 31249行め `<cmap_format_14 platformID="0" platEncID="5" format="14" length="26795" numVarSelectorRecords="15">`
* 45930行め `<cmap_format_2 platformID="1" platEncID="1" language="0">`
* 53954行め `<cmap_format_4 platformID="3" platEncID="1" language="0">`
* 69313行め `<cmap_format_12 platformID="3" platEncID="10" format="12" reserved="0" length="167728" language="0" nGroups="13976">`

の、cmap_format_4 と cmap_format_12 のノードに上記が入っていた。
なんでこうなってるのか？

資料を探す。

* [Microsoft Typography - OpenType Specification](https://www.microsoft.com/en-us/Typography/OpenTypeSpecification.aspx)
* [TTXを使用してTrueTypeフォントのcmapテーブルを修正 - itouhiroはてなブログ](http://itouhiro.hatenablog.com/entry/20141004/font)
    * [OpenTypeフォントの続き(6)・・・cmapテーブル: vanillaの日記](http://vanillasky-room.cocolog-nifty.com/blog/2008/02/opentype6cmap-d.html)
    * [OpenTypeフォントの続き(3)・・・nameテーブル: vanillaの日記](http://vanillasky-room.cocolog-nifty.com/blog/2008/02/opentype3name-a.html)

差分をとって調べる。

* cmap_format_2、cmap_format_4、cmap_format_12 には普通にUNICODEが入っている。
* cmap_format_4 の2つに差分はない。cmap_format_12 の2つにも差分はない。
* cmap_format_12 には、 code="0x1f100" より大きな文字が入っている。それ以外にcmap_format_4との差はない。
* cmap_format_2 は、 cmap_format_4、cmap_format_12 とはかなり違う。platformID="1" だからMac用グリフなんだけど、異体字とかも入っている。

cmap_format_4 は `<map code="0x5c" name="cid00097"/>`
cmap_format_2 は `<map code="0x5c" name="cid00061"/>`
これバックスラッシュだ、確かに違う。MacのInDesignで見ると、バックスラッシュはcid97。

cmap_format_4 は `<map code="0x7c" name="cid00099"/>`
cmap_format_2 は `<map code="0x7c" name="cid00093"/>`
これは縦線。cid93は、縦線が中央で途切れている。MacのInDesignで見ると、途切れていないcid99で見えている。

cmap_format_14 に入っている情報は異体字セレクタっぽい。
これの差分をとるのも面白そう。pr6とpr6Nのフォントの違いを出したり…。
本気で調べだすと時間がかかりすぎるので中断。

とりあえず、cmap_format_4 の1つめの中を参考にすればOKそう。

## ttxの出力をxml解析して、文字の一覧を出す

単純にxmlの問題になったので、 groovy の XmlSlurper でスクリプトを書いて解析する。
ここは使いやすい言語を使えばいいと思う。

```groovy:font_pwid_check.groovy
def slurper = new XmlSlurper()
def fontFilePath = new File(args[0]).canonicalPath
println fontFilePath

//ttxを実行
def proc = ['ttx','-o','-','-q','-t','GSUB','-t','cmap',fontFilePath].execute()
//標準出力をXmlSlurperにかける
def ttx = slurper.parseText(proc.in.text)

//cmapとunicodeの対応をMapに集める
def cmapList = ttx.cmap.cmap_format_4[0].map.collectEntries { [it.@name.text(), it.@code.text()] }
//pwidテーブルのidを集める
def pwidLookup = ttx.GSUB.FeatureList.FeatureRecord.findAll{
    it.FeatureTag.@value=='pwid'
}.collect{
    it.Feature.LookupListIndex.@value
}.unique()

def charCodeSet = new HashSet()
pwidLookup.each {pwidIndex ->
    ttx.GSUB.LookupList.Lookup.find{it.@index==pwidIndex}.SingleSubst.Substitution.each{
        def cid = it.@in.text()
        //グリフがunicodeを持ってたら
        if(cmapList[cid]){
            //unicodeを文字にして収集
            charCodeSet << new String((char)Integer.decode(cmapList[cid]).intValue())
        }
    }
}
println charCodeSet.join('')
```

実行する。

```bash:ターミナル画面
% groovy font_pwid_check.groovy /Library/Fonts/KozMinPr6N-Regular.otf
/Library/Fonts/KozMinPr6N-Regular.otf
　 〃〆〈〉《》‐―†‡…‰′″ぁあぃいぅうぇえぉおかがきぎくぐけげこごさざしじすずせぜそぞただちぢっつづてでとどなにぬねのはばぱひびぴふぶぷへべぺほぼぽまみむめもゃやゅゆょよらりるれろゎわゐゑをんゔゕゖゝゞゟ゠ァアィイゥウ§ェ¨エォオカガキギク°グ±ケゲコ´ゴサ¶ザシジスズセゼソゾタダチヂッツヅテデトドナニヌネノハバパヒビピフブ×プヘベペホボポマミムメモャヤュユョヨラリルレロヮワヰヱヲンヴヵヶ÷ヷヸヹヺ・ーヽヾヿ℃ℏℵ↔⇒⇔ㇰㇱㇲㇳㇴㇵㇶㇷㇸㇹㇺㇻㇼㇽㇾㇿ∀∂∃∅∇∈∊∋∑∓√∝∞∟∠∧∨∩∪∫∬∭∮∴∵∽≃≒≠≡≦≧≪≫≲≳⊂⊃⊆⊇⊕⊖⊗⊘⊠⊥⊿！＂＃＄％＆＇（）＊＋，－．／０１２３４５６７８９：；＜＝＞？＠ＡＢＣＤＥＦＧＨＩＪＫＬＭＮＯＰＱＲＳＴＵＶＷＸＹＺ［＼］＾＿｀ａｂｃｄｅｆｇｈｉｊｋｌｍｎｏｐｑｒｓｔｕｖｗｘｙｚ｛｜｝～ｦｧｨｩｪｫｬｭｮｯｰｱｲｳｴｵｶｷｸｹｺｻｼｽｾｿﾀﾁﾂﾃﾄﾅﾆﾇﾈﾉﾊﾋﾌﾍﾎﾏﾐﾑﾒﾓﾔﾕﾖﾗﾘﾙﾚﾛﾜﾝΩμ￠￡￣￤￥￨
```

GSUBテーブルのpwidを持つ文字の一覧が表示された。