New file |
| | |
| | | // |
| | | // UIFont+YYAdd.m |
| | | // YYCategories <https://github.com/ibireme/YYCategories> |
| | | // |
| | | // Created by ibireme on 14/5/11. |
| | | // Copyright (c) 2015 ibireme. |
| | | // |
| | | // This source code is licensed under the MIT-style license found in the |
| | | // LICENSE file in the root directory of this source tree. |
| | | // |
| | | |
| | | #import "UIFont+YYAdd.h" |
| | | #import "YYCategoriesMacro.h" |
| | | |
| | | YYSYNTH_DUMMY_CLASS(UIFont_YYAdd) |
| | | |
| | | |
| | | #pragma clang diagnostic push |
| | | #pragma clang diagnostic ignored "-Wprotocol" |
| | | // Apple has implemented UIFont<NSCoding>, but did not make it public. |
| | | |
| | | @implementation UIFont (YYAdd) |
| | | |
| | | - (BOOL)isBold { |
| | | if (![self respondsToSelector:@selector(fontDescriptor)]) return NO; |
| | | return (self.fontDescriptor.symbolicTraits & UIFontDescriptorTraitBold) > 0; |
| | | } |
| | | |
| | | - (BOOL)isItalic { |
| | | if (![self respondsToSelector:@selector(fontDescriptor)]) return NO; |
| | | return (self.fontDescriptor.symbolicTraits & UIFontDescriptorTraitItalic) > 0; |
| | | } |
| | | |
| | | - (BOOL)isMonoSpace { |
| | | if (![self respondsToSelector:@selector(fontDescriptor)]) return NO; |
| | | return (self.fontDescriptor.symbolicTraits & UIFontDescriptorTraitMonoSpace) > 0; |
| | | } |
| | | |
| | | - (BOOL)isColorGlyphs { |
| | | if (![self respondsToSelector:@selector(fontDescriptor)]) return NO; |
| | | return (CTFontGetSymbolicTraits((__bridge CTFontRef)self) & kCTFontTraitColorGlyphs) != 0; |
| | | } |
| | | |
| | | - (CGFloat)fontWeight { |
| | | NSDictionary *traits = [self.fontDescriptor objectForKey:UIFontDescriptorTraitsAttribute]; |
| | | return [traits[UIFontWeightTrait] floatValue]; |
| | | } |
| | | |
| | | - (UIFont *)fontWithBold { |
| | | if (![self respondsToSelector:@selector(fontDescriptor)]) return nil; |
| | | return [UIFont fontWithDescriptor:[self.fontDescriptor fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitBold] size:self.pointSize]; |
| | | } |
| | | |
| | | - (UIFont *)fontWithItalic { |
| | | if (![self respondsToSelector:@selector(fontDescriptor)]) return nil; |
| | | return [UIFont fontWithDescriptor:[self.fontDescriptor fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitItalic] size:self.pointSize]; |
| | | } |
| | | |
| | | - (UIFont *)fontWithBoldItalic { |
| | | if (![self respondsToSelector:@selector(fontDescriptor)]) return nil; |
| | | return [UIFont fontWithDescriptor:[self.fontDescriptor fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitBold | UIFontDescriptorTraitItalic] size:self.pointSize]; |
| | | } |
| | | |
| | | - (UIFont *)fontWithNormal { |
| | | if (![self respondsToSelector:@selector(fontDescriptor)]) return nil; |
| | | return [UIFont fontWithDescriptor:[self.fontDescriptor fontDescriptorWithSymbolicTraits:0] size:self.pointSize]; |
| | | } |
| | | |
| | | + (UIFont *)fontWithCTFont:(CTFontRef)CTFont { |
| | | if (!CTFont) return nil; |
| | | CFStringRef name = CTFontCopyPostScriptName(CTFont); |
| | | if (!name) return nil; |
| | | CGFloat size = CTFontGetSize(CTFont); |
| | | UIFont *font = [self fontWithName:(__bridge NSString *)(name) size:size]; |
| | | CFRelease(name); |
| | | return font; |
| | | } |
| | | |
| | | + (UIFont *)fontWithCGFont:(CGFontRef)CGFont size:(CGFloat)size { |
| | | if (!CGFont) return nil; |
| | | CFStringRef name = CGFontCopyPostScriptName(CGFont); |
| | | if (!name) return nil; |
| | | UIFont *font = [self fontWithName:(__bridge NSString *)(name) size:size]; |
| | | CFRelease(name); |
| | | return font; |
| | | } |
| | | |
| | | - (CTFontRef)CTFontRef CF_RETURNS_RETAINED { |
| | | CTFontRef font = CTFontCreateWithName((__bridge CFStringRef)self.fontName, self.pointSize, NULL); |
| | | return font; |
| | | } |
| | | |
| | | - (CGFontRef)CGFontRef CF_RETURNS_RETAINED { |
| | | CGFontRef font = CGFontCreateWithFontName((__bridge CFStringRef)self.fontName); |
| | | return font; |
| | | } |
| | | |
| | | + (BOOL)loadFontFromPath:(NSString *)path { |
| | | NSURL *url = [NSURL fileURLWithPath:path]; |
| | | CFErrorRef error; |
| | | BOOL suc = CTFontManagerRegisterFontsForURL((__bridge CFTypeRef)url, kCTFontManagerScopeNone, &error); |
| | | if (!suc) { |
| | | NSLog(@"Failed to load font: %@", error); |
| | | } |
| | | return suc; |
| | | } |
| | | |
| | | + (void)unloadFontFromPath:(NSString *)path { |
| | | NSURL *url = [NSURL fileURLWithPath:path]; |
| | | CTFontManagerUnregisterFontsForURL((__bridge CFTypeRef)url, kCTFontManagerScopeNone, NULL); |
| | | } |
| | | |
| | | + (UIFont *)loadFontFromData:(NSData *)data { |
| | | CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data); |
| | | if (!provider) return nil; |
| | | CGFontRef fontRef = CGFontCreateWithDataProvider(provider); |
| | | CGDataProviderRelease(provider); |
| | | if (!fontRef) return nil; |
| | | |
| | | CFErrorRef errorRef; |
| | | BOOL suc = CTFontManagerRegisterGraphicsFont(fontRef, &errorRef); |
| | | if (!suc) { |
| | | CFRelease(fontRef); |
| | | NSLog(@"%@", errorRef); |
| | | return nil; |
| | | } else { |
| | | CFStringRef fontName = CGFontCopyPostScriptName(fontRef); |
| | | UIFont *font = [UIFont fontWithName:(__bridge NSString *)(fontName) size:[UIFont systemFontSize]]; |
| | | if (fontName) CFRelease(fontName); |
| | | CGFontRelease(fontRef); |
| | | return font; |
| | | } |
| | | } |
| | | |
| | | + (BOOL)unloadFontFromData:(UIFont *)font { |
| | | CGFontRef fontRef = CGFontCreateWithFontName((__bridge CFStringRef)font.fontName); |
| | | if (!fontRef) return NO; |
| | | CFErrorRef errorRef; |
| | | BOOL suc = CTFontManagerUnregisterGraphicsFont(fontRef, &errorRef); |
| | | CFRelease(fontRef); |
| | | if (!suc) NSLog(@"%@", errorRef); |
| | | return suc; |
| | | } |
| | | |
| | | + (NSData *)dataFromFont:(UIFont *)font { |
| | | CGFontRef cgFont = font.CGFontRef; |
| | | NSData *data = [self dataFromCGFont:cgFont]; |
| | | CGFontRelease(cgFont); |
| | | return data; |
| | | } |
| | | |
| | | typedef struct FontHeader { |
| | | int32_t fVersion; |
| | | uint16_t fNumTables; |
| | | uint16_t fSearchRange; |
| | | uint16_t fEntrySelector; |
| | | uint16_t fRangeShift; |
| | | } FontHeader; |
| | | |
| | | typedef struct TableEntry { |
| | | uint32_t fTag; |
| | | uint32_t fCheckSum; |
| | | uint32_t fOffset; |
| | | uint32_t fLength; |
| | | } TableEntry; |
| | | |
| | | static uint32_t CalcTableCheckSum(const uint32_t *table, uint32_t numberOfBytesInTable) { |
| | | uint32_t sum = 0; |
| | | uint32_t nLongs = (numberOfBytesInTable + 3) / 4; |
| | | while (nLongs-- > 0) { |
| | | sum += CFSwapInt32HostToBig(*table++); |
| | | } |
| | | return sum; |
| | | } |
| | | |
| | | //Reference: |
| | | //https://github.com/google/skia/blob/master/src%2Fports%2FSkFontHost_mac.cpp |
| | | + (NSData *)dataFromCGFont:(CGFontRef)cgFont { |
| | | if (!cgFont) return nil; |
| | | |
| | | CFRetain(cgFont); |
| | | |
| | | CFArrayRef tags = CGFontCopyTableTags(cgFont); |
| | | if (!tags) return nil; |
| | | CFIndex tableCount = CFArrayGetCount(tags); |
| | | |
| | | size_t *tableSizes = malloc(sizeof(size_t) * tableCount); |
| | | memset(tableSizes, 0, sizeof(size_t) * tableCount); |
| | | |
| | | BOOL containsCFFTable = NO; |
| | | |
| | | size_t totalSize = sizeof(FontHeader) + sizeof(TableEntry) * tableCount; |
| | | |
| | | for (CFIndex index = 0; index < tableCount; index++) { |
| | | size_t tableSize = 0; |
| | | uint32_t aTag = (uint32_t)CFArrayGetValueAtIndex(tags, index); |
| | | if (aTag == kCTFontTableCFF && !containsCFFTable) { |
| | | containsCFFTable = YES; |
| | | } |
| | | CFDataRef tableDataRef = CGFontCopyTableForTag(cgFont, aTag); |
| | | if (tableDataRef) { |
| | | tableSize = CFDataGetLength(tableDataRef); |
| | | CFRelease(tableDataRef); |
| | | } |
| | | totalSize += (tableSize + 3) & ~3; |
| | | tableSizes[index] = tableSize; |
| | | } |
| | | |
| | | unsigned char *stream = malloc(totalSize); |
| | | memset(stream, 0, totalSize); |
| | | char *dataStart = (char *)stream; |
| | | char *dataPtr = dataStart; |
| | | |
| | | // compute font header entries |
| | | uint16_t entrySelector = 0; |
| | | uint16_t searchRange = 1; |
| | | while (searchRange < tableCount >> 1) { |
| | | entrySelector++; |
| | | searchRange <<= 1; |
| | | } |
| | | searchRange <<= 4; |
| | | |
| | | uint16_t rangeShift = (tableCount << 4) - searchRange; |
| | | |
| | | // write font header (also called sfnt header, offset subtable) |
| | | FontHeader *offsetTable = (FontHeader *)dataPtr; |
| | | |
| | | //OpenType Font contains CFF Table use 'OTTO' as version, and with .otf extension |
| | | //otherwise 0001 0000 |
| | | offsetTable->fVersion = containsCFFTable ? 'OTTO' : CFSwapInt16HostToBig(1); |
| | | offsetTable->fNumTables = CFSwapInt16HostToBig((uint16_t)tableCount); |
| | | offsetTable->fSearchRange = CFSwapInt16HostToBig((uint16_t)searchRange); |
| | | offsetTable->fEntrySelector = CFSwapInt16HostToBig((uint16_t)entrySelector); |
| | | offsetTable->fRangeShift = CFSwapInt16HostToBig((uint16_t)rangeShift); |
| | | |
| | | dataPtr += sizeof(FontHeader); |
| | | |
| | | // write tables |
| | | TableEntry *entry = (TableEntry *)dataPtr; |
| | | dataPtr += sizeof(TableEntry) * tableCount; |
| | | |
| | | for (int index = 0; index < tableCount; ++index) { |
| | | uint32_t aTag = (uint32_t)CFArrayGetValueAtIndex(tags, index); |
| | | CFDataRef tableDataRef = CGFontCopyTableForTag(cgFont, aTag); |
| | | size_t tableSize = CFDataGetLength(tableDataRef); |
| | | |
| | | memcpy(dataPtr, CFDataGetBytePtr(tableDataRef), tableSize); |
| | | |
| | | entry->fTag = CFSwapInt32HostToBig((uint32_t)aTag); |
| | | entry->fCheckSum = CFSwapInt32HostToBig(CalcTableCheckSum((uint32_t *)dataPtr, (uint32_t)tableSize)); |
| | | |
| | | uint32_t offset = (uint32_t)dataPtr - (uint32_t)dataStart; |
| | | entry->fOffset = CFSwapInt32HostToBig((uint32_t)offset); |
| | | entry->fLength = CFSwapInt32HostToBig((uint32_t)tableSize); |
| | | dataPtr += (tableSize + 3) & ~3; |
| | | ++entry; |
| | | CFRelease(tableDataRef); |
| | | } |
| | | |
| | | CFRelease(cgFont); |
| | | CFRelease(tags); |
| | | free(tableSizes); |
| | | NSData *fontData = [NSData dataWithBytesNoCopy:stream length:totalSize freeWhenDone:YES]; |
| | | return fontData; |
| | | } |
| | | |
| | | @end |
| | | |
| | | #pragma clang diagnostic pop |