home / uni / ps / font PostScript言語 / フォント

 ちょっとした覚え書き。

●フォントは辞書だ
GS>/MS-Gothic-90ms-RKSJ-H findfont
Loading a TT font from /cygdrive/c/windows/fonts/msgothic.ttc to emulate a CID font MS-Gothic ... Done.
GS<1>pstack
-dict-
GS<1>
●指定したフォントがあるかどうか?

 GSにしろDistillerにしろ、findfontが指定したフォントを見つけられなくてもデフォルトではエラーになりません。 一生懸命置き換えようとします。 でも指定したフォントがきちんとあるかどうか確かめたい場合もあるわけで。 そういうときはfindfontして/FontNameを調べます。

GS>/MS-Gothic-90ms-RKSJ-H findfont
Loading a TT font from /cygdrive/c/windows/fonts/msgothic.ttc to emulate a CID font MS-Gothic ... Done.
GS<1>/FontName get ==
/MS-Gothic-90ms-RKSJ-H
GS>/hoge findfont
Can't find (or can't open) font file /usr/share/ghostscript/9.06/Resource/Font/hoge.
Can't find (or can't open) font file hoge.
Querying operating system for font files...
Can't find (or can't open) font file /usr/share/ghostscript/9.06/Resource/Font/hoge.
Can't find (or can't open) font file hoge.
Didn't find this font on the system!
Substituting font Courier for hoge.
Loading NimbusMonL-Regu font from /usr/share/ghostscript/fonts/n022003l.pfb... 3903192 2482606 2995112 1659037 1 done.
GS<1>/FontName get ==
/Courier
GS>
●フォントの列挙
 PLRMに書いてあります。 GSでこれをやるとすごく時間がかかります。
GS>(*) { == } 256 string /Font resourceforall
(MS-Gothic-90ms-RKSJ-H)
(MS-PMincho-WP-Symbol)
(MS-Gothic-78-H)
...

 文字列ではなく、オブジェクトが返ってくる(definefontしたような場合か?)もあるらしい。

●Ghostscriptでフォント指定を間違えた時にさっさと検索を終わらせてほしい
 -dNONATIVEFONTMAP
●DistillerでMSゴシック・MS明朝・メイリオが使えない
 フォントをコピーしてCIDMapをきちんと指定すれば使えるようになります。 CIDMapは同じフォントをGhostscriptでロードして持ってくるのが簡単です。 ASCII85エンコードしてPostscriptファイルに詰め込んでおけばいいでしょう。FlateEncodeすることもできます。 ただし、GhostscriptのCIDMapは縦書きグリフがきちんと置換されていません。 Distillerではきちんと縦書きが動くので、できれば置換してからコピーしたいところですが。
●Ghostscriptで縦書きがおかしい(2013年版)
 おかしくなる方向性がフォントやバージョンによって違うみたいですが、9.10では、文字幅(つまり文字列が表示されていく方向)と、縦書きグリフの置換に問題があります。 どちらもCIDフォントをコピーして適当にハックする必要があります。 コピー元のフォントは等幅フォントが向いています。 プロポーショナルフォントは横方向の幅が違いますから、横書きのものを縦書きに流用すると行がガタガタになってしまいます。
/MS-Mincho /CIDFont findresource
dup length dict copy begin
currentdict /FID undef
currentdict /UIDBase undef
currentdict /UniqueID undef
/XUID [ XUID aload pop 0 ] def
/CIDFontName /MS-Mincho-Vert def
/FontName CIDFontName def
/FontType 42 def
・・・
CIDFontName currentdict end definefont pop

XUIDは要素をひとつ増やしています(とりあえず0にしてある)。 FontTypeを42にしているのはPLRMにそう書いてあるから(TrueTypeの場合definefontするまではそうしておけと書いてある)。

 字間・文字列の描画方向を直すにはCDevProcを再定義します。 CDevProcで指定する値はフォント座標系の単位なので、本来はFontMatrixを参照する必要があります。 しかし、TrueTypeのFontMatrixは恒等変換になっているのが普通なので、

/CDevProc { pop 10 array astore dup 0 0 put dup 1 -1 put aload pop } bind def

でOKでしょう。 これでちゃんと縦に書けます。 細かいことを言うと書き出し位置が横書きのベースラインの位置になっているので、これも直したい場合はFontMatrixを適当にいじってください。

 縦書きグリフの置換がうまくいかないのはCIDMapに問題があります。 たとえば、90ms-RKSJ-Vを指定すると、「」ぁぃぅーなどの 文字はきちんと縦書きグリフのCIDに変換されます。 これをやっているのはCMapです。 TrueTypeの場合、CIDがさらにTrueTypeフォントのグリフIDに変換されます。 これをやっているのがCIDMapです。 ややこしいですが。

 TrueTypeには文字コードからグリフIDへの変換テーブルが入っていて、Ghostscriptはこれを使ってCIDMapを作っています(たぶん)。 Unicodeには一部に縦書きグリフ用の文字コード割り当てがあるようですが、showに渡す文字列を縦書き用に変えなければ意味がありません。 縦書き用に変えてもそういう文字コードへのグリフ割り当てがなければ表示できません。 そもそも文字コードがないものは、Ghostscriptは横書きのグリフIDを縦書きにもそのまま使います。 結局、縦書きを指定しても通常は全部横書きグリフでレンダリングされます。

 TrueTypeではさらにいろいろなグリフ置換を担当するGSUBというテーブルがあります。 縦横変換もGSUBの中に入っています(kanaスクリプト、JAN言語、vertフィーチャー)。 Ghostscriptはこのテーブルを見ていません。 仕方がないのでcidfmap上で元のフォントの字形集合をIdentity 0と指定したフォントを作り(CIDがそのままグリフIDとして使われるようになります)、それを読み込んでコピーした上で、元のフォントのCIDMapをコピーして、さらに縦横グリフを入れ替えます。 cidfmapはこんな感じ。

/MS-Mincho << /CSI [(Japan1) 3] /Path (/cygdrive/c/windows/fonts/msmincho.ttc) /SubfontID 0 /FileType /TrueType >> ;
/MS-Mincho-Unity << /CSI [(Identity) 0] /Path (/cygdrive/c/windows/fonts/msmincho.ttc) /SubfontID 0 /FileType /TrueType >> ;

 TrueTypeの仕様はMSDNに行けば見つかります。 現在、TrueTypeはOpenType Font(OTF)というフォーマットに含まれているので、MSDNで「OpenType」と検索すればこんなのが見つかるでしょう。 リンクをたどっていくと無料でダウンロードできるISO/IECのサイトへのリンクがあったりします(ISO/IECのドキュメントが無料で手に入るなんてめずらしいんだぞ)。 で、それを見ながら作ったのがコレ

 一部対応してないテーブル形式があるので、動かなくても文句は言わないでください(自分で改造してください・・・OTFの仕様が難解なのでちょっときついかもしれませんが)。 少なくともWin7付属のMS明朝・MSゴシック・メイリオでは動いています。 昔Windows Drawに付いてきた古いDFフォントはGSUBではなく、mortテーブルで縦横置換をしていて、このプログラムでは置換テーブルは出てきません。 このフォントはグリフテーブルの文字コードがシフトJISで、GhostscriptではUnicodeじゃないとIdentityを指定できないので、置換テーブルが取り出せてもダメなんですけどね。 同じDFフォントでも今売っているUnicode TrueTypeフォント(ex: TypeMuseum 3712・・・買ったらしい)は大丈夫です。

 コンパイルしてコマンドラインにttc(TrueTypeコレクション、複数のフォント、たいていは等幅とプロポーショナルフォント、を、TrueTypeフォントを集めた形になっているもの)フォントファイルを指定して実行すると、標準出力に

0 beginsubfont
  605 beginsubsttable
  @ 826 18594 ;
  ...
  endsubsttable
endsubfont

こんな調子で置換テーブルを吐くので、ファイルにリダイレクトして使ってください。 @;にはさまれた一組の数値のうち、前に書いてあるのが横書きグリフのID、後ろに書いてあるのが対応する縦書きグリフのIDです。 @;もPostscriptプログラム内では名前として使えます。 MS明朝の等幅フォントはサブフォント0ですので、

6 dict begin
/beginsubfont {
	/@ { } def
	/endsubfont { } def
	0 eq {
		/; { def } def
		/beginsubsttable { dict begin } def
		/endsubsttable { currentdict end } def
	} {
		/; { pop pop } def
		/beginsubsttable { pop } def
		/endsubsttable { } def
	} ifelse
} def
(msmincho.subst) run
end
/MS-Mincho-subst exch readonly def

こんな感じにするとサブフォント0の分だけの辞書ができあがります。 セミコロンが見づらいですが。 この辞書はキーが名前ではなく、横書きグリフIDの数値そのものになっています。 実はPostscriptの辞書のキーはどんなオブジェクトでも構いません。 キーをeqオペレータで比較したときにtrueになれば、その値を返すようになっています。 この場合は横書きのグリフIDをキーとして検索すれば縦書きグリフIDが得られますから、CIDMapの置換は以下のようにします。

% str cid glyphid <setgid> str
/setgid {
    2 dict begin
    /glyphid exch def
    /cid exch def
    dup cid 2 mul glyphid 256 idiv put
    dup cid 2 mul 1 add glyphid 256 mod put
    end
} def

% str cid <getgid> str glyphid
/getgid {
    1 dict begin
    /cid exch def
    dup cid 2 mul get 256 mul
    1 index cid 2 mul 1 add get add
    end
} bind def

/MS-Mincho-Unity /CIDFont findresource
dup length dict copy begin
/CIDFontName /MS-Mincho-Vert def
・・・
/CIDMap [
    /MS-Mincho /CIDFont findresource /CIDMap get 0 get
    dup length string copy
    2 dict begin
    /i 0 def /gid null def
    MS-Mincho-subst begin
    dup length 2 idiv  {
        i getgid /gid exch store
        currentdict gid known { i currentdict gid get setgid } if
        /i i 1 add store
    } repeat
    end end
] readonly def

MS-Mincho-Unityが実際に置換を行うフォント、MS-Minchoがパクり元になります。 できあがりのフォント名はMS-Mincho-Vertにしてあります。 元のフォントをコピーしてCIDSystemInfoIdentity 0に書き換えてdefinefontしても一応動きますが、コアを吐いたりと不安定になります。 だからと言ってCIDSystemInfoを書き換えないとCIDMapの書き換えがうまくいきません(これで相当悩んだ)。 TT_cmapを書き換えるという手もありますが、ps2pdfでPDFにしたときに反映されないようです。

 GhostscriptのCIDMapは配列の中にひとつだけ大きな文字列が入った形をしているので、このコードでもそうなっていることを前提にしています。 で、CIDMapをコピーしておいて前から順に調べていき、subst辞書を引いて値が見つかれば置換しています。 このプログラムでは置換すべき縦書きグリフIDのある横書きグリフIDはすべて書き換えられてしまいます。 本来は縦書きCIDに対応するグリフIDだけを置き換えるのが正しく、さらにCDevProcをきちんと書けばふたつのWModeにもきちんと対応できます。 でも面倒くさいので横書きグリフもろとも入れ替えてしまい、横書きのフリをして縦書きグリフを表示することにします。

CIDFontName currentdict end definefont pop
/MS-Mincho-Vert-90ms-RKSJ-H /90ms-RKSJ-H [ /MS-Mincho-Vert ] composefont pop
/MS-Mincho-Vert-90ms-RKSJ-H 36 selectfont
72 720 moveto (「縦書きー」) show

メイリオは等幅フォントがありませんが、通常の(メイリオUIではない)フォントの全角部分はおおむね等幅でできているようで、いわゆる半角文字を混ぜなければ見られる程度の縦書きにはなります。

 この方法は字形集合をIdentity 0とした部分だけ、PDF上で文字列のコピーがきちんと動きません。 なるべく文字列が正しくコピーできるようにしたい場合は、縦書きグリフが必要な部分だけIdentity 0なフォントを使い、他の部分は普通のフォントを使うなどの対象が必要です。 それでもコピーできない場所は残りますが(縦書きの拗音・句読点は全部化けます、当然ですが)。

 Distillerの場合はCIDMapでの置換が普通に効くので、GhostscriptからパクったCIDMapにGSUBによる置換をきちんと施してやれば、90ms-RKSJ-Vを指定すればきちんと縦書きになり、さらにコピーも普通にできるはずです。 文字幅も正しく計算されるので、CDevProcを置き換えなくても正しい方向に正しい字間でレンダリングされます。

●Ghostscriptで縦書きがおかしい(2016年版)

 今年は頑張りました。 Acrobatでカレット位置がおかしい以外は実用上差し支えない程度になっています。 PostScriptコードだけで完結していて、Unityフォントも必要ありません。 以下に原理を示します。 実装はがらくた箱に載せる予定です。

 PostScriptでの多バイト文字表示は、まず、文字をCMapでCIDに変換し、フォントのCIDMapでフォント依存のグリフID(GID)に変換する。 縦書きの場合、CMapを縦書き用、例えばUniJIS-UTF8-Vにすると、横書きと縦書きでグリフが違う文字は横書きとは異なるCIDにマッピングされる。 フォントドライバではCIDとGIDを1対1でマッピングすればいいように作ってある。 GhostscriptでもCMapはAdobe謹製のものが用いられている。 Resource/CMap以下のファイルを見ると、一部のファイルを除いてAdobeのCopyrightが書いてあり、その下にBSDライセンスっぽい文言が書かれてるだろう。 CIDMapの方はフォントドライバが用意することになる。 CIDの一覧(文字集合)はAdobeから公開されてるよ → The Adobe-Japan1-6 Character Collection

 TrueTypeでの多バイト文字表示は、文字コードからGIDへ変換するcmapというテーブルがある。 どの文字コードを使うかはcmapテーブルのヘッダに書いてある。 複数の文字コードをサポートしていることもある。 TrueTypeでの縦書きは、GSUBテーブルにvertフィーチャーがあれば縦書き対応であり、ここに横書きのGIDに対する縦書きのGIDが書いてある。 つまり、文字コード ―(cmap)→ 横書きGID ―(GSUB/vert)→ 縦書きGID という変換をする。 現在、TrueTypeの仕様はOpenTypeの仕様に包含されており、例えばMicrosoftなんかが公開してる → OpenType specification

 GhostscriptでTrueTypeを使って多バイト文字表示する場合、文字コードをCMapでCIDに変換したあと、Decodingというテーブルを使って一度Unicodeに変換しているようだ。 元がUnicodeなら一度CIDに変換したものをUnicodeに戻していることになる。 その後、TrueTypeのcmapテーブルでGIDに変換してグリフを得ている。 このcmapテーブルは当然 Unicode → GID のものを使う。 つまり、文字コード ―(CMap)→ CID ―(Decoding)→ Unicode ―(cmap)→ GID という流れである。 CMapはPostScriptの、cmapはTrueTypeフォントのテーブル。 How to use Ghostscriptに「Using Unicode True Type fonts」という章があるが、Unicodeじゃないといけないのはこれが理由らしい。

 TrueTypeのcmapはフォント辞書にTT_cmapという名前で含まれており、これを変更すればGIDも変わる。 このパスではCIDMapは使わない。 2013年版でCIDMapが効かなかったのはこれが理由らしい。 そして、CIDSystemInfoIdentity 0にするとCIDMapが直接使われるようになるらしい。 フォント辞書にはsubst_CID_on_WModeという辞書が含まれていて、これが横書きGIDから縦書きGIDへの変換を担当しているようなのだが、どうもこの機能が中途半端らしく、きちんと縦書きにならないわ、元のフォントをコピーしてCIDSystemInfoIdentity 0に書き換えて使うとコアを吐くわ、ということらしい。 subst_CID_on_WModeundefすればコアを吐かなくなることも判明した。

 DecodingTT_cmapはGhostscript独自のエントリーで、Acrobat(Reader含む)では使わない。 だから2013年版でTT_cmapを書き換えてもPDFとして閲覧したときには反映されなかったわけだ。 逆に、AcrobatではCIDMapによる置換が普通に効く。 たとえGhostscriptでPDFを生成した場合であっても、である。 PDFを生成するときにはグリフをレンダリングする必要はない。 グリフをレンダリングするのはPDFを表示するときであって、これをやるのはAcrobatである。 Ghostscriptがフォント辞書さえきちんとPDFに吐いていれば、あとはAcrobatがきちんと表示してくれる。 また、AcrobatはCIDを基にしてクリップボードへのコピーを行うらしく、CIDはCIDSystemInfoが決まらないとグリフとの対応、つまり文字との対応が決まらない。 日本語ならJapan1と指定するのだが、GhostscriptでCIDMapによる置換ができるようにIdentity 0としてしまうと、文字集合が分からなくなってしまうのでコピーができない。 しかし、PDFにしてAcrobatで見る分にはJapan1を指定してもCIDMapでの置換が普通に効くので、これでコピー問題も解決である。

 したがって

とすると、2013年版のようなUnityフォントは不要になり、GhostscriptでもAcrobatでもきちんと表示できて、さらにAcrobatではコピーもきちんとできるようになる。

 次にグリフの入れ替えについてである。 概要は2013年版に書いたとおりである。 スクリプトはkanaの他にhani(CJK Ideographic、直訳すると中日韓表意文字、つまり漢字のこと)もサポートした方がいいかもしれないが、日本語のフォントはだいたいどちらも同じテーブルを指しているようだ。 言語についてはJANが含まれておらず、デフォルト言語を使わなければならないフォントもあった(例: IPAexフォント)。

 縦横共用のCIDMapを作る場合、問題になるのは「横書きのGIDはそのままにしておく」点である。 TrueTypeの縦書きGIDは、横書きGIDから得るようになっている。 したがって、2013年版のようにCIDMap内にあるGIDを調べ、置換できるなら片っ端から置換するのが簡単なのだが、これだと本来横書きのCIDも縦書きのグリフで描画されてしまう。 縦書きのCIDについてのみ置換したい場合、まずCIDが横書きか縦書きか判断する必要がある。 これが第1の難関。 縦書きだと分かったら、対応する横書きのグリフIDを得なければならない。 これが第2の難関。

 さらにややこしくしているのが、横書きと縦書きで異なる文字が同じCIDに割り当てられていることがあり、さらにGSUBで置換したあとのGIDが横書きでも使われる場合があるという点。 UnicodeのU+2190は、UniIJS-UTF32-Hを見るとCID 737に、UniIJS-UTF32-Vを見るとCID 738に変換されることが分かる。 先ほどのAdobe-Japan1 Character Collectionを見ると、横書きでは左向き、縦書きでは上向きの矢印になる。 あれ? と思って横書きでの上向き矢印、UnicodeでU+2191を見ると、UniIJS-UTF32-HではCID738で、CID738は縦書きからも横書きからも使われることが分かる。 UniJIS-UTF32の場合、矢印は以下のようになっている。

文字UnicodeUniJIS-UTF32-HUniJIS-UTF32-V
U+2190737738
U+2191738736
U+2192736739
U+2193739737

 このような例は罫線類にも大量にある。 また、このような置換を行うCMapと行わないCMapがある。 矢印の場合、上に挙げたUnicode系、90ms-RKSJ-VのようなシフトJIS系は置換される。 EUC-VなどEUC系は置換されない。 他の文字についてもこのようなことがありうる。 調べてないけど。

 TrueTypeフォント側のGSUBも普通は上のとおりの対応になっている。 実際にはCID側で置換が起こるので、GSUB側での置換は不要である。 CIDもGIDも置換後の対応は同じはずなので通常は置換しても問題ない。 ところが、MSゴシックなどはGSUBで置換すると、横書きのGIDとは異なる縦書き専用のGIDになる。 実際にakamoz-ttfproc.psでVerboseログを吐かせてみると、

  <00002190>: vcid=738   hcid=737   hgid=17081 vgid=18838 (orig=17082)
  <00002191>: vcid=736   hcid=738   hgid=17082 vgid=18839 (orig=17083)
  <00002192>: vcid=739   hcid=736   hgid=17083 vgid=18840 (orig=17084)
  <00002193>: vcid=737   hcid=739   hgid=17084 vgid=18841 (orig=17081)

という置換が起きていることが分かる。 おそらくGID 17081とGID 18841は同じグリフなんだろうけれども、もし横書きと縦書きで矢印のグリフが違ったら、CIDMapを縦書きと横書きで共用できないことになる。

 また、このような入れ替え・巡回置換は処理上でも若干注意が必要である。 例えば上の順に処理が進んだとすると、CID 738のGIDがまず18838に書き換えられる。 次にCID 736のGIDを書き換えるのだが、対応する横書きのCIDが738で、このGIDは既に18838に書き換わってしまっているので、GSUBテーブルが引けない。 正しく処理するためには、横書きのCIDからGIDを得るときに書き換え前のテーブルを引かなければならない。

 もうひとつ、Unicodeには変な仕様があり、「縦書きのカギカッコ」という文字がある。 普通の「カギカッコ」はU+300Cで、これは横書きなら横書きのカギカッコのCID(686)、縦書きなら縦書きのカギカッコのCID(7911)になる。 一方、「縦書きのカギカッコ」という「文字」は CJK Compatibility Forms というグループに入っていて、U+FE41である。 この表を見ると他にも変な文字がいろいろあることが分かるだろう。 CMapを見ると横書きでも縦書きでも縦書きのカギカッコのCIDになっている。 正確には、UniJIS-UTF32-VUniJIS-UTF32-Hをベースとして縦書きでCIDが異なるものだけ上書きするような定義になっているが、U+FE41はUniJIS-UTF32-Vで定義されていない。 つまり、UniJIS-UTF32-Hのものがそのまま使われ、CIDは7911になる。

 Ghostscriptがこれをレンダリングするとき、CIDを一度Unicodeに戻すが、CID 7911はU+FE41に戻るらしい。 MSゴシックなどはU+FE41に対応するGIDがないため、次善の策としてU+300CのGIDが使われるらしく、横書きのカギカッコが出る。 一方、IPAexフォントはU+FE41に対応するGIDが定義されていて、たとえばIPAex明朝ではGID 7497となっており、U+300CのGID 452をGSUBで縦書きに置換したGIDと同じである。 縦書きのCMapを使った場合、前者はU+300C・U+FE41のどちらでも縦書きのグリフが出ず、後者はどちらでも縦書きのグリフが出る。 逆に、Unicodeで縦書き文字がない文字、たとえば「ー」については、どちらのフォントも全部横書きのグリフが出る。 このせいでGhostscriptでそのまま縦書きしようとするとなんとも中途半端な表示になる。

 先ほど、GhostscriptのフォントドライバはレンダリングにCIDMapを使わない、と書いたが、一応CIDMapは用意してくれている。 このCIDMap、縦書き用のCIDについてはどうも縦書き文字を使って生成しているらしく、縦書きカギカッコのCID 7911は、U+FE41をサポートしているIPAexフォントでは縦書きカギカッコのGIDが入っているのに対し、U+FE41をサポートしていないMSゴシックでは横書きカギカッコのGIDになってしまっている。 まあ要するにGhostscriptはGSUBを見ていない、ということである。

 最初、縦横共用のグリフについては置換を行わない、という方針で書いていたのだが、Unicodeで縦書き文字があると、縦書き専用と考えられるグリフが縦横共用と判定され、置換の対象から漏れるようになってしまった。 IPAexのように元から正しいグリフが入って入ればよいが、MSゴシックのように縦書き文字に対するグリフがない場合、横書きのまんまという事態が生じる。

 かといって共用グリフも置換すると、MSゴシックのようにCIDは縦横共用なのにGIDが別々の場合、横書きのときのCIDMapが「あやしい」ということになる。 なんつうか、UnicodeとMicrosoftがAdobeの成果を台無しにしている感じである。 このあたりをどうするかは悩ましいところだが、縦書きfixプロシージャを呼んでいるという事は、縦書きしたいのだろうということで、「GSUBにあれば共用だろうがなんだろうが置換」という方針を採ることにした。 CMapとGSUBの考えていることが同じならば、たとえGIDが違ったとしてもCIDが同じである以上、ほぼ同じグリフが出るはずである。 CMapとGSUBが食い違っていた場合はGSUBの考えていることが優先、ということになる。

 それで、肝心の第1の難関と第2の難関だが、第1の難関、CIDが横書きか縦書きかを判断するためには、要するにCMapをデコードできればよい。 これは長年悩んでいて、CMapの辞書はImplementation-dependentなのである(PLRM 5.11.4)。 だから、CMapを定義しているリソースファイルを読んで自力でテーブルを構築する必要があるのだが、リソースファイルのありかを簡単に調べる方法が分からなかったのである。 これは実はPostScriptの仕様として用意されていて、リソースのページに書いてあるように、カテゴリ辞書のResourceFileNameを使えばよい。 これはGhostscriptでもDistillerでも使えるはずだ。 ファイルが分かってしまえばこっちのもんで、CIDInit ProcSetのオペレータ(プロシージャ)を定義しなおしてファイルを突っ込んでしまえばよい。

 先ほど書いたとおり、CMapは横書きがベースで、縦書きは必要な分だけ置き換えるようになっている。 だから、CMapテーブルができればあとは簡単で、縦書きCMapで置き換えているCIDだけGSUBを引いて置換すればよい。 縦書きCMapさえ指定してもらえば、横書きCMapはusecmapのオペランドとして勝手にくっついてくる。 逆に言うと、そうなってないCMapではちゃんと置換ができない。

 第2の難関、対応する横書きGIDを得るには、同じ文字コードで横書きのCMapを引いて横書きのCIDを得、さらにCIDMapから横書きのGIDを得ればよい。 このとき、先ほど書いたとおり巡回置換に注意する必要がある。 あとはこのGIDでTrueTypeフォントのGSUBテーブルを引けばよい。 最初はCMapの逆引きテーブルを作っていたのだが、CMapテーブルがあるということは、縦書きCMapで置き換えている文字も全部分かるので、文字を列挙して縦書き・横書きのCMapを引くようにしている。

 あと残っている作業として、グリフのメトリックを指定する必要がある。 グリフのメトリックはフォント辞書内のMetrics2辞書か、CDevProcプロシージャで指定する。 最初は前者で作っていたが、生成に時間がかかるので後者に切り替えた。

 具体的にはグリフ座標系原点がグリフの上端中央に動き、進む方向が下になる。 TrueTypeフォント内ではグリフ座標系原点は横書きが基準である。 横方向の原点差分に関しては、CDevProcでは(縦書きであっても)横書きのメトリックが分かるので、その横幅の半分の位置を指定すればよい・・・のだが、Ghostscriptは縦書きを指定すると横書きのメトリック部分が縦書きのメトリックになってしまう。 かといって横書きのフリをすると、グリフ座標原点が動いてくれない(横書きだからね)。 結局、TrueTypeのhmtxテーブルを引くハメになった。

 縦方向の原点差分はOS/2テーブル(まさかのOS/2!)のTypoAscenderから得ている。 縦書きのメトリックはvmtxテーブルから得られる。 ここまでやると縦書きもプロポーショナルになる。 hmtxを読むのにhheaを、vmtxを読むのにvheaを読む必要がある。 座標系はFUnitという単位の整数値なので、これをheadテーブルにあるUnitsPerEmで割る必要がある。 無効なGIDを弾き飛ばすのにmaxpテーブルの総グリフ数もいるかもしれない。 あとはTrueType Collectionから目的のサブフォントを見つけるためにnameテーブルが必要である。 必要なテーブルは多分これで全部。

 そして、TrueTypeフォントのファイル名もCIDFontカテゴリリソースのResourceFileNameで分かるため、全部PostScriptコードで書ける。 ページ記述ではリソースファイルを直接読むな、って書いてあるけどね(PLRM 3.9.4)。 最初、フォント辞書のsfntsを使っていたのだが、GhostscriptではちゃんとTrueTypeフォントの中身が入ってるのに、Distillerではフォントデータのサイズを示すらしい整数値が返ってきて愕然としてしまった。 その後しばらく悩んだのだが、ResourceFileNameでファイル名を取得できることに気づいて解決である。

 ファイルがTrueTypeコレクションだった場合、サブフォントを見つける必要がある。 Ghostscriptでcidfmapを使っていると、CIDFontNameの方はcidfmapで指定した名前になるので注意。 このため、TrueTypeのnameテーブルとの比較はCIDFontNameFontNameの両方で行っている。

 これで当面の課題は全部解決である。 年末にはとりあえず動くものができていたのだが、いろいろ対応していくうちにこれだけの問題が噴出してしまい、結局こんな時期になってしまった。 ひとつだけ、「Acrobat上でカレットの位置がおかしい」という問題が残っているが。 実装は「がらくた箱」の方に書く予定である。 このページが日本語を読めるGhostscriptの開発者の方の目に留まって、Ghostscriptで何も考えなくても縦書きができるようになる日が来ることを願うばかりである。 願ってないでcontributeしろよ、って話もあるけど。


Copyright (C) 2012-2016 akamoz.jp

$Id: font.htm,v 1.12 2018/02/16 15:38:27 you Exp $