CoreText是用来文字排版和处理字体的一个高级的底层技术。CoreText直接和CoreGraphics(CG)一起工作。CoreGraphics是一个高速的物理渲染引擎,它使用OSX和iOS的最底层技术处理二维图像。、
重要提示:CoreText是为开发高级文本处理框架而设计的。一般应用开发者应该使用TextKit in iOS(查看Text Programming Guide for iOS),或者Cocoa文本系统in OS X(查看 Cocoa Text Architecture Guide)。
CoreText协调文本布局、高级frameworks提供的字体支持和Quartz提供给所有文本和字体frameworks的低级能力。Quartz框架作用于字符和它们的位置。CoreText知道字符和字体之间的对应关系,并且在调用Quartz渲染文本之前计算样式信息、字体和其它属性。Quartz是在基础层面上唯一一种绘制字符的方法,并且,因为CoreText用一种Quartz可以直接使用的形式提供所有的数据,结果是高性能的文本渲染。
多线程:CoreText函数可能在多个线程同时调用,提供客户端不改变任何参数,比如被多个线程共享的attributed strings。
Multithreading: Core Text functions may be invoked from multiple threads simultaneously provided that the client is not mutating any parameters such as attributed strings that are shared between threads.
CoreText是一个基于C的平台无关的API
CoreText API在iOS和OSX上几乎是相同的,尽管OSX版本提供更丰富的字体管理API,包括mutable font collections。然而,当你在平台之间转换时,UIKit和AppKit之间有一些不同之处你必须要考虑。举个例子:你必须要有一个Quartz graphic context来渲染CoreText生成的字符,但是在不同的平台上获取graphic context的方式不同。在iOS上你绘制的view是一个UIView的子类,OS X上是NSView的子类。你应该知道UIView的drawRect:方法接受一个CGRect类型的参数,而OS X版本的drawRect:方法接受一个NSRect类型的参数。(你可以在OS X使用NSRectToCGRect函数来把NSRect对象转换成CGRect对象。)
UIView里使用UIGraphicsGetCurrentContext函数返回的graphic context是上下翻转的,相对于未经修改的Quartz graphic context(就是说,UIView返回的context的原点在左上角),所以在iOS中你必须要重新翻转graphic context,而OS X你不需要这么做。这项技术的示例代码请查看表2-1。
CoreText尽可能的使用系统数据类型和服务,在iOS和OS X中你使用和其它core frameworks中相同的约定。例如,CoreText使用CoreFundation对象作为许多入参和出参,所以你可以把它们保存在CoreFoundation集合中。CoreText中的其它对象,例如CGPath,是Core Graphics框架提供的。
CoreText对象是C语言类型
为了简单和高效,OS X和iOS中的许多底层类库都是用纯C写的。使用CoreText的时候,你会使用许多函数,比如CTFramesetterCreateWithAttributedString和CTFramesetterCreateFrame,而不是Object-C类和方法。
CoreText类型
CoreText布局引擎经常和attributed strings (CFAttributedStringRef)和graphics paths (CGPathRef)一起工作。一个attributed-string对象封装了一个字符串需要显示的文本和其它文体方面的属性—例如字体和颜色。CoreText的排版机制使用attributed string中的信息来把character转换成glyph。
Graphics path定义了一个文本框架的形状。在OS X v10.7和iOS 3.2之后,路径可以是非矩形的。
CFAttributedString引用类型,CFAttributedStringRef,和它的Foundation对象版本NSAttributedString是toll-free bridged的,这意味着Core Foundation类型和Foundation在函数和方法中是可以互换的。因此,在一个参数是NSAttributedString *类型的方法中,你可以传CFAttributedStringRef类型。在一个参数是CFAttributedStringRef类型的方法中,你可以传NSAttributedString实例。(你可能需要转换一个类型到另一个类型来防止编译警告。)对于NSAttributedString的具体子类也是一样的。
属性是定义字符串中的字符的风格的键值对,它们按range分组分享相同的属性。属性使用CFDictionary对象类传递属性到字符串或者从字符串中获取属性。为了给glyph run (CTRun object)提供样式,创建一个CFDictionary对象来保存你需要的属性,然后创建一个attributed string,把这个dictionary作为参数传过去。或者,你可以给一个已经存在的CFMutableAttributedString对象提供属性。尽管CFDictionaryRef和NSDictionary是toll-free bridged的,保存在dictionary中的个别属性可能不是。
CoreText在运行时形成一个结构体系,像图1-1中显示的那样。在这个结构的最顶层是framesetter object(CTFramesetterRef)。一个frame setter用一个attributed string和一个graphics path作为输入,来生成一个或更多的frames of text(CTFrameRef)。每个CTFrame对象代表一个段落。
Figure 1-1 Core Text layout engine的结构
为了生成frames,frame setter调用一个typesetter object(CTTypesetterRef)。当它把文本摆放在frame上时,frame setter为它提供段落样式,包括一些属性:对齐方式、制表符设置、行间距、缩进和换行模式。typesetter把attributed string中的characters转换成glyphs,并且把那些glyphs塞进lines来填满整个text frame。
每个CTFrame对象包含这个段落的line (CTLine) 对象。每个line object代表一行文字。一个CTFrame对象可能包含仅仅一个长的CTLine对象,或者夜可能包含很多lines。Line对象是在一个framesetting操作中被typesetter创建的,像frames一样,可以直接把它们自己画到一个graphics context上。
每个CTLine对象包含一个glyph run (CTRun) objects的数组。一个glyph run是一个共享相同属性和方向的连贯的glyphs。当typesetter从character strings、attributes、font objects里生成lines时,同时生成glyph runs。意思是一个line由一个或者更多glyphs runs组成。Glyph runs可以把它们自己绘制到graphic context上,如果你想着么做的话,然而大部分客户端不需要直接和glyph runs交互。
字体对象
字体为相对于另一个glyphs来摆放glyphs提供帮助,并用于建立在绘制到graphics context上时使用的字体。CoreText字体类型,CTFont,是一个封装了许多信息的具体字体实例。它的引用类型,CTFontRef,和iOS中的UIFont、OS X中的NSFont是toll-free bridged的。当你创建一个CTFont对象,你通常设置(或使用默认的)字号和变换矩阵,给字体提供具体的特征。然后你可以查询当前字号下字体对象的许多信息,比如character-to-glyph mapping、encodings、font metric data和glyph data。Font metrics是类似ascent、descent、leading、cap height、x-height等等之类的参数。Glyph data包含类似bounding rectangles和glyph advances的参数。
CoreText字体对象是不可变的,所以它们可以同时在不同的operations、work queues或者threads中使用。有许多方法来创建字体对象。首选的方法是用CTFontCreateWithFontDescriptor来葱一个font descriptor创建。你也可以用一些替换的APIs,取决于你有什么数据。举个例子,你可以使用PostScript字体的名称(CTFontCreateWithName)或者一个Core Graphics字体引用(CTFontCreateWithGraphicsFont)。还可以使用CTFontCreateUIFontForLanguage,创建一个你的程序的本地化使用的用户界面字体的引用。
Core Text字体引用提供了一个被称为font cascading的复杂的、自动的字体替换机制,它可以选择一个合适的字体来替代缺失字体,同时考虑字体特征。Font cascading基于cascade lists(许多有序的字体descriptors数组)。有一个系统默认的cascade list(多态的, 基于用户语言设置和当前字体),还有一个在字体床建时具像化的cascade list。使用font descriptors里的信息,cascading机制可以根据风格匹配字体,同时匹配字符。CTFontCreateForString函数使用cascade lists挑选一个合适的字体来编码一个给定的string。为了制定或恢复字体cascade lists,使用kCTFontCascadeListAttribute属性。
Font Descriptors
Font Descriptors(CTFontDescriptor),提供一个完全用一个字典描述字体的机制,和一个容易使用的创建新字体的字体匹配设置。你可以从一个font descriptor生成一个字体对象,你可以从一个字体对象中获取一个descriptor,你还可以改变一个descriptor并用它生成一个新的字体对象。你可以创建一个font descriptor来部分描述一个字体,例如,只描述一个family name或者宽度,然后可以从系统中找到所有符合给定特征的字体。CTFontDescriptorRef类型和iOS中的UIFontDescriptor、OS X中的NSFontDescriptor是toll-free bridged的。
你可以创建一个字体属性的字典,这个字典包含类似PostScript name、font family and style、traits (for example, bold or italic) as a CTFontDescriptor object,而不是处理复杂的变换矩阵。你可以用font descriptor创建一个CTFont对象。Font descriptors可以被序列化并存储到文档里来为字体提供持久化。图1-2说明了字体系统用一个font descriptor创建一个具体的字体实例。
Figure 1-2 Creating a font from a font descriptor
你可以把font descriptors想象成字体系统里的查询。你可以用一个不完整的描述创建一个font descriptor,就是说,用一个或者只是属性字典中的少数值,系统将会从可用字体中挑选出最匹配的字体。例如,如果你用一个family name descriptor查询,没有指定其它的特征,将会匹配family中所有的字体。但是如果你用一个kCTFontTraitsAttribute是kCTFontTraitBold的字典来指定字体特征,结果将会减少到family中复合bold特征的字体。通过CTFontDescriptorCreateMatchingFontDescriptors系统可以给你一个符合查询的font descriptors的完整列表。
在iOS6.0之后,应用可以根据需求使用CTFontDescriptorMatchFontDescriptorsWithProgressHandler函数下载所有没有安装的可用字体。用这种方式下载的字体不会永久安装,系统在特定情况下会移除它们。可以下载的字体在“Additional Information”中的 iOS 6: Font list和iOS 7: Font list中列举。DownloadFont(in the iOS Developer Library)示范了下载技术。根据需求下载字体在OS X中是不必要的,因为所有的可以用字体都已经安装在系统中。
Font Collections
Font collections是一组font descriptors组成的一个单个对象。一个font collection用CTFontCollection类型来代表。Font collections提供字体美剧能力、全局和自定义font collections访问、访问组成collection的font descriptors。例如,你可以用CTFontCollectionCreateFromAvailableFonts创建一个所有系统可用字体的font collection,然后你可以用这个collection来获取所有的成员font descriptors的数组。