Postscript プログラミングメモ

これは小波秀雄が Postscript で図形を描くために書き溜めているメモです.
拙作のライブラリと併用すれば, 簡単な記述で描画を実現できます.


目次:

凡例

10分間 Postscript 入門

ファイルを作成してヘッダを記入,線を引いてみる
手続きを定義する
他のいくつかの基本的な命令

構文

EPS ファイルのヘッダ
スタック操作
repeat ループ
for ループ
破線,点線
他のファイルを読み込む
配列を扱う
グラフィック状態を保存する

文字列の表示

フォント情報の設定
文字列を出力する
アスキー文字以外の文字と記号を出力する
文字列出力の例
その他の欧文フォント
日本語文字列出力
日本語文字列出力のサンプル

役に立つ手続き

ハッチ(斜線による敷き詰め)
クリップ (任意の図形や文字で切り抜いた型の中に描く)
長さの単位を使う
スタックの値を変数に代入する
擬似的なローカル変数を使う
ローカル変数に値を渡す方法
文字列の横幅の中央を指定x座標に合わせる
translate を使って任意の場所に描画
Ghostscript で ps ファイルを png に変換する

凡例


10分間 Postscript 入門

まずは直線を引いたり,円を描くための EPS ファイルを 作成してみます.これで試した後,とにかく早く描画をしたい 人は,小波秀雄作のPostscript ライブラリを利用するとよいでしょう.

ファイルを作成してヘッダを記入,線を引いてみる

  1. エディタで pstest.ps という名前のファイルを開く
  2. 下の内容をファイルに書いてセーブする.これがEPSファイルのヘッダ
    %!PS-Adobe-3.0 EPSF-3.0
    %%BoundingBox: 0 0 400 300
    
  3. pstest.ps を gsview で開いてみると 400 × 300 のサイズの白い長方形が表示される.この画面に描画する.
  4. 線を引くパスを作るためには次のようにする.ペンを最初の点に moveto して,その後線をつないでいく(lineto)というイメージで.
    50 50 moveto
    100 90 lineto
    100 160 lineto
    10 70 lineto
    
  5. パスから,線の連結,閉じた多角形,塗りつぶしの3通りの描画が可能である. 下の説明に従って,3つそれぞれ試してみなさい.

手続きを定義する

変数,手続きを簡単に定義して使うことができる.

他のいくつかの基本的な命令

とりあえず簡単な絵を作りたい人は,ここからPostscript ライブラリ の方へ飛んでみるといいでしょう.


構文

EPS ファイルのヘッダ

EPS(Encapsulated Postscirpt)は,1枚の画像の大きさを決めて出力するタイプの フォーマットで,下のようなヘッダを付けるだけで,EPS になります. showpage は使いません.

%!PS-Adobe-3.0 EPSF-3.0
%%BoundingBox: 0 0 600 600
%%HiResBoundingBox: 0 0 599.5 600.2

3行目は拡張版用,2行目と併用するらしい.あまり使わないみたい.

スタック操作

repeat ループ

指定回数繰り返す

5 {proc} repeat

for ループ

x1 から始めて終了条件 x2 に達するまで dx ずつ増やして繰り返す

x1  dx  x2 {proc} for
変数の順序に注意

円を水平に並べる

50 10 200 {50 10 0 360 arc stroke} for

決まった長さの縦線を横に並べる

x1 dx x2 {y moveto 0 length rlineto stroke} for

決まった長さの横線を縦に並べる

y1 dy y2 {x exch  moveto length 0 rlineto stroke} for

破線,点線

w をオフセットとして,a, b, c, d ピクセルずつの黒白の線を描く

[a b c d ...] w setdash

実線に戻す

[] 0 setdash

他のファイルを読み込む

(foo.ps) run

ある PS ファイル foo.ps があったときに,それを直接 描画に使うのではなく, 別ファイル hoge1.ps , hoge2.ps などから上のように読み込んで使うようにするとよい. dviout だと異なった bmc ファイルになるし, GIMP で加工して書き戻したときにも,原型の方は壊されないですむ.

配列を扱う

複数の変数を配列に取り込む

d c b a 3 array astore →  |d|[c b a]|

配列要素をスタックに展開する

[c b a] aload → |c|b|a|[c b a]|

このとき,トップには元の配列が残されているので, 不要なら pop して捨てる.

上の応用:現在の色を保存しておく

現在の色を変数に保存しておいて,後でまたその色に戻すには, 次のようにするとよい.

currentrgbcolor 3 array astore
/rgb exch def
1 0 0 setrgbcolor % 他の色に設定
... 
...
rgb aload pop setrgbcolor % 元の色に戻す

グラフィック状態を保存する

gsave, grestore を使うと,現在のパス,色などをセーブしておいて,あとで再利用できる.

塗りつぶして輪郭を描く

塗りつぶしと輪郭描きを同一のパスについて行うには次のようにするとよい.

4 setlinewidth
1 0 0 setrgbcolor
50 50 moveto 150 50 lineto 100 130 lineto % パスを生成
gsave  % 上の状態をすべてセーブ.
0.5 setlinewidth
1 1 0 setrgbcolor
fill
grestore % 線の太さ,描画色,パスが呼び戻される. 
closepath stroke

複数回 gsave する

gsave はグラフィック状態をスタックに保存するので, 複数回実行すると,その回数分の状態が記憶される.

1 setlinewidth
1 0 0 setrgbcolor
50 150 moveto
150 150 lineto
100 230 lineto
closepath
gsave
gsave
1 1 0 setrgbcolor
fill
grestore
4 setlinewidth
stroke
grestore
1 1 1 setrgbcolor
0.5 setlinewidth
stroke

文字列の表示

文字列を表示させるためには,次の例のような手続きを必要とします.

フォント情報の設定

フォントの種類を選択する
/Times-Roman findfont
サイズを指定する
10 scalefont setfont
フォント情報を確定する
setfont

上の処理は一度行っておけば,その後の出力は同じ設定のままで行われることになります.

文字列を出力する

(String) show

打ち出される位置について

show によって打ち出される文字列は,左下隅がカレントポイントに置かれます. したがって,moveto などによってカレントポイントが設定されていないと エラーになります.ただし,カレントポイントは文字出力によっても 更新されていきますので,常に moveto を使う必要があるわけではありません.

文字列出力の例

/Times-Roman findfont 10 scalefont setfont
100 200 moveto
(ABC) show

アスキー文字以外の文字と記号を出力する

下のPDFファイル(chartable.pdf) は,Postscript で使われる欧文フォントの一覧です. 表は Times-Roman, Times-Italic 等のフォント名ごとに 作成されています. それぞれの表は 40 〜 377(8進法表記)の整数,(あれば) 対応するアスキー文字,そして該当するフォントそのものが表示されています.

chartable.pdf

含まれているフォントは次のとおりです.

Times 系
/Times-Roman
/Times-Bold
/Times-Italic
/Times-BoldItalic
Helvetica 系
/Helvetica-Oblique
/Helvetica-Bold
/Helvetica-BoldOblique
Courier系
/Courier
/Courier-Oblique
/Courier-Bold
Symbol (ギリシャ文字など)
/Symbol

アスキーコード外に割り当てられた文字を使いたい

文字によってはアスキー文字の中にないものもあります.たとえば 英国のポンドを表す £ のような文字です.それらの文字は アスキーコードの 0x80 から 0xFF までに割り当てています. ただし文字の扱いでは 8進法で使うのが普通で, 0200 から 0377 がその範囲になります. その場合にこの表を参照して使います.

表を見て,文字に 対応する8進数を調べます. £ であれば 243 となりますから, バックスラッシュを付けて次のように記述すればオーケーです.

/Times-Roman findfont 12 scalefont setfont
100 250 moveto
(I have 120\243 in my purse.) show
なお,次のようにすれば16進法を使うことも可能ですが, 8進法を使うときのように文字列に埋め込むことが出来ないので, 使いやすくはありません.
/Times-Roman findfont 12 scalefont setfont
100 200 moveto
(I have 120)show
<A3> show
( in my purse.) show

ギリシャ文字を使いたい

ギリシャ文字は Symbol というフォントを指定すると出力することが出来ます. この場合 アスキーのアルファベットと同じ場所に 割り振られているので,たとえば "a" → "α" のように すれば,特に問題なく利用できます.ただし,数学記号なども Symbol の中に 入っていて,80H (0200) 以上に割り当てられていますので, この表を見る必要のあるケースも出てきます.次は α' を出力する例です.

/Symbol findfont 12 scalefont setfont
(a\242) show

その他の欧文フォント

Ghostscript で使える欧文フォントのサンプルは次にありますので,さらに多様な フォントが必要になったら,参考にしてください.

xfigType1.pdf(表示はこちら)
xfigType1.ps(ダウンロードして調べて下さい)

日本語文字列出力

和文フォントを利用するには,文字コードの問題を押さえておくことが必要です. Postscript で使いやすい文字コードは UTF-8 です. Shift-JIS は,特定の文字で文字化けを起こす現象が知られており, 使うべきではありません.

UTF-8 インストールされていれば,それを使うのがよいでしょう. EUC-JPもまだ使えます.

2つの書体

Windows の Ghostscript では,実質的に2つの書体しか利用できません. 明朝体ゴシック体です. これらのフォント名は次の形になります.XXXX の部分は文字コードによって 異なります.
明朝体
/Ryumin-Light-XXXX-H
ゴシック体
/GothicBBB-Medium-XXXX-H

文字コードの指定

使用する文字コードごとに,上の XXXX の部分を次のようにします. 最近の OS であればUTF-8 を使えば大丈夫でしょう。

Shift-JIS
90pv-RKSJ
EUC-JP (UJIS)
EUC
UTF-8
UniJIS-UTF8

縦書きにする

日本語は縦書きにもできます.この場合,次のようにフォント指定を変更すれば 自動的に縦書きになります.例は明朝体を UTF-8 で使っている場合です.

横書き
/Ryumin-Light-UniJIS-UTF8-H
縦書き
/Ryumin-Light-UniJIS-UTF8-V

HHorizontal VVertical からきています.

日本語文字列出力のサンプル

明朝,UTF-8,横書き

Ryumin-Light-UniJIS-UTF8-H findfont 12 scalefont setfont
100 200 moveto
(日本語の表示) show

ゴシック,UTF-8,縦書き

/GothicBBB-Medium-UniJIS-UTF8-V findfont 12 scalefont setfont
100 200 moveto
(日本語の表示) show

役に立つ手続き

クリップ (任意の図形や文字で切り抜いた型の中に描く)

図形でクリップする

% 型となる図形の定義.ただし fill, stroke は行わない!
clip
% 図形などを描く.すると型で切り抜かれて描画される.
initclip % clip を解除

文字でクリップする

/Times-Italic findfont 70 scalefont setfont
45 100  moveto
(QP) false charpath  clip
80 3 170 {5 exch  moveto 200 0 rlineto stroke} for
initclip

上の2つを使ったサンプルファイル
文字で/文字をクリップ

ハッチ(斜線による敷き詰め)

/m {moveto} def
/l {lineto} def
/s {stroke} def
-1000 10 1000 { % -1000 左端 水平間隔 右端
70 120 120 % 下端 斜線 x y : 比と符号でデザイン変更
4 2 roll 2 copy moveto
3 2 roll add 3 1 roll add exch lineto stroke
} for
Hatch sample

ある図形の内部をハッチする

% 2つの円の内側にハッチを入れる
/m {moveto} def
/l {lineto} def
/s {stroke} def
newpath
100 100 60 0 360 arc % 左の円を作る
240 100 60 sub m     % 右の円の円周上に移動 newpath は使えない
240 100 60 0 360 arc %  右の円
clip
% ハッチを描く
-800 10 1000 { % 水平方向の間隔は 10
0 200 200 % 左斜め下への斜線
4 2 roll 2 copy m
3 2 roll add 3 1 roll add exch l s
} for
initclip
Hatch sample

図形には輪郭だけを与えて, stroke していないことに注意.

長さの単位を使う

/cm {28.346 mul} def

上のようにしておけば, 12.3 cm と,単位付きの長さを扱うように記述できる.

スタックの値を変数に代入する

100 200
/y exch def
/x exch def
これで x, y に 100, 200 が代入されている。

擬似的なローカル変数を使う

Blue Book に示されているサンプル
/localdict 1 dict def
/sampleproc
{ localdict begin
/localvariable const  def
end
} def
ここで,/localdict 1 の 1 は常にこの値でよい.この値は 初期の言語仕様で バッファサイズ を指定するために使われたのだが,現在の仕様では自動的に メモリが割り当てられる.

ローカル変数に値を渡す方法

ある手続きを定義して,その中のローカル変数に パラメータを渡したいというのはよく起きる. このときには上のサンプルの形の中で exch 演算子を使うとよい.

次のサンプルでは,x1, y1 にそれぞれ 10, 20 が, x2, y2 にそれぞれ 100, 190 が代入される. 実行して確かめるとよい.このやり方を使えば, 複雑なスタック操作をやらなくて済む.

%!PS-Adobe-3.0 EPSF-3.0
%%BoundingBox: 0  0  300  250
/ldict 1 dict def
/proc {
    ldict begin
    /y2 exch def
    /x2 exch def
    /y1 exch def
    /x1 exch def
    x1 y1 10 0 360 arc stroke
    x2 y2 5 0 360 arc fill
    end
} def
10 20 100 190 proc

文字列の横幅の中央を指定x座標に合わせる

(ABC) dup stringwidth pop 2 div neg 
x add y moveto show

注意:この手続きで縦位置を合わせることはできない.

translate を使って任意の場所に描画

決められた図形を任意の場所に描画するには, moveto などよりも translate を使ったほうが汎用性が高い. ただし,この場合には,移動した後で translate による移動を原点に ふたたび戻すことになるので, 多少面倒ではある.

今,原点付近にある図形を描画する手続きを proc とすると,それを任意の場所に描画してから状態を 元に戻す手続き putprocat は次のように 書く.

/putprocat {
    2 copy neg exch neg exch 4 2 roll
    translate
    proc
    translate
} def
putprocat は次のように使う.
x y putprocat

Ghostscript で ps ファイルを png に変換する

gs -q -dEPSCrop -dNOSAFER -dBATCH -dNOPAUSE -sDEVICE=png16m -r600 -dText AlphaBits=2 -dGraphicsAlphaBits=2 -sOutputFile=hoge.png hoge.ps

この例では解像度を 600dpiにしている。 なお 次のようにコマンド化しておくと便利である。 次のシェルスクリプトを ps2png というファイル名でパスの通ったディレクトリに置いておく。

#!/bin/sh
fname=$1
pngfile=${fname%.*}.png
echo $pngfile
gs -q -dNOSAFER -dBATCH -dEPSCrop -r600 -dNOPAUSE \
    -sDEVICE=png16m -dGraphicsAlphaBits=4 \
    -dTextAlphaBits=4 \
     -sOutputFile=$pngfile $fname

実行は次のように簡単で,これで拡張子を .png にしたファイル hoge.png が生成する。

ps2png hoge.ps