このページは過去に掲載していたものをそのまま使用しています。
EPS出力では日本語と英語の文字別に出力を変更する 必要があります。その仕掛けについて説明します。
テキストの中に英語と日本語が混在しているなどした時に
ちゃんと表示できなかったので、epsを生成する、app/render_eps.c、
に仕掛をつくりました。
このちゃんと表示できないというのはEPSのデータ構造に問題があります。 tgifを使ってテキストで「テストtest」を書き、EPSに出力しました。
/* ascii文字と2byte文字を含む一続きの文字列 (『テストtest』の場合)*/
0 SG
/Ryumin-Light-EUC-H FF dup /WMode known {dup /WMode get 1 eq {[0 1 -1 0 0 0.3] makefont} if} if [14 0 0 -14 0 0] MS
(\244\306\244\271\244\310) SH
0 SG
/Helvetica FF [14 0 0 -14 0 0] MS
(test) SH
この斜字体になっているところのように、2byte文字では「/Ryumin-Light-EUC-H」を ascii文字では「/Helvetica」というように、フォントの指定を切り換える必要が あります。
このような処理を追加するためには、いくつかの方法があったのですが 2byte文字用のPSフォント名を追加する事にしました。
/* 古い定義 */
typedef struct _FontData {
char *fontname;
char *fontname_ps;
char *fontname_x11[NUM_X11_FONTS]; /* First choice */
} FontData;
いままでは上のような構造体を使っていたが、複数のPS用フォント名を指定できる ように改造した。
#define NUM_PS_FONTS 2
/* 新しい定義 */
typedef struct _FontData {
char *fontname;
char *fontname_ps[NUM_PS_FONTS];
char *fontname_x11[NUM_X11_FONTS]; /* First choice */
} FontData;
これに合せるためにlib/font.cを書換えた。
{ "Helvetica",
{ "Helvetica", "Ryumin-Light" }, /* ← 変更点 */
{ "-adobe-helvetica-medium-r-normal-*-%d-*-*-*-*-*-*-*,-*-mincho-medium-r-normal-*-%d-*-*-*-*-*-*-*",
"-adobe-helvetica-medium-r-normal-*-%d-*-*-*-*-*-*-*,-*-fixed-medium-r-normal-*-%d-*-*-*-*-*-*-*"
}
},
{ "Helvetica-Bold",
{ "Helvetica-Bold", "GothicBBB-Medium" }, /* ← 変更点 */
{ "-adobe-helvetica-bold-r-normal-*-%d-*-*-*-*-*-*-*,-*-mincho-bold-r-normal-*-%d-*-*-*-*-*-*-*",
"-adobe-helvetica-bold-r-normal-*-%d-*-*-*-*-*-*-*,-*-fixed-bold-r-normal-*-%d-*-*-*-*-*-*-*"
}
},
このように構造を変更することによって、EPS出力時におけるフォントの指定を 複数持たせました。これを利用して実際に日本語フォントとascii文字フォントを 切り換える方法については次に述べる。
EPS出力についての処理の全ては、app/render_eps.cの中に
書かれている。このset_font関数に次のような仕掛をいれる。
str = font_get_psfontname(font)[0]; if(strcmp(str, "Ryumin-Light")==0||strcmp(str, "GothicBBB-Medium")==0) /* when using the EUC-JP Charset, */ fprintf(renderer->file, "/%s-EUC-H ff %f scf sf\n", str, (double)height); else fprintf(renderer->file, "/%s-latin1 ff %f scf sf\n", str, (double)height); }
さらに二つ目のPSフォント名に対応するset_mbfont関数を作る。
ほとんどset_font関数で一箇所だけ違う。
str = font_get_psfontname(font)[1]; if(strcmp(str, "Ryumin-Light")==0||strcmp(str, "GothicBBB-Medium")==0) /* when using the EUC-JP Charset, */ fprintf(renderer->file, "/%s-EUC-H ff %f scf sf\n", str, (double)height); else fprintf(renderer->file, "/%s-latin1 ff %f scf sf\n", str, (double)height); }
これだけの機能追加で、日本語を表示するのには十分になる。あとは 適切にこの関数を呼ぶだけ。
たとえば「テストtest」という文字列があった場合に、「トt」の二つの文字の 間が境界になる。ここで文字を分割して処理を分けなければならない。 このアルゴリズムは何も考えずに、正数でなければEUCだと認識している。
以前は何も考えなさすぎて、文字の境界を次のように認識していた。
しかしこれは次のようなアルゴリズムで置き換える事ができる。
なんか頭が悪くて嫌になりますね、何やってんだか。
いづれにしても、境界なら適切なset_font関数か
set_mbfont関数を呼んであげます。それから、普通に
draw_string関数を呼んであげましょう。
この二つの関数は主にapp/render_eps.cの事を指していますが、 おおもとは、lib/render.hでプロトタイプが宣言されています。 C言語をC++風に使っているので、わかりづらいかもしれませんね。
この二つの関数は大抵一緒に使われています。それなら、面倒な処理を
全部のdraw_string関数を呼びだしている所に追加しなくても、
app/render_eps.cのdraw_string関数を変更して
set_font関数をdraw_string関数の中に隠して
しまえばいいんじゃないのかなと思いました。
後になってdraw_string関数以外でもset_font関数を実行する必要のある 関数があることが発覚してバグを潜ませる結果になりました。
lib/text.cなどでは一々テキストを出力するのに次のような
手法を取っていました。
void
text_draw(Text *text, Renderer *renderer)
{
...省略
renderer->ops->set_font(renderer, text->font, text->height);
pos = text->position;
for (i=0;i<text->numlines;i++) {
renderer->ops->draw_string(renderer,
text->line[i],
&pos, text->alignment,
&text->color);
pos.y += text->height;
}
...省略
}
日本語化のために作ったパッチは、EPSの時にフォントの設定を切り替える
必要があるためにset_mbfont()を新たに作りましたが、そういう処理は
draw_string()の中に閉じ込めてしまえばよいのではないでしょうか。
なぜ、こんな事をするのかといえば、UMLのダイアグラム全部に
ASCIIとEUCの境界を判断させて、フォントを切り替えさせるのが面倒だから。
lib/text.cだけでも嫌になってくる作業を、これ以外の
独自にテキスト領域を描画させている部分、全部にするなんて冗談ではない。
そういうわけで、後からコンポーネントが追加された場合の事を 考えて変更のほとんどは、renderオブジェクト側でする事にしました。 変更すると上記のプログラムは次のような形になります。
void
text_draw(Text *text, Renderer *renderer)
{
...省略
pos = text->position;
/* modified only this loop */
for (i=0;i<text->numlines;i++) {
renderer->ops->draw_mbstring(renderer,
text->line[i],
&pos, text->alignment,
&text->color,
text->font,
text->height);
pos.y += text->height;
}
...省略
}
新たにdraw_mbstring関数を作りましたが、実際はdraw_string関数 を変更しても良いわけです。ただし他の人が作った新しいコンポーネントで このプロトタイプに従って使わないとどうしようもないので、新しい関数に変更する 手間の方を選びました。
もともとdiaの内部は1byte環境を前提にしたコーディングなので、 ここまでやって日本語化(あわよくばi18n)への対応を取るための 土台ができたといった感じです。
rubyなどで使われている手法を使って2byteな文字かどうか判定する方法の 方がわかりやすいのではないかなと思う。時間が取れたらオリジナルのソースに 変更を加えてみましょう。