iOS 定制应用程序字体

使用Quartz Core绘制文字非常简单,苹果的Quartz 2D参考中演示了如何使用CGContextShowTextAtPoint函数绘制文本。不幸的是,这个函数不支持Unicode字符的绘制(这个函数只支持MacRoman一种编码)。如果你使用中文、日文等亚洲字体,那么就不得不悲催了。

许多童鞋肯定会被文档中的这句话所吸引:

“如果想使用MacRoman以外的文本编码,⋯⋯调用CGContextShowGlyphsAtPoint替代CGContextShowTextAtPoint。”

如果你采用这种办法,那么另一种悲剧就产生了。

一、CGContextShowGlyphsAtPoint的挑战

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



你可能感兴趣的:(iOS 定制应用程序字体)