LECTURE 11 日本語文字列を表示する

○ はじめに

LECTURE 5 で紹介した XDrawString() や XDrawImageString() では、英数字のような 1 バイト文字しか描画できません。しかし日本人としてはぜひとも日本語を表示したいものです。

ここでは X11R5 以上のバージョンで導入された国際化機構を基にして、日本語のようなマルチバイト文字を描画する方法について説明していきます。

○ サンプルプログラム

表示したウィンドウに "こんにちは、お元気ですか?" という日本語文字列を表示するサンプルプログラム xmbcs.c です。はじめに のページから、サンプルプログラムを固めた tar ボールをダウンロードすることもできます。

実行すると、下図のように日本語文字列が表示されると思います。うまく表示されない場合は、システムの環境変数 LANG が正しく設定されているか、xmbcs.c がシステムのロケールで使用されている文字コードで保存されているかどうか調べてみて下さい。

011mbcs.jpg

--- xmbcs.c ---

    1	#include <X11/Xlib.h>
    2	#include <X11/Xlocale.h> /* ロケール用ヘッダーファイル */
    3	#include <stdio.h>
    4	#include <string.h>
    5	
    6	#define WIN_W  350  /* ウィンドウの幅   */
    7	#define WIN_H  250  /* ウィンドウの高さ */
    8	#define WIN_X  100  /* ウィンドウ表示位置(X) */
    9	#define WIN_Y  100  /* ウィンドウ表示位置(Y) */
   10	#define BORDER 2    /* ボーダの幅 */
   11	
   12	
   13	int
   14	main( void )
   15	{
   16		Display*      dpy;            /* ディスプレイ変数 */
   17		Window        root;           /* ルートウィンドウ */
   18		Window        win;            /* 表示するウィンドウ */
   19		int           screen;         /* スクリーン */
   20		unsigned long black,white;    /* 黒と白のピクセル値 */
   21		GC            gc;             /* グラフィックスコンテキスト */
   22		XEvent        evt;            /* イベント構造体 */
   23	
   24		XFontSet      fs;             /* フォントセット */
   25		int           missing_count;  /* 存在しない文字集合の数 */
   26		char**        missing_list;   /* 存在しない文字集合 */
   27		char*         def_string;     /* ↑に対して描画される文字列 */
   28	
   29	
   30		/* 描画する全角文字列 */
   31		char*         string = "こんにちは、お元気ですか?";
   32	
   33	
   34		/* ロケールを設定する(現在システムに設定されているロケールを使用) */
   35		if ( setlocale( LC_CTYPE, "" ) == NULL ) {
   36			printf( "Can't set locale\n" );
   37			return 1;
   38		}
   39	
   40		/* Xlib が現在のロケールを扱えるかどうかを判断する */
   41		if ( ! XSupportsLocale() ) {
   42			printf( "Current locale is not supported\n" );
   43			return 1;
   44		}
   45	
   46	
   47		dpy = XOpenDisplay( "" );
   48	
   49		root   = DefaultRootWindow( dpy );
   50		screen = DefaultScreen( dpy );
   51		white  = WhitePixel( dpy, screen );
   52		black  = BlackPixel( dpy, screen );
   53	
   54	
   55		win = XCreateSimpleWindow( dpy, root,
   56			   WIN_X, WIN_Y, WIN_W, WIN_H, BORDER, black, white );
   57	
   58	
   59		gc = XCreateGC( dpy, win, 0, NULL );
   60		XSetBackground( dpy, gc, white );
   61		XSetForeground( dpy, gc, black );
   62	
   63	
   64		/* フォントセットを生成する */
   65		fs = XCreateFontSet( dpy, "-*-fixed-medium-r-normal--16-*-*-*", 
   66				     &missing_list, &missing_count, &def_string );
   67	
   68		if ( fs == NULL ) {
   69			printf( "Failed to create fontset\n" );
   70			return 1;
   71		}
   72	
   73		XFreeStringList( missing_list );
   74	
   75		XSelectInput( dpy, win, ExposureMask | KeyPressMask );
   76		XMapWindow( dpy, win );
   77	
   78	
   79		while( 1 ) {
   80			XNextEvent( dpy, &evt );
   81	
   82			switch( evt.type ) {
   83				case Expose:
   84					if( evt.xexpose.count == 0 ) {
   85						XmbDrawString( dpy, win, fs, gc, 
   86							50, 50, string, strlen( string ) );
   87					}
   88					break;
   89	
   90				case KeyPress:
   91					XFreeGC( dpy, gc );
   92					XFreeFontSet( dpy, fs );
   93					XDestroyWindow( dpy, win );
   94					XCloseDisplay( dpy );
   95					return 0;
   96			}
   97		}
   98	}
   99	
  100	/* End of xmbcs.c */

○ サンプルプログラムの説明

例によって、サンプルプログラムについて説明していきます。

  • 2 行目

    Xlib の国際化機構を使って日本語(マルチバイト文字)を扱うために必要な <X11/Xlocale.h> をインクルードしています。

  • 24 〜 27 行目

    マルチバイト文字の描画に必要なフォントセットを作成するために使う変数が宣言されています。 フォントセットとは、マルチバイト文字列を描画するのに必要な複数のフォントをまとめて扱う ための仕組みです。例えば、半角英数字と全角文字を描画するには異なるフォントが必要となり ますが、これをまとめて扱うための仕組みと思えば良い、と思います。マルチバイト文字描画関 数が指定されたフォントセットから必要なフォントを随時選択して文字を描画してくれます。

  • 34 〜 38 行目

    日本語(以外のマルチバイト文字列も同様)を扱いたい場合には、プログラム中でまず最初にロケ ールを設定する必要があります。

    最近の Unix では、ロケールという仕組みが用意されています。これは異なる言語環境下におい てもコンパイルしなおすことなくアプリケーションが動作するための仕組みです。これはシステ ムの C ライブラリの方で用意されることもありますが、システムにロケールの仕組みが無い場 合でもその仕組みを利用できるように Xlib 側も独自にロケールの仕組みを持っています。

    ロケールを設定するには setlocale() を使いますが、C ライブラリの方で setlocale() が定義 されている場合はそちらを、そうでない場合は setlocale() が呼ばれると _Xsetlocale() を使 うように <X11/Xlocale.h> 内で定義されています。

      char* setlocale( int category, char* locale );
    
      category : ロケールの影響を受ける関数区分
      locale   : ロケール名 
    
    

    category に LC_CTYPE を指定して、locale にロケール名を指定すると、文字種別判定および文 字変換を行うルーチンがそのロケールに従って動作するようになります。とりあえずこれを設定 しておけば日本語を表示できます( その他の category についてはよく調べていませんm(__)m )。 なお、サンプルプログラムのように locale に "" を指定すると、LANG 環境変数に設定されて いる値が使用されます。

  • 40 〜 44 行目

    setlocale() を実行した直後に、設定されたロケールを Xlib が扱えるかどうかを判断しなけれ ばなりません。それには XSupportsLocale() を使います。

      Bool XSupportsLocale();
    
    

    現在設定されているロケールが扱えるならば True を、扱うことができないならば False を返 します。

  • 64 〜 73 行目

    日本語文字列(以外のマルチバイト文字列も同様)を描画するためには、まず X サーバに必要な フォントセットをロードしなければいけません。フォントセットとは、前述のように文字列を描 画するのに必要な複数のフォントをまとめて扱うための仕組みです。

    現在のロケールが必要とするフォントからフォントセットを作成するには、XCreateFontSet() を使用します。

      XFontSet XCreateFontSet( Display* display,
                               char*    base_font_name_list,
                               char***  missing_charset_list, 
                               int*     missing_charset_count, 
                               char**   def_string 
                             );
    
      base_font_name_list   : ベースフォント名のリスト 
      missing_charset_list  : 見付からなかった文字セット
      missing_charset_count : missing_charset_listの個数
      def_string            : 見付からなかった文字の描画に用いる文字列
    
    

    base_font_name_list で指定したフォント名をベースとして、現在のロケールに基づいたフォン トセットを作成して返します。フォントセットの作成に失敗した場合は NULL を返します。ベー スフォント名のリストとは、フォントサイズなどのフォント種別を特定しない部分のみを指定し て他はアスタリスクで指定した 1 つのフォント名、もしくは一部をアスタリスク指定したフォ ント名のリスト、あるいは完全にフォントを指定したフォント名のリストです。

    missing_charset_list には、対応するフォントを発見できなかった文字の集合が格納されてい ます。特に使用しない場合は、XFreeStringList() を使って解放しておきます。使用する場合で も、プログラム終了時には解放してください。

      XFreeStringList( char** list );
      list : 文字列の集合
    
    

  • 83 〜 88 行目

    Expose イベントを受け取った時に、作成したフォントセットを使った日本語文字列を描画して います。マルチバイト文字列を実際に描画するには XmbDrawString() もしくは XmbDrawImageString() を使います( xmbcs.c では前者を使用)。

      void XmbDrawString( Display* display,
                          Drawable d, 
                          XFontSet font_set, 
                          GC       gc, 
                          int      x, 
                          int      y, 
                          char*    string, 
                          int      num_bytes 
                        );
    
      font_set  : フォントセット
      gc        : GC 
      x, y      : 文字列のベース座標
      string    : 文字列
      num_bytes : stringのバイト数
    
    
      void XmbDrawImageString( display, d, font_set, gc, x, y, string, num_bytes );
    
    

    XmbDrawString() が GC のフォアグラウンドカラーを用いて文字列を描画するのみであるのに対 して、XmbDrawImageString() は、まず GC のバックグラウンドカラーを用いて文字列に対する バウンディングボックスを描画した後に、フォアグラウンドカラーを用いて文字列を描画する点 が異なります。

  • 92 行目

    リソースの解放処理として、新たにフォントセットの解放処理が加わっています。生成したフォ ントセットが不必要になった場合は、XFreeFontSet() を使ってリソースを解放します。

      XFreeFontSet( Display* dpy, XFontSet font_set );
    
    

以上で xmbcs.c の説明は終りです。日本語文字列(マルチバイト文字列)の描画の仕方が分かったでしょうか? なお、xmbcs.c では使用しませんでしたが、指定したフォントで文字列を描画する際にその文字列がどれくらいの領域を占めるのかについて知りたい場合がよくあります(エディタなんかを作る場合には必須ですよね)。この方法について以下に紹介しておきます。

○ マルチバイト文字列の描画領域情報の取得

指定したフォントで文字列を描画する際に、その文字列がどれくらいの描画領域を占めるかを知りたい場合が良くあります。マルチバイト文字列を設定されているフォントで描画した場合に、占めるであろう描画領域情報を取得するには XmbTextExtents() を使います。

int XmbTextExtents( XFontSet    font_set, 
                    char*       string, 
                    int         num_bytes, 
                    XRectangle* overall_ink_return, 
                    XRectangle* overall_logical_return 
                  );

font_set               : フォントセット
string                 : 文字列 
num_bytes              : 引数stringのバイト数を指定 
overall_ink_return     : 描画される領域のディメンジョンを返す
overall_logical_return : 論理的なディメンジョンを返す

string で指定した文字列を font_set のフォントセットで描画した場合に、描画された文字列のサイズに関する情報を得ることができます。各情報は、引数で与えた overall_ink_return、overall_logical_return に格納されます。overall_ink_return には文字列の画像イメージのバウンディングボックスが、overall_logical_return には論理的なバウンディングボックスの大きさがセットされます。

と書いてみてもよく分かりませんが(マニュアルの直訳だから?)、overall_logical_return の方は要するに、描画した文字列が完全に囲まれる長方形の幅と高さが格納されます。なお、over_all_logical_return の x と y には、描画する文字列の描画原点からみたバウンディングボックスの左上角の位置が格納されます。