今回からは、Localizable.stringsなどから入手したテキストをウィンドウに表示したりプリントアウト(印刷)したりする状況を考えてみます。Carbonモダンアプリケーションでは、いったいどんなAPIを使えば最良の結果を得られるのでしょうか?
昔々、ToolBox APIを使ったテキスト描画には、QuickDrawのDrawString()やDrawText()、もしくはTextEdit APIのTETextBox()などを選択していました(いや〜懐かしい)。しかし残念ながら、これらのAPIはMac OS X 10.4以降ではすべてDEPRECATED指定ですので、そのうち消えてしまう運命です。それではテキスト描画を実行するにはどんなCarbon APIを使えば良いのでしょうか?
最初に思いつくのは、CoreGraphics(Quartz 2D)のテキスト描画用APIを使うことです。CoreGraphicsには、以下の2つのテキスト描画用APIが用意されています。
void CGContextShowText( CGContextRef c,const char *string,size_t length );
void CGContextShowTextAtPoint( CGContextRef c,float x,float y,const char *string, size_t length );
CGContextShowText()とCGContextShowTextAtPoint()の違いは、先んじてテキスト描画位置をCGContextSetTextPosition()で指示しておくかどうかです。また、描画用フォントのサイズや種類はCGContextSelectFont()で、描画モードはCGContextSetTextDrawingMode()で設定します。QuickDraw環境とは異なり、CoreGraphics環境ではテキスト描画位置は左下が原点となり、位置指定用の座標値(X,Y)には浮動小数点(1.0が1/72インチ)を利用しますので注意してください。

上記ルーチンを実行すると、ウィンドウの左下付近に64ポイントのGenevaフォントで赤色の「Think Different」が描画されます。ちなみに、CGContextSetTextDrawingMode()で指定できるテキスト描画モードは以下の8種類です。

kCGTextFill指定は文字のペイント描画です、kCGTextStrokeは縁取りのみの描画となります。kCGTextFillStrokeは各パートに別々の色が指定できます。kCGTextInvisibleは描画はしませんが、カレント描画位置のみを移動させます。最後にClipが付いたモードで描画した場合には、現在のクリッピング領域(Current Clipping Path)により描画領域が制限されます。一番最後の、kCGTextClipはテキスト描画はせず、描画対象がクリッピング領域に影響を受けるかどうかの判定に用います。
テキスト描画での色の指定方法は、CoreGraphicsで描画する他の図形とまったく同じであり、CGContextSetRGBFillColor()やCGContextSetRGBStrokeColor()で指定します。RGBの値は0.0から1.0の範囲で(0.0,0.0,0.0)で黒を(1.0,1.0,1.0)で白を表します。最後の引数は描画時のαチャンネル値(透明度)の指定で、1.0は完全な不透明を表します。また、描画文字のアンチエイリアスを制御したい時にはCGContextSetAllowsAntialiasing()を使います。2つ目の引数にfalseを渡せばアンチエイリアスはOFFになり、trueを渡せば再度ONとなります。
テキスト描画の位置やその方向は、他の図形描画と同様に、CGContextのCTM(Current Transformation Matrix)の影響を受けます。CTMとは描画用変換マトリックスのことです。例えば、CGContextRotateCTM()で角度を指定すれば、ある位置を中心にテキストを回転させて描画することも可能です(QuickDrawでは難しかった)。先ほどのルーチンのCGContextSaveGState()の次の行に以下の処理を追加すれば、描画テキストを原点(左下)を中心に左回りに45度回転させることができます。
CGContextRotateCTM( cont,3.1415/4.0 );
また、同様に以下のルーチンを追加すると、テキストはウィンドウの左上(QuickDrawと同じ座標系)に上下ミラー反転されて描画されることになります。

これらのAPI、使い勝手はQuickDrawのDrawText()と似たようなレベルなのですが、テキスト描画に日本語フォントを指定しようとすると大きな壁にぶつかります。フォント指定方法が良く分からないのです。先ほどの例では、CGContextSelectFont()に"Geneva"という文字列を代入し指定しています。この名称にはPostScriptフォント名を使う必要があるようですが、日本語フォント(例えばヒラギノ)のPostScriptフォント名がすぐに分かりません。そこで、もっと簡単な指定方法がないものかと探してみると、CGContext.hには以下のようなAPIも用意されていました。
void CGContextSetFont( CGContextRef c,CGFontRef font);
CGFontRefについては、CGFont.hの方に色々と操作するAPIが定義されていますので、そちらを探してみると、CGFontRefを得るために以下のルーチンが定義されていました。
CGFontRef CGFontCreateWithPlatformFont( void *platformFontReference );
で、引数として渡す「platformFontReference」とは何ぞや?ということでヘッダファイルのコメントを読んでみると、「Mac OS XではATSFontRefのことである」と記載されています。そこで今度はATSFont.hを探索して以下の2つのAPIを探し出しました。

フォントパネルで入手した情報を使いATSFontFindFromName()でフォントのファミリー名を得て、それをATSFontFindFromName()に渡してATSFontRefを得ました。やっとのことでCGContextSetFont()にATSFontRefを渡してCGFontRefを得ることができたわけです。しかし、この方法で手続きとしては正しいような気がしますが...日本語を描画させても文字化けしてしまいます(涙)。
CGContextSelectFont()に渡すエンコード指定のkCGEncodingMacRomanが間違っているのかと思い、こちらをkCGEncodingFontSpecificに変更してみても正しい描画は行われません。こうなると、さすがにギブアップです(笑)。どのたか、CoreGraphics APIで日本語フォントを指定し正しく日本語を描画する方法をご存じの方はいらしゃいませんか?
まあどちらにしろ、矩形領域指定によるテキストのジャスティフィケーションなど、最低でもTETextBox()レベルのテキスト処理が可能でないと実際の作業では使いものになりません。また、CFStringRefで指定されたテキストやユニコードテキストを描画する時にもこのままでは扱いが大変そうです。そこで、CoreGraphics APIを利用することは止めにして、もう少し高級な(レベルが高い)テキスト描画用APIを探すことにしました。
次回は、CoreGraphics APIに頼らないテキスト描画を試します。何かと処理が大げさで複雑になりがちな、MLTE(Multilingual Text Engine)APIやATS(Apple Type Services)APIは避けたいところですので、それらには頼らないテキスト描画ルーチンを紹介したいと思います。