原文地址:http://blog.csdn.net/kmyhy/article/details/7643568
使用Quartz Core绘制文字非常简单,苹果的Quartz 2D参考中演示了如何使用CGContextShowTextAtPoint函数绘制文本。不幸的是,这个函数不支持Unicode字符的绘制(这个函数只支持MacRoman一种编码)。如果你使用中文、日文等亚洲字体,那么就不得不悲催了。
许多童鞋肯定会被文档中的这句话所吸引:
“如果想使用MacRoman以外的文本编码,⋯⋯调用CGContextShowGlyphsAtPoint替代CGContextShowTextAtPoint。”
如果你采用这种办法,那么另一种悲剧就产生了。
CGContextShowGlyphsAtPoint 函数的第4个参数要使用到CGGlyph数组。意思是你需要自己把unichar字符自已映射成字体文件中的Glyph(字符图形,有称字模、字符点阵)索引。如果你不使用私有框架,那么这是一个几乎不可能完成的任务。
实际上,大部分程序员会建议你用UIKit框架或者[NSString drawAtPoint:]方法而不是NSStringCGContextShowGlyphsAtPoint来绘制unicode字符。
如果你真是只是想在UIView上绘制几句文本,那么drawAtPoint能够满足你的需要,下面的内容你也不必要看了。
但是,CGContextShowGlyphsAtPoint就真的没有任何用处了吗?
实际上,苹果最初设计这个API的目的,是为了提供给Application使用定制字体的能力。基本上iOS提供的系统字体非常少,我们在一些app中为了实现一些效果(比如让字体更加清晰)不得不使用自己的字体文件,这个时候这些API就显得非常必要。
然而,关于使用自定字体,苹果的文档描述得非常少。我们知道的仅仅是CGContextSetFont和CGContextSetFontSize、CGContextShowGlyphsAtPoint等3个Quartz函数。所有的google文档都语焉不详。显然,真正要想使用自定字体,根本没有这么简单。
那么真正的挑战在哪里?
StackOverFlow上有人发了一个帖子,帖子问到如何用绘制日文字符(中文也是一样)。其中 Brad Larson 的回复描述得很精确:
You may also be able to print custom characters withCGContextShowGlyphsAtPoint(), but the challenge is generating the glyphs on theiPhone. This post has an example: gogo-robot.com/devblog/?p=5 – Brad Larson
真正的挑战是unicharàglyph,也就是 CGContextShowGlyphsAtPoint的第4个参数如何获得。Brad提到的例子实际上描述了一种不可能的情况,他假设字体文件中的glyph编码是在unicode编码的基础上加一个固定的偏移量(他称之为Magic Number)构成。实际上对于某个.ttf/.otf字体文件,我们无法获得这个MagicNumber,我们不能靠猜测来编写程序代码。
那么我们只有自己解析字体文件的数据结构了。万幸的是,Zynga Game Networks的 Kevin Ballard实现了一个unichar到glyph的映射。项目地址:https://github.com/zynga/FontLabel。
我们可以利用这个实现来解析cmap为format4和format12的字体文件。
关于cmap的内容,请参考True Type 字体文件相关文档(微软和苹果的网站)。
二、实现过程
1、在程序中加载ttf文件
首先要解决的问题,是在程序中加载.ttf/otf文件。我们在示例程序的资源束中加入了一个“方正大黑简体.ttf”的文件。这个文件是我从Mac系统中搜索到的,应该是MicrosoftOffice中提供的字体文件。我们用以下代码来加载它:
NSString *fontPath = [[NSBundlemainBundle] pathForResource:@"方正大黑简体" ofType:@"ttf"];
CGDataProviderReffontDataProvider = CGDataProviderCreateWithFilename([fontPath UTF8String]);
font_ref =CGFontCreateWithDataProvider(fontDataProvider);
currentTable=readFontTableFromCGFont(font_ref);
CGDataProviderRelease(fontDataProvider);
提示:在iOS3.2以后,还可以在plist文件中添加 UIAppFonts键的方式添加自定字体。见苹果文档“Custom Font Support”主题。
2、从字体文件中获取Glyph
这部分的实现来自Ballard的FontLabel,非常感谢!整个实现主要包含2个函数定义:
staticfontTable*readFontTableFromCGFont(CGFontRef font);
staticvoidmapCharactersToGlyphsInFont(constfontTable *table, unichar characters[], size_t charLen,CGGlyph outGlyphs[], size_t *outGlyphLen) ;
第一个函数用于从CGFont中读取cmap表,第二个函数用于将unichar数组映射为Glyphs数组(实现cmap format4和format12子表)。
3、绘制字符
Quartz绘图是在UIView的drawRect方法中进行,字符也不例外。下面是字符绘制代码:
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextClearRect(context, rect);
CGContextSetFont(context, font_ref);
CGContextSetTextDrawingMode(context, kCGTextFillStroke);
CGContextSetFillColorWithColor(context, fontColor.CGColor);
UIColor * strokeColor = [UIColorblackColor];
CGContextSetStrokeColorWithColor(context, strokeColor.CGColor);
CGContextSetFontSize(context, 24.0f);
CGGlyph glyphs[[self.textlength]];
size_t glyphCount;
unichar textChars[[textlength]];
[textgetCharacters:textChars range:NSMakeRange(0, text.length)];
mapCharactersToGlyphsInFont(currentTable, textChars, text.length, glyphs, &glyphCount);
CGAffineTransform textTransform = CGAffineTransformMake(1.0, 0.0, 0.0, -1.0,0.0, 0.0);
CGContextSetTextMatrix(context,textTransform);
CGContextShowGlyphsAtPoint(context, 20, 30, glyphs, [self.textlength]);
代码中先使用Ballard实现的mapCharactersToGlyphsInFont函数将unicode字符转换为Glyphs数组,然后用CGContextShowGlyphsAtPoint进行绘制。
注意:由于Quartz空间和用户空间的坐标系统不同(Quartz空间坐标原点位于屏幕左下角,用户空间坐标原点位于屏幕左上角,二者坐标转换只需y坐标取反即可),所以我们使用了一个渐变反射来进行坐标转换并应用于文字矩阵。
4、运行效果
图片
5、源代码下载
整个示例工程见资源下载:http://download.csdn.net/detail/kmyhy/4359481