11
11
12
12
#import < CoreText/CoreText.h>
13
13
14
- #import < mutex>
15
-
16
14
typedef CGFloat RCTFontWeight;
17
15
static RCTFontWeight weightOfFont (UIFont *font)
18
16
{
19
- static NSArray *fontNames ;
20
- static NSArray *fontWeights;
17
+ static NSArray < NSString *> *weightSuffixes ;
18
+ static NSArray < NSNumber *> *fontWeights;
21
19
static dispatch_once_t onceToken;
22
20
dispatch_once (&onceToken, ^{
23
21
// We use two arrays instead of one map because
24
22
// the order is important for suffix matching.
25
- fontNames = @[
23
+ weightSuffixes = @[
26
24
@" normal" ,
27
25
@" ultralight" ,
28
26
@" thin" ,
@@ -54,28 +52,29 @@ static RCTFontWeight weightOfFont(UIFont *font)
54
52
];
55
53
});
56
54
57
- for (NSInteger i = 0 ; i < 0 || i < (unsigned )fontNames.count ; i++) {
58
- if ([font.fontName.lowercaseString hasSuffix: fontNames[i]]) {
59
- return (RCTFontWeight)[fontWeights[i] doubleValue ];
55
+ NSString *fontName = font.fontName ;
56
+ NSInteger i = 0 ;
57
+ for (NSString *suffix in weightSuffixes) {
58
+ // CFStringFind is much faster than any variant of rangeOfString: because it does not use a locale.
59
+ auto options = kCFCompareCaseInsensitive | kCFCompareAnchored | kCFCompareBackwards ;
60
+ if (CFStringFind ((CFStringRef )fontName, (CFStringRef )suffix, options).location != kCFNotFound ) {
61
+ return (RCTFontWeight)fontWeights[i].doubleValue ;
60
62
}
63
+ i++;
61
64
}
62
65
63
- NSDictionary * traits = [font.fontDescriptor objectForKey: UIFontDescriptorTraitsAttribute] ;
66
+ auto traits = (__bridge_transfer NSDictionary *) CTFontCopyTraits ((CTFontRef)font) ;
64
67
return (RCTFontWeight)[traits[UIFontWeightTrait] doubleValue ];
65
68
}
66
69
67
70
static BOOL isItalicFont (UIFont *font)
68
71
{
69
- NSDictionary *traits = [font.fontDescriptor objectForKey: UIFontDescriptorTraitsAttribute];
70
- UIFontDescriptorSymbolicTraits symbolicTraits = [traits[UIFontSymbolicTrait] unsignedIntValue ];
71
- return (symbolicTraits & UIFontDescriptorTraitItalic) != 0 ;
72
+ return (CTFontGetSymbolicTraits ((CTFontRef)font) & kCTFontTraitItalic ) != 0 ;
72
73
}
73
74
74
75
static BOOL isCondensedFont (UIFont *font)
75
76
{
76
- NSDictionary *traits = [font.fontDescriptor objectForKey: UIFontDescriptorTraitsAttribute];
77
- UIFontDescriptorSymbolicTraits symbolicTraits = [traits[UIFontSymbolicTrait] unsignedIntValue ];
78
- return (symbolicTraits & UIFontDescriptorTraitCondensed) != 0 ;
77
+ return (CTFontGetSymbolicTraits ((CTFontRef)font) & kCTFontTraitCondensed ) != 0 ;
79
78
}
80
79
81
80
static RCTFontHandler defaultFontHandler;
@@ -130,18 +129,16 @@ static inline BOOL CompareFontWeights(UIFontWeight firstWeight, UIFontWeight sec
130
129
131
130
static UIFont *cachedSystemFont (CGFloat size, RCTFontWeight weight)
132
131
{
133
- static NSCache *fontCache;
134
- static std::mutex *fontCacheMutex = new std::mutex;
135
-
136
- NSString *cacheKey = [NSString stringWithFormat: @" %.1f /%.2f " , size, weight];
137
- UIFont *font;
138
- {
139
- std::lock_guard<std::mutex> lock (*fontCacheMutex);
140
- if (!fontCache) {
141
- fontCache = [NSCache new ];
142
- }
143
- font = [fontCache objectForKey: cacheKey];
144
- }
132
+ static NSCache <NSValue *, UIFont *> *fontCache = [NSCache new ];
133
+
134
+ struct __attribute__ ((__packed__)) CacheKey {
135
+ CGFloat size;
136
+ RCTFontWeight weight;
137
+ };
138
+
139
+ CacheKey key{size, weight};
140
+ NSValue *cacheKey = [[NSValue alloc ] initWithBytes: &key objCType: @encode (CacheKey)];
141
+ UIFont *font = [fontCache objectForKey: cacheKey];
145
142
146
143
if (!font) {
147
144
if (defaultFontHandler) {
@@ -151,15 +148,36 @@ static inline BOOL CompareFontWeights(UIFontWeight firstWeight, UIFontWeight sec
151
148
font = [UIFont systemFontOfSize: size weight: weight];
152
149
}
153
150
154
- {
155
- std::lock_guard<std::mutex> lock (*fontCacheMutex);
156
- [fontCache setObject: font forKey: cacheKey];
157
- }
151
+ [fontCache setObject: font forKey: cacheKey];
158
152
}
159
153
160
154
return font;
161
155
}
162
156
157
+ // Caching wrapper around expensive +[UIFont fontNamesForFamilyName:]
158
+ static NSArray <NSString *> *fontNamesForFamilyName (NSString *familyName)
159
+ {
160
+ static NSCache <NSString *, NSArray <NSString *> *> *cache;
161
+ static dispatch_once_t onceToken;
162
+ dispatch_once (&onceToken, ^{
163
+ cache = [NSCache new ];
164
+ [NSNotificationCenter .defaultCenter
165
+ addObserverForName: (NSNotificationName )kCTFontManagerRegisteredFontsChangedNotification
166
+ object: nil
167
+ queue: nil
168
+ usingBlock: ^(NSNotification *) {
169
+ [cache removeAllObjects ];
170
+ }];
171
+ });
172
+
173
+ auto names = [cache objectForKey: familyName];
174
+ if (!names) {
175
+ names = [UIFont fontNamesForFamilyName: familyName];
176
+ [cache setObject: names forKey: familyName];
177
+ }
178
+ return names;
179
+ }
180
+
163
181
@implementation RCTConvert (RCTFont)
164
182
165
183
+ (UIFont *)UIFont : (id )json
@@ -315,7 +333,7 @@ + (UIFont *)updateFont:(UIFont *)font
315
333
316
334
// Gracefully handle being given a font name rather than font family, for
317
335
// example: "Helvetica Light Oblique" rather than just "Helvetica".
318
- if (!didFindFont && [UIFont fontNamesForFamilyName: familyName] .count == 0 ) {
336
+ if (!didFindFont && fontNamesForFamilyName ( familyName) .count == 0 ) {
319
337
font = [UIFont fontWithName: familyName size: fontSize];
320
338
if (font) {
321
339
// It's actually a font name, not a font family name,
@@ -339,7 +357,8 @@ + (UIFont *)updateFont:(UIFont *)font
339
357
340
358
// Get the closest font that matches the given weight for the fontFamily
341
359
CGFloat closestWeight = INFINITY;
342
- for (NSString *name in [UIFont fontNamesForFamilyName: familyName]) {
360
+ NSArray <NSString *> *names = fontNamesForFamilyName (familyName);
361
+ for (NSString *name in names) {
343
362
UIFont *match = [UIFont fontWithName: name size: fontSize];
344
363
if (isItalic == isItalicFont (match) && isCondensed == isCondensedFont (match)) {
345
364
CGFloat testWeight = weightOfFont (match);
@@ -352,11 +371,8 @@ + (UIFont *)updateFont:(UIFont *)font
352
371
353
372
// If we still don't have a match at least return the first font in the fontFamily
354
373
// This is to support built-in font Zapfino and other custom single font families like Impact
355
- if (!font) {
356
- NSArray *names = [UIFont fontNamesForFamilyName: familyName];
357
- if (names.count > 0 ) {
358
- font = [UIFont fontWithName: names[0 ] size: fontSize];
359
- }
374
+ if (!font && names.count > 0 ) {
375
+ font = [UIFont fontWithName: names[0 ] size: fontSize];
360
376
}
361
377
362
378
// Apply font variants to font object
0 commit comments