Add iOS support for CSS property font-variant, accepting tabular-nums…
Summary: Ground work for allowing `font-variant`s. Currently allows switching between `tabular-nums` and `proportional-nums`. I will need guidance on how to test this, and a few pointers on code style (new to Objective C, and had to make one or two hacks). Closes https://github.com/facebook/react-native/pull/9045 Reviewed By: majak Differential Revision: D3664338 Pulled By: javache fbshipit-source-id: 032f326c37ee6150348da2b33b6a3fc1988e8920
This commit is contained in:
parent
ced272d906
commit
f951da912d
|
@ -16,6 +16,8 @@
|
|||
|
||||
#import "RCTFont.h"
|
||||
|
||||
#import <CoreText/CoreText.h>
|
||||
|
||||
@interface RCTFontTests : XCTestCase
|
||||
|
||||
@end
|
||||
|
@ -178,6 +180,27 @@
|
|||
}
|
||||
}
|
||||
|
||||
- (void)testVariant
|
||||
{
|
||||
{
|
||||
UIFont *expected = [UIFont monospacedDigitSystemFontOfSize:14 weight:UIFontWeightRegular];
|
||||
UIFont *result = [RCTConvert UIFont:@{@"fontVariant": @[@"tabular-nums"]}];
|
||||
RCTAssertEqualFonts(expected, result);
|
||||
}
|
||||
{
|
||||
UIFont *monospaceFont = [UIFont monospacedDigitSystemFontOfSize:14 weight:UIFontWeightRegular];
|
||||
UIFontDescriptor *fontDescriptor = [monospaceFont.fontDescriptor fontDescriptorByAddingAttributes:@{
|
||||
UIFontDescriptorFeatureSettingsAttribute: @[@{
|
||||
UIFontFeatureTypeIdentifierKey: @(kLowerCaseType),
|
||||
UIFontFeatureSelectorIdentifierKey: @(kLowerCaseSmallCapsSelector),
|
||||
}]
|
||||
}];
|
||||
UIFont *expected = [UIFont fontWithDescriptor:fontDescriptor size:14];
|
||||
UIFont *result = [RCTConvert UIFont:@{@"fontVariant": @[@"tabular-nums", @"small-caps"]}];
|
||||
RCTAssertEqualFonts(expected, result);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testInvalidFont
|
||||
{
|
||||
{
|
||||
|
|
|
@ -465,6 +465,33 @@ exports.examples = [
|
|||
</View>
|
||||
);
|
||||
},
|
||||
}, {
|
||||
title: 'Font variants',
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<Text style={{fontVariant: ['small-caps']}}>
|
||||
Small Caps{'\n'}
|
||||
</Text>
|
||||
<Text style={{fontFamily: 'Hoefler Text', fontVariant: ['oldstyle-nums']}}>
|
||||
Old Style nums 0123456789{'\n'}
|
||||
</Text>
|
||||
<Text style={{fontFamily: 'Hoefler Text', fontVariant: ['lining-nums']}}>
|
||||
Lining nums 0123456789{'\n'}
|
||||
</Text>
|
||||
<Text style={{fontVariant: ['tabular-nums']}}>
|
||||
Tabular nums{'\n'}
|
||||
1111{'\n'}
|
||||
2222{'\n'}
|
||||
</Text>
|
||||
<Text style={{fontVariant: ['proportional-nums']}}>
|
||||
Proportional nums{'\n'}
|
||||
1111{'\n'}
|
||||
2222{'\n'}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
}];
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
|
|
|
@ -20,6 +20,7 @@ extern NSString *const RCTReactTagAttributeName;
|
|||
@property (nonatomic, assign) CGFloat fontSize;
|
||||
@property (nonatomic, copy) NSString *fontWeight;
|
||||
@property (nonatomic, copy) NSString *fontStyle;
|
||||
@property (nonatomic, copy) NSArray *fontVariant;
|
||||
@property (nonatomic, assign) BOOL isHighlighted;
|
||||
@property (nonatomic, assign) CGFloat letterSpacing;
|
||||
@property (nonatomic, assign) CGFloat lineHeight;
|
||||
|
|
|
@ -10,16 +10,15 @@
|
|||
#import "RCTShadowText.h"
|
||||
|
||||
#import "RCTAccessibilityManager.h"
|
||||
#import "RCTUIManager.h"
|
||||
#import "RCTBridge.h"
|
||||
#import "RCTConvert.h"
|
||||
#import "RCTFont.h"
|
||||
#import "RCTLog.h"
|
||||
#import "RCTShadowRawText.h"
|
||||
#import "RCTText.h"
|
||||
#import "RCTUtils.h"
|
||||
#import "RCTConvert.h"
|
||||
#import "RCTTextView.h"
|
||||
#import "RCTFont.h"
|
||||
#import "RCTUIManager.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
NSString *const RCTShadowViewAttributeName = @"RCTShadowViewAttributeName";
|
||||
NSString *const RCTIsHighlightedAttributeName = @"IsHighlightedAttributeName";
|
||||
|
@ -267,8 +266,12 @@ static CSSSize RCTMeasure(void *context, float width, CSSMeasureMode widthMode,
|
|||
|
||||
_effectiveLetterSpacing = letterSpacing.doubleValue;
|
||||
|
||||
UIFont *font = [RCTFont updateFont:nil withFamily:fontFamily
|
||||
size:fontSize weight:fontWeight style:fontStyle
|
||||
UIFont *font = [RCTFont updateFont:nil
|
||||
withFamily:fontFamily
|
||||
size:fontSize
|
||||
weight:fontWeight
|
||||
style:fontStyle
|
||||
variant:_fontVariant
|
||||
scaleMultiplier:_allowFontScaling ? _fontSizeMultiplier : 1.0];
|
||||
|
||||
CGFloat heightOfTallestSubview = 0.0;
|
||||
|
@ -467,6 +470,7 @@ RCT_TEXT_PROPERTY(FontFamily, _fontFamily, NSString *)
|
|||
RCT_TEXT_PROPERTY(FontSize, _fontSize, CGFloat)
|
||||
RCT_TEXT_PROPERTY(FontWeight, _fontWeight, NSString *)
|
||||
RCT_TEXT_PROPERTY(FontStyle, _fontStyle, NSString *)
|
||||
RCT_TEXT_PROPERTY(FontVariant, _fontVariant, NSArray *)
|
||||
RCT_TEXT_PROPERTY(IsHighlighted, _isHighlighted, BOOL)
|
||||
RCT_TEXT_PROPERTY(LetterSpacing, _letterSpacing, CGFloat)
|
||||
RCT_TEXT_PROPERTY(LineHeight, _lineHeight, CGFloat)
|
||||
|
|
|
@ -60,6 +60,7 @@ RCT_EXPORT_SHADOW_PROPERTY(fontFamily, NSString)
|
|||
RCT_EXPORT_SHADOW_PROPERTY(fontSize, CGFloat)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(fontWeight, NSString)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(fontStyle, NSString)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(fontVariant, NSArray)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(isHighlighted, BOOL)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(letterSpacing, CGFloat)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(lineHeight, CGFloat)
|
||||
|
|
|
@ -30,6 +30,15 @@ var TextStylePropTypes = Object.assign(Object.create(ViewStylePropTypes), {
|
|||
['normal' /*default*/, 'bold',
|
||||
'100', '200', '300', '400', '500', '600', '700', '800', '900']
|
||||
),
|
||||
fontVariant: ReactPropTypes.arrayOf(
|
||||
ReactPropTypes.oneOf([
|
||||
'small-caps',
|
||||
'oldstyle-nums',
|
||||
'lining-nums',
|
||||
'tabular-nums',
|
||||
'proportional-nums',
|
||||
])
|
||||
),
|
||||
textShadowOffset: ReactPropTypes.shape(
|
||||
{width: ReactPropTypes.number, height: ReactPropTypes.number}
|
||||
),
|
||||
|
|
|
@ -232,7 +232,7 @@ RCT_CUSTOM_CONVERTER(type, type, [RCT_DEBUG ? [self NSNumber:json] : json getter
|
|||
* This macro is used for creating converter functions for typed arrays.
|
||||
*/
|
||||
#define RCT_ARRAY_CONVERTER(type) \
|
||||
+ (NSArray<id> *)type##Array:(id)json \
|
||||
+ (NSArray<type *> *)type##Array:(id)json \
|
||||
{ \
|
||||
return RCTConvertArrayValue(@selector(type:), json); \
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import <CoreText/CoreText.h>
|
||||
|
||||
#import "RCTConvert.h"
|
||||
|
||||
#import <objc/message.h>
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
size:(NSNumber *)size
|
||||
weight:(NSString *)weight
|
||||
style:(NSString *)style
|
||||
variant:(NSArray<NSString *> *)variant
|
||||
scaleMultiplier:(CGFloat)scaleMultiplier;
|
||||
|
||||
+ (UIFont *)updateFont:(UIFont *)font withFamily:(NSString *)family;
|
||||
|
|
|
@ -8,6 +8,9 @@
|
|||
*/
|
||||
|
||||
#import "RCTFont.h"
|
||||
#import "RCTLog.h"
|
||||
|
||||
#import <CoreText/CoreText.h>
|
||||
|
||||
#import <mutex>
|
||||
|
||||
|
@ -127,6 +130,7 @@ static UIFont *cachedSystemFont(CGFloat size, RCTFontWeight weight)
|
|||
size:[RCTConvert NSNumber:json[@"fontSize"]]
|
||||
weight:[RCTConvert NSString:json[@"fontWeight"]]
|
||||
style:[RCTConvert NSString:json[@"fontStyle"]]
|
||||
variant:[RCTConvert NSStringArray:json[@"fontVariant"]]
|
||||
scaleMultiplier:1];
|
||||
}
|
||||
|
||||
|
@ -151,6 +155,45 @@ RCT_ENUM_CONVERTER(RCTFontStyle, (@{
|
|||
@"oblique": @YES,
|
||||
}), NO, boolValue)
|
||||
|
||||
typedef NSDictionary RCTFontVariantDescriptor;
|
||||
+ (RCTFontVariantDescriptor *)RCTFontVariantDescriptor:(id)json
|
||||
{
|
||||
static NSDictionary *mapping;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
mapping = @{
|
||||
@"small-caps": @{
|
||||
UIFontFeatureTypeIdentifierKey: @(kLowerCaseType),
|
||||
UIFontFeatureSelectorIdentifierKey: @(kLowerCaseSmallCapsSelector),
|
||||
},
|
||||
@"oldstyle-nums": @{
|
||||
UIFontFeatureTypeIdentifierKey: @(kNumberCaseType),
|
||||
UIFontFeatureSelectorIdentifierKey: @(kLowerCaseNumbersSelector),
|
||||
},
|
||||
@"lining-nums": @{
|
||||
UIFontFeatureTypeIdentifierKey: @(kNumberCaseType),
|
||||
UIFontFeatureSelectorIdentifierKey: @(kUpperCaseNumbersSelector),
|
||||
},
|
||||
@"tabular-nums": @{
|
||||
UIFontFeatureTypeIdentifierKey: @(kNumberSpacingType),
|
||||
UIFontFeatureSelectorIdentifierKey: @(kMonospacedNumbersSelector),
|
||||
},
|
||||
@"proportional-nums": @{
|
||||
UIFontFeatureTypeIdentifierKey: @(kNumberSpacingType),
|
||||
UIFontFeatureSelectorIdentifierKey: @(kProportionalNumbersSelector),
|
||||
},
|
||||
};
|
||||
});
|
||||
RCTFontVariantDescriptor *value = mapping[json];
|
||||
if (RCT_DEBUG && !value && [json description].length > 0) {
|
||||
RCTLogError(@"Invalid RCTFontVariantDescriptor '%@'. should be one of: %@", json,
|
||||
[[mapping allKeys] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)]);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
RCT_ARRAY_CONVERTER(RCTFontVariantDescriptor)
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTFont
|
||||
|
@ -160,6 +203,7 @@ RCT_ENUM_CONVERTER(RCTFontStyle, (@{
|
|||
size:(NSNumber *)size
|
||||
weight:(NSString *)weight
|
||||
style:(NSString *)style
|
||||
variant:(NSArray<RCTFontVariantDescriptor *> *)variant
|
||||
scaleMultiplier:(CGFloat)scaleMultiplier
|
||||
{
|
||||
// Defaults
|
||||
|
@ -195,11 +239,15 @@ RCT_ENUM_CONVERTER(RCTFontStyle, (@{
|
|||
isItalic = style ? [RCTConvert RCTFontStyle:style] : isItalic;
|
||||
fontWeight = weight ? [RCTConvert RCTFontWeight:weight] : fontWeight;
|
||||
|
||||
BOOL didFindFont = NO;
|
||||
|
||||
// Handle system font as special case. This ensures that we preserve
|
||||
// the specific metrics of the standard system font as closely as possible.
|
||||
if ([familyName isEqual:defaultFontFamily] || [familyName isEqualToString:@"System"]) {
|
||||
font = cachedSystemFont(fontSize, fontWeight);
|
||||
if (font) {
|
||||
didFindFont = YES;
|
||||
|
||||
if (isItalic || isCondensed) {
|
||||
UIFontDescriptor *fontDescriptor = [font fontDescriptor];
|
||||
UIFontDescriptorSymbolicTraits symbolicTraits = fontDescriptor.symbolicTraits;
|
||||
|
@ -212,13 +260,12 @@ RCT_ENUM_CONVERTER(RCTFontStyle, (@{
|
|||
fontDescriptor = [fontDescriptor fontDescriptorWithSymbolicTraits:symbolicTraits];
|
||||
font = [UIFont fontWithDescriptor:fontDescriptor size:fontSize];
|
||||
}
|
||||
return font;
|
||||
}
|
||||
}
|
||||
|
||||
// Gracefully handle being given a font name rather than font family, for
|
||||
// example: "Helvetica Light Oblique" rather than just "Helvetica".
|
||||
if ([UIFont fontNamesForFamilyName:familyName].count == 0) {
|
||||
if (!didFindFont && [UIFont fontNamesForFamilyName:familyName].count == 0) {
|
||||
font = [UIFont fontWithName:familyName size:fontSize];
|
||||
if (font) {
|
||||
// It's actually a font name, not a font family name,
|
||||
|
@ -241,7 +288,6 @@ RCT_ENUM_CONVERTER(RCTFontStyle, (@{
|
|||
}
|
||||
|
||||
// Get the closest font that matches the given weight for the fontFamily
|
||||
UIFont *bestMatch = font;
|
||||
CGFloat closestWeight = INFINITY;
|
||||
for (NSString *name in [UIFont fontNamesForFamilyName:familyName]) {
|
||||
UIFont *match = [UIFont fontWithName:name size:fontSize];
|
||||
|
@ -249,33 +295,42 @@ RCT_ENUM_CONVERTER(RCTFontStyle, (@{
|
|||
isCondensed == isCondensedFont(match)) {
|
||||
CGFloat testWeight = weightOfFont(match);
|
||||
if (ABS(testWeight - fontWeight) < ABS(closestWeight - fontWeight)) {
|
||||
bestMatch = match;
|
||||
font = match;
|
||||
closestWeight = testWeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bestMatch;
|
||||
// Apply font variants to font object
|
||||
if (variant) {
|
||||
NSArray *fontFeatures = [RCTConvert RCTFontVariantDescriptorArray:variant];
|
||||
UIFontDescriptor *fontDescriptor = [font.fontDescriptor fontDescriptorByAddingAttributes:@{
|
||||
UIFontDescriptorFeatureSettingsAttribute: fontFeatures
|
||||
}];
|
||||
font = [UIFont fontWithDescriptor:fontDescriptor size:fontSize];
|
||||
}
|
||||
|
||||
return font;
|
||||
}
|
||||
|
||||
+ (UIFont *)updateFont:(UIFont *)font withFamily:(NSString *)family
|
||||
{
|
||||
return [self updateFont:font withFamily:family size:nil weight:nil style:nil scaleMultiplier:1];
|
||||
return [self updateFont:font withFamily:family size:nil weight:nil style:nil variant:nil scaleMultiplier:1];
|
||||
}
|
||||
|
||||
+ (UIFont *)updateFont:(UIFont *)font withSize:(NSNumber *)size
|
||||
{
|
||||
return [self updateFont:font withFamily:nil size:size weight:nil style:nil scaleMultiplier:1];
|
||||
return [self updateFont:font withFamily:nil size:size weight:nil style:nil variant:nil scaleMultiplier:1];
|
||||
}
|
||||
|
||||
+ (UIFont *)updateFont:(UIFont *)font withWeight:(NSString *)weight
|
||||
{
|
||||
return [self updateFont:font withFamily:nil size:nil weight:weight style:nil scaleMultiplier:1];
|
||||
return [self updateFont:font withFamily:nil size:nil weight:weight style:nil variant:nil scaleMultiplier:1];
|
||||
}
|
||||
|
||||
+ (UIFont *)updateFont:(UIFont *)font withStyle:(NSString *)style
|
||||
{
|
||||
return [self updateFont:font withFamily:nil size:nil weight:nil style:style scaleMultiplier:1];
|
||||
return [self updateFont:font withFamily:nil size:nil weight:nil style:style variant:nil scaleMultiplier:1];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
Loading…
Reference in New Issue