UIFont+YYAdd.m 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. //
  2. // UIFont+YYAdd.m
  3. // YYKit <https://github.com/ibireme/YYKit>
  4. //
  5. // Created by ibireme on 14/5/11.
  6. // Copyright (c) 2015 ibireme.
  7. //
  8. // This source code is licensed under the MIT-style license found in the
  9. // LICENSE file in the root directory of this source tree.
  10. //
  11. #import "UIFont+YYAdd.h"
  12. #import "YYKitMacro.h"
  13. YYSYNTH_DUMMY_CLASS(UIFont_YYAdd)
  14. #pragma clang diagnostic push
  15. #pragma clang diagnostic ignored "-Wprotocol"
  16. // Apple has implemented UIFont<NSCoding>, but did not make it public.
  17. @implementation UIFont (YYAdd)
  18. - (BOOL)isBold {
  19. if (![self respondsToSelector:@selector(fontDescriptor)]) return NO;
  20. return (self.fontDescriptor.symbolicTraits & UIFontDescriptorTraitBold) > 0;
  21. }
  22. - (BOOL)isItalic {
  23. if (![self respondsToSelector:@selector(fontDescriptor)]) return NO;
  24. return (self.fontDescriptor.symbolicTraits & UIFontDescriptorTraitItalic) > 0;
  25. }
  26. - (BOOL)isMonoSpace {
  27. if (![self respondsToSelector:@selector(fontDescriptor)]) return NO;
  28. return (self.fontDescriptor.symbolicTraits & UIFontDescriptorTraitMonoSpace) > 0;
  29. }
  30. - (BOOL)isColorGlyphs {
  31. if (![self respondsToSelector:@selector(fontDescriptor)]) return NO;
  32. return (CTFontGetSymbolicTraits((__bridge CTFontRef)self) & kCTFontTraitColorGlyphs) != 0;
  33. }
  34. - (CGFloat)fontWeight {
  35. NSDictionary *traits = [self.fontDescriptor objectForKey:UIFontDescriptorTraitsAttribute];
  36. return [traits[UIFontWeightTrait] floatValue];
  37. }
  38. - (UIFont *)fontWithBold {
  39. if (![self respondsToSelector:@selector(fontDescriptor)]) return self;
  40. return [UIFont fontWithDescriptor:[self.fontDescriptor fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitBold] size:self.pointSize];
  41. }
  42. - (UIFont *)fontWithItalic {
  43. if (![self respondsToSelector:@selector(fontDescriptor)]) return self;
  44. return [UIFont fontWithDescriptor:[self.fontDescriptor fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitItalic] size:self.pointSize];
  45. }
  46. - (UIFont *)fontWithBoldItalic {
  47. if (![self respondsToSelector:@selector(fontDescriptor)]) return self;
  48. return [UIFont fontWithDescriptor:[self.fontDescriptor fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitBold | UIFontDescriptorTraitItalic] size:self.pointSize];
  49. }
  50. - (UIFont *)fontWithNormal {
  51. if (![self respondsToSelector:@selector(fontDescriptor)]) return self;
  52. return [UIFont fontWithDescriptor:[self.fontDescriptor fontDescriptorWithSymbolicTraits:0] size:self.pointSize];
  53. }
  54. + (UIFont *)fontWithCTFont:(CTFontRef)CTFont {
  55. if (!CTFont) return nil;
  56. CFStringRef name = CTFontCopyPostScriptName(CTFont);
  57. if (!name) return nil;
  58. CGFloat size = CTFontGetSize(CTFont);
  59. UIFont *font = [self fontWithName:(__bridge NSString *)(name) size:size];
  60. CFRelease(name);
  61. return font;
  62. }
  63. + (UIFont *)fontWithCGFont:(CGFontRef)CGFont size:(CGFloat)size {
  64. if (!CGFont) return nil;
  65. CFStringRef name = CGFontCopyPostScriptName(CGFont);
  66. if (!name) return nil;
  67. UIFont *font = [self fontWithName:(__bridge NSString *)(name) size:size];
  68. CFRelease(name);
  69. return font;
  70. }
  71. - (CTFontRef)CTFontRef CF_RETURNS_RETAINED {
  72. CTFontRef font = CTFontCreateWithName((__bridge CFStringRef)self.fontName, self.pointSize, NULL);
  73. return font;
  74. }
  75. - (CGFontRef)CGFontRef CF_RETURNS_RETAINED {
  76. CGFontRef font = CGFontCreateWithFontName((__bridge CFStringRef)self.fontName);
  77. return font;
  78. }
  79. + (BOOL)loadFontFromPath:(NSString *)path {
  80. NSURL *url = [NSURL fileURLWithPath:path];
  81. CFErrorRef error;
  82. BOOL suc = CTFontManagerRegisterFontsForURL((__bridge CFTypeRef)url, kCTFontManagerScopeNone, &error);
  83. if (!suc) {
  84. NSLog(@"Failed to load font: %@", error);
  85. }
  86. return suc;
  87. }
  88. + (void)unloadFontFromPath:(NSString *)path {
  89. NSURL *url = [NSURL fileURLWithPath:path];
  90. CTFontManagerUnregisterFontsForURL((__bridge CFTypeRef)url, kCTFontManagerScopeNone, NULL);
  91. }
  92. + (UIFont *)loadFontFromData:(NSData *)data {
  93. CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data);
  94. if (!provider) return nil;
  95. CGFontRef fontRef = CGFontCreateWithDataProvider(provider);
  96. CGDataProviderRelease(provider);
  97. if (!fontRef) return nil;
  98. CFErrorRef errorRef;
  99. BOOL suc = CTFontManagerRegisterGraphicsFont(fontRef, &errorRef);
  100. if (!suc) {
  101. CFRelease(fontRef);
  102. NSLog(@"%@", errorRef);
  103. return nil;
  104. } else {
  105. CFStringRef fontName = CGFontCopyPostScriptName(fontRef);
  106. UIFont *font = [UIFont fontWithName:(__bridge NSString *)(fontName) size:[UIFont systemFontSize]];
  107. if (fontName) CFRelease(fontName);
  108. CGFontRelease(fontRef);
  109. return font;
  110. }
  111. }
  112. + (BOOL)unloadFontFromData:(UIFont *)font {
  113. CGFontRef fontRef = CGFontCreateWithFontName((__bridge CFStringRef)font.fontName);
  114. if (!fontRef) return NO;
  115. CFErrorRef errorRef;
  116. BOOL suc = CTFontManagerUnregisterGraphicsFont(fontRef, &errorRef);
  117. CFRelease(fontRef);
  118. if (!suc) NSLog(@"%@", errorRef);
  119. return suc;
  120. }
  121. + (NSData *)dataFromFont:(UIFont *)font {
  122. CGFontRef cgFont = font.CGFontRef;
  123. NSData *data = [self dataFromCGFont:cgFont];
  124. CGFontRelease(cgFont);
  125. return data;
  126. }
  127. typedef struct FontHeader {
  128. int32_t fVersion;
  129. uint16_t fNumTables;
  130. uint16_t fSearchRange;
  131. uint16_t fEntrySelector;
  132. uint16_t fRangeShift;
  133. } FontHeader;
  134. typedef struct TableEntry {
  135. uint32_t fTag;
  136. uint32_t fCheckSum;
  137. uint32_t fOffset;
  138. uint32_t fLength;
  139. } TableEntry;
  140. static uint32_t CalcTableCheckSum(const uint32_t *table, uint32_t numberOfBytesInTable) {
  141. uint32_t sum = 0;
  142. uint32_t nLongs = (numberOfBytesInTable + 3) / 4;
  143. while (nLongs-- > 0) {
  144. sum += CFSwapInt32HostToBig(*table++);
  145. }
  146. return sum;
  147. }
  148. //Reference:
  149. //https://github.com/google/skia/blob/master/src%2Fports%2FSkFontHost_mac.cpp
  150. + (NSData *)dataFromCGFont:(CGFontRef)cgFont {
  151. if (!cgFont) return nil;
  152. CFRetain(cgFont);
  153. CFArrayRef tags = CGFontCopyTableTags(cgFont);
  154. if (!tags) return nil;
  155. CFIndex tableCount = CFArrayGetCount(tags);
  156. size_t *tableSizes = malloc(sizeof(size_t) * tableCount);
  157. memset(tableSizes, 0, sizeof(size_t) * tableCount);
  158. BOOL containsCFFTable = NO;
  159. size_t totalSize = sizeof(FontHeader) + sizeof(TableEntry) * tableCount;
  160. for (CFIndex index = 0; index < tableCount; index++) {
  161. size_t tableSize = 0;
  162. uint32_t aTag = (uint32_t)CFArrayGetValueAtIndex(tags, index);
  163. if (aTag == kCTFontTableCFF && !containsCFFTable) {
  164. containsCFFTable = YES;
  165. }
  166. CFDataRef tableDataRef = CGFontCopyTableForTag(cgFont, aTag);
  167. if (tableDataRef) {
  168. tableSize = CFDataGetLength(tableDataRef);
  169. CFRelease(tableDataRef);
  170. }
  171. totalSize += (tableSize + 3) & ~3;
  172. tableSizes[index] = tableSize;
  173. }
  174. unsigned char *stream = malloc(totalSize);
  175. memset(stream, 0, totalSize);
  176. char *dataStart = (char *)stream;
  177. char *dataPtr = dataStart;
  178. // compute font header entries
  179. uint16_t entrySelector = 0;
  180. uint16_t searchRange = 1;
  181. while (searchRange < tableCount >> 1) {
  182. entrySelector++;
  183. searchRange <<= 1;
  184. }
  185. searchRange <<= 4;
  186. uint16_t rangeShift = (tableCount << 4) - searchRange;
  187. // write font header (also called sfnt header, offset subtable)
  188. FontHeader *offsetTable = (FontHeader *)dataPtr;
  189. //OpenType Font contains CFF Table use 'OTTO' as version, and with .otf extension
  190. //otherwise 0001 0000
  191. offsetTable->fVersion = containsCFFTable ? 'OTTO' : CFSwapInt16HostToBig(1);
  192. offsetTable->fNumTables = CFSwapInt16HostToBig((uint16_t)tableCount);
  193. offsetTable->fSearchRange = CFSwapInt16HostToBig((uint16_t)searchRange);
  194. offsetTable->fEntrySelector = CFSwapInt16HostToBig((uint16_t)entrySelector);
  195. offsetTable->fRangeShift = CFSwapInt16HostToBig((uint16_t)rangeShift);
  196. dataPtr += sizeof(FontHeader);
  197. // write tables
  198. TableEntry *entry = (TableEntry *)dataPtr;
  199. dataPtr += sizeof(TableEntry) * tableCount;
  200. for (int index = 0; index < tableCount; ++index) {
  201. uint32_t aTag = (uint32_t)CFArrayGetValueAtIndex(tags, index);
  202. CFDataRef tableDataRef = CGFontCopyTableForTag(cgFont, aTag);
  203. size_t tableSize = CFDataGetLength(tableDataRef);
  204. memcpy(dataPtr, CFDataGetBytePtr(tableDataRef), tableSize);
  205. entry->fTag = CFSwapInt32HostToBig((uint32_t)aTag);
  206. entry->fCheckSum = CFSwapInt32HostToBig(CalcTableCheckSum((uint32_t *)dataPtr, (uint32_t)tableSize));
  207. uint32_t offset = (uint32_t)dataPtr - (uint32_t)dataStart;
  208. entry->fOffset = CFSwapInt32HostToBig((uint32_t)offset);
  209. entry->fLength = CFSwapInt32HostToBig((uint32_t)tableSize);
  210. dataPtr += (tableSize + 3) & ~3;
  211. ++entry;
  212. CFRelease(tableDataRef);
  213. }
  214. CFRelease(cgFont);
  215. CFRelease(tags);
  216. free(tableSizes);
  217. NSData *fontData = [NSData dataWithBytesNoCopy:stream length:totalSize freeWhenDone:YES];
  218. return fontData;
  219. }
  220. @end
  221. #pragma clang diagnostic pop