Core Text 是一种用于处理字体和文本布局的底层高级技术,自 Mac OS X v10.5 和 iOS 3.2 开始引入,你可以从所有 iOS 及 OS X 的开发环境中使用其 API。
重要:Core Text 是为一些必须处理底层字体处理和文字布局的开发者准备,如无必要,你应该使用 TextKit(Text Programming Guide for iOS)、CocoaText(Cocoa Text Architecture Guide)等框架开发你的 App 或 Mac 应用。Core Text 是以上两种文本框架的底层实现,因此它们的速度和效率是共享的。除此之外,以上两种文本框架提供了富文本编辑及页面布局引擎。如果你的 App 只使用 Core Text,则需要为其提供其他的基础实现。
省略了一些摘要和相关文档,基本后面都会提到,有需要看原文。
复制代码
概述
Core Text 直接与 Core Graphics(Quartz)协调作业。Quartz 是一个图形渲染引擎,可以在 iOS 及 OS X 中处理最底层的二维图像生成。
Core Text 是为高级框架中的文本布局及字体处理功能提供支持的中间层,Quartz 则为所有的文本和字体框架提供更为底层的支持,Quartz 作用于文本的字形(CTGlyphInfo)和位置,而 Core Text 清楚如何为字符匹配字体,在调用 Quartz 绘制文本之前,它会处理文本的样式,字体规格和其他属性,Quartz 是获取基本级别绘制字形的唯一方式。因为 Core Text 直接提供了 Quartz 可用数据,因此其文本绘制性能很高。
如果客户端不改变线程之间共享的任何参数(如 attributed strings),则可以同时从多个线程调用 Core Text 函数。
Core Text 是基于 C 的跨平台 API
Core Text API 在 iOS 和 OS X 上几乎相同,不过 OS X 版本上提供了更加丰富的字体管理 API,包括可变字体集合。虽然 API 相差不多,但是 UIKit 和 AppKit 之间存在差异,如在平台之间移植代码时需要考虑到这些差异。例如,你需要一个 Quartz 图形上下文来绘制 Core Text 生成的字形,而你在每个平台上获得的图形上下文并非一样。因为 iOS 中绘制的视图是UIView,OS X 中则是NSView。你需要知道的是CGRect对象是传递给UIViewdrawRect:方法的,而 OS X 版本drawRect:是传递给NSRect对象的。(OS X 中使用NSRectToCGRect函数可将传入的NSRect对象转换为CGRect 作为 Core Text 函数参数所需的对象。)
由UIView函数UIGraphicsGetCurrentContext返回的图形上下文相对于未经修改的 Quartz 图形上下文(UIView返回的图形上下文,原点在左上角)进行了翻转,因此在 iOS 中你需要翻转 Quartz 图形上下文,而在 OS X 中则不必如此,有关此技术的示例代码,参阅布局一个段落小节。
Core Text 使用了与 OS X 和 iOS 中其他核心框架相同的约定,并且尽可能的使用系统数据类型和服务,举例来说,Core Text 中许多入参和返回值是 Core Foundation 对象。因此你可以将它们存储在 Core Foundation 集合类中,Core Text 使用的其他对象,如CGPath对象,实际由 Core Graphics 提供支持。
Core Text 对象是 C 的不透明类型
为了速度和简洁性,iOS 和 OS X 中的许多底层库使用 C 编写。因此使用 Core Text 时,你需要使用使用 C 函数,例如CTFramesetterCreateWithAttributedString,CTFramesetterCreateFrame,而不是 OC 的类与方法。
Core Tex 不透明类型
Core Text 的布局作业通常需要由属性字符串(CFAttributedStringRef)和图形路径(CGPathRef)共同完成,CFAttributedStringRef 包含需要绘制的字符串、字符的样式属性(如颜色和字体)。Core Text 中的排版机制使用其中的信息,完成字符到字形的转换。
CGPathRef 定义了文本绘制区域的形状。在 OS X 10.7 和 iOS 3.2 及更高版本中,路径可以是非矩形的。
CFAttributedString 的引用类型 CFAttributedStringRef 可与 NSAttributedString 桥接,这意味着在函数中,你可以在 Core Foundation 类型和桥接类型间进行转换。因此,在看到参数为NSAttributedString *的方法中,可以传入CFAttributedStringRef,在看到参数为CFAttributedStringRef的函数中,可以传入NSAttributedString及其子类的实例。(你可能需要将一种类型转换为另一种类型,以消除编译器警告。)
排版中最常见的操作之一是在任意大小的矩形区域内布局多行的段落。Core Text 使此操作变得简单,只需要几行特定的 Core Text 的代码。如要布局段落,你需要获得绘制图形上下文(CGContext),文本布局路径(CGPath)以及属性字符串(CFAttributedString),文本布局路径则需要一个矩形路径(CGReact)。这个例子中大多数代码都需要创建和初始化上下文,路径和字符串。完成此操作后,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);
复制代码
// 获取图形上下文。guardlet context = UIGraphicsGetCurrentContext() else { return }
// 翻转上下文坐标,仅iOS。
context.translateBy(x: 0, y: self.bounds.size.height)
context.scaleBy(x: 1.0, y: -1.0)
// 设置文本绘制的矩形
context.textMatrix = .identity
// 创建一个绘制文本的区域的路径 不必是矩形let path = CGMutablePath()
// 初始化一个矩形路径。let bounds = CGRect(x: 10.0, y: 10.0, width: 200.0, height: 200.0)
path.addRect(bounds, transform: .identity)
// 初始化一个字符串let textString = "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."asCFString// 创建一个最大长度为0的可变属性字符串// kCFAllocatorDefault表示要一个内存分配器// 0表示最大长度guardlet attrString = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0) else { return }
// 将textString复制到新创建的attrString中CFAttributedStringReplaceString(attrString, CFRangeMake(0, 0), textString)
// 创建一个将添加到attrString的颜色属性。let rgbColorSpace = CGColorSpaceCreateDeviceRGB()
var components: [CGFloat] = [1.0, 0.0, 0.0, 0.8]
let red = CGColor(colorSpace: rgbColorSpace, components: &components)
// 将前12个字符的颜色设置为红色.CFAttributedStringSetAttribute(attrString, CFRangeMake(0, 12), kCTForegroundColorAttributeName, red)
// 使用属性字符串创建framesetter。let framesetter = CTFramesetterCreateWithAttributedString(attrString asCFAttributedString)
// 创建一个framelet frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, nil)
// 在给定的frame绘制上下文CTFrameDraw(frame, context)
复制代码
简单的文字标签
还有一种常见的排版操作是绘制单行文本以用作于用户界面元素的标签(label)。在 Core Text 中,这只需要两行代码:一行创建具有 CFAttributedString 的 CTLine 对象,另一行将 CTLine 绘制到图形上下文中。
以下代码展示了如何在UIView或NSView的drawRect:方法中完成此操作。代码中省略了plain text string、font、graphics context等已在本文档其他小节中展示过的操作,展示了如何创建属性字典并用其创建attributed string。(字体创建展示在创建字体描述和根据字体描述创建字体小节。)
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);
复制代码
// 初始化 string, font, and contextlet string: CFString = "Hello, World! I know nothing in the world that has as much power as a word"asCFStringlet font: CTFont = CTFontCreateUIFontForLanguage(.label, 28, nil)!
let context: CGContext = UIGraphicsGetCurrentContext()!
// 1// let attributes = [kCTFontAttributeName : font] as CFDictionary// 2let key = UnsafeMutablePointer<CFString>.allocate(capacity: 1)
key.initialize(to: kCTFontAttributeName)
let keyPointer = unsafeBitCast(key, to: UnsafeMutablePointer<UnsafeRawPointer?>.self)
defer {
keyPointer.deinitialize(count: 1)
keyPointer.deallocate()
}
let value = UnsafeMutablePointer<CTFont>.allocate(capacity: 1)
value.initialize(to: font)
let valuePointer = unsafeBitCast(value, to: UnsafeMutablePointer<UnsafeRawPointer?>.self)
defer {
valuePointer.deinitialize(count: 1)
valuePointer.deallocate()
}
guardlet attributes = CFDictionaryCreate(kCFAllocatorDefault, keyPointer, valuePointer, 1, nil, nil) else {
debugPrint("attributes create fail")
return
}
guardlet attributeString = CFAttributedStringCreate(kCFAllocatorDefault, string, attributes) else {
debugPrint("attributeString create fail")
return
}
let line = CTLineCreateWithAttributedString(attributeString)
context.textPosition = CGPoint(x: 100, y: 100)
CTLineDraw(line, context)
复制代码
多列布局
还有一种常见的排版操作是在多个列中布局文本。严格的讲,Core Text 本身一次只显示一列,并不计算列的尺寸或位置。不过在调用 Core Text 布局文本之前,计算列的路径区域,可以使文本在列中绘制,以此达到多列的文本,在这个示例中,Core Text 除了布局每一列文本外,还为每一列提供了字符串的子范围。
// 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);
}
复制代码
guardlet context = UIGraphicsGetCurrentContext() else { return }
let text = "replace that, use long long long text"asCFStringguardlet attributedString = CFAttributedStringCreate(kCFAllocatorDefault, text, nil) else {
debugPrint("create attributedString fail")
return
}
context.translateBy(x: 0, y: self.bounds.size.height)
context.scaleBy(x: 1.0, y: -1.0)
context.textMatrix = .identity
let framesetter = CTFramesetterCreateWithAttributedString(attributedString)
guardlet columPaths = self.createColumns(withColumnCount: 3) else {
debugPrint("columPaths create fail")
return
}
let pathCount = CFArrayGetCount(columPaths)
var startIndex = 0for i in0 ..< pathCount {
guardlet pointer = CFArrayGetValueAtIndex(columPaths, i) else {
debugPrint("columPaths index \(i) load fail")
continue
}
let path = Unmanaged<CGMutablePath>.fromOpaque(pointer)
let frame = CTFramesetterCreateFrame(framesetter, .init(location: startIndex, length: 0), path.takeUnretainedValue(), nil)
CTFrameDraw(frame, context)
startIndex += CTFrameGetVisibleStringRange(frame).length
path.release()
}
复制代码
手动换行
在Core Text中,除非你有特殊的断字过程或类似的需求,否则不需要手动换行。framesetter可以自动换行。不过Core Text 也可以让你准确的指定在哪里中断一行文本。以下代码展示了如何创建 typesetter 以及直接使用 typesetter 查找换行符并手动创建 typeset line。这个示例还展示了如何在绘制之前使行居中。
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;
CFIndexcount = 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;
复制代码
- (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);
}
复制代码
// 在iOS中初始化上下文。guardlet context = UIGraphicsGetCurrentContext() else { return }
// 仅在iOS中翻转上下文坐标
context.translateBy(x: 0, y: self.bounds.size.height)
context.scaleBy(x: 1.0, y: -1.0)
// 设置文本矩阵(就是设置字符绘制的方向,以免字符上下或左右翻转,因为在iOS上Core Text和Core Graphicsz坐标系不同)
context.textMatrix = .identity
// 创建有段落样式的attributed stringlet string = "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."let attString = applyParaStyle(fontName: "Didot-Italic"asCFString, pointSize: 24, plainText: string, lineSpaceInc: 50)
// 根据attributed string创建framesetterlet framesetter = CTFramesetterCreateWithAttributedString(attString asCFAttributedString)
// 创建要填充的区域的路径let path = CGPath(rect: rect, transform: nil)
// 创建绘制区域let frame = CTFramesetterCreateFrame(framesetter, .init(location: 0, length: 0), path, nil)
// 绘制CTFrameDraw(frame, context)
复制代码
在非矩形区域中绘制文本的难点在于描述非矩形路径。以下代码的AddSquashedDonutPath方法返回一个环形路径。有了路径后,只需调用常用的 Core Text 函数即可应用属性并绘制。
// 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);
}
复制代码
将一个已经存在的字体转换为相关或类似字体非常实用。以下代码中的示例函数展示了如何利用函数调用传入Boolean值使字体加粗或取消加粗。如果当前 font family 没有要求的 font,函数返回NULL。
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);
}
复制代码
以下代码中的示例函数将传入一个给定的字体,返回另一个 font family 中相似的字体,如果可能,保留原字体的 trait。这个函数可能返回NULL。将 size 传入0.0,matrix 传入NULL,可以使返回的字体 size 等同于原字体。
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);
}
复制代码
CFDataRefCreateFlattenedFontData(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;
}
复制代码
以下代码中的示例函数展示了如何从 XML 数据中反序列出字体的属性字典,并利用属性字典创建一个字体引用。
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;
}
复制代码
funcCreateFontFromFlattenedFontData(data: CFData) -> CTFont? {
let immutable = CFPropertyListMutabilityOptions.mutableContainers.rawValue
guardlet attributesUnm = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, data, immutable, nil) else {
returnnil
}
let attributes = attributesUnm.takeRetainedValue() as! CFDictionary// defer {// attributesUnm.release()// }let descriptor = CTFontDescriptorCreateWithAttributes(attributes)
let font = CTFontCreateWithFontDescriptor(descriptor, 0, nil)
return font
}
复制代码
// 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);
复制代码
let attributedString = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0)
CFAttributedStringReplaceString(attributedString, .init(location: 0, length: 0), "Hello, World! I know nothing in the world that has as much power as a word."asCFString)
CFAttributedStringSetAttribute(attributedString, .init(location: 0, length: 0), kCTForegroundColorAttributeName, UIColor.red.cgColor)
var num: CGFloat = 20let cfNum = CFNumberCreate(kCFAllocatorNull, .cgFloatType, &num)
CFAttributedStringSetAttribute(attributedString, .init(location: 0, length: 18), kCTKernAttributeName, cfNum)
复制代码
从字符获取字形
以下代码展示了如何从一个只有一个字体的string的characters中获取字形(glyphs),大部分情况下,你应该从 CTLine 中获取这些信息,因为string中可能包含不止一种字体。此外,对于比较复杂的文本绘制而言,简单的character to glyphs不能得到预期的外观,如果你希望使用一种字体,显示特定的Unicode字符(Characters),这种字符到字形的映射是适合的。
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);
}
复制代码
介绍原文链接: Core Text Programming Guide重要:此文档不再更新,有关 Apple SDKs 的最新信息,访问 documentation website.Core Text 是一种用于处理字体和文本布局的底层高级技术,自 Mac OS X v10.5 和 iOS 3.2 开始引入,你可以从所有 iOS 及 OS X 的开发环境中使用其 API。重要:Cor...