Ghostscript の日本語フォントの問題

2012/03/04
2012/03/14 改訂
2012/03/17 改訂

Tcl/Tk で作った EPS ファイルは Mac (OSX 10.6) の Preview.app では何故か扱えない。Preview.app はメッセージを出さないので手がかりがつかめない。原因を探るのは絶望的である。そこで gs ではどうか?

詳しくは
Tk Postscript の日本語フォントの問題
を見よ。

環境

僕は、gs の環境を整備して来なかった。これまでは Mac の Preview.app で間に合っていたから...

Tcl/Tk の簡単なプログラム (a.tcl)

問題点

Ghostscript 9.02 が動いている。

「ABC」の問題は Helvetica の代用として NimbusSanL-Regu が使われたことが原因だと思われる。「いろは」の問題は HiraKakuPro-W6 を認識せずに、勝手に Courier で置換したから。

メッセージの中の

%rom%Resource/Font/NimbusSanL-Regu
%rom%Resource/Font/HiraKakuPro-W6
の %rom% とは何か?
僕の環境では NimbusSanL は次の場所にあった。
/usr/local/share/ghostscript/8.63/Resource/Font/
この結果と比較すると %rom% とは /usr/local/share/ghostscript/8.63/の事と考えたくなるのだが、他方では

の表示をみていると、違うかも...

なお、gs -h で表示された Search path のうち、僕のシステムで実際に存在するのは、

   /usr/local/share/ghostscript/9.02/lib 
   /usr/local/share/ghostscript/fonts 
   /usr/share/cups/fonts
だけで、このうち/usr/share/cups/fonts は空である。なぜか
/usr/local/share/ghostscript/9.02/Resource/
が存在しない。
/usr/local/share/ghostscript/8.63/Resource/
は存在するのに...

ghostscript-9.05 をインストール

念のため ghostscript を再インストール。最新版は ghostscript-9.05 であった。 http://www.ghostscript.com/download/gsdnld.html

今回は、「ABC」が大きく改善された。アウトラインフォントで描かれている!
しかし、相変わらず「いろは」は表示されない。

メッセージをみると、ghostscript 9.05 は、%rom%Resource/Font/ に HiraKakuPro-W6 が見つからないと言って諦めていない。OSX のシステムフォントを探し、見つけるのに成功している。しかし、どうした訳か、Loading に失敗している。何故、Loading に失敗するのか、理由を知る事が問題解決の鍵である。

相変わらず/usr/local/share/ghostscript/9.05/Resourceは存在せず、存在するのは

   /usr/local/share/ghostscript/9.05/lib
   /usr/local/share/ghostscript/fonts
   /usr/share/cups/fonts
だけであり、cups/fontは空である。ダウンロードしたパッケージには Resourceが含まれ、その中には多数のフォント関連のファイルが存在し、期待していたのに... 何故インストールされなかったのか?

パッケージのドキュメントには、必要とあれば自分でインストールせよと書かれている。そこで取りあえずインストール。インストールされた内容は以下の通り。

今回は CID 関係のディレクトリが含まれている。

Resource をインストールしても、状況は変わらない。

    Loading HiraKakuPro-W6 font failed.
である。

もう一つ気になるのは、

  /usr/local/share/ghostscript/9.05/Resource
を追加したにも係わらず
  gs a.eps
を実行しても gs は、このディレクトリを調べに行った気配がない事である。

%rom%の正体

gs a.eps
を実行した時のメッセージ:
  Loading NimbusSanL-Regu font from %rom%Resource/Font/NimbusSanL-Regu...
に現れた %rom% とは何か?

ghostscript-9.05 のソースコード(ghostscript-9.05/obj/gsromfs1.c)の中に “NimbusSanL-Regu” が現れる。どうやら gsromfs1.c の中に、フォントファイル NimbusSanL-Regu がハードエンコードされているのだ。ここで「ハード」と言ったのは、ユーザ側からは変更できないと言う意味である。NimbusSanL-Regu だけではなく、多数のフォント名が、このファイルの中に見られる。従って、配布ファイルの中の Resource の部分が、gsromfs1.c の中の %rom% だと思われる。

改めて make の出力を見てみると、gsromfs1.c は ghostscript-9.05/Resource/ から自動生成されている。

図: make の出力の一部
こうして生成された gsromfs1.c は 15.5MB もある。

ghostscript はどのように Helvetica を NimbusSanL-Regu で置き換えるか?

/usr/local/share/ghostscript/9.05/Resource/Init/Fontmap
をみると、Fontmap.GS が参照されている。そこで
/usr/local/share/ghostscript/9.05/Resource/Init/Fontmap.GS
をみるとフォントの alias として以下の行が書かれている。
/Helvetica			/NimbusSanL-Regu	;
/Helvetica-Oblique		/NimbusSanL-ReguItal	;
/Helvetica-Bold			/NimbusSanL-Bold	;
/Helvetica-BoldOblique		/NimbusSanL-BoldItal	;
つまり Helvetica は NimbusSanL-Regu に置き換えられる。

注意: ファイルだけを見れば、「なるほど...」となるのであるが、実は非常に分かりにくい。プログラムの中に alias に相当するコードが存在するのである。base/gdevpdtb.cには次のテーブルがある。

    /* Standard mapping of URW fonts */
    {"NimbusMonL-Regu",       "Courier"              },
    {"NimbusMonL-Bold",       "Courier-Bold"         },
    {"NimbusMonL-ReguObli",   "Courier-Oblique"      },
    {"NimbusMonL-BoldObli",   "Courier-BoldOblique"  },
    {"NimbusSanL-Regu",       "Helvetica"            },
    {"NimbusSanL-Bold",       "Helvetica-Bold"       },
    {"NimbusSanL-ReguItal",   "Helvetica-Oblique"    },
    {"NimbusSanL-BoldItal",   "Helvetica-BoldOblique"},
この他に、既に述べたように obj/gsromfs1.c の中にも、同様な情報が含まれている。(%tom%はResource/Init/Fontmap.GSを取り込んでいるから...。)

こうした、ハードエンコードされた情報と、

/usr/local/share/ghostscript/9.05/Resource/Init/Fontmap.GS
との不一致が発生した場合の振る舞いが定かではないのである。なんで ghostscript を書いたプログラマはこんなヤヤコしい事をやるのだ? センスを疑う。もっとも彼らも、このスタイルが良いとは思っていないらしい。
bash$ grep Fontmap /users/arisawa/src/ghostscript-9.05/*/*.c
/users/arisawa/src/ghostscript-9.05/base/gdevmsxf.c:/* This table should be an external resource, like Fontmap, */
/users/arisawa/src/ghostscript-9.05/obj/gsromfs1.c:		/* file name 'Resource/Init/Fontmap' */
/users/arisawa/src/ghostscript-9.05/obj/gsromfs1.c:		/* file name 'Resource/Init/Fontmap.GS' */
/users/arisawa/src/ghostscript-9.05/obj/gsromfs1_.c:		/* file name 'Resource/Init/Fontmap' */
/users/arisawa/src/ghostscript-9.05/obj/gsromfs1_.c:		/* file name 'Resource/Init/Fontmap.GS' */
/users/arisawa/src/ghostscript-9.05/psi/zfontenum.c:   to the Fontmap to reference fonts stored on the system.

追記:
NimbusSanL-Regu は /usr/local/share/ghostscript/fonts/にもある
n019003l.afm, n019003l.pfb, n019003l.pfm
と関係があるらしい。

bash$ grep NimbusSanL-Regu /usr/local/share/ghostscript/fonts/*
/usr/local/share/ghostscript/fonts/n019003l.afm:FontName NimbusSanL-Regu
Binary file /usr/local/share/ghostscript/fonts/n019003l.pfb matches
Binary file /usr/local/share/ghostscript/fonts/n019003l.pfm matches
/usr/local/share/ghostscript/fonts/n019023l.afm:FontName NimbusSanL-ReguItal
Binary file /usr/local/share/ghostscript/fonts/n019023l.pfb matches
Binary file /usr/local/share/ghostscript/fonts/n019023l.pfm matches
/usr/local/share/ghostscript/fonts/n019043l.afm:FontName NimbusSanL-ReguCond
Binary file /usr/local/share/ghostscript/fonts/n019043l.pfb matches
/usr/local/share/ghostscript/fonts/n019063l.afm:FontName NimbusSanL-ReguCondItal
Binary file /usr/local/share/ghostscript/fonts/n019063l.pfb matches
bash$ edit /usr/local/share/ghostscript/fonts/n019003l.afm
bash$ 

何故 Loading ... failed か?

gs a.eps を実行すると、次のメッセージを出し、「いろは」を表示しない。
  Loading HiraKakuPro-W6 font from /Library/Fonts/ヒラギノ角ゴ Pro W6.otf... 3531392 2173798 20393248 12769844 3 done.
  Loading HiraKakuPro-W6 font failed.
ファイルは見つかって、それを読み取った。そして failed と言う事は、読み取った内容に問題があったと言う事であろう。

実際、gs が読み取ろうとしたファイルは拡張子の付かない HiraKakuPro-W6 であり(Resource/Font/の中のファイルは、どれも拡張子が付いていない)、実際に読み取ったのは拡張子 otf が付いたファイルである。形式が異なるかも知れない。

注記: Resource/Font/の中のファイルはフォントファイルではないらしい。

True Type Font

Ghostscript の配布パッケージ付属の、フォントに関するドキュメントを読んでいたら、次の文が書いてあった。
Ghostscript compiled with the "ttfont" option can also use TrueType fonts with the extension .ttf.
ttfontオプションを付けてコンパイルすれば True Type font が使えるって! しかし...
Makefile を見ても、ttfontオプションが見えて来ない。僕は何か勘違いしているかも...

%rom%なし gs

そもそも、gs が %rom% を参照するのが混乱の元なのだ。そこで、これを外せないかと思ってネット情報を調べていたら次のページが見つかった。
http://oku.edu.mie-u.ac.jp/~okumura/texwiki/
に、ghostscript 9.05 では configureで、disable-compile-initsのオプションを指定しないと cidmap を参照しないらしいと書いている。実際 9.05 のconfigureの内容を見ると、

と書かれている。この下でインストールをやり直したら、確かに %rom% を参照しないで、/usr/local/share/ghostscript/9.05/Resource/を見に行く。

以下は全て %rom% なしの gs を使った結果である。

Fontmap.GS と cidfmap

Ghostscript は Font alias に関する2つのファイルを持っている。Resource/Initの中のFontmap.GScidfmapである。これらの役割をはっきりさせないと、いつまでも頭の名が ????? である。

久しぶりに 「PostScript Language Reference Manual」(Adobe Systems)を開いて、デバッグコードは

(Hello World) print
のようにコードの中に挿入すればよろしい事が分かった。

( ) は Postscript では文字列である。この部分は「(Hello World) を print しなさい」と読む。語順は日本語と同じであるから日本人には分かりやすい。print の結果は標準出力に出る。

僕の Resource/FontにはHiraKakuPro-W6-Hの内容は次のようになっていた。(このファイルはどこかから採ってきたものだ。)

オリジナル HiraKakuPro-W6-H

これにデバッグコードを入れるには(例えば)次のようにすればよい。

デバッグコード入り HiraKakuPro-W6-H
こうして実験した結果によれば、Resource/Init/Fontmap.GSには、
/HiraKakuPro-W6	   /HiraKakuPro-W6-H
のような行を入れておかなくてはならない事がわかる。a.eps の中ではフォント名が HiraKakuPro-W6 で指定されている。従って、この行が無いと、gs は Resource/Font/HiraKakuPro-W6を探しに行って、存在しないと言う1。 gs 自体は HiraKakuPro-W6 が CID フォントである事を知らないのだから...

注1: 後に見るように、この言い方は正確ではない。gs は内部でフォント名の変換を行い、処理を行っている。

このケースでは gs は Resource/Font/HiraKakuPro-W6-Hを読み取って、次の行によって、初めて HiraKakuPro-W6 が CID フォントである事が分かる。gs は

[/HiraKakuPro-W6 /CIDFont findresource]
によって、CIDFont の中の HiraKakuPro-W6 を読みに行こうとするのだが、その前に Resource/Init/cidfmapを見る。ここに HiraKakuPro-W6 の情報がなければ、そのまま Resource/CIDFont/HiraKakuPro-W6を読む。あれば、それに基づいて他のファイルを読もうとする。このへんの動作は Fontmap.GSと同じなのであろう。ルーチンを再利用しない理由は無い。(と思ったのだが、実際には違うようである。実ファイルの場所を示す
<< .... >>
が必要らしい。

「ヒラギノ角ゴ-Pro-W6」を読み取らせるには2つの方法があるのだろう。

  1. Resource/CIDFont/HiraKakuPro-W6 に「ヒラギノ角ゴ-Pro-W6.otf」のリンクを張る。
  2. /usr/local/share/ghostscript/fonts に HiraKakuPro-W6 の名で、代用するTrueTypeフォントへのリンクを張る。
第一の方法は単純である。cidfmapは弄らない。
第二の方法では cidfmap に以下の1行を書き込む。
/HiraKakuPro-W6 << /FileType /TrueType   /CSI [(Japan1) 6]   /Path (ipagp.ttf) >> ;
ここではIPAフォントで代用した。ここに直接 HiraKakuPro-W6 へのリンクを張ると

====== findresource: STARTED ======
Loading a TT font from /usr/local/share/ghostscript/fonts/HiraKakuPro-W6.otf to emulate a CID font HiraKakuPro-W6 ... Done.
  Error: /invalidfont in /findfont
と表示される。

ちなみに IPA フォントで置き換えた場合、次のメッセージで終わる。

====== findresource: STARTED ======
====== findresource: FINISHED  ======
19713816 12055061 3994944 2598670 3 done.
Error: /typecheck in definefont
これは findresource を無事にパスし、その後で問題があった事を意味している。

追記: 僕の環境(OSX 10.6)には "Arial Unicode.ttf" があり、日本語が表示できる。これも IPA フォントと同様に、typecheck エラーとなる。

注意: Ghostscript のマニュアルには次のように書かれている。
Note that loading truetype fonts directly from /Resources/CIDFont is no longer supported (ghostscript/9.05/doc/Use.htm)
であるから、ここに述べたTrueType フォントに大しては、第1の方法は使ってはならない。サポートを止めた理由は、gs の辞書に読み取るための十分な情報がないからだと言う。

日本語表示の簡単な postscript プログラム

日本語文字「いろは」を含む、Tcl/Tk で作った a.eps の表示に成功するか? 今のところ、No である。 これまでの実験によれば、使用するフォントによって、エラーメッセージには2つのパターンがある。"invalidfont" と "typecheck" である。前者は OpenType で、後者は TrueType で発生している。前者はフォントファイルの読み取りに失敗している。読み取ったはよいが、内容に不満があるのである。後者は、どうやら読み取った内容に不満は無いらしい。しかし確認が必要である。そのためには、もっと簡単なプログラムが必要である。

Hello をグラフィクス画面に書き出すプログラム (h.eps)

これは成功する。それではフォントを HiraKakuPro-W6 に置き換えて、「こんにちは」を書き出すのはどうか? もちろん、そんなに甘くはないと考えているが、問題解決のヒントにはなるだろう。

「こんにちは」をグラフィクス画面に書き出すプログラム (k.eps)

このプログラムは、エラーを吐き出さずに、正常終了する。 しかし、文字が化けている。正常終了するのは gs の中でのフォント変換によって、ttf フォントが指定された時である。

このことから3つの事が分かる。

  1. TrueTypeフォントの読み取りには問題はない。
  2. 文字のエンコードが UTF-8 である事を gs に知らせる必要がある。
  3. a.eps (あるいは a.eps) のコードの中に問題が含まれている。
cの問題は後回しにして、bの問題の解決を図ろう。gs の UTF-8 対応に関してネットを調べていると次のように書かれているページに出会った。
PostScriptファイル内で指定するフォント名は、上で指定した/MS-Gothicの後ろに、ハイフンに続けてCMap名を付けたものになる。CMap名はResource/CMapにあるファイル名を指定すればよい。CMapは各文字の文字コードをCID・・・フォント内の字形の番号に変換する働きがあるので、CMap名で文字コードも決まる。 ( http://www.akamoz.jp/you/uni/gs-cyg-ttf.htm )

これによると僕の場合にはフォント名は HiraKakuPro-W6-UniJIS-UTF8-H である。

「こんにちは」をグラフィクス画面に書き出すプログラム (k1.eps)
で実験。そして驚くべき事に、「こんにちは」が表示された。驚いたのは、Fontmap.GScidfmap を変更していないし、Resource/Font/の中に、新たに何も追加していないまま試したからである。gs はどのようにして HiraKakuPro-W6-UniJIS-UTF8-Hを見つける事ができたのか?

僕の環境では Resource/Font/ の中には HiraKakuPro-W6HiraKakuPro-W6-H が置かれている。これらは、実行された時にメッセージを吐き出すように修正されている。しかし、ここからのメッセージは観測されない。

あらためて Ggostscript 付属のドキュメントを読む。Fonts.htm には次のように書かれている。 as part of normal initialization Ghostscript runs a file gs_fonts.ps,

そして、Resource/Init/gs_fonts.ps を見ると、フォント名の置き換えらしきコードが多数存在するのである。こんなプログラムの作り方は嫌いだ~~~

プログラムを作る時にはユーザーズ・インターフェースに気を使う。ユーザーズ・インターフェースと言うのは、GUIのボタン等や、対話型プログラムのコマンドセットだけではない。設定ファイルも、また重要なインターフェースなのだ。ユーザの意思を無視したインターフェースは最悪のプログラムだと思う。現在の gs はそのような酷さがある。

さて我々の目標は、gs の設定に特化した k1.ps ではなく、一般的な postscript 名でフォントを指定した k.ps を gs で動かすことである。これまでの試行錯誤を行えば後は簡単である。まずは Resource/Init/Fontmap.GS を次のように設定し、 Resource/Font/HiraKakuPro-W6-UniJIS-UTF8-H に制御が移るようにする。

Resource/Init/Fontmap.GSの設定
次にResource/Font/HiraKakuPro-W6-UniJIS-UTF8-Hを次の内容で作成する。
Resource/Font/HiraKakuPro-W6-UniJIS-UTF8-Hの内容
この中には print を含む行が3カ所にあるが、デバッグのためなので、後で消してもよい。 Resource/Init/cidfmapで HiraKakuPro-W6 を実際のフォントファイルに関係させる。ここでは ArialUnicode.ttf と関係付けている。
Resource/Init/cidfmapの設定
ArialUnicode.ttf/usr/local/share/ghostscript/fontsに置く。

注意: /CSI [(Japan1) 6] の部分はいい加減である。多分間違っているが、一応は「こんにちは」が表示される。

ヒラギノフォントのまとめ

目標

postscript コードで /HiraKakuPro-W6 を指定して、ヒラギノ角ゴシックで「こんにちは」を表示させる。 具体的には EPS 形式の次のファイルから「こんにちは」を表示する PDF ファイルを生成する。

k2.eps の内容

Resource/Init/cidfmap

cidfmap の中に /HiraKakuPro-W6 のエントリーを書かない。あるいはせいぜい

  /HiraginoKakuGothicPro-Bold   /HiraKakuPro-W6 ;
にとどめる。

Resource/CIDFont

Resource/CIDFont/ の中に「ヒラギノ角ゴ.otf」へのリンクを HiraKakuPro-W6 の名で張る。 あるいは、ここに、「ヒラギノ角ゴ.otf」をコピーして、HiraKakuPro-W6 と名前を変更してもよいが、管理上は(元が分かるように)リンクの方が僕の好みである。

Resource/Init/Fontmap.GS

次の行を追加する。
  /HiraKakuPro-W6		/HiraKakuPro-W6-UniJIS-UTF8-H ;
  /HiraginoKakuGothicPro-Bold  /HiraKakuPro-W6-UniJIS-UTF8-H ;
この意味は、Postscript コードで、左のフォント名が指定された時に、右のファイルの実行を指示する。右のファイルは
Resource/Font
に置く。

HiraKakuPro-W6-UniJIS-UTF8-H

配布パッケージには

  Resource/Font/HiraKakuPro-W6-UniJIS-UTF8-H
は存在しないので、次の内容で作成する。

PDFファイルの生成

今回は EPS ファイルの形式にのっとり、BoundingBox を入れた。 ネットの情報によれば、これを gs に認識させるには
  gs -dEPSCrop k2.eps
のように gs に d オプションで EPSCrop を指定する。PDF ファイルを生成するには
  ps2pdf -dEPSCrop k2.eps
とする。これで BoundingBox で指定されたサイズのPDFファイル、k2.pdf が生成される。これを gs で見るには
  gs k2.pdf
で、その結果は次のようになる。

Adobe Reader では(僕の環境では)

  open -a "/Applications/Adobe Reader 9/Adobe Reader.app" k2.pdf
としなくてはならない。Adobe Reader へのパスを open で開かなくてはならない。コマンドを使わざるを得ないのは、Mac の Preview.app に問題があるからである。k2.pdf をマウスで選択すると、Preview.app がそれを開く。そして、転ける。 しかし、そうした問題があるにも係わらず Adobe Reader はちゃんと表示してくれる。イメージ形式の PDF ファイルではなく、文字として表示されている。大きく引き延ばしても、ギザギザにならない。

問題点

(A) 生成された k2.pdf は 500KB もある。これはヒラギノ角ゴの全てのフォントを埋め込んだ結果だと思う。必要な分だけの埋め込みを行ってくれれば、もっと小さくて済んだはずであるが、これは現在の gs の到達段階を示しているのだろう。

(B) k2.pdf は Preview.app を転けさせる。Preview.app は k2.eps の中の HiraKakuPro-W6 を認識しない。Courier に置き換えられる。

(C) 僕は、たぶん、手動で設定しすぎているだろう。不要な部分があるだろう。

/CSI [(Japan1) 6]について

英語版 WikiPedia (http://en.wikipedia.org/wiki/PostScript_fonts) に書かれている事と、僕のシステムを比較すると、「(Japan1) 6」は Resource/CMAP/の中の Abode-Japan1-6を指しているらしい。

このディレクトリの中には、これとよく似た名前のファイルが6つある。すなわち、
Adobe-Japan1-1,...,Adobe-Japan1-6
である。このファイルの役割は、文字コードとサポートされているグリフとのマッピング情報を提供しているらしい。そして、語尾の数字が大きい程、マッピング情報は多くなり、数字の大きいファイルは小さいファイルのスーパーセットになっている。これらのファイルはテキストファイルなので、内容を見る事が可能であり、実際、比較してみるとスーパーセットになっていることが分かる。

フォントファイルにとって、このような基本的な情報が、ファイルの内部に含まれていないらしい。(だから、ファイルを読み取る時に指定するのだろう。) では、我々はどこからこの情報を手に入れたらよいのだろうか?

IPAフォント

IPAフォントの情報は

http://ossipedia.ipa.go.jp/ipafont/fontspec.html
から手に入る。このフォントは「JIS X 0213:2004」に基づいていると言う。この規格の概要は
「JIS漢字コード表の改訂について」(経済産業省,平成16年2月20日)
http://www.jisc.go.jp/newstopics/2005/040220kanjicode.pdf
から手に入る。それによると、この規格では10040文字がサポートされている。

他方では Adobe-Japan1-3 は 9472 文字、 Adobe-Japan1-4 は 15616 文字のテーブルを持っているので、Adobe-Japan1-4 以上を指定しておけば大丈夫という事か?
何となく釈然としない。それなら一番大きな数字のもの(Adobe-Japan1-6)で何か不都合な事があるか?

Arial Unicode MS

「Arial Unicode MS」はどのように指定したらよいのか?
適当に設定して動いているのだが...