Rのポストスクリプト画像(eps・psファイル)出力

Rでeps形式の画像を出力する際のあれこれ。
とりあえずは自分用のメモのレベルです。
万一参考になればということで公開していますが。
期待せずご覧ください。

忙しい人のためのまとめ

ポストスクリプトデバイスはpostscriptで開きます。
file引数はファイル名(epsまたはps)。
widthheightはインチ単位のカンバスサイズです。
paperは用紙サイズで"special"固定。
horizontalFALSE固定で運用します。
でないと、文書ファイルに貼るときに図が回転しちゃったり。
描画範囲の設定がおかしくなったりするかもしれません。
(ケースバイケースなので、問題ないこともあります。)
出力されたepsには、フォントは埋め込まれていません。
フォントの埋め込みにはembedFonts関数を使用します。
ただしGhostscriptのインストールと環境変数設定が必要です。

eps画像の基本

さて、ここからもうちょい詳しい解説。
まずはそもそもepsってなんぞな、というトコロを簡単に。
ご存知のかたは読み飛ばしてください。

epsとベクタ画像

epsは"Encapsulated PostScript"の略。
もともとpostscript(ps)という形式の画像形式があり。
それをちょっと拡張したのがeps形式です。
epsとpsはどこが違うかは、あまり気にする必要なし。
Rのpostscript関数はどちらも保存できるので。
自分の用途において使いやすいほうを選べばOKです。
で、ポストスクリプトでグラフを保存する利点。
それはベクタ形式(ベクトル形式)であること。
ベクタ画像は、図を構成するオブジェクト情報のあつまりなので。
拡大・縮小しても画質が劣化しません。
一方、身近なpng・gif・tiffなどはラスタ形式の画像です。
ラスタ形式において、画像は色点の集合として描画されているため。
縮小してるうちに1ピクセル未満になった点や線が消えたり。
拡大したら境界線がぼやけたり。
学術データのグラフには致命的な現象が起こります。
ましてやjpgは、写真用途に最適化された圧縮形式で。
基本的にデータサイズを抑えるための圧縮がかかるし。
その結果として、近傍領域がティザ状ににじむ傾向がある。
そんな形式でグラフを保存するとか、ありえません。
散布図の点がにじんでさらに散布してたら、笑うでしょ?
ということで、一般的には聞き慣れない画像形式でしょうが。
グラフデータの保存にはベクタ画像が向いていて。
そのなかでは身近な形式がポストスクリプトなのです。

ベクタ形式の画像ファイル

もちろんベクタ画像を扱える形式はeps・ps以外ではなく。
ざっと思いつくところでは、
  • eps (Encapsulated PostScript)
  • ps (PostScript)
  • svg (Scalar Vector Graphics)
  • wmf (Windows Metafile)
  • pdf (Portable Document Format)
  • ai (Adobe Illustrator)
などがあります。
この選択肢をみると、
「お、なじみのあるpdfがあるからコレでいいやん」
と思うのがフツーは人情です。
現にあたしも、初期にはpdfでグラフを出力していました。
ただ、pdfは「完成したひとつづりの文書」を単位とする設計なので。
「パーツとしての個別の図」の保存には、細かいトコで不便です。
(具体的には用紙や描画領域の扱いなど。)
またai形式は、たしかにベクタ画像の一種ではありますが。
これはAdobeがIllustrator用に策定した独自の形式。
あくまで「イラレのセーブデータ」です。
仕様も非公開なうえ、頻繁に改定されるので。
今回の用途には向いていません。
他のドロー系ソフト(inkscapeなど)用の形式も同様です。
その他のベクタ形式にも、もちろん長所短所ありますが。
svgやwmfなどは、Rから直で出力できるので。
epsよりもそちらがよければ、ヘルプを参照してください。
(wmfはWindows版のみ。)
使い方は本稿で説明するepsとほぼ変わりません。
あたしの場合、文章の執筆は兎角LaTeXなので。
標準LaTeXで親和性の高いepsを愛用。
ちなみにポストスクリプト形式は、印刷業では業界標準。
Windows環境では、対応ソフトが少なくて不便かもしれませんが。
LinuxやMacではデフォルトの画像閲覧ソフトで開けるし。
昔よりも身近な存在になってきてる気がします。

Rとeps画像

Rにおけるeps画像の取り扱いについて説明します。

postscript関数

Rにおいて、画像ファイル出力の基本は
  1. デバイスを開く
  2. 描画処理を行なう
  3. デバイスを閉じる
の3段階。
「デバイスを開く」のが、カンバスを広げることに該当し。
その後「閉じる」までの描画がカンバス上になされます。
なので、最初に用意したカンバスで画像形式が決まり。
カンバスとしてpngデバイスを開けばpng画像が。
psデバイスを開けばポストスクリプト画像が保存される、と。
Rにおいてps・epsデバイスを開くのはpostscript関数。
これを使って、ファイル名を.epsないし.psの拡張子にし。
あとはフツーに描画処理をすればOKです。
dev.offで閉じるまではコネクションが開きっぱなしなので。
描画が終わったらとっととデバイスを閉じる。
これも他の形式での画像出力と変わりませんね。

file引数

file引数は出力する画像のファイル名です。
他の画像形式と同様、連番ファイル名なども使えます。

width・height引数

次に画像のサイズを決めるwidthheight
これらは正確には「描画領域のサイズ」を決めています。
またpostscriptは、サイズをインチで指定します。
ピクセル単位だと勘違いして640×480などとすると、
横640インチ×縦480インチ(16m×12m)
というバカでかいサイズを指定してることになるので注意。
日本人にはなじみの薄いインチという単位ですが。
Rのデフォルトの描画ウィンドウ(※)が7インチ四方なので。
(※ 外部デバイスを開かずにplotすると出るウィンドウ)
まずはそのぐらいのサイズで様子をみるといいでしょう。
ちなみに1インチは25.4ミリです。
まあ正直、ここでのサイズ指定はそれほど重要ではないです。
出力したepsファイルは、他の原稿に貼り込んだりして使うわけで。
その際、原稿の幅にあわせて拡大・縮小するはずです。
よって、元の画像が単体で何インチ四方かは関係ないですし。
ベクタ画像なので、引き伸ばしても画質の劣化はありません。
ただし、文字や線の太さはインチやポイント等の絶対値で管理されており。
描画領域を変えても、このサイズは連動しません。
たとえばwidthheightを増すと、描画領域だけが大きくなり。
画像全体に対して、文字や線は相対的に小さく・細くなります。
よってwidthheightの指定は、
この値にしなければならない
という決まりはないですが。
相対的な文字や線のあんばいをみて調整しつつ。
ほどほどのサイズにしておきましょう。
(わたしの場合、一枚モノの図ではたいがい10以下です。)

paper引数

widthheightで「描画領域」のサイズを決めましたが。
epsには、それとは別に「用紙」のサイズというものがあります。
これを指定するのがpaper引数です。
この用紙サイズは、正直ほとんど意味がありません。
paperのサイズは、実際の印刷時の用紙とは無関係ですし。
描画内容は描画領域という「小箱」のなかに描かれるので。
描画領域部分だけを切り取ってみれば、中身は変わらないからです。
たとえばイラストを描くとしましょう。
10センチ四方の枠を引いて、そのなかに絵を描くことにします。
この枠が描画領域にあたります。
この枠を基準に描けば、用紙全体の大きさは関係ないですよね?
用紙がA4でもB5でも、そのなかの10センチ四方の部分が内容であって。
余白になる領域の多さが変わるだけです。
枠の外を切り取ってしまえば、できあがるものは変わりません。
ということで、paperには"a4"とか"letter"とか選べますが。
サイズの観点からは、どれを選ぶべきということはありません。
…が、ここでちょっと別の問題がありまして。
paper"special"を指定するようにしてください。
"special"というのは
描画領域の縦横にぴったし合った用紙サイズ
という意味です。
たとえばwidth = 6, height = 4なら、用紙も6×4インチになります。
「その描画内容に特注サイズの用紙」という意味での"special"ですね。
これはなぜかというと、R側のバグにより、
"special"以外だと、できたeps画像の座標情報が狂うことがある
からです。
具体的には、LaTeXに貼ったとき天地が逆転してしまいます。
他のソフトに貼り込む場合にも、問題が生じるかもしれません。
たとえばA4は横210mm・縦297mmの用紙なので、
paper = "a4"
とだけ指定するのは、
paper = "special", width = 210/25.4, height = 297/25.4
と指定するのと、基本的に同じはずですよね?
(25.4で割るのはミリからインチへの変換。)
実際、eps閲覧ソフトによっては、両者は同じにみえることもあります。
しかし内部的には「A4」と「210×297の特注サイズ」は違うものらしく。
前者の場合のみ、
LaTeXに取り込んでみたらことごとく逆さま
という謎の現象が起こり得ます。
正直、理由はよくわからないですし。
paper"special"以外にしても、問題ないこともあります。
また、おそらくこの挙動は仕様というよりバグなので。
今後のRのアップデートで解消されるかもしれません。
とはいえ、どのみち描画領域外に余白をとる意味はないわけで。
用紙サイズはつねに"special"で描画領域にフィットさせればOKです。

horizontal引数

もうひとつ、余計な誤解を生むhorizontalという引数があります。
horizontalは「水平」なので、ちょっとカンのよいひとは、
「ははあ、ヨコ長の図を出力するときに使う引数だな」
とか思ってしまうかもしれません。
しかし実際には違います。
horizontal引数は
90°回転した状態で図を出力する
という意味をもつ論理値です。
実際にヨコ長の図をつくって試してみましょう。
たとえば6×4のヨコ長グラフを出力したいとします。
その場合、たんにwidthheight
width = 6, height = 4
と指定するだけでよいのです。
horizontalFALSEにしておきます。

horizontal = FALSE, width = 6, height = 4
しかしここで、変に気を利かせて
「ヨコ長のグラフなんだからhorizontal = TRUEだろう」
とかやると、おかしなことになります。

horizontal = TRUE, width = 6, height = 4
先に書いたとおり、horizontalTRUEにすると図が90°回転します。
また、加えてwidthheightも自動的に逆転されます。
結果、できあがる画像のファイルとしての横は4インチ、縦は6インチ。
画像ファイルとしてはタテ長になっています。
ただしいうまでもなく、中身の描画が90°回転していますので。
描画内容にとっては、タテが幅、ヨコが高さ。
90°首をかしげてみると、ヨコ長の図になっています。
ようするにhorizontal引数は、
  • 中身を90°回転させて描画する
  • その際、もとの指定のwidthheightを逆転する
  • 結果、図の内容的にはもと通りの縦横比が保たれる
というややこしいことをやってくれるワケです。
論文等で、幅広の図表を回転してページにおさめることがありますよね。
ようはあのノリです。
ただ、それにしてもこの機能は使う必要がありません。
だって、図の回転なんぞ原稿作成時にすればいいわけで。
個々のパーツとしての図の段階で、回転しとく意味はないからです。
1つひとつの図は、全部フツーに正立で準備しておいて。
原稿を書く段階で、ページにおさまらなければ回転すればよい。
なので、horizontalFALSE固定でよいと思います。

和文フォントの扱い

epsにおける和文の使用とフォント埋め込みについて説明します。

epsのフォント問題

epsにおいて、画像内の文字はテキスト情報として保持されます。
これにより、拡大・縮小しても文字がつぶれる心配は消えますが。
(単純に小さすぎて読めないというケースはべつとして。)
そのかわり、文字化けなどの新たな問題の可能性が浮上します。
Rの画像描画は、たいがいデバイスの開始時にフォントを指定し。
その画像内では、とくに変更しなければそのフォントが使われます。
postscript関数の場合、フォント設定を担うのはfamily引数。
前節ではこの引数についてはとりあげませんでしたが。
デフォルトで"Helvetica"が指定されています。
そこで問題となるのが、和文テキストの扱い。
Helveticaは和文のフォントを含んでいませんので。
軸ラベルや凡例に和文を使いたくても、そのままでは表示できません。
たとえば、標準の描画ウィンドウにおいて
x11(width = 3, height = 3)
par(mar = c(3.5, 3.5, 0.5, 0.5), mgp=c(2.5, 1, 0))
plot(0, 0, xlab = "x軸", ylab = "y軸", type = "n", bty = "l")
text(0, 0, "和文ためし")
などとすると、問題なく日本語が表示できますが。
epsデバイスにおなじ描画をすると、ドットに化けてしまいます。

描画ウィンドウでの和文テキスト表示

epsファイルでの和文表示エラー
このeps画像の表示は、正確には
ファイル内のテキスト情報は正しく出力・保存されているが、
表示すべき文字がHelveticaというフォント内に存在しない
ために、和文部分がドットに化けている状態です。
「『軸』っていわれてもHelveticaにそんな字ねーぜ」
というわけですね。
そこで、eps画像で和文を正しく表示するためには、
デバイス開始時に和文を含むフォントを指定する
という必要があります。
ではまず、選択肢を知りましょう。
postscriptで使えるフォントは以下のように確認できます。
> names(postscriptFonts())
 [1] "serif"          "sans"               "mono"                
 [4] "AvantGarde"     "Bookman"            "Courier"             
 [7] "Helvetica"      "Helvetica-Narrow"   "NewCenturySchoolbook"
[10] "Palatino"       "Times"              "URWGothic"           
[13] "URWBookman"     "NimbusMon"          "NimbusSan"           
[16] "URWHelvetica"   "NimbusSanCond"      "CenturySch"          
[19] "URWPalladio"    "NimbusRom"          "URWTimes"            
[22] "ArialMT"        "ComputerModern"     "ComputerModernItalic"
[25] "Japan1"         "Japan1HeiMin"       "Japan1GothicBBB"     
[28] "Japan1Ryumin"   "Korea1"             "Korea1deb"           
[31] "CNS1"           "GB1"
これらがpostscriptfamily引数に指定できるフォントの一覧です。
このリストを眺めるとわかりますが。
ありがたいことに、最近のRには和文フォントがもとから入っています。
"Japan1"ではじまるフォントファミリーですね。
それぞれの実体はpostscriptFontsのヘルプにあるとおり、
  • Japan1 ... 平成角ゴシック(HeiseiKakuGo-W5)
  • Japan1HeiMin ... 平成明朝(HeiseiMin-W3)
  • Japan1GothicBBB ... 中ゴシックBBB(GothicBBB-Medium)
  • Japan1Ryumin ... リュウミンライト(Ryumin-Light)
となります。

postscriptで使用可能な和文フォントファミリ
一般的に、グラフにはサンセリフ体を使用するのが通例。
なので"Japan1""Japan1GothicBBB"が選択肢となります。
いずれにしても、選んだフォントをfamily引数に指定してやり。
あとは普通にグラフ出力をすればOKです。
postscript("test.eps", paper = "special", horizontal = FALSE,
  width = 3, height = 3, family = "Japan1GothicBBB")
par(mar = c(3.5, 3.5, 0.5, 0.5), mgp=c(2.5, 1, 0))
plot(0, 0, xlab = "x軸", ylab = "y軸", type = "n", bty = "l")
text(0, 0, "和文ためし")
dev.off()

family = "Japan1GothicBBB"を指定したeps画像
ちなみに、できあがったepsをよーくみるとわかりますが。
軸目盛の数字などもほんの少し丸っこくなっています。
これは半角英数も含めて文字がすべてJapan1GothicBBBになっているためです。
(全角になっているわけではありません。)
このようにfamily引数に指定したフォントは、一括で適用されます。
  • 部分によって違うフォントを使う
  • 途中で和文フォントを切り替える
ぐらいのことは、できてもバチはあたらないと思うんですが。
いまのところわたしはそういう方法を知りません。
もし知ってるひとがいたら教えてください。
postscriptFontsのヘルプによると
Once a font family is in use it cannot be changed.
'In use' means that it has been specified via a 'family' or
'fonts' argument to an invocation of the same graphics device
already in the R session.
ということのようです。

フォントの埋め込み

Rのpostscript関数は、フォントを埋め込みません。
上記のとおり、familyの指定でeps画像上に和文を表示できましたが。
これはあくまで
Japan1GothicBBBというフォントを使って表示してくださいな」
という注意書きをepsファイルにつけてあげてるだけなので。
閲覧環境にそもそもそのフォントがなければ、やはり表示できません。
まあ平成やゴシックBBB・リュウミンは、標準的な和文フォントなので。
日本語環境であればまず入っているとは思いますが。
英語版のOSなどを使っていると、未インストールかもしれません。
(そんな環境で使う図には、そもそも和文は使わないでしょうが。)
この問題の回避には、epsにフォントの埋め込みを行ないます。
さっきの例でいえば
Japan1GothicBBBってのはコレだからどうぞ使ってよ」
と、フォントが付録でついた状態にするわけですね。
Rにおいてこれをするのは、embedFonts関数。
フォントの埋め込みをしたいepsファイルを指定してやると。
内部で使われているフォントを画像ファイルに埋め込んでくれます。
embedFonts("test.eps")
デフォルトでは指定した画像ファイルに上書き保存されますが。
別のepsファイルとして出力することも可能です。
ただし、embedFontsの利用にはGhostscriptというソフトが必要です。
というか、embedFontsはGhostscriptを呼び出しているだけにすぎず。
実際の埋め込み作業はGhostscriptがやっています。
Ghostscriptのインストールは、Linux系の環境なら単純にaptで。
$ sudo apt-get install ghostscript
$ gs -v
GPL Ghostscript 9.10 (2013-08-30)
Copyright (C) 2013 Artifex Software, Inc.  All rights reserved.
Windowsでは東大数理科学研究科 大島研のミラーなどから取得してください。
Linuxでは、インストールでgsコマンドがすぐ使えるので。
それだけでembedFontsも使えるようになります。
Windows環境でも、運がよければRがGSを自動でみつけてくれますが。
場合によっては、Ghostscriptの場所を明示的に示す必要があります。
たとえば32bit版Ghostscript 8.64をWinXPに入れたとすると。
実行形式のGS本体は
C:\gs\gs8.64\bin\gswin32c.exe
といったカンジになります。
これをRターミナル内でSys.setenvを用いて
Sys.setenv("R_GSCMD" = "C:\gs\gs8.64\bin\gswin32c.exe")
のようにR_GSCMDという環境変数にセットしてやるか。
あるいはマイコンピュータから同名の環境変数に設定してください。
([システム情報の表示]→[詳細設定]→[環境変数]→[ユーザー環境変数])
もちろん、フォントを埋め込むとそのぶんファイルサイズは増えます。
先の例の「和文ためし」のepsファイルは、もとは5.1kBでしたが。
embedFontsのあとでは101.1kBになっていました。
ちなみにembedFontsは、option引数からGSへの制御ができて。
"-dSubsetFonts=true"をつけると、サブセット埋め込み。
"-dEmbedAllFonts=true"だとフルセットの埋め込みになるハズですが。
わたしの環境では、どちらも埋め込み後のサイズは変わりませんでした。
また、Ghostscriptは"special"の用紙サイズを許容しないようで。
embetFontsすると、epsのサイズが"a4"になって余白が生じます。
そのため実用的には、ここでもoption引数を利用して
embedFonts("test.eps",
  options="-dDEVICEWIDTHPOINTS=216 -dDEVICEHEIGHTPOINTS=216")
のように、画像サイズをGSに伝えるとよいでしょう。
postscriptのデフォルトeps出力は72dpiなので、指定するサイズは
epsの縦横のインチ × 72
になります。
(上記の例では3インチ四方なので216です。)