Core Text的使用

关于Core Text

Core Text是先进的,低级别的布局文本和处理字体的技术。Core Text的API包含在Mac OS X v10.5和IOS 3.2中,能被所有的OS X和IOS环境获取。

Core Text是关联Core Graphics框架(Quartz)的低级别文本处理技术。如果你需要直接使用Quartz并描绘一些文本,使用Core Text。例如,你拥有自己的页面布局引擎;你还有一些文本;你还知道在哪里布局文本;你可以使用Core Text来生成字形并根据所有的排版特征来互相放置它们,排版特征包括字距调整,连字,换行,连字符连接,对齐。

Core Text布局文本

Core Text生成字形(根据字符码和字体数据)和使用字形运行(glyph runs)互相放置它们。Core Text拆分字形运行成行并聚合行为多行的框架(例如,段落)。Core Text也提供字形和布局相关的数据,例如字形位置和行与框架(frames)的测量。它处理字符属性和段落样式;段落样式包含各种标签样式和定位。

你可以使用Core Text管理字体

Core Text字体API提供字体,字体集合,字体描述和简单地获取字体数据。它提供支持多个主字体,字体变更,字体串联,字体链接。除了Quartz,Core Text也提供加载自己字体到当前进程,也就是说,字体激活。


Core Text概述

Core Text是先进的,低级别的布局文本和处理字体的技术。Core Text直接和Core Graphics(CG,Quartz)工作;Quartz是高速的图形渲染引擎;它低级别地处理2维图形在OS X和IOS平台上。

Core Text调解文本布局和高级别字体框架以及Quartz的提供的低级别文本和字体框架。Quartz作用于字形和字形位置。Core Text知道字符怎样映射到字体和影响样式,字体度量和其他Quartz渲染文本前的属性信息。Quartz是在基础级别的唯一方式进行字形描绘,因为Core Text提供所有可以被Quartz直接使用的数据形式,这样可以进行高性能的文本渲染。

多线程:Core Text函数可能涉及多线程同步;客户端不能修改任何被多条线程共享的参数,例如属性字符串。

Core Text是基于C语言的,平台无关的API

在IOS和OS X,Core Text的API几乎是一样的,虽然OS X版本提供更丰富的字体管理API,包括可变文本集合。然而,这里有一些不同在UIKit和APPKit,你在两个平台编写代码时必须考虑到这个问题。

UIView的UIGraphicsGetCurrentContext函数返回的图形上下文相对于没有修改的Quartz图形上下文是被翻过的(UIView的上下文原点在左上方)。

Core Text使用系统数据类型并尽可能地服务,你可以使用相同的约定在其他核心框架。Core Text使用Core Foundation对象作为很多输入和输出参数,所以你可以使用Core Foundation集合来存储它们。其他Core Text处理的对象,例如Core Graphics框架提供的CGPath对象。

Core Text对象是C语言的不透明对象类型

很多OS X和IOS下的低级别库是用C语言编写的,这样会比较高速和简单。当使用Core Text,你使用一组C函数,例如CTFramesetterCreateWithAttributedString和CTFramesetterCreateFrame函数,而不是OC对象和方法。

Core Text不透明类型

Core Text布局引擎经常使用属性字符串(CFAttributedStringRef)和图形路径(CGPathRef)。属性字符串封装了需要展示的字符串和属性(properties或者attributes);属性定义字符的样式,例如字体和颜色。Core Text的排版装置使用属性字符串的信息来进行字符到字形的转换。

图形路径定义文本框架的形状。在OS X v10.7和IOS 3.2之后,路径可以不为矩形。

CFAttributedString引用类型,CFAttributedStringRef,它能对象桥接(toll-free bridged)到对应的Foundation框架的NSAttributedString对象。这意味着Core Foundation类型是可转换的;在函数或者方法调用中使用可桥接的Foundation对象。因此,在方法中看到NSAttributedString * 参数,你可以传递CFAttributedStringRef;在函数中看到CFAttributedStringRef参数,你可以传递NSAttributedString实例(你可能需要抑制编译器警告来转换类型)。你可以应用具体的NSAttributedString子类。

属性是定义字符串字符样式特征的键值对,这些字符通过范围(range)分组并共享相同的属性。属性本身被传递到属性字符串并从属性字符串以CFDictionary对象获取它。为了应用样式到字形运行(CTRun对象),创建CFDictionary对象去保存这个属性,之后创建属性字符串,传递这个字典作为参数。或者,你可以应用属性到已经存在的CFMutableAttributedString对象。虽然CFDictionaryRef和NSDictionary是对象桥接(toll-free bridged),个别存储在字典的属性对象可能不是。

Core Text对象在运行时(runtime)形成层次结构。在层级的顶层是framesetter对象(CTFramesetterRef)。以属性字符串和图形路径作为输入,framesetter生成一个或者多个文本的框架(CTFrameRef)。每个CTFrame对象代表一个段落。


为了生成框架,framesetter调用一个typesetter对象(CTTypesetterRef)。当布局文本到框架,framesetter对它应用段落样式,它包含对齐,制表符设置,行间距,缩进,换行模式。typesetter转换属性字符串的字符到字形并调整这些字形为填充文本框架的行。

每个CTFrame对象包含段落行对象(CTLine)。每个行对象代表一行文本。一个CTFrame对象可能包含一个单独的长CTLine对象或者它包含一组行对象。在framesetting操作中,typesetter创建行对象,它像框架对象一样,可以直接描绘自己到图形上下文。

每个CTLine对象包含一组字形运行对象(CTRun)。字形运行是一组连续的字形,它们共享相同的属性和方向。当typesetter从字符字符串,属性,字体对象创建行对象时,会创建字形运行。这意味着行是由一个或者多个字形运行组成。字形运行能够描绘自己到图形上下文,虽然大多数客户端没必要直接和字形运行交互。

字体对象

在相对于另外一个字形进行字形布局和建立当前字体并描绘到图形上下文, 字体对象提供帮助。Core Text字体不透明类型,CTFont,是一个封装了很多信息的特殊字体实例。它的引用类型,CTFontRef,是和IOS的UIFont和OS X的NSFont对象进行对象桥接。当你创建CTFont对象,你可以指定(或者使用默认)点大小和转换矩阵,它给与字体实例指定的特征。你可以查询字体对象的很多信息,例如点大小,字符到字形的映射,编码,字体度量数据等其他信息。字体度量是一组参数,例如ascent,descent, leading,cap height,x-height等。字形数据包括多个参数,例如边界矩阵和字形前进(glyph advances)。

Core Text的字体对象是不可变的,它可以被多个操作对象和工作队列或者线程同时使用。这里有很多方式创建字体对象。最佳的方法是使用字体描述(CTFontCreateWithFontDescriptor)来创建。你可以使用大量便利API。例如,你可以使用字体的附言(PostScript)名称(CTFontCreateWithName)来创建字体或者Core Graphics字体引用(CTFontCreateWithGraphicsFont)。也可以使用CTFontCreateUIFontForLanguage函数创建本地化应用的用户界面字体引用。

Core Text字体引用提供复杂,自动的字体替换机制;这个机制叫做级联。在获取字体特征时,它为丢失的字体选择适当的字体。字体级联基于级联列表;它是一个有序的字体描述数组。系统默认级联列表是多态的,基于用户语言设置和当前字体。在字体创建时,字体级联列表会被指定。通过使用字体描述信息,这个机制根据样式和匹配的字符来匹配字体。CTFontCreateForString函数使用级联列表来获取适当的字体并编码提供的字符串。为了指定和获取字体级联列表,使用字体对象的kCTFontCascadeListAttribute属性。

字体描述

字体描述使用CTFontDescriptor不透明类型来代表,它可以通过属性字典来描述描述字体并且能简单使用字体匹配来创建字体。你可以使用字体描述来创建字体;你可以提供字体对象来获取字体描述;你可以改变字体描述并使用它创建新的字体对象。你可以通过字体描述来部分描述一个字体,例如,只是字体族名和权重,之后就可以发现所有匹配的这些内容的系统字体。CTFontDescriptorRef类型能和IOS的UIFontDescriptor以及OS X的NSFontDescriptor进行对象桥接。

并不是处理复杂的转换矩阵,而是创建一个字体属性字典作为CTFontDescriptor对象;这个字典包括附言名称(PostScript),字体族,样式,特征(粗体或者斜体)。你可以使用这个描述来创建CTFont对象。字体描述能被序列化并保存在文档。


你可以认为字体描述是对字体系统的查询。你可以使用一个不完整的说明来创建字体描述,也就是说,在属性字典使用一个或者几个值,系统会通过这些可用信息来选择最适合的字体。例如,如果你使用字体族名的字体描述进行查询(普通、粗体、斜体、粗斜体)又没有指定任何特征将会匹配字体族的所有特征;你可以指定带有kCTFontTraitsAttribute和kCTFontTraitBold的特征字典,获取的结果会更窄,它只包含指定族名和满足粗体特征的字体。CTFontDescriptorCreateMatchingFontDescriptors提供匹配查询的字体描述列表。

在IOS 6。0之后,可以使用CTFontDescriptorMatchFontDescriptorsWithProgressHandler函数下载和请求各种没有安装的字体。字体下载不会永久地安装字体,系统可能在某些情况下删除它们。可用下载的字体被列在IOS6:和IOS7:字体列表的“额外信息”。

字体集合

字体集合会组合一组字体描述为单独的对象。字体集合使用CTFontCollection不透明类型。字体集合提供字体遍历,获取全局和自定义字体集合以及字体描述集合的功能。你可以调用CTFontCollectionCreateFromAvailableFonts创建所有可用的字体的集合;你可以使用这个集合获取所有子集的字体描述数组。


通用文本布局操作
布局段落

其中一个最通用的排版操作是在任意矩形区域中布局多行段落。Core Text让这种操作变得简单,只需要几行的Core Text代码。为了排版段落,你需要一个图形上下文进行绘制,文本布局区域的矩形路径,属性字符串。

// Initialize a graphics context in iOS.
CGContextRef context = UIGraphicsGetCurrentContext();
 
// Flip the context coordinates, in iOS only.
CGContextTranslateCTM(context, 0, self.bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
 
// Initializing a graphic context in OS X is different:
// CGContextRef context =
//     (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
 
// Set the text matrix.
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
 
// Create a path which bounds the area where you will be drawing text.
// The path need not be rectangular.
CGMutablePathRef path = CGPathCreateMutable();
 
// In this simple example, initialize a rectangular path.
CGRect bounds = CGRectMake(10.0, 10.0, 200.0, 200.0);
CGPathAddRect(path, NULL, bounds );
 
// Initialize a string.
CFStringRef textString = CFSTR("Hello, World! I know nothing in the world that has as much power as a word. Sometimes I write one, and I look at it, until it begins to shine.");
 
// Create a mutable attributed string with a max length of 0.
// The max length is a hint as to how much internal storage to reserve.
// 0 means no hint.
CFMutableAttributedStringRef attrString =
         CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);
 
// Copy the textString into the newly created attrString
CFAttributedStringReplaceString (attrString, CFRangeMake(0, 0),
         textString);
 
// Create a color that will be added as an attribute to the attrString.
CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat components[] = { 1.0, 0.0, 0.0, 0.8 };
CGColorRef red = CGColorCreate(rgbColorSpace, components);
CGColorSpaceRelease(rgbColorSpace);
 
// Set the color of the first 12 chars to red.
CFAttributedStringSetAttribute(attrString, CFRangeMake(0, 12),
         kCTForegroundColorAttributeName, red);
 
// Create the framesetter with the attributed string.
CTFramesetterRef framesetter =
         CTFramesetterCreateWithAttributedString(attrString);
CFRelease(attrString);
 
// Create a frame.
CTFrameRef frame = CTFramesetterCreateFrame(framesetter,
          CFRangeMake(0, 0), path, NULL);
 
// Draw the specified frame in the given context.
CTFrameDraw(frame, context);
 
// Release the objects we used.
CFRelease(frame);
CFRelease(path);
CFRelease(framesetter);
简单的文本标签

另一个通用的排版操作就是描绘单行文本就像使用图形界面的标签(label)。在Core Text,只需要2行代码:使用CFAttributedString创建行对象和描绘行对象到图形上下文。

下面的代码是在drawRect: 方法中执行,它省略文本字符串,字体,图形上下文的创建。

CFStringRef string; CTFontRef font; CGContextRef context;
// Initialize the string, font, and context
 
CFStringRef keys[] = { kCTFontAttributeName };
CFTypeRef values[] = { font };
 
CFDictionaryRef attributes =
    CFDictionaryCreate(kCFAllocatorDefault, (const void**)&keys,
        (const void**)&values, sizeof(keys) / sizeof(keys[0]),
        &kCFTypeDictionaryKeyCallBacks,
        &kCFTypeDictionaryValueCallBacks);
 
CFAttributedStringRef attrString =
    CFAttributedStringCreate(kCFAllocatorDefault, string, attributes);
CFRelease(string);
CFRelease(attributes);
 
CTLineRef line = CTLineCreateWithAttributedString(attrString);
 
// Set text position and draw the line into the graphics context
CGContextSetTextPosition(context, 10.0, 10.0);
CTLineDraw(line, context);
CFRelease(line);
柱状布局

布局多列的文本是另一通用排版操作。严格地讲,Core Text本身只能一次布局一列而且不会计算列的大小或者位置。你需要做这些操作在调用Core Text布局文本到你计算的区域。在下面的例子中,Core Text,除了布局每一列的文本还要提供每一列的子文本。

下面的createColumnsWithColumnCount: 方法接收列数参数并返回一组路径,每个路径代表一列。

之后的例子是实现在drawRect: 方法,它会调用createColumnsWithColumnCount方法。

- (CFArrayRef)createColumnsWithColumnCount:(int)columnCount
{
    int column;
 
    CGRect* columnRects = (CGRect*)calloc(columnCount, sizeof(*columnRects));
    // Set the first column to cover the entire view.
    columnRects[0] = self.bounds;
 
    // Divide the columns equally across the frame's width.
    CGFloat columnWidth = CGRectGetWidth(self.bounds) / columnCount;
    for (column = 0; column < columnCount - 1; column++) {
        CGRectDivide(columnRects[column], &columnRects[column],
                     &columnRects[column + 1], columnWidth, CGRectMinXEdge);
    }
 
   // Inset all columns by a few pixels of margin.
    for (column = 0; column < columnCount; column++) {
        columnRects[column] = CGRectInset(columnRects[column], 8.0, 15.0);
    }
 
    // Create an array of layout paths, one for each column.
    CFMutableArrayRef array =
                     CFArrayCreateMutable(kCFAllocatorDefault,
                                  columnCount, &kCFTypeArrayCallBacks);
 
    for (column = 0; column < columnCount; column++) {
        CGMutablePathRef path = CGPathCreateMutable();
        CGPathAddRect(path, NULL, columnRects[column]);
        CFArrayInsertValueAtIndex(array, column, path);
        CFRelease(path);
    }
    free(columnRects);
    return array;
}
// Override drawRect: to draw the attributed string into columns.
// (In OS X, the drawRect: method of NSView takes an NSRect parameter,
//  but that parameter is not used in this listing.)
- (void)drawRect:(CGRect)rect
{
    // Initialize a graphics context in iOS.
    CGContextRef context = UIGraphicsGetCurrentContext();
 
    // Flip the context coordinates in iOS only.
    CGContextTranslateCTM(context, 0, self.bounds.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);
 
    // Initializing a graphic context in OS X is different:
    // CGContextRef context =
    //     (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
 
    // Set the text matrix.
    CGContextSetTextMatrix(context, CGAffineTransformIdentity);
 
    // Create the framesetter with the attributed string.
    CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(
                                      (CFAttributedStringRef)self.attributedString);
 
    // Call createColumnsWithColumnCount function to create an array of
    // three paths (columns).
    CFArrayRef columnPaths = [self createColumnsWithColumnCount:3];
 
    CFIndex pathCount = CFArrayGetCount(columnPaths);
    CFIndex startIndex = 0;
    int column;
 
    // Create a frame for each column (path).
    for (column = 0; column < pathCount; column++) {
        // Get the path for this column.
        CGPathRef path = (CGPathRef)CFArrayGetValueAtIndex(columnPaths, column);
 
        // Create a frame for this column and draw it.
        CTFrameRef frame = CTFramesetterCreateFrame(
                             framesetter, CFRangeMake(startIndex, 0), path, NULL);
        CTFrameDraw(frame, context);
 
        // Start the next frame at the first character not visible in this frame.
        CFRange frameRange = CTFrameGetVisibleStringRange(frame);
        startIndex += frameRange.length;
        CFRelease(frame);
 
    }
    CFRelease(columnPaths);
    CFRelease(framesetter);
 
}
手动换行

在Core Text,你通常不需要手动换行除非你有一个特殊的连字符处理或者相似的请求。framesetter会自动执行换行。另外,Core Text允许你明确指定哪一行文本换行。这个例子展示在描绘前怎样居中行。

这个例子在drawRect: 方法中。

double width; CGContextRef context; CGPoint textPosition; CFAttributedStringRef attrString;
// Initialize those variables.
 
// Create a typesetter using the attributed string.
CTTypesetterRef typesetter = CTTypesetterCreateWithAttributedString(attrString);
 
// Find a break for line from the beginning of the string to the given width.
CFIndex start = 0;
CFIndex count = CTTypesetterSuggestLineBreak(typesetter, start, width);
 
// Use the returned character count (to the break) to create the line.
CTLineRef line = CTTypesetterCreateLine(typesetter, CFRangeMake(start, count));
 
// Get the offset needed to center the line.
float flush = 0.5; // centered
double penOffset = CTLineGetPenOffsetForFlush(line, flush, width);
 
// Move the given text drawing position by the calculated offset and draw the line.
CGContextSetTextPosition(context, textPosition.x + penOffset, textPosition.y);
CTLineDraw(line, context);
 
// Move the index beyond the line break.
start += count;
应用段落样式
NSAttributedString* applyParaStyle(
                CFStringRef fontName , CGFloat pointSize,
                NSString *plainText, CGFloat lineSpaceInc){
 
    // Create the font so we can determine its height.
    CTFontRef font = CTFontCreateWithName(fontName, pointSize, NULL);
 
    // Set the lineSpacing.
    CGFloat lineSpacing = (CTFontGetLeading(font) + lineSpaceInc) * 2;
 
    // Create the paragraph style settings.
    CTParagraphStyleSetting setting;
 
    setting.spec = kCTParagraphStyleSpecifierLineSpacing;
    setting.valueSize = sizeof(CGFloat);
    setting.value = &lineSpacing;
 
    CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(&setting, 1);
 
    // Add the paragraph style to the dictionary.
    NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:
                               (__bridge id)font, (id)kCTFontNameAttribute,
                               (__bridge id)paragraphStyle,
                               (id)kCTParagraphStyleAttributeName, nil];
    CFRelease(font);
    CFRelease(paragraphStyle);
 
    // Apply the paragraph style to the string to created the attributed string.
    NSAttributedString* attrString = [[NSAttributedString alloc]
                               initWithString:(NSString*)plainText
                               attributes:attributes];
 
    return attrString;
}

描绘段落样式

- (void)drawRect:(CGRect)rect {
    // Initialize a graphics context in iOS.
    CGContextRef context = UIGraphicsGetCurrentContext();
 
    // Flip the context coordinates in iOS only.
    CGContextTranslateCTM(context, 0, self.bounds.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);
 
    // Set the text matrix.
    CGContextSetTextMatrix(context, CGAffineTransformIdentity);
 
    CFStringRef fontName = CFSTR("Didot Italic");
    CGFloat pointSize = 24.0;
 
    CFStringRef string = CFSTR("Hello, World! I know nothing in the world that has
                                   as much power as a word. Sometimes I write one,
                                   and I look at it, until it begins to shine.");
 
    // Apply the paragraph style.
    NSAttributedString* attrString = applyParaStyle(fontName, pointSize, string, 50.0);
 
    // Put the attributed string with applied paragraph style into a framesetter.
    CTFramesetterRef framesetter =
             CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attrString);
 
    // Create a path to fill the View.
    CGPathRef path = CGPathCreateWithRect(rect, NULL);
 
    // Create a frame in which to draw.
    CTFrameRef frame = CTFramesetterCreateFrame(
                                    framesetter, CFRangeMake(0, 0), path, NULL);
 
    // Draw the frame.
    CTFrameDraw(frame, context);
    CFRelease(frame);
    CGPathRelease(path);
    CFRelease(framesetter);
}
在非矩形区域展示文本
// Create a path in the shape of a donut.
static void AddSquashedDonutPath(CGMutablePathRef path,
              const CGAffineTransform *m, CGRect rect)
{
    CGFloat width = CGRectGetWidth(rect);
    CGFloat height = CGRectGetHeight(rect);
 
    CGFloat radiusH = width / 3.0;
    CGFloat radiusV = height / 3.0;
 
    CGPathMoveToPoint( path, m, rect.origin.x, rect.origin.y + height - radiusV);
    CGPathAddQuadCurveToPoint( path, m, rect.origin.x, rect.origin.y + height,
                               rect.origin.x + radiusH, rect.origin.y + height);
    CGPathAddLineToPoint( path, m, rect.origin.x + width - radiusH,
                               rect.origin.y + height);
    CGPathAddQuadCurveToPoint( path, m, rect.origin.x + width,
                               rect.origin.y + height,
                               rect.origin.x + width,
                               rect.origin.y + height - radiusV);
    CGPathAddLineToPoint( path, m, rect.origin.x + width,
                               rect.origin.y + radiusV);
    CGPathAddQuadCurveToPoint( path, m, rect.origin.x + width, rect.origin.y,
                               rect.origin.x + width - radiusH, rect.origin.y);
    CGPathAddLineToPoint( path, m, rect.origin.x + radiusH, rect.origin.y);
    CGPathAddQuadCurveToPoint( path, m, rect.origin.x, rect.origin.y,
                               rect.origin.x, rect.origin.y + radiusV);
    CGPathCloseSubpath( path);
 
    CGPathAddEllipseInRect( path, m,
                            CGRectMake( rect.origin.x + width / 2.0 - width / 5.0,
                            rect.origin.y + height / 2.0 - height / 5.0,
                            width / 5.0 * 2.0, height / 5.0 * 2.0));
}
 
// Generate the path outside of the drawRect call so the path is calculated only once.
- (NSArray *)paths
{
    CGMutablePathRef path = CGPathCreateMutable();
    CGRect bounds = self.bounds;
    bounds = CGRectInset(bounds, 10.0, 10.0);
    AddSquashedDonutPath(path, NULL, bounds);
 
    NSMutableArray *result =
              [NSMutableArray arrayWithObject:CFBridgingRelease(path)];
    return result;
}
 
- (void)drawRect:(CGRect)rect
{
    [super drawRect:rect];
 
    // Initialize a graphics context in iOS.
    CGContextRef context = UIGraphicsGetCurrentContext();
 
    // Flip the context coordinates in iOS only.
    CGContextTranslateCTM(context, 0, self.bounds.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);
 
    // Set the text matrix.
    CGContextSetTextMatrix(context, CGAffineTransformIdentity);
 
    // Initialize an attributed string.
    CFStringRef textString = CFSTR("Hello, World! I know nothing in the world that
    has as much power as a word. Sometimes I write one, and I look at it,
    until it begins to shine.");
 
    // Create a mutable attributed string.
     CFMutableAttributedStringRef attrString =
                CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);
 
    // Copy the textString into the newly created attrString.
    CFAttributedStringReplaceString (attrString, CFRangeMake(0, 0), textString);
 
    // Create a color that will be added as an attribute to the attrString.
    CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
    CGFloat components[] = { 1.0, 0.0, 0.0, 0.8 };
    CGColorRef red = CGColorCreate(rgbColorSpace, components);
    CGColorSpaceRelease(rgbColorSpace);
 
    // Set the color of the first 13 chars to red.
    CFAttributedStringSetAttribute(attrString, CFRangeMake(0, 13),
                                     kCTForegroundColorAttributeName, red);
 
    // Create the framesetter with the attributed string.
    CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(attrString);
 
    // Create the array of paths in which to draw the text.
    NSArray *paths = [self paths];
 
    CFIndex startIndex = 0;
 
    // In OS X, use NSColor instead of UIColor.
    #define GREEN_COLOR [UIColor greenColor]
    #define YELLOW_COLOR [UIColor yellowColor]
    #define BLACK_COLOR [UIColor blackColor]
 
    // For each path in the array of paths...
    for (id object in paths) {
        CGPathRef path = (__bridge CGPathRef)object;
 
        // Set the background of the path to yellow.
        CGContextSetFillColorWithColor(context, [YELLOW_COLOR CGColor]);
 
        CGContextAddPath(context, path);
        CGContextFillPath(context);
 
        CGContextDrawPath(context, kCGPathStroke);
 
        // Create a frame for this path and draw the text.
        CTFrameRef frame = CTFramesetterCreateFrame(framesetter,
                                         CFRangeMake(startIndex, 0), path, NULL);
        CTFrameDraw(frame, context);
 
        // Start the next frame at the first character not visible in this frame.
        CFRange frameRange = CTFrameGetVisibleStringRange(frame);
        startIndex += frameRange.length;
        CFRelease(frame);
}
 
CFRelease(attrString);
CFRelease(framesetter);
}

通用字体操作
创建字体描述

指定PostScript字体名称和点大小来创建字体描述。

CTFontDescriptorRef CreateFontDescriptorFromName(CFStringRef postScriptName,
                                                  CGFloat size)
{
    return CTFontDescriptorCreateWithNameAndSize(postScriptName, size);
}

使用字体族名和字体特征来创建字体描述。

NSString* familyName = @"Papyrus";
CTFontSymbolicTraits symbolicTraits = kCTFontTraitCondensed;
CGFloat size = 24.0;
 
NSMutableDictionary* attributes = [NSMutableDictionary dictionary];
[attributes setObject:familyName forKey:(id)kCTFontFamilyNameAttribute];
 
// The attributes dictionary contains another dictionary, the traits dictionary,
// which in this example specifies only the symbolic traits.
NSMutableDictionary* traits = [NSMutableDictionary dictionary];
[traits setObject:[NSNumber numberWithUnsignedInt:symbolicTraits]
                                           forKey:(id)kCTFontSymbolicTrait];
 
[attributes setObject:traits forKey:(id)kCTFontTraitsAttribute];
[attributes setObject:[NSNumber numberWithFloat:size]
                                         forKey:(id)kCTFontSizeAttribute];
 
CTFontDescriptorRef descriptor =
             CTFontDescriptorCreateWithAttributes((CFDictionaryRef)attributes);
CFRelease(descriptor);
使用字体描述创建字体
NSDictionary *fontAttributes =
                  [NSDictionary dictionaryWithObjectsAndKeys:
                          @"Courier", (NSString *)kCTFontFamilyNameAttribute,
                          @"Bold", (NSString *)kCTFontStyleNameAttribute,
                          [NSNumber numberWithFloat:16.0],
                          (NSString *)kCTFontSizeAttribute,
                          nil];
// Create a descriptor.
CTFontDescriptorRef descriptor =
          CTFontDescriptorCreateWithAttributes((CFDictionaryRef)fontAttributes);
 
// Create a font using the descriptor.
CTFontRef font = CTFontCreateWithFontDescriptor(descriptor, 0.0, NULL);
CFRelease(descriptor);
创建相关字体

你可以转换存在的字体为相关或者相似的字体。

CTFontRef CreateBoldFont(CTFontRef font, Boolean makeBold)
{
    CTFontSymbolicTraits desiredTrait = 0;
    CTFontSymbolicTraits traitMask;
 
    // If requesting that the font be bold, set the desired trait
    // to be bold.
    if (makeBold) desiredTrait = kCTFontBoldTrait;
 
    // Mask off the bold trait to indicate that it is the only trait
    // to be modified. As CTFontSymbolicTraits is a bit field,
    // could change multiple traits if desired.
    traitMask = kCTFontBoldTrait;
 
    // Create a copy of the original font with the masked trait set to the
    // desired value. If the font family does not have the appropriate style,
    // returns NULL.
 
    return CTFontCreateCopyWithSymbolicTraits(font, 0.0, NULL, desiredTrait, traitMask);
}
CTFontRef CreateFontConvertedToFamily(CTFontRef font, CFStringRef family)
{
    // Create a copy of the original font with the new family. This call
    // attempts to preserve traits, and may return NULL if that is not possible.
    // Pass in 0.0 and NULL for size and matrix to preserve the values from
    // the original font.
 
    return CTFontCreateCopyWithFamily(font, 0.0, NULL, family);
}
序列化字体
CFDataRef CreateFlattenedFontData(CTFontRef font)
{
    CFDataRef           result = NULL;
    CTFontDescriptorRef descriptor;
    CFDictionaryRef     attributes;
 
    // Get the font descriptor for the font.
    descriptor = CTFontCopyFontDescriptor(font);
 
    if (descriptor != NULL) {
        // Get the font attributes from the descriptor. This should be enough
        // information to recreate the descriptor and the font later.
        attributes = CTFontDescriptorCopyAttributes(descriptor);
 
        if (attributes != NULL) {
            // If attributes are a valid property list, directly flatten
            // the property list. Otherwise we may need to analyze the attributes
            // and remove or manually convert them to serializable forms.
            // This is left as an exercise for the reader.
           if (CFPropertyListIsValid(attributes, kCFPropertyListXMLFormat_v1_0)) {
                result = CFPropertyListCreateXMLData(kCFAllocatorDefault, attributes);
            }
        }
    }
    return result;
}
使用序列化数据创建字体
CTFontRef CreateFontFromFlattenedFontData(CFDataRef iData)
{
    CTFontRef           font = NULL;
    CFDictionaryRef     attributes;
    CTFontDescriptorRef descriptor;
 
    // Create our font attributes from the property list.
    // For simplicity, this example creates an immutable object.
    // If you needed to massage or convert certain attributes
    // from their serializable form to the Core Text usable form,
    // do it here.
    attributes =
          (CFDictionaryRef)CFPropertyListCreateFromXMLData(
                               kCFAllocatorDefault,
                               iData, kCFPropertyListImmutable, NULL);
    if (attributes != NULL) {
        // Create the font descriptor from the attributes.
        descriptor = CTFontDescriptorCreateWithAttributes(attributes);
        if (descriptor != NULL) {
            // Create the font from the font descriptor. This sample uses
            // 0.0 and NULL for the size and matrix parameters. This
            // causes the font to be created with the size and/or matrix
            // that exist in the descriptor, if present. Otherwise default
            // values are used.
            font = CTFontCreateWithFontDescriptor(descriptor, 0.0, NULL);
        }
    }
    return font;
}
改变字距调整
连字和字距调整默认是激活的。你可以设置kCTKernAttributeName为0来失效它。
 // Set the color of the first 13 characters to red
 // using a previously defined red CGColor object.
 CFAttributedStringSetAttribute(attrString, CFRangeMake(0, 13),
                                      kCTForegroundColorAttributeName, red);
 
 // Set kerning between the first 18 chars to be 20
 CGFloat otherNum = 20;
 CFNumberRef otherCFNum = CFNumberCreate(NULL, kCFNumberCGFloatType, &otherNum);
 CFAttributedStringSetAttribute(attrString, CFRangeMake(0,18),
                                           kCTKernAttributeName, otherCFNum);
获取字符的字形
void GetGlyphsForCharacters(CTFontRef font, CFStringRef string)
{
    // Get the string length.
    CFIndex count = CFStringGetLength(string);
 
    // Allocate our buffers for characters and glyphs.
    UniChar *characters = (UniChar *)malloc(sizeof(UniChar) * count);
    CGGlyph *glyphs = (CGGlyph *)malloc(sizeof(CGGlyph) * count);
 
    // Get the characters from the string.
    CFStringGetCharacters(string, CFRangeMake(0, count), characters);
 
    // Get the glyphs for the characters.
    CTFontGetGlyphsForCharacters(font, characters, glyphs, count);
 
    // Do something with the glyphs here. Characters not mapped by this font will be zero.
    // ...
 
    // Free the buffers
    free(characters);
    free(glyphs);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值