From 0a94b1ee891bf441445d1cb5d4af7036bbc89a22 Mon Sep 17 00:00:00 2001 From: Mikael Sand Date: Fri, 19 Jul 2019 18:11:05 +0300 Subject: [PATCH] [ios] support numeric, relative and animated font-weight optimize enum conversion handling for null and empty string --- ios/RNSVGNode.h | 2 +- ios/Text/RNSVGFontData.h | 1 + ios/Text/RNSVGFontData.m | 78 +++++++++++++++++++++++++++- ios/Text/RNSVGGlyphContext.m | 2 +- ios/Text/RNSVGTSpan.m | 32 +++++++++--- ios/Text/RNSVGTextProperties.h | 23 ++++++-- ios/Text/RNSVGTextProperties.m | 43 +++++++++++++-- ios/ViewManagers/RNSVGGroupManager.m | 12 +++++ ios/ViewManagers/RNSVGTextManager.m | 12 +++++ 9 files changed, 187 insertions(+), 18 deletions(-) diff --git a/ios/RNSVGNode.h b/ios/RNSVGNode.h index d992478b..ba835786 100644 --- a/ios/RNSVGNode.h +++ b/ios/RNSVGNode.h @@ -13,7 +13,7 @@ /** * RNSVG nodes are implemented as base UIViews. They should be implementation for all basic - *interfaces for all non-defination nodes. + *interfaces for all non-definition nodes. */ @interface RNSVGNode : UIView diff --git a/ios/Text/RNSVGFontData.h b/ios/Text/RNSVGFontData.h index 22d68211..a988ca4b 100644 --- a/ios/Text/RNSVGFontData.h +++ b/ios/Text/RNSVGFontData.h @@ -12,6 +12,7 @@ enum RNSVGFontStyle fontStyle; NSDictionary * fontData; enum RNSVGFontWeight fontWeight; + int absoluteFontWeight; NSString *fontFeatureSettings; enum RNSVGFontVariantLigatures fontVariantLigatures; enum RNSVGTextAnchor textAnchor; diff --git a/ios/Text/RNSVGFontData.m b/ios/Text/RNSVGFontData.m index 9b9d9805..58f45a4b 100644 --- a/ios/Text/RNSVGFontData.m +++ b/ios/Text/RNSVGFontData.m @@ -30,6 +30,7 @@ RNSVGFontData *RNSVGFontData_Defaults; self->fontFamily = @""; self->fontStyle = RNSVGFontStyleNormal; self->fontWeight = RNSVGFontWeightNormal; + self->absoluteFontWeight = 400; self->fontFeatureSettings = @""; self->fontVariantLigatures = RNSVGFontVariantLigaturesNormal; self->textAnchor = RNSVGTextAnchorStart; @@ -51,6 +52,60 @@ RNSVGFontData *RNSVGFontData_Defaults; fontSize:fontSize]; } +- (void)setInheritedWeight:(RNSVGFontData*) parent { + fontWeight = parent->fontWeight; + absoluteFontWeight = parent->absoluteFontWeight; +} + +RNSVGFontWeight nearestFontWeight(int absoluteFontWeight) { + return RNSVGFontWeights[(int)round(absoluteFontWeight / 100.0)]; +} + +- (void)handleNumericWeight:(RNSVGFontData*)parent weight:(double)weight { + long roundWeight = round(weight); + if (roundWeight >= 1 && roundWeight <= 1000) { + absoluteFontWeight = (int)roundWeight; + fontWeight = nearestFontWeight(absoluteFontWeight); + } else { + [self setInheritedWeight:parent]; + } +} + +// https://drafts.csswg.org/css-fonts-4/#relative-weights +int AbsoluteFontWeight(RNSVGFontWeight fontWeight, RNSVGFontData* parent) { + if (fontWeight == RNSVGFontWeightBolder) { + return bolder(parent->absoluteFontWeight); + } else if (fontWeight == RNSVGFontWeightLighter) { + return lighter(parent->absoluteFontWeight); + } else { + return RNSVGAbsoluteFontWeights[fontWeight]; + } +} + +int bolder(int inherited) { + if (inherited < 350) { + return 400; + } else if (inherited < 550) { + return 700; + } else if (inherited < 900) { + return 900; + } else { + return inherited; + } +} + +int lighter(int inherited) { + if (inherited < 100) { + return inherited; + } else if (inherited < 550) { + return 100; + } else if (inherited < 750) { + return 400; + } else { + return 700; + } +} + + (instancetype)initWithNSDictionary:(NSDictionary *)font parent:(RNSVGFontData *)parent { RNSVGFontData *data = [RNSVGFontData alloc]; @@ -69,12 +124,31 @@ RNSVGFontData *RNSVGFontData_Defaults; else { data->fontSize = parentFontSize; } + + if ([font objectForKey:FONT_WEIGHT]) { + id fontWeight = [font objectForKey:FONT_WEIGHT]; + if ([fontWeight isKindOfClass:NSNumber.class]) { + [data handleNumericWeight:parent weight:[fontWeight doubleValue]]; + } else { + NSString* weight = fontWeight; + NSInteger fw = RNSVGFontWeightFromString(weight); + if (fw != -1) { + data->absoluteFontWeight = AbsoluteFontWeight(fw, parent); + data->fontWeight = nearestFontWeight(data->absoluteFontWeight); + } else if ([weight length] != 0) { + [data handleNumericWeight:parent weight:[weight doubleValue]]; + } else { + [data setInheritedWeight:parent]; + } + } + } else { + [data setInheritedWeight:parent]; + } + data->fontData = [font objectForKey:FONT_DATA] ? [font objectForKey:FONT_DATA] : parent->fontData; data->fontFamily = [font objectForKey:FONT_FAMILY] ? [font objectForKey:FONT_FAMILY] : parent->fontFamily; NSString* style = [font objectForKey:FONT_STYLE]; data->fontStyle = style ? RNSVGFontStyleFromString(style) : parent->fontStyle; - NSString* weight = [font objectForKey:FONT_WEIGHT]; - data->fontWeight = weight ? RNSVGFontWeightFromString(weight) : parent->fontWeight; NSString* feature = [font objectForKey:FONT_FEATURE_SETTINGS]; data->fontFeatureSettings = feature ? [font objectForKey:FONT_FEATURE_SETTINGS] : parent->fontFeatureSettings; NSString* variant = [font objectForKey:FONT_VARIANT_LIGATURES]; diff --git a/ios/Text/RNSVGGlyphContext.m b/ios/Text/RNSVGGlyphContext.m index 43f30a12..80fd0855 100644 --- a/ios/Text/RNSVGGlyphContext.m +++ b/ios/Text/RNSVGGlyphContext.m @@ -116,7 +116,7 @@ NSString *fontFamily = topFont_->fontFamily; NSNumber * fontSize = [NSNumber numberWithDouble:topFont_->fontSize]; - NSString * fontWeight = [RNSVGFontWeightToString(topFont_->fontWeight) lowercaseString]; + NSString * fontWeight = RNSVGFontWeightStrings[topFont_->fontWeight]; NSString * fontStyle = RNSVGFontStyleStrings[topFont_->fontStyle]; BOOL fontFamilyFound = NO; diff --git a/ios/Text/RNSVGTSpan.m b/ios/Text/RNSVGTSpan.m index 11a0e98c..eefeac25 100644 --- a/ios/Text/RNSVGTSpan.m +++ b/ios/Text/RNSVGTSpan.m @@ -137,10 +137,10 @@ static CGFloat RNSVGTSpan_radToDeg = 180 / (CGFloat)M_PI; bool allowOptionalLigatures = letterSpacing == 0 && font->fontVariantLigatures == RNSVGFontVariantLigaturesNormal; NSMutableDictionary *attrs = [[NSMutableDictionary alloc] init]; + CFMutableDictionaryRef attributes = (__bridge CFMutableDictionaryRef)attrs; NSNumber *lig = [NSNumber numberWithInt:allowOptionalLigatures ? 2 : 1]; attrs[NSLigatureAttributeName] = lig; - CFDictionaryRef attributes; if (fontRef != nil) { attrs[NSFontAttributeName] = (__bridge id)fontRef; } @@ -157,8 +157,18 @@ static CGFloat RNSVGTSpan_radToDeg = 180 / (CGFloat)M_PI; { [attrs setObject:kernAttr forKey:(id)kCTKernAttributeName]; } - - attributes = (__bridge CFDictionaryRef)attrs; + + int weight = font->absoluteFontWeight; + if (weight != 400) { + // https://github.com/WebKit/webkit/blob/73b06fb2cc31aaff91119718d9abdc7be703d41b/Source/WebCore/platform/graphics/cocoa/FontCacheCoreText.cpp#L429-L444 + float denormalizedWeight = (weight + 109.3) / 523.7; + CFMutableDictionaryRef variationDictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + long long bitwiseTag = 'w' << 24 | 'g' << 16 | 'h' << 8 | 't'; + CFNumberRef tagNumber = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongLongType, &bitwiseTag); + CFNumberRef valueNumber = CFNumberCreate(kCFAllocatorDefault, kCFNumberFloatType, &denormalizedWeight); + CFDictionarySetValue(variationDictionary, tagNumber, valueNumber); + CFDictionaryAddValue(attributes, kCTFontVariationAttribute, variationDictionary); + } CFStringRef string = (__bridge CFStringRef)str; CFAttributedStringRef attrString = CFAttributedStringCreate(kCFAllocatorDefault, string, attributes); @@ -310,10 +320,10 @@ static CGFloat RNSVGTSpan_radToDeg = 180 / (CGFloat)M_PI; // OpenType.js font data NSDictionary * fontData = font->fontData; NSMutableDictionary *attrs = [[NSMutableDictionary alloc] init]; + CFMutableDictionaryRef attributes = (__bridge CFMutableDictionaryRef)attrs; NSNumber *lig = [NSNumber numberWithInt:allowOptionalLigatures ? 2 : 1]; attrs[NSLigatureAttributeName] = lig; - CFDictionaryRef attributes; if (fontRef != nil) { attrs[NSFontAttributeName] = (__bridge id)fontRef; } @@ -331,8 +341,18 @@ static CGFloat RNSVGTSpan_radToDeg = 180 / (CGFloat)M_PI; [attrs setObject:noAutoKern forKey:(id)kCTKernAttributeName]; } } - - attributes = (__bridge CFDictionaryRef)attrs; + + int weight = font->absoluteFontWeight; + if (weight != 400) { + // https://github.com/WebKit/webkit/blob/73b06fb2cc31aaff91119718d9abdc7be703d41b/Source/WebCore/platform/graphics/cocoa/FontCacheCoreText.cpp#L429-L444 + float denormalizedWeight = (weight + 109.3) / 523.7; + CFMutableDictionaryRef variationDictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + long long bitwiseTag = 'w' << 24 | 'g' << 16 | 'h' << 8 | 't'; + CFNumberRef tagNumber = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongLongType, &bitwiseTag); + CFNumberRef valueNumber = CFNumberCreate(kCFAllocatorDefault, kCFNumberFloatType, &denormalizedWeight); + CFDictionarySetValue(variationDictionary, tagNumber, valueNumber); + CFDictionaryAddValue(attributes, kCTFontVariationAttribute, variationDictionary); + } CFStringRef string = (__bridge CFStringRef)str; CFAttributedStringRef attrString = CFAttributedStringCreate(kCFAllocatorDefault, string, attributes); diff --git a/ios/Text/RNSVGTextProperties.h b/ios/Text/RNSVGTextProperties.h index d86db41d..527ce3fb 100644 --- a/ios/Text/RNSVGTextProperties.h +++ b/ios/Text/RNSVGTextProperties.h @@ -86,10 +86,9 @@ NSString* RNSVGFontVariantLigaturesToString( enum RNSVGFontVariantLigatures fw ) enum RNSVGFontVariantLigatures RNSVGFontVariantLigaturesFromString( NSString* s ); typedef NS_ENUM(NSInteger, RNSVGFontWeight) { + // Absolute RNSVGFontWeightNormal, RNSVGFontWeightBold, - RNSVGFontWeightBolder, - RNSVGFontWeightLighter, RNSVGFontWeight100, RNSVGFontWeight200, RNSVGFontWeight300, @@ -99,11 +98,29 @@ typedef NS_ENUM(NSInteger, RNSVGFontWeight) { RNSVGFontWeight700, RNSVGFontWeight800, RNSVGFontWeight900, + // Relative + RNSVGFontWeightBolder, + RNSVGFontWeightLighter, RNSVGFontWeightDEFAULT = RNSVGFontWeightNormal, }; -static NSString* const RNSVGFontWeightStrings[] = {@"Normal", @"Bold", @"Bolder", @"Lighter", @"100", @"200", @"300", @"400", @"500", @"600", @"700", @"800", @"900", nil}; +static NSString* const RNSVGFontWeightStrings[] = {@"normal", @"bold", @"100", @"200", @"300", @"400", @"500", @"600", @"700", @"800", @"900", @"bolder", @"lighter", nil}; +static int const RNSVGAbsoluteFontWeights[] = {400, 700, 100, 200, 300, 400, 500, 600, 700, 800, 900}; + +static RNSVGFontWeight const RNSVGFontWeights[] = { + RNSVGFontWeight100, + RNSVGFontWeight100, + RNSVGFontWeight200, + RNSVGFontWeight300, + RNSVGFontWeightNormal, + RNSVGFontWeight500, + RNSVGFontWeight600, + RNSVGFontWeightBold, + RNSVGFontWeight800, + RNSVGFontWeight900, + RNSVGFontWeight900 +}; NSString* RNSVGFontWeightToString( enum RNSVGFontWeight fw ); diff --git a/ios/Text/RNSVGTextProperties.m b/ios/Text/RNSVGTextProperties.m index ff5f980e..a812d07f 100644 --- a/ios/Text/RNSVGTextProperties.m +++ b/ios/Text/RNSVGTextProperties.m @@ -9,6 +9,9 @@ NSString* RNSVGAlignmentBaselineToString( enum RNSVGAlignmentBaseline fw ) enum RNSVGAlignmentBaseline RNSVGAlignmentBaselineFromString( NSString* s ) { + if ([s length] == 0) { + return RNSVGAlignmentBaselineDEFAULT; + } const NSUInteger l = sizeof(RNSVGAlignmentBaselineStrings) / sizeof(NSString*); for (NSUInteger i = 0; i < l; i++) { if ([s isEqualToString:RNSVGAlignmentBaselineStrings[i]]) { @@ -27,6 +30,9 @@ NSString* RNSVGFontStyleToString( enum RNSVGFontStyle fw ) enum RNSVGFontStyle RNSVGFontStyleFromString( NSString* s ) { + if ([s length] == 0) { + return RNSVGFontStyleDEFAULT; + } const NSUInteger l = sizeof(RNSVGFontStyleStrings) / sizeof(NSString*); for (NSUInteger i = 0; i < l; i++) { if ([s isEqualToString:RNSVGFontStyleStrings[i]]) { @@ -45,6 +51,9 @@ NSString* RNSVGFontVariantLigaturesToString( enum RNSVGFontVariantLigatures fw ) enum RNSVGFontVariantLigatures RNSVGFontVariantLigaturesFromString( NSString* s ) { + if ([s length] == 0) { + return RNSVGFontVariantLigaturesDEFAULT; + } const NSUInteger l = sizeof(RNSVGFontVariantLigaturesStrings) / sizeof(NSString*); for (NSUInteger i = 0; i < l; i++) { if ([s isEqualToString:RNSVGFontVariantLigaturesStrings[i]]) { @@ -61,15 +70,18 @@ NSString* RNSVGFontWeightToString( enum RNSVGFontWeight fw ) return RNSVGFontWeightStrings[fw]; } -enum RNSVGFontWeight RNSVGFontWeightFromString( NSString* s ) +NSInteger RNSVGFontWeightFromString( NSString* s ) { - const NSUInteger l = sizeof(RNSVGFontWeightStrings) / sizeof(NSString*); - for (NSUInteger i = 0; i < l; i++) { - if ([[s capitalizedString] isEqualToString:RNSVGFontWeightStrings[i]]) { + if ([s length] == 0) { + return -1; + } + const NSInteger l = sizeof(RNSVGFontWeightStrings) / sizeof(NSString*); + for (NSInteger i = 0; i < l; i++) { + if ([s isEqualToString:RNSVGFontWeightStrings[i]]) { return i; } } - return RNSVGFontWeightDEFAULT; + return -1; } #pragma mark - RNSVGTextAnchor @@ -81,6 +93,9 @@ NSString* RNSVGTextAnchorToString( enum RNSVGTextAnchor fw ) enum RNSVGTextAnchor RNSVGTextAnchorFromString( NSString* s ) { + if ([s length] == 0) { + return RNSVGTextAnchorDEFAULT; + } const NSUInteger l = sizeof(RNSVGTextAnchorStrings) / sizeof(NSString*); for (NSUInteger i = 0; i < l; i++) { if ([s isEqualToString:RNSVGTextAnchorStrings[i]]) { @@ -99,6 +114,9 @@ NSString* RNSVGTextDecorationToString( enum RNSVGTextDecoration fw ) enum RNSVGTextDecoration RNSVGTextDecorationFromString( NSString* s ) { + if ([s length] == 0) { + return RNSVGTextDecorationDEFAULT; + } const NSUInteger l = sizeof(RNSVGTextDecorationStrings) / sizeof(NSString*); for (NSUInteger i = 0; i < l; i++) { if ([s isEqualToString:RNSVGTextDecorationStrings[i]]) { @@ -117,6 +135,9 @@ NSString* RNSVGTextLengthAdjustToString( enum RNSVGTextLengthAdjust fw ) enum RNSVGTextLengthAdjust RNSVGTextLengthAdjustFromString( NSString* s ) { + if ([s length] == 0) { + return RNSVGTextLengthAdjustDEFAULT; + } const NSUInteger l = sizeof(RNSVGTextLengthAdjustStrings) / sizeof(NSString*); for (NSUInteger i = 0; i < l; i++) { if ([s isEqualToString:RNSVGTextLengthAdjustStrings[i]]) { @@ -135,6 +156,9 @@ NSString* RNSVGTextPathMethodToString( enum RNSVGTextPathMethod fw ) enum RNSVGTextPathMethod RNSVGTextPathMethodFromString( NSString* s ) { + if ([s length] == 0) { + return RNSVGTextPathMethodDEFAULT; + } const NSUInteger l = sizeof(RNSVGTextPathMethodStrings) / sizeof(NSString*); for (NSUInteger i = 0; i < l; i++) { if ([s isEqualToString:RNSVGTextPathMethodStrings[i]]) { @@ -153,6 +177,9 @@ NSString* RNSVGTextPathMidLineToString( enum RNSVGTextPathMidLine fw ) enum RNSVGTextPathMidLine RNSVGTextPathMidLineFromString( NSString* s ) { + if ([s length] == 0) { + return RNSVGTextPathMidLineDEFAULT; + } const NSUInteger l = sizeof(RNSVGTextPathMidLineStrings) / sizeof(NSString*); for (NSUInteger i = 0; i < l; i++) { if ([s isEqualToString:RNSVGTextPathMidLineStrings[i]]) { @@ -171,6 +198,9 @@ NSString* RNSVGTextPathSideToString( enum RNSVGTextPathSide fw ) enum RNSVGTextPathSide RNSVGTextPathSideFromString( NSString* s ) { + if ([s length] == 0) { + return RNSVGTextPathSideDEFAULT; + } const NSUInteger l = sizeof(RNSVGTextPathSideStrings) / sizeof(NSString*); for (NSUInteger i = 0; i < l; i++) { if ([s isEqualToString:RNSVGTextPathSideStrings[i]]) { @@ -189,6 +219,9 @@ NSString* RNSVGTextPathSpacingToString( enum RNSVGTextPathSpacing fw ) enum RNSVGTextPathSpacing RNSVGTextPathSpacingFromString( NSString* s ) { + if ([s length] == 0) { + return RNSVGTextPathSpacingDEFAULT; + } const NSUInteger l = sizeof(RNSVGTextPathSpacingStrings) / sizeof(NSString*); for (NSUInteger i = 0; i < l; i++) { if ([s isEqualToString:RNSVGTextPathSpacingStrings[i]]) { diff --git a/ios/ViewManagers/RNSVGGroupManager.m b/ios/ViewManagers/RNSVGGroupManager.m index 8daa2335..d29c0a9a 100644 --- a/ios/ViewManagers/RNSVGGroupManager.m +++ b/ios/ViewManagers/RNSVGGroupManager.m @@ -33,4 +33,16 @@ RCT_CUSTOM_VIEW_PROPERTY(fontSize, id, RNSVGGroup) } } +RCT_CUSTOM_VIEW_PROPERTY(fontWeight, id, RNSVGGroup) +{ + if ([json isKindOfClass:[NSString class]]) { + NSString *stringValue = (NSString *)json; + view.font = @{ @"fontWeight": stringValue }; + } else { + NSNumber* number = (NSNumber*)json; + double num = [number doubleValue]; + view.font = @{@"fontWeight": [NSNumber numberWithDouble:num] }; + } +} + @end diff --git a/ios/ViewManagers/RNSVGTextManager.m b/ios/ViewManagers/RNSVGTextManager.m index a273ff08..3e7bf73f 100644 --- a/ios/ViewManagers/RNSVGTextManager.m +++ b/ios/ViewManagers/RNSVGTextManager.m @@ -80,4 +80,16 @@ RCT_CUSTOM_VIEW_PROPERTY(fontSize, id, RNSVGGroup) } } +RCT_CUSTOM_VIEW_PROPERTY(fontWeight, id, RNSVGGroup) +{ + if ([json isKindOfClass:[NSString class]]) { + NSString *stringValue = (NSString *)json; + view.font = @{ @"fontWeight": stringValue }; + } else { + NSNumber* number = (NSNumber*)json; + double num = [number doubleValue]; + view.font = @{@"fontWeight": [NSNumber numberWithDouble:num] }; + } +} + @end