Core Text 是一種用於處理字體和文本佈局的底層高級技術,自 Mac OS X v10.5 和 iOS 3.2 開始引入,你能夠從全部 iOS 及 OS X 的開發環境中使用其 API。swift
重要: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 中處理最底層的二維圖像生成。bash
Core Text 是爲高級框架中的文本佈局及字體處理功能提供支持的中間層,Quartz 則爲全部的文本和字體框架提供更爲底層的支持,Quartz 做用於文本的字形(CTGlyphInfo)和位置,而 Core Text 清楚如何爲字符匹配字體,在調用 Quartz 繪製文本以前,它會處理文本的樣式,字體規格和其餘屬性,Quartz 是獲取基本級別繪製字形的惟一方式。由於 Core Text 直接提供了 Quartz 可用數據,所以其文本繪製性能很高。app
若是客戶端不改變線程之間共享的任何參數(如 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 函數參數所需的對象。)ide
由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);
}
複製代碼