このページは過去に掲載していたものをそのまま使用しています。
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な文字かどうか判定する方法の 方がわかりやすいのではないかなと思う。時間が取れたらオリジナルのソースに 変更を加えてみましょう。