2010年7月7日水曜日

Core Graphics (Quartz) のみで日本語文字列を描画するライブラリのヘッダファイルを書いてみた

とある理由で UIKit の描画機能が使えず、 Core Graphics のみで文字列の描画処理を行わなければならないことになってしまったので、適当に調べてみました。


■ことのはじめ

Core Graphics の機能だけで日本語の文字列を描画する方法については、既に先人の方々が調べて記事にまとめてくださっていたので、そちらを見ていただければ大丈夫です。
http://iphone-dev.g.hatena.ne.jp/ktakayama/20100129
http://d.hatena.ne.jp/r_kurain/20100316
基本的にはこちらで紹介されている方法に従って進めていけば困ることはありません。ただし、描画した文字が上下反対になることがありますので、 CGContextSetTextMatrix を使う箇所を調整したりする必要があるかも。

で、最大の問題になるのがこれらの記事で紹介されている CGFontGetGlyphsForUnichars と呼ばれる関数です。この関数を使えば楽に日本語文字列を文字化けすることなく描画することができるのですが、あろうことかこの関数はプライベートAPIであり、使うとリジェクトされてしまうらしいです。ということで別の作戦をとらなければなりません。

上記の記事にいろいろな対処方法が載っているのですが、私は一番単純に
http://www.mexircus.com/codes/GlyphDrawing.mm
というライブラリを自分のアプリに組み込む方法を試してみることにしました。この方法を使えば、 CGFontGetGlyphsForUnichars でまず作って動くようにしてから、 CMFontGetGlyphsForUnichars と書き換えるだけでそのまま動作するので楽です。

問題はこのライブラリ、mmファイルしか用意されていないのでそのままではうまく使えません。適当にヘッダファイルを用意してやる必要があります。


■と言うわけで書いたもの

と言うわけで GlyphDrawing.h を書いてみました。
http://gist.github.com/466297

ライセンスは元の GlyphDrawing.mm に準じますが、 GlyphDrawing.mm のライセンスが不明なので、不安ならばまず作者さん (http://mexircus.com/blog/) に一言聞いてみるのがいいと思います。


■使い方
  1. http://gist.github.com/466297http://www.mexircus.com/codes/GlyphDrawing.mm をダウンロードします。
  2. GlyphDrawing.mm のインポートを以下のように書き換えます:
    //#import <Foundation/Foundation.h>
    #import "GlyphDrawing.h"
  3. GlyphDrawing.hGlyphDrawing.mm をプロジェクトに追加してビルドします。
  4. CGFontGetGlyphsForUnichars の代わりに CMFontGetGlyphsForUnichars を呼びだせばOKです。


■実際に使ったコードの例
NSString *message = @"本日は快晴なり";

// フォントを設定
// ついでにmessageのサイズも取得
// ただしsizeWithFontは UIGraphics の機能なので、本当に Core Graphics だけで描画したいならここで使ってはいけません
UIFont *font = [UIFont fontWithName:@"HiraKakuProN-W6" size:32.0];
CGRect messageRect = [message sizeWithFont:font];
CGFontRef fontRef = CGFontCreateWithFontName((CFStringRef)font.fontName);
CGContextSetFont(c, fontRef);
CGContextSetFontSize(c, font.pointSize);

// Glyphを作成
size_t length = [message length];
CGGlyph glyphs[length];
UniChar chars[length];
[message getCharacters:chars range:NSMakeRange(0, length)];
CMFontGetGlyphsForUnichars(fontRef, chars, glyphs, length);

// 文字列が上下反対になるのを防止する
CGAffineTransform transform = CGAffineTransformMakeScale(1.0, -1.0);
CGContextSetTextMatrix(c, transform);
CGContextTranslateCTM(c, 0, messageRect.size.height/2);

// 描画
CGFloat x = 100.0f;
CGFloat y = 100.0f;
CGContextShowGlyphsAtPoint(c, x, y, glyphs, length);