// // ZFont.m // FontLabel // // Created by Kevin Ballard on 7/2/09. // Copyright © 2009 Zynga Game Networks // // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #import "ZFont.h" @interface ZFont () @property (nonatomic, readonly) CGFloat ratio; - (NSString *)copyNameTableEntryForID:(UInt16)nameID; @end @implementation ZFont @synthesize cgFont=_cgFont, pointSize=_pointSize, ratio=_ratio; + (ZFont *)fontWithCGFont:(CGFontRef)cgFont size:(CGFloat)fontSize { return [[[self alloc] initWithCGFont:cgFont size:fontSize] autorelease]; } + (ZFont *)fontWithUIFont:(UIFont *)uiFont { NSParameterAssert(uiFont != nil); CGFontRef cgFont = CGFontCreateWithFontName((CFStringRef)uiFont.fontName); ZFont *zFont = [[self alloc] initWithCGFont:cgFont size:uiFont.pointSize]; CGFontRelease(cgFont); return [zFont autorelease]; } - (id)initWithCGFont:(CGFontRef)cgFont size:(CGFloat)fontSize { if ((self = [super init])) { _cgFont = CGFontRetain(cgFont); _pointSize = fontSize; _ratio = fontSize/CGFontGetUnitsPerEm(cgFont); } return self; } - (id)init { NSAssert(NO, @"-init is not valid for ZFont"); return nil; } - (CGFloat)ascender { return ceilf(self.ratio * CGFontGetAscent(self.cgFont)); } - (CGFloat)descender { return floorf(self.ratio * CGFontGetDescent(self.cgFont)); } - (CGFloat)leading { return (self.ascender - self.descender); } - (CGFloat)capHeight { return ceilf(self.ratio * CGFontGetCapHeight(self.cgFont)); } - (CGFloat)xHeight { return ceilf(self.ratio * CGFontGetXHeight(self.cgFont)); } - (NSString *)familyName { if (_familyName == nil) { _familyName = [self copyNameTableEntryForID:1]; } return _familyName; } - (NSString *)fontName { if (_fontName == nil) { _fontName = [self copyNameTableEntryForID:4]; } return _fontName; } - (NSString *)postScriptName { if (_postScriptName == nil) { _postScriptName = [self copyNameTableEntryForID:6]; } return _postScriptName; } - (ZFont *)fontWithSize:(CGFloat)fontSize { if (fontSize == self.pointSize) return self; NSParameterAssert(fontSize > 0.0); return [[[ZFont alloc] initWithCGFont:self.cgFont size:fontSize] autorelease]; } - (BOOL)isEqual:(id)object { if (![object isKindOfClass:[ZFont class]]) return NO; ZFont *font = (ZFont *)object; return (font.cgFont == self.cgFont && font.pointSize == self.pointSize); } - (NSString *)copyNameTableEntryForID:(UInt16)aNameID { CFDataRef nameTable = CGFontCopyTableForTag(self.cgFont, 'name'); NSAssert1(nameTable != NULL, @"CGFontCopyTableForTag returned NULL for 'name' tag in font %@", [(id)CFCopyDescription(self.cgFont) autorelease]); const UInt8 * const bytes = CFDataGetBytePtr(nameTable); NSAssert1(OSReadBigInt16(bytes, 0) == 0, @"name table for font %@ has bad version number", [(id)CFCopyDescription(self.cgFont) autorelease]); const UInt16 count = OSReadBigInt16(bytes, 2); const UInt16 stringOffset = OSReadBigInt16(bytes, 4); const UInt8 * const nameRecords = &bytes[6]; UInt16 nameLength = 0; UInt16 nameOffset = 0; NSStringEncoding encoding = 0; for (UInt16 idx = 0; idx < count; idx++) { const uintptr_t recordOffset = 12 * idx; const UInt16 nameID = OSReadBigInt16(nameRecords, recordOffset + 6); if (nameID != aNameID) continue; const UInt16 platformID = OSReadBigInt16(nameRecords, recordOffset + 0); const UInt16 platformSpecificID = OSReadBigInt16(nameRecords, recordOffset + 2); encoding = 0; // for now, we only support a subset of encodings switch (platformID) { case 0: // Unicode encoding = NSUTF16StringEncoding; break; case 1: // Macintosh switch (platformSpecificID) { case 0: encoding = NSMacOSRomanStringEncoding; break; } case 3: // Microsoft switch (platformSpecificID) { case 1: encoding = NSUTF16StringEncoding; break; } } if (encoding == 0) continue; nameLength = OSReadBigInt16(nameRecords, recordOffset + 8); nameOffset = OSReadBigInt16(nameRecords, recordOffset + 10); break; } NSString *result = nil; if (nameOffset > 0) { const UInt8 *nameBytes = &bytes[stringOffset + nameOffset]; result = [[NSString alloc] initWithBytes:nameBytes length:nameLength encoding:encoding]; } CFRelease(nameTable); return result; } - (void)dealloc { CGFontRelease(_cgFont); [_familyName release]; [_fontName release]; [_postScriptName release]; [super dealloc]; } @end