使用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