本章介绍一些常见的字体处理操作,并说明如何使用核心文本对其进行编码。这些操作在iOS和OS X上是相同的。本章括以下代码清单操作:
- 创建字体描述符
- 从字体描述符创建字体
- 创建相关字体
- 序列化字体
- 从序列化数据创建字体
- 改变字距
- 获取字符的标志符号
创建字体描述符
清单3-1中的示例函数通过指定PostScript字体名称和点大小的参数值创建字体描述符。
清单3-1从名称和点大小创建字体描述符
CTFontDescriptorRef CreateFontDescriptorFromName(CFStringRef postScriptName,
CGFloat size)
{
return CTFontDescriptorCreateWithNameAndSize(postScriptName, size)
清单3-2中的示例函数根据字体系列名称和字体特征创建字体描述符。
清单3-2从一个族和特征创建字体描述符
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);
从字体描述符创建字体
清单3-3展示了如何创建字体描述符并使用它创建字体。调用CTFontCreateWithFontDescriptor时,通常为矩阵参数传递NULL以指定默认(标识)矩阵。CTFontCreateWithFontDescriptor的大小和矩阵(第二个和第三个)参数将覆盖字体描述符中指定的任何参数,除非未指定(0.0表示大小,NULL表示矩阵)。
清单3-3从字体描述符创建字体
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);
创建相关字体
将现有字体转换为相关或类似字体通常很有用。清单3-4中的示例函数显示了如何根据函数调用传递的布尔参数值使字体加粗或取消加粗。如果当前字体系列没有请求的样式,则函数返回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);
清单3-8中的示例函数将给定字体转换为另一个字体系列中的类似字体,如果可能,保留特征。它可能返回NULL。为size参数传入0.0,为matrix参数传入NULL将保留原始字体的大小。
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);
}
序列化字体
清单3-6中的示例函数展示了如何创建XML数据来序列化可嵌入到文档中的字体。或者,优选地,可以使用NSArchiver。这只是完成此任务的一种方法,但它保留了以后重新创建精确字体所需的字体中的所有数据。
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;
}
从序列化数据创建字体
清单3-7中的示例函数展示了如何从平坦的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;
}
改变字距
默认情况下启用连字和紧排。要禁用,请将kCTKernAttributeName属性设置为0。清单3-8将所绘制的前几个字符的内核大小设置为一个较大的数字。
// 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);
获取字符的标志符号
清单3-9显示了如何使用单个字体获取字符串中字符的标志符号。大多数情况下,您应该只使用CTLine对象来获取此信息,因为一种字体可能不会对整个字符串进行编码。此外,对于复杂脚本,简单的字符到字形映射将无法获得正确的外观。如果您试图显示字体的特定Unicode字符,则此简单的字形映射可能是合适的。
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);
}