Postscript機能呼び出しで角丸領域

枠線の作り方

LaTeXでは \framebox などを使って枠線を書くことができますし、fancyboxパッケージの \ovalbox 等を使えば角の丸い枠線を書くこともできます。終わり。

……って、そうじゃないです(笑)。

いや、確かにそれでできるんですよ。でも、そうやって組んだ結果をdvipsでPostscriptファイルに変換し、さらにAcrobat Distiller (製品版) でPDFに変換してから、超拡大して見てみて下さい。なんか汚い。ドット単位でずれている所もあれば、線の太さも一定していないです。

まあ、印刷したときにはあまり気にならないレベルなのかもしれませんが、これをもうちょっときれいにしてみたいです。さらに角の丸い枠線は半径や背景色等をもう少し柔軟に指定できるようにしたいです。

Postscript機能呼び出し

基本的にTeXは長方形しか扱うことができないソフトです。各文字は活字イメージの長方形だし、線分も縦横比が極端に違う長方形ですから扱えますが、TeXの機能では曲線は書けません。どうするかというと、通常、次のどれかをします。

LaTeXをインストールすると一緒にフォントもインストールされるのですが、その中に、いろんな角度の斜め線や、4分の1円とかが入ったフォントがあります。LaTeXのpicture環境等で書かれた図は結局、そのフォントで組んでいるだけなのです。先述の \ovalbox も、縦線・横線部分はTeXの機能で書かれていますが、角の丸い部分はその特殊フォントで組んで、くっつけているだけです。

そんな状況なので、Postscript変換の際の丸め誤差でズレることもあれば、線の太さが一定しない問題も起こりやすくなるのがわかるでしょう。円の半径を変えるにはフォントサイズを変えないとならないので、線の太さも太くなります。実際には線の太さはいくつか用意されていますが、円の半径と線の太さを完全独立で変更することはできません。

というわけで、きれいに描くにはPostscript機能を呼び出すしかないでしょう。これはDVIware (dvipsなどのDVIファイルを扱うソフト) に依存してしまうことになりますが、最近のソフトならたいてい対応しています。

TeXでは「\special{ps:命令}」という命令で、その位置にPostscriptの命令を埋め込むことができます (実際に埋め込んでいるのはdvips等のソフトですが)。ここに曲線を書くPostscript命令を書くことで、きれいな曲線を書かせることができるのです。

dvipsの問題点

Postscriptファイル内では、位置を指定する際に座標を使います。デフォルトでは紙の左下が原点で、右向きにx軸、上向きにy軸を取り、Postscriptポイント (1インチの72分の1) 単位でxy座標を指定します。そして、この座標系 (原点の位置、軸の方向、長さの単位など) はPostscriptファイル内で自在に変更することができます。

dvipsは、この座標系を変更したPostscriptファイルを出力します。具体的には、紙の左上を原点、y軸は下向きが正、長さの単位は解像度から計算されるドット単位に変更します。もちろん、座標系を変えても、座標の値もそれに応じて変えて出力するので、同じ位置を表すことができます。

ただ、「\special{ps:……}」は「……」に書かれたそのままをPostscriptファイルに埋め込むので、ここに座標が書かれていると、それがどの位置を表すかは解像度によって変わることになります。これは問題です。

解決方法として、Postscriptファイルのヘッダ部分で初期の座標系 (アフィン変換行列) を保存しておき、個々の \special で、保存していた座標系を呼び出して描画するという方法が考えられます。

%!
userdict begin
/bop-hook {
 /origmatrix matrix currentmatrix def
} def
end

例えば、上記のような内容のファイルを「savematrix.pro」というファイル名で保存しておき、dvipsを使うときはこれをヘッダ指定することにしましょう。

角丸マクロ

というわけで、作ったマクロが以下の通りです。

\newif\ifdvips\dvipstrue
\ifdvips\special{header=savematrix.pro}\fi
\catcode`@=11
% \ovalframe{幅}{高さ}{半径}{線幅}{線色}{背景色}{テキスト}
\long\def\ovalframe#1#2#3#4#5#6#7{%
 \edef\w{#1}\edef\h{#2}\edef\rd{#3}%
 \edef\lw{#4}\edef\fg{#5}\edef\bg{#6}%
 \ifx\lw\empty\def\lw{0pt}\def\fg{}\fi
 \ifx\fg\empty\def\lw{0pt}\fi
 \dimen@=\w\advance\dimen@-\lw\advance\dimen@-\rd
 \divide\dimen@ 1024\edef\x{\number\dimen@\space}%
 \dimen@=\h\advance\dimen@-\lw\advance\dimen@-\rd
 \divide\dimen@ 1024\edef\y{\number\dimen@\space}%
 \dimen@=\rd\divide\dimen@ 1024\edef\r{\number\dimen@\space}%
 \dimen@=\lw\divide\dimen@ 2048\edef\l{\number\dimen@\space}%
 \ifdvips\edef\sp{origmatrix setmatrix }\else\edef\sp{}\fi
 \edef\sp{gsave \sp currentpoint translate^^0a
 72 72.27 64 mul div 72 72.27 64 mul div scale^^0a
 \l \l translate newpath^^0a
 \x \y \r 0 90 arc \r \y \r 90 180 arc^^0a
 \r \r \r 180 270 arc \x \r \r 270 360 arc^^0a
 closepath^^0a }%
 \ifx\bg\empty\else
  \edef\sp{\sp gsave \bg\space setgray fill grestore }\fi
 \dimen@=\lw\divide\dimen@ 1024\edef\l{\number\dimen@\space}%
 \ifx\fg\empty\else
  \edef\sp{\sp\fg\space setgray \l setlinewidth stroke }\fi
 \edef\sp{\sp grestore}\setbox0\hbox{#7}%
 \dimen@=\h\advance\dimen@-\ht0\advance\dimen@-\dp0%
 \divide\dimen@ 2\advance\dimen@\dp0\dp0=\dimen@\dimen@=\h
 \advance\dimen@-\dp0\ht0=\dimen@
 \hbox to\w{\raise-\dp0\hbox{\special{ps: \sp}}\hss\box0\hss}%
}
\catcode`@=12

デフォルトではdvips用のマクロになっていますが、dvips以外を使うときは1行目の「\dvipstrue」を「\dvipsfalse」に書き換えて使って下さい。trueやfalseの前には空白は入れません。dvips以外のDVIwareでも、dvipsと同じ仕様のものもあるでしょうから、その場合はtrueにしないとうまく行かないかもしれません。この辺はスタイルファイルにしてオプション指定できるようにするといいのでしょうけどね(笑)。

さて、2行目でdvipsなら先ほど作ったファイル「savematrix.pro」をヘッダ指定するようになっています。そしてマクロ内では、dvipsなら保存していた座標系を復活させてから描画し、再び元の座標系を戻すように書かれています。dvips以外ならそのまま描画するだけです。

かなり泥臭いソースになっていますが、最初からdvipsしか使わないと仮定するなら、先ほどのヘッダファイル内にPostscriptでマクロを書けばいいので、もう少しすっきりしたソースにできると思います。

さて、実際に使うときは、

\ovalframe{5zw}{3zw}{1zw}{0.3mm}{0}{0.9}{あいう}

このように、横幅、高さ、角の半径、線の幅、線の色、背景色、テキストの順に指定します。テキスト部分に \parbox 等を入れて複数段落を囲むこともできます。

色は「0」が黒で「1」が白です。0.9なら10% の薄灰色になります。なお、このマクロは白黒 (グレースケール) 用になっていますが、このマクロにある2か所の「setgray」の部分を「setcmykcolor」に書き換えれば、CMYKカラー用マクロになります。この場合、色は空白区切りでCMYKの割合 (1で100%) を指定することになります。「1 0 0 0」でシアン、「0 0.5 1 0」ぐらいでオレンジでしょうか。もちろん「setrgbcolor」にすればRGB空白区切りになります。

また、線幅または線の色を空にすると (「{}」と書く)、枠線が書かれず背景色だけになります。背景色を空にすると枠線だけで背景が塗られません。というわけで、

\long\def\ovalfrmL#1#2#3#4{\ovalframe{#1}{#2}{#3}{.3mm}{0}{}{#4}}
\long\def\ovalfrmB#1#2#3#4{\ovalframe{#1}{#2}{#3}{}{}{0.9}{#4}}

上記のマクロを追加で作っておけば、\ovalfrmL を使って黒線で囲むだけ、\ovalfrmB で枠線なしで背景を塗るだけとかにすることもできますね。もっとも、実際にこれらを使うときは、これらを前にやった「n行取り」マクロの中に入れて使う方がいいと思います。

なお、このマクロで組んだDVIファイルは、xdviだと、文字の位置だけはちゃんとプレビューできますが、枠線はうまく出ないと思います。これは、xdviがPostscriptをきちんと解釈できないソフトだから仕方がないでしょう。

他の図形でも

角丸の枠線を中心に考えましたが、楕円や点線の枠でも同様で、Postscript機能を呼び出せば、いろんな飾り罫を付けることができます。その際、dvipsでは座標系に注意しないとならないというのが今回のポイントでした。


渡邉たけし <t-wata@dab.hi-ho.ne.jp>