目的
InDesignの「書式」メニュー → 「字形」パネルのオプションから「プロポーショナル字形」を選択すると、一部の約物が見た目ぜんぜん違う字形に置き換わってしまう。
これはフォントの持つGSUBテーブルのpwidの情報をもとに、字形を置き換えているらしい。
どの文字が置き換わるのかを把握したくて、フォントのGSUBテーブルを読む方法を調べた。
フォントからGSUBテーブルを書き出す
Adobeの配布している、AFDKO (Adobe Font Development Kit for OpenType)。
これの ttx
コマンドを使えば、フォントの持つテーブルを .ttx ファイルとして書き出せるらしい。
「download AFDKO」に入って、最新の「FDK.2.5.65463-MAC.zip」を落として解凍。
中の Read_Me_First.html に従ってインストールする。
使い方に関しては、 ttx -h
で確認する。
GSUBテーブルを書き出す。
% 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 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行めまで
<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 のテーブルにリンクしてるっぽい。
CID:684 が CID:109 に変換されるというのはわかっているので、KozMinPr6N-Regular.ttx の中を cid00684
で検索して該当部分を探す。
82242行めに、
<Substitution in="cid00684" out="cid00109"/>
という記述があった。上のノードが、
<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 に当たるので間違いなさそう。
ということは、
- FeatureRecord -> FeatureTag の value が pwid のものを探す
- それの Feature -> LookupListIndex の value を収集
- そのindexを持つ Lookup の中の SingleSubst -> Substitution の in と out を集める
で変更されるグリフを集められそう。
フォントからcmapテーブルを書き出して解析
グリフがどの unicode に当たっているかを知りたいので、今度は cmap テーブルを書き出す。
% 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行めに全く同じ記述があった。
<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 のノードに上記が入っていた。
なんでこうなってるのか?
資料を探す。
差分をとって調べる。
- 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 でスクリプトを書いて解析する。
ここは使いやすい言語を使えばいいと思う。
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('')
実行する。
% groovy font_pwid_check.groovy /Library/Fonts/KozMinPr6N-Regular.otf
/Library/Fonts/KozMinPr6N-Regular.otf
〃〆〈〉《》‐―†‡…‰′″ぁあぃいぅうぇえぉおかがきぎくぐけげこごさざしじすずせぜそぞただちぢっつづてでとどなにぬねのはばぱひびぴふぶぷへべぺほぼぽまみむめもゃやゅゆょよらりるれろゎわゐゑをんゔゕゖゝゞゟ゠ァアィイゥウ§ェ¨エォオカガキギク°グ±ケゲコ´ゴサ¶ザシジスズセゼソゾタダチヂッツヅテデトドナニヌネノハバパヒビピフブ×プヘベペホボポマミムメモャヤュユョヨラリルレロヮワヰヱヲンヴヵヶ÷ヷヸヹヺ・ーヽヾヿ℃ℏℵ↔⇒⇔ㇰㇱㇲㇳㇴㇵㇶㇷㇸㇹㇺㇻㇼㇽㇾㇿ∀∂∃∅∇∈∊∋∑∓√∝∞∟∠∧∨∩∪∫∬∭∮∴∵∽≃≒≠≡≦≧≪≫≲≳⊂⊃⊆⊇⊕⊖⊗⊘⊠⊥⊿!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ヲァィゥェォャュョッーアイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワンΩμ¢£ ̄¦¥│
GSUBテーブルのpwidを持つ文字の一覧が表示された。