はじめに
みなさん、EPSファイル、使ってますか?
近年、(La)TeXの文書作成においては、「EPSファイルを使うな」というマナーが確立しています。 マナーにはデフォルトで抗っていくということで、この記事では、現代的なLaTeX環境におけるEPSファイルの可能性を探りたいと思います。
もちろん、「EPSファイルを使うな」は、実際にはマナーではありません。 技術的な根拠があるベストプラクティスのひとつです。 したがって、この記事の真の目的は、以下の2点だといえます。
- ベストプラクティスをマナーへと貶めないために技術的な背景を知る
- 条件があえばEPSファイルにもまだまだ使い道があることを示す
コンピュータサイエンスには、"All problems in computer science can be solved by another level of indirection"(「コンピュータ科学のあらゆる問題は、別の階層を挟むことで解決できる」)という格言があります (David WheelerまたはButler Lampson)。 上記の2では、MetaPostというアプリケーションをindirectionとして、EPSファイルをTeXが直接扱える形式として利用する道を示したいと思います。
この記事について
この記事は、TeX Conf 2018にてLT発表した「2018年でもTeXでEPSを使う」で話す予定だった内容のメモを文章として清書し、「TeX & LaTeX Advent Calendar 2018」の4日目の記事としてまとめたものです。
こうして文章にしてみると、この記事を原稿として読むだけでも5分で終わらなそうな話だった。。
目次
- はじめに
- 目次
- (La)TeX文書においてEPSはどう扱われるか
- 「EPSファイルを使うな」の意味
- pdfTeXやdvipdfmxがネイティブに扱えるEPS
- ここまでのまとめ
- 一般のEPSをPurified EPSに変換する
- Purified EPSをTeXに書き換える
- まとめ
(La)TeX文書においてEPSはどう扱われるか
LaTeXで画像を挿入したいとき、もっとも歴史があるのは、EPSファイルを\includegraphics
する方法でしょう。
EPS形式で用意した画像を取り込むという手法は、LaTeX以前からTeXのエコシステムでは一般的だったようで、plain TeXにもepsf.sty
という仕組みがあります。
epsf.sty
のマニュアルでは開発履歴も触れられており、それを見ると、かなり初期のころにクヌース自身が手を入れていたようです(参考)。
ここで注意が必要なんですが、TeXそのものには、一般のEPSファイルを解釈する仕組みがありません。
\includegraphics
を提供するgraphicx
パッケージやepsf.styの
仕事は、「最終的な印刷を担うデバイス」へとEPSファイルを丸投げするだけです。
では、「最終的な印刷を担うデバイス」は、どうやって丸投げされるEPSファイルを画像にする、つまり、紙に印刷したり、PDFに変換したり、あるいはベクター画像としてレンダリングしたりするのでしょうか? そもそも、「最終的な印刷を担うデバイス」とは何なのでしょうか?
昔ながらのTeXであれば、原稿はDVIへとコンパイルされます。したがって、最終的な印刷を担うデバイスは、「DVIウェア」などと総称されるソフトウェアです。
DVIウェアがdvipsであれば、原稿で指定されていたEPSファイルの中身は、出力されるPostScriptにそのまま埋め込まれます。 それをGhostscriptやAdobe Distiller、あるいはPostScriptプリンタに渡すことで、それらのPostScript処理系が最終的な画像を生成します。
DVIウェアがdvipdfmxであれば、EPSファイルを最終的な画像にするのは、一般にはGhostscriptというPostScript処理系の仕事です。
これは、dvipdfmxの設定ファイル(dvipdfmx.cfg
)に、「一般的なEPSファイルがあったらGhostscriptを起動してPDFに変換する」という内容の処理がデフォルトで記述されているからです。
DVIウェアには、XdviやWindvi、あるいはdviout、あるいはEvinceのような、DVIをそのまま画面上でプレビューするものも多々あります。 これらのほとんども、PostScript処理系を自前でもっているわけではなく、外部のGhostscriptを呼び出してEPSファイルを何らかの形式の画像に変換して描画しています。
いずれの場合も、「一般的なEPSファイルを最終的な画像にするにはPostScript処理系が必要」という点がポイントです。 これは、EPSファイルが、そもそもは一枚絵のために規定されたPostScriptのコーディング規約(DSC、Document Structring Conventionといいます)に基づくものであることを考えれば、当然だといえます。 PostScript処理系がないと、TeXのエコシステムは一般のEPSファイルを画像として再現できないというわけです。
そして、ここでいうPostScript処理系は、事実上Ghostscript一択です。 プロプラエタリなソフトウェアを利用せずに、PostScriptをベースとした技術を利用しようと思ったら、現状ではGhostscriptに頼らざるをえず、TeXもその例外ではないのです。
「EPSファイルを使うな」の意味
前節で見たように、PDFが出力となるようなTeXのエコシステムでは、一般にはTeXのソースでEPSファイルが出てくるたびに外部のPostScript処理系(事実上Ghostscriptであることが多い)が走ります。 常識的に考えてこれは効率が悪い。
そもそも、現在もっとも広く利用されているTeXの組版エンジンであるpdfTeXは、ネイティブでPDFの取り込みに対応しています(参考)。 日本語環境ではdvipdfmxがよく利用されていますが、これもネイティブでPDFの取り込みに対応しています(参考)。 そのため、EPSファイルとして用意した画像をそのつどGhostscriptを呼び出して処理するのは完全な無駄であり、最初からGhostscriptでPDFに変換しておけば十分なのです。
これが、いま「EPSファイルを(La)TeX文書に取り込むときの画像に使うな」という主張の背景にある事情です。
pdfTeXやdvipdfmxがネイティブに扱えるEPS
EPSファイルが現代的なTeXのエコシステムにおいて扱いにくい代物であるのは紛れもない現実です。 PDFならTeXエンジンがネイティブで扱えるのに、そこでEPSファイルを使い続ければ、Ghostscriptという厄介なアプリケーションが作業フローに関与してしまいます。
とはいえ、長らくTeXにおけるデフォルトの画像形式であったEPSファイルを利用しなければならないドキュメントもあるでしょう。 アプリケーションがEPSしか出力してくれない場合もあります。 あらかじめGhostscriptでPDFにしておけばいいとも言えますが、管理しているファイルがPDFでは扱いにくいという事情もありえます。 なにしろEPSの中身はPostScriptであり、PostScriptを知っていればテキストエディタで直接編集できます。 テキストファイルなので、バージョン管理にも都合がいい。
はたして、Ghostscriptを起動せず、TeXのエコシステムでPostScriptを直接処理することは不可能なのでしょうか?
実は、TeXのエコシステムで直接処理できるようなEPSファイルがあります。 そのようなEPSファイルであれば、画像としてTeXの文書中で取り込んでも、処理中にGhostscriptが呼び出されることはありません。 EPSファイル中のPostScriptを、pdfTeXやdvipdfmxが、直接解釈して画像にしてくれるのです。
そのようなEPSファイルは、Purified EPSと呼ばれています。純化されたEPSというわけです。 具体的には、MetaPostというアプリケーションによって生成されたEPSだけがPurified EPSになります(参考)。
重要かつややこしい箇所なので、MetaPostとPurified EPSについてもう少し詳しく見ておきましょう。
MetaPostは、ベクター画像を生成するためのプログラミング言語です。 MetaPostについては、2018年のTeX & LaTeXアドベントカレンダーの北川さんの記事(参照)がとても参考になります。 たとえば、ここにラムダノートという出版社のロゴマークがありますが…
このロゴは、こんな感じのMetaPostソースで再現できます。
prologues := 1; beginfig(1); drawoptions (withcolor (0.6,0.6,0.6)); linecap := butt; linejoin := mitered; fill (26.867201,14.046900)..controls (26.914101,11.453100) and (27.386700,8.511720)..(26.027300,6.519530) ..controls (23.898399,3.402340) and (19.093800,1.851560)..(15.031300,1.777340) ..controls (8.378910,1.660160) and (2.886720,6.957030)..(2.765630,13.613300) ..controls (2.644530,20.269501) and (7.945310,25.761700)..(14.597700,25.878901) ..controls (21.253901,26.000000) and (26.746099,20.703100)..(26.867201,14.046900) --cycle; drawoptions (withcolor (0,0,0)); fill (111.976997,17.812500)..controls (110.969002,18.261700) and (109.785004,17.804701)..(109.339996,16.796900) --(109.188004,16.445299)..controls (109.188004,16.445299) and (109.140999,16.339800)..(109.046997,16.132799) ..controls (108.941002,15.929700) and (108.875000,15.621100)..(108.612999,15.234400) (つづく)
なお、MetaPostによく似た名前のアプリケーションとして、クヌースが作ったフォント開発のための言語および処理系のMetaFontがあります。 名前がよく似ていることからわかるとおり、MetaPostはMetaFontに影響を受けていますが、もっとずっと新しい時代に別の人が作った別のアプリケーションです。 実際、MetaFontはPostScriptに埋め込めるビットマップフォントを生成するのに対し、MetaPostはPostScriptをEPSファイルとして生成します(MetaPostのPostはPostScriptのPostです)。
MetaPostの処理系は mpost
というコマンドです。
上記のMetaPostのソースファイルlogo.mp
をmpost
コマンドに指定して次のように実行すると、EPSファイルが生成されます。
$ mpost logo.mp This is MetaPost, version 2.00 (TeX Live 2018) (kpathsea version 6.3.0) (/usr/local/texlive/2018/texmf-dist/metapost/base/mpost.mp (/usr/local/texlive/2018/texmf-dist/metapost/base/plain.mp Preloading the plain mem file, version 1.005) ) (./logo.mp [1{psfonts.map}] ) 1 output file written: logo.1 Transcript written on logo.log. $ ls logo.mp logo.1 logo.log
拡張子が.eps
のファイルがありませんが、logo.1
というのがMetaPostによって生成されたEPSファイルです。
中身を見ると、以下のように、お馴染みのPostScriptコマンドとPostScriptコメント(%%
のスタイルは正確にはDSCコメントといいます)がならんだEPSファイルであることがわかります。
%!PS-Adobe-3.0 EPSF-3.0 %%BoundingBox: 0 -1 114 94 %%HiResBoundingBox: 0.00034 -0.00009 113.16754 93.72906 %%Creator: MetaPost 2.00 %%CreationDate: 2018.12.04:1520 %%Pages: 1 %%BeginProlog %%EndProlog %%Page: 1 1 0.6 0.6 0.6 setrgbcolor newpath 26.8672 14.0469 moveto 26.91411 11.4531 27.3867 8.51172 26.0273 6.51953 curveto 23.89839 3.40234 19.0938 1.85156 15.0313 1.77734 curveto 8.3789 1.66016 2.88672 6.95703 2.76563 13.6133 curveto 2.64453 20.2695 7.94531 25.7617 14.5977 25.8789 curveto 21.2539 26 26.7461 20.7031 26.8672 14.0469 curveto closepath fill 0 0 0 setrgbcolor newpath 111.97699 17.8125 moveto 110.96901 18.2617 109.785 17.8047 109.34 16.7969 curveto 109.188 16.4453 lineto 109.188 16.4453 109.141 16.3398 109.047 16.1328 curveto (つづく)
この「MetaPostが生成したEPS」こそが、Purified EPSの正体です。 残念ながらPurified EPSの明確な仕様はよくわからないのですが、MetaPostのマニュアルで「Purified EPS」と名づけられていることから、「MetaPostが生成したEPSがPurified EPS」だと考えて問題ないでしょう。
PostScriptとして見れば、Purified EPSはフルのEPSから機能を削り取ったDSCであり、EPSファイルのサブセットといえます。 したがって、通常のEPSと同じように扱えます。
さらに特筆すべき特徴として、Purified EPSは、pdfTeX(およびLuaTeX)やdvipdfmxに完全な処理系が実装されています。 これが何を意味するかというと、「TeXエンジンにPostScriptの処理系が実装されていないのでEPSファイルは使うべきではない」というベストプラクティスが、Purified EPSには適用されないのです。 Purified EPSは、むしろTeXのエコシステムで積極的に使ってよい形式であるとさえいえます。
ちなみに、dvipdfmxに至っては、その名称に反し、(DVIではなく)生のPurified EPSを受け取ってPDFに変換するオプションまで用意されています。
$ dvipdfmx -M logo.1 # dvipdfmxに、DVIファイルではなくEPSファイル(ただしPurified)を渡せる! logo.1 -> logo.1.pdf 4105 bytes written
ここまでのまとめ
ここまでの話をまとめましょう。
- 一般のEPSファイルは、完全なPostScriptの処理系を持たないTeXのエコシステムでは直接扱えないので、必要な場合には外部アプリケーションであるGhostscriptをそのつど起動して処理を投げている
- あらかじめEPSファイルをPDFにしておけば、現代的なTeXのエコシステムはPDFを直接扱えるので、そのつどGhostscriptが起動されることはなくなる(これが現在のTeXワークフローの主流)
- しかし、テキストファイルであるEPSファイルには、PDFにない利点もある
- ところで、MetaPostが生成するEPS(Purified EPS)でれば、現代的なTeXのエコシステムに完全な処理系が内蔵されている
このように状況を整理すると、そのつどGhostscriptが起動されるというハンデを負わずに、テキストファイルとしての扱いやすさだけを享受できる、そんな作業フローの可能性があることに気づきます。 そう、一般のEPSファイルを、可能な場合にはPurified EPSに変換してしまえばいいのです。
一般のEPSをPurified EPSに変換する
ずばり、purifyepsというツールがあります。その名のとおり、EPSファイルをpurifyしてくれるユーティリティです。 TeX Liveに入っているので、ふつうにTeX環境をインストールしていれば利用可能です(参考)。
このpurifyeps、実態は、pstoeditとmpostという別々のツールを順番に呼び出すだけのPerlスクリプトです。 前段のpstoeditが、EPSファイルをMetaPostのソースファイルに変換してくれます。 後段のmpostは、MetaPostの処理系そのものです。 前段のpstoeditで生成されたMetaPostのソースファイルを、後段のmpostにかけて、Purified EPSが出力されるという寸法です。
purifyepsを使うには、pstoeditにフォントマップを指定して、もとのEPSファイルで使われているフォントをmpostが知っているフォント名に置き換えてあげる必要があります。
これはmpost.fmp
として、かなり長大なファイルがpstoeditに付随してくるはずです。
しかし、このデフォルトのmpost.fmp
に登録されていないフォントがEPSファイルで使われていたり、対応するtfmを自分の環境に持っていなかったりすると、変換がコケます。
その場合は、エラーメッセージをもとに、PostScriptフォント名と対応するtfm名からなる行をmpost.fmp
に追記してあげれば、とりあえず通ります。
対応するtfm名は、ぶっちゃけなんでもいいので、とりあえずcmr10でも指定しておいて変換を通してみましょう。
あと、ちょっと罠があって、素のpurifyepsだとGhostscriptが意味不明なエラーを吐き出す可能性があります。
これは、pstoeditでGhostscriptのDELAYBIND
オプションが暗に指定されているからです。
DELAYBIND
は、PostScriptのbind
コマンドの動作をちょっと変えることで、標準ライブラリのコマンド名を上書きしているような場合でも問題なく扱えるようにするためのGhostscript独自の仕掛けです(参考)。
しかし、Adobe Distillerが隠し持っているinternaldict
辞書を操作するsuperexec
というコマンドがsystemdict
にあったのを取り除いたときに、副作用があるので取り除かれてしまいました(参考)。
結果として、Adobe Illustratorなどで生成されたEPSの多くは、素のpstoedit(したがってpurifyeps)で変換しようとすると意味不明なPostScriptエラーを吐き出して終了するわけです。
pstoeditは、DELAYBIND
を無効にするGhostscriptの-dNOBIND
オプションを有効にするために、-nb
というオプションを指定してあげます。
しかし、これをpurifyepsから実現する手段はありません。
そのため、purifyepsのソースでpstoeditを呼び出している箇所に直接-nb
オプションを書き加えるしかなさそうです。
以下のようにpstoeditを呼び出している箇所があるはずなので、そこに-nb
オプションを追記しましょう。
(省略) # Utilize pstoedit to convert from EPS to MetaPost. my $mpfile = $tempbase . ".mp"; executeCommand "pstoedit -ssp -mergetext -f mpost -fontmap \"$fontmap\" \"$infile\" \"$mpfile\""; (省略)
Perlスクリプトなので、パスが通っているディレクトリにあるpurifyepsというファイルを直接いじってしまえば目的は達成されます。
Purified EPSをTeXに書き換える
こうして変換できたPurified EPSは、\includegraphic
で読み込んでも、Ghostscriptが起動することもなくpdfTeX/LuaTeXやdvipdfmxだけでPDFの画像になります。
変換前の一般のEPSファイルを読み込んだときに比べて、爆速で処理が終わります。
ここで満足してもいいんですが、ここでさらに、Purified EPSをTeXソースそのものに変換できないか考えてみましょう。
なんでそんなことを考えるかというと、画像を外部ファイルではなくTeXソースで持っていることには、次のようなメリットが考えられるからです。
- 本文と同じスキームで図中のフォントを扱える(数式も日本語も)
- 図中の長文を組版してもらえる(数式も日本語も)
- 図中で脚注や相互参照や索引が使える
つまり、メリットしかない。 補足しておくと、実はここまでの話はすべて図中のフォントがASCIIであることを前提としていて、EPSファイル中で日本語が使われているとpurifyepsが日本語をアウトライン化してしまうのでした。 もしTeXソースにできてしまえば、日本語が使えるように設定したTeXで処理するだけで図中に日本語を埋め込めます。
幸い、最近のTeXには、PGFという高度なベクター画像のための仕組みがあります。 PGFは、人間が書くときにはTikZという独自のインターフェースを使うのですが、PGFそのものはかなりPostScriptに近いシンタックスであり、いかにも直接変換できそうな雰囲気がします(参考)。 これを利用すれば、Purified EPSをTeXのマクロにできそうです。
同じことを考える人はいるもので、すでにeps2pgfというJava製の変換ツールを作っている人が過去にいました(参考)。 現在はSourceforgeにポストされているもののソースもない状態で、メンテもされてなさそうなんですが、とりあえずダウンロードして使ってみるとそれなりに満足のいく結果が得られました。
$ java -jar eps2pgf.jar logo.1 -o logo.tex
結果として生成されるTeXファイルは、 pgfpicture 環境にPGFのコマンドが並んだこんな状態です。
% Created by Eps2pgf 0.7.0 (build on 2008-08-24) on Tue Dec 04 15:38:00 JST 2018 \begin{pgfpicture} \pgfpathmoveto{\pgfqpoint{0cm}{-0cm}} \pgfpathlineto{\pgfqpoint{3.992cm}{-0cm}} \pgfpathlineto{\pgfqpoint{3.992cm}{3.307cm}} \pgfpathlineto{\pgfqpoint{0cm}{3.307cm}} \pgfpathclose \pgfusepath{clip} \definecolor{eps2pgf_color}{rgb}{0.6,0.6,0.6}\pgfsetstrokecolor{eps2pgf_color}\pgfsetfillcolor{eps2pgf_color} \pgfpathmoveto{\pgfqpoint{0.948cm}{0.496cm}} \pgfpathcurveto{\pgfqpoint{0.949cm}{0.404cm}}{\pgfqpoint{0.966cm}{0.3cm}}{\pgfqpoint{0.918cm}{0.23cm}} \pgfpathcurveto{\pgfqpoint{0.843cm}{0.12cm}}{\pgfqpoint{0.674cm}{0.065cm}}{\pgfqpoint{0.53cm}{0.063cm}} \pgfpathcurveto{\pgfqpoint{0.296cm}{0.059cm}}{\pgfqpoint{0.102cm}{0.245cm}}{\pgfqpoint{0.098cm}{0.48cm}} \pgfpathcurveto{\pgfqpoint{0.093cm}{0.715cm}}{\pgfqpoint{0.28cm}{0.909cm}}{\pgfqpoint{0.515cm}{0.913cm}} \pgfpathcurveto{\pgfqpoint{0.75cm}{0.917cm}}{\pgfqpoint{0.944cm}{0.73cm}}{\pgfqpoint{0.948cm}{0.496cm}} \pgfpathclose \pgfusepath{fill} \definecolor{eps2pgf_color}{rgb}{0,0,0}\pgfsetstrokecolor{eps2pgf_color}\pgfsetfillcolor{eps2pgf_color} \pgfpathmoveto{\pgfqpoint{3.95cm}{0.628cm}} \pgfpathcurveto{\pgfqpoint{3.915cm}{0.644cm}}{\pgfqpoint{3.873cm}{0.628cm}}{\pgfqpoint{3.857cm}{0.593cm}} (つづく)
これはもう完全にTeXなので、uplatexでもpdflatexでも好きなものでPDFにすればいいでしょう。 もちろん、ふつうにTeXソースとして編集できるので、LaTeXのパッケージの機能もフルに使えます。
% Created by Eps2pgf 0.7.0 (build on 2008-08-24) on Tue Dec 04 15:38:00 JST 2018 \begin{pgfpicture} \pgfpathmoveto{\pgfqpoint{0cm}{-0cm}} \pgfpathlineto{\pgfqpoint{4cm}{-0cm}} \pgfpathlineto{\pgfqpoint{4cm}{4.0cm}} \pgfpathlineto{\pgfqpoint{0cm}{4.0cm}} \pgfpathclose \pgfusepath{clip} % 追記 \pgftext[x=2cm,y=3.7cm,rotate=0]{% \fontsize{12}{12}\selectfont\parbox{4cm}{\centering ラムダノート}} \pgftext[x=2.12cm,y=2.5cm,rotate=-65]{% \fontsize{18}{10}\selectfont\scsnowman[hat=true,muffler=red,arms=true]} \pgftext[x=3.73cm,y=0.95cm,rotate=80]{% \fontsize{18}{10}\selectfont\scsnowman[hat=true,muffler=red,arms=true]} % ここまで追記 \definecolor{eps2pgf_color}{rgb}{0.6,0.6,0.6}\pgfsetstrokecolor{eps2pgf_color}\pgfsetfillcolor{eps2pgf_color} \pgfpathmoveto{\pgfqpoint{0.948cm}{0.496cm}} \pgfpathcurveto{\pgfqpoint{0.949cm}{0.404cm}}{\pgfqpoint{0.966cm}{0.3cm}}{\pgfqpoint{0.918cm}{0.23cm}} \pgfpathcurveto{\pgfqpoint{0.843cm}{0.12cm}}{\pgfqpoint{0.674cm}{0.065cm}}{\pgfqpoint{0.53cm}{0.063cm}} \pgfpathcurveto{\pgfqpoint{0.296cm}{0.059cm}}{\pgfqpoint{0.102cm}{0.245cm}}{\pgfqpoint{0.098cm}{0.48cm}} \pgfpathcurveto{\pgfqpoint{0.093cm}{0.715cm}}{\pgfqpoint{0.28cm}{0.909cm}}{\pgfqpoint{0.515cm}{0.913cm}} \pgfpathcurveto{\pgfqpoint{0.75cm}{0.917cm}}{\pgfqpoint{0.944cm}{0.73cm}}{\pgfqpoint{0.948cm}{0.496cm}} \pgfpathclose \pgfusepath{fill} \definecolor{eps2pgf_color}{rgb}{0,0,0}\pgfsetstrokecolor{eps2pgf_color}\pgfsetfillcolor{eps2pgf_color} \pgfpathmoveto{\pgfqpoint{3.95cm}{0.628cm}} \pgfpathcurveto{\pgfqpoint{3.915cm}{0.644cm}}{\pgfqpoint{3.873cm}{0.628cm}}{\pgfqpoint{3.857cm}{0.593cm}} (つづく)
以上で、PostScriptをある程度読めてベジエを手で調整する気力とフォントの位置を微調整する忍耐力がある人にとっては、実に快適なテキストベースの図環境が得られることがわかりました。
まとめ
MetaPost、そのまま作図のためのツールとして使うにはハードルが高いんですが、画像編集ソフトで生成したEPSファイルを本稿の手法でTeXにするための中間形式としては魅力がありますね。
ただし、その変換パスにも、pstoeditという形でGhostscriptが関与します。TeXはGhostscriptから離れては生きていけないのです。
ところでラムダノートという会社では、「古いEPSを活用してドキュメントを作りたい」といったお仕事も、こんな感じでわりと効率よく引き受けることが可能です。 著者や原文のソースにあるメタ情報をフル活用したい、バージョン管理したい、ミスが怖いので人力で変換したくない、といったドキュメント関連のお悩みがありましたら、下記のフォームよりご連絡ください。