読者です 読者をやめる 読者になる 読者になる

Acetaminophen’s diary

化学に関すること,TeXに関すること,ゆきだるまに関すること。

Ghostscript のこと(1):アウトライン化の詳細

追記:2015-03-14 21:10
アウトライン化の挙動について新たな知見が得られたため、一部書き換えた。

今回は、Ghostscript のアウトライン化に関する知見をまとめることにした。

文字や図形といったすべての要素のアウトライン化は、Ghostscript の epswrite/eps2write によって実現できる。この機能は IllustratorInkscapeTeX の数式を貼り付けたい場合などに必要不可欠であり、相当の需要が見込まれる。TeX2img の心臓部であるというだけでなく、コマンドラインから Ghostscript を利用しようとする一般ユーザにとっても重要であろう。

この件について、TeX2img のアップデートに携わった身として情報発信する必要を感じたため

「文字や図形を含む全てをアウトライン化したい」

場合に必読となる日本語リソースを提供できれば幸いである。

 

全2回で、第1回は一般論としての Ghostscript のアウトライン化機能を詳細に考察し、第2回は現状で把握した gs9.15 の不具合?を説明する。単に「画像ができればいい」ならば TeX2img の記事だけでも十分かもしれないが、仕組みの詳細も興味深いと思う。

本記事で取り上げる内容は今回の TeX2img アップデートに不可欠だったため、Windows 版開発者である阿部さんと Mac 版開発者である寺田さんと僕の3人で Ghostscript の挙動を調べる必要が生じました。この記事はその実験によって得た知見の備忘録のようなものです。したがって、この記事を書くにあたって、阿部さんと寺田さんの全面的な協力が必要不可欠でした。本当にありがとうございました。

 

前提:Ghostscript の epswrite/eps2write について

gs の epswrite というデバイスは、従来から文字のテキストとしてのフォント情報を失わせてアウトラインをとることに用いられてきた。これは解像度を示す -r オプションを大きな値にしたときに可能となる。例えば Windows

> gswin32c -q -sDEVICE=epswrite -sOutputFile="img-outline.eps" -dNOPAUSE -dBATCH -r20016 "img.pdf"

を実行すると、文字や図形のアウトラインがとられる(一部の例外を除く:新しい記事を参照)。それと同時に、文字や図形が存在する領域だけをクロップして余白をゼロにするはたらきもある。上のコマンドでは -r オプションの値を 20016 と高解像度にしたが、値をたとえば 720 のように低めに設定すると、クロップは行われるがアウトライン化は行われず、“マスク付ビットマップ化”(あとで説明)されるといわれてきた。そこで、経験的に -r オプションを高く設定することで、アウトライン化を強制しようという試みがこれまでなされてきたようだ。

この epswrite デバイスについて、前回の記事でも触れた通り gs9.15 では以下のような仕様変更がなされた。

  • gs9.10 [2013-08-30] 以前 → epswrite を使用
  • gs9.14 [2014-03-26] → eps2write 追加・epswrite は残すが非推奨
  • gs9.15 [2014-09-22] → eps2write を使用・epswrite は廃止

公式ページでも確認:History of Ghostscript versions 9.n

Version 9.15 (2014-09-22)
Get rid of last vestiges of pswrite....
Remove epswrite (the final "subdevice" of pswrite) and references to it.

Version 9.14 (2014-03-26)
A new device 'eps2write' has been added which allows for the creation of EPS files using the ps2write device instead of the old (deprecated and removed) pswrite device. This produces considerably better quality EPS files than the old epswrite device which is now also deprecated and will be removed in a future release.

そこで、この変更は何を意味するのか考える必要が生じる。

 

Ghostscript 9.14 以前の場合:epswrite の挙動

実験として WindowsMac で同じソースを用い、いずれも Ghostscript 9.10 で PDF から EPS への変換を実行した結果を概説する。用いたソースはこちら:

今回の記事執筆にあたり、理系ジンさんに許可を頂きロゴを使わせていただきました*1。ありがとうございます。

Windows の場合
> "C:\gs\gs9.10\bin\gswin32c.exe" -q -sDEVICE=epswrite -sOutputFile="tmp27E4-%d.eps" -dNOPAUSE -dBATCH -r432 "tmp27E4.pdf"

というコマンドは成功。

> "C:\gs\gs9.10\bin\gswin32c.exe" -q -sDEVICE=epswrite -sOutputFile="tmpB120-%d.eps" -dNOPAUSE -dBATCH -r20016 "tmpB120.pdf"

というコマンドは

Error: /VMerror in --.pushpdf14devicefilter--
GPL Ghostscript 9.10: Unrecoverable error, exit code 1
VM status: 3 4658272 5960088

というエラーで失敗。

Mac の場合
$ /usr/local/bin/gs -sDEVICE=epswrite -dNOPAUSE -dBATCH -dFirstPage=1 -dLastPage=1 -r720 -sOutputFile=temp3661.eps temp3661.pdf 2>&1

というコマンドは成功。

$ /usr/local/bin/gs -sDEVICE=epswrite -dNOPAUSE -dBATCH -dFirstPage=1 -dLastPage=1 -r20016 -sOutputFile=temp3661.eps temp3661.pdf 2>&1

というコマンドを送ると

Error: /VMerror in --run--
VM status: 3 4702950 5995888
Current allocation mode is local
Last OS error: 22
GPL Ghostscript 9.10: Unrecoverable error, exit code 1

というエラーで失敗。

いずれの場合も解像度の違いだけで結果が異なっていた。

 

Mac の場合について、寺田さんに Illustrator でパスの成り立ちを解析していただいた。

元の PDF ファイルのパスは以下のようになっている(図は画面キャプチャ:PDF のテキスト部分はもちろんフォント保持)。

f:id:acetaminophen:20141016102932p:plain

これを gs の epswrite で EPS 化してアウトライン化しようとすると以下のようになって、ものすごい数のパスに分割されてしまう(図は -r オプションを約 5000 に調節して作成した EPS の画面キャプチャ)。

f:id:acetaminophen:20141016102957p:plain

より高解像度の -r20016 ではあまりにもパスの数が多くなりすぎて、gs がエラーを起こしているらしい。ここではこの「ものすごい数のパスへの分割」を“不完全アウトライン化”とよぶことにする。

これはどうも TikZ だからというわけではないようで、現に同じ理系ジンさんのページの「TikZ_math.tex」は -r20016 でも通る。結論としては Ghostscript との“相性”の問題のようで、一概にどのような場合にエラーが発生するとは言えないらしい。

では低解像度の -r432 や -r720 にすればどうかというと、今度は最初からアウトライン化を行わず、ビットマップ画像をマスクで覆ったような画像が生成する。この現象を“マスク付ビットマップ化”とよぶことにする。

分かりにくい気がしたので、僕の Google サイトにそれぞれの例を置いておく。ただし“不完全アウトライン化”は上の画面キャプチャで解りそうなので除き(ファイルサイズも大きいためそぐわない)、「アウトライン化」「“マスク付ビットマップ化”」「ビットマップ化」の例のみとする。出力に使用したコマンド等の詳細は Google サイト参照*2

以上のことから判断すると、以下のような可能性が考えられる:

gs9.14 以前の epswrite の場合は -r オプションに応じて

  • 低解像度の場合:常に“マスク付ビットマップ化”が起こる
  • 高解像度の場合:基本的にはアウトライン化するが、場合によっては PostScript と「相性が悪い」ために“不完全アウトライン化”が起こる

そして、この「相性が悪い」例の一つがロゴのようだ*3

追記:2015-03-14 なお、この低解像度・高解像度という表現はあまり適切ではないと思われる:新しい記事へ。

 

Ghostscript 9.14 以降の場合:eps2write の挙動

おそらく gs9.14 で output device として追加された eps2write は、以上のように epswrite が“相性が悪いことがある”という問題を解消するために導入されたものだろう。TeX2img が活用している epswrite は昔 level 1 出力をサポートしていた pswrite の最後の名残らしく、pswrite は文字をビットマップ化(“不完全アウトライン化”のこと?)してしまう問題があったようだ。

In the upcoming Ghostscript 9.01 (to be released end of Feb 2011) the "ps2write" output device will produce DSC-conforming PostScript (gives PASS with current cupstestdsc) and therefore the old "pswrite" will get deprecated. Especially the old "pswrite" turns text characters into bitmaps and so output files will get huge which easily causes crashes of PostScript printers. The new "ps2write" handles text correctly and so files will not blow up. In addition, "pswrite" will most probably not get maintained any more.

Therefore I recommend that in the pdftops filter the "ps2write" output device of Ghostscript gets used when compiled in Ghostscript mode. Or better a check in ./configure should be done to check which Ghostscript version is installed and in case of 9.01 or newer "ps2write" should be used, otherwise "pswrite".

STR #3766: pdftops in Ghostscript mode should use the "ps2write"... - CUPS.org

 

eps2write はもっと前から既に導入されていた ps2write と規格を統一したもののようで、この ps2write というのは以下によると PostScript level 2 or 3 を出力するためのエンジンである*4。ついでに pswrite の記述もある gs9.01 からの引用が以下。

4.2 PS2 writer
The ps2write device outputs postscript language levels 2 and 3. Please refer to Ps2ps2.htm for the ps2write device options. It is recommnded that this device is used for PostScript output, unless level 1 PostScript is required in which case you must use the pswrite device below.

4.3 PS writer
The pswrite device outputs postscript. This device is now deprecated and should only be used when level 1 PostScript output is required.

Details of Ghostscript output devices (9.01)

 

ここで epswrite の時を参考にすると、例えばコマンドは以下のようになる。

> gswin32c -q -sDEVICE=eps2write -sOutputFile="img-outline.eps" -dNOPAUSE -dBATCH -r20016 "img.pdf"

このようにして eps2write を使えば例のロゴ TikZ ソースで VMerror が起こることはなくなるが、手放しで喜んでよいかというとそうではない。

寺田さんの実験によると、gs9.15 の場合に次のような挙動が生じることが分かった。

  • eps2write で生成される EPS は PS Level 2 or 3 であり、この場合出力される EPS のソースにバイナリが含まれることがある。
  • eps2write で EPS 化する際は、小さなパーツはアウトライン化せずビットマップ化する。

順序は逆だが初めに 2 つ目の件から。

「小さなパーツはアウトライン化せずビットマップ化する」という挙動の結果、例のロゴ TikZ ソースは無理に“不完全アウトライン化”されるのではなく、初めからアウトライン化をあきらめて全体がビットマップ化される。この結果、高解像度指定 -r20016 でも処理できる一方で

フォントサイズの小さな文字はアウトライン化されずビットマップ化される

という問題が生じる。「小さな」とはいっても通常の TeX 文書で使う程度のサイズの文字はすべてそれに該当し*5、TeX2img の使用用途のうちで結構大きな部分を占めるであろう「TeX の数式を IllustratorInkscape にアウトライン化して貼り込む」という用途では致命的である。

しかしありがたいことに、gs9.15 以上では eps2write に -dNoOutputFonts オプションが新たに増設された。これを用いると、フォントを強制的にアウトライン化することができる(gs9.14 にはこのオプションがないため注意が必要)。

The pdfwrite and ps2write (and related) devices can now be forced to "flatten" glyphs into "basic" marking operations (rather than writing fonts to the output), by giving the -dNoOutputFonts command line option (defaults to "false")

History of Ghostscript versions 9.n (9.15)

 

次に 1 つ目の件*6

「EPS のソースにバイナリが含まれる」ことにより、余白を付与する目的で EPS ソースの BoundingBox 情報を書き換える際などに注意する必要が生じた。PS level 1 の場合ではテキストエディタで簡単に書き換えることができたが、PS level 2 or 3 ではそうはいかない。寺田さんによると eps2write により生成される EPS ソースのヘッダ部はテキストだが、途中から LZW 圧縮バイナリになっているようだ。

追記:2014-10-18 この件に関連したバグが、 gs9.14 についてであるが報告されているようである。

 

続く

*1:短めのソースで分かりやすく、かつ Ghostscript のエラーが発生しうる例として適切だと考えたという後付けの理由は置いておき、そもそも epswrite の不具合と今回の TeX2img 改修の必要性を認識させられた最初の例だったからである。

*2:ImageMagick以前の記事で述べた通り勝手にビットマップ化するので、今回の例としての利用に適した。

*3:探してみると、ページがかなり古そうな説明もあったが、今回のエラーを考えるとこちらの記事は上記のバグを考慮していないともいえる。

*4:廃止された pswrite や epswrite は PostScript level 1 を出力できた。ということは今回の gs9.15 リリースにより、Ghostscript は level 1 の出力サポートを完全に取りやめたことになる!

*5:jsarticle の [30pt] クラスオプションを付けるくらい巨大文字を使うとアウトライン化されるらしい。

*6:こちらは僕 Acetaminophen の理解を超えた…