diff --git a/packages/react-native/Libraries/StyleSheet/StyleSheetTypes.d.ts b/packages/react-native/Libraries/StyleSheet/StyleSheetTypes.d.ts index c951a773d0ec..ef72aab6b704 100644 --- a/packages/react-native/Libraries/StyleSheet/StyleSheetTypes.d.ts +++ b/packages/react-native/Libraries/StyleSheet/StyleSheetTypes.d.ts @@ -568,7 +568,8 @@ export type FontVariant = | 'stylistic-seventeen' | 'stylistic-eighteen' | 'stylistic-nineteen' - | 'stylistic-twenty'; + | 'stylistic-twenty' + | 'case-sensitive'; export interface TextStyleIOS extends ViewStyle { fontVariant?: FontVariant[] | undefined; textDecorationColor?: ColorValue | undefined; diff --git a/packages/react-native/Libraries/StyleSheet/StyleSheetTypes.js b/packages/react-native/Libraries/StyleSheet/StyleSheetTypes.js index 1bf46c32b078..e7714f394a7b 100644 --- a/packages/react-native/Libraries/StyleSheet/StyleSheetTypes.js +++ b/packages/react-native/Libraries/StyleSheet/StyleSheetTypes.js @@ -990,7 +990,8 @@ export type ____FontVariant_Internal = | 'stylistic-seventeen' | 'stylistic-eighteen' | 'stylistic-nineteen' - | 'stylistic-twenty'; + | 'stylistic-twenty' + | 'case-sensitive'; export type ____FontVariantArray_Internal = ReadonlyArray<____FontVariant_Internal>; diff --git a/packages/react-native/Libraries/StyleSheet/__tests__/processFontVariant-test.js b/packages/react-native/Libraries/StyleSheet/__tests__/processFontVariant-test.js index 0c93e06a9217..72bec7c64c2c 100644 --- a/packages/react-native/Libraries/StyleSheet/__tests__/processFontVariant-test.js +++ b/packages/react-native/Libraries/StyleSheet/__tests__/processFontVariant-test.js @@ -28,6 +28,15 @@ describe('processFontVariant', () => { expect(processFontVariant(' tabular-nums')).toEqual(['tabular-nums']); }); + it('should accept case-sensitive variant', () => { + expect(processFontVariant(['case-sensitive'])).toEqual(['case-sensitive']); + expect(processFontVariant('case-sensitive')).toEqual(['case-sensitive']); + expect(processFontVariant(['case-sensitive', 'tabular-nums'])).toEqual([ + 'case-sensitive', + 'tabular-nums', + ]); + }); + it('should accept string with multiple values', () => { expect(processFontVariant('oldstyle-nums lining-nums')).toEqual([ 'oldstyle-nums', diff --git a/packages/react-native/React/Views/RCTFont.mm b/packages/react-native/React/Views/RCTFont.mm index 675f40c3131c..ce34198f9203 100644 --- a/packages/react-native/React/Views/RCTFont.mm +++ b/packages/react-native/React/Views/RCTFont.mm @@ -365,7 +365,11 @@ + (RCTFontVariantDescriptor *)RCTFontVariantDescriptor:(id)json @"stylistic-twenty" : @{ UIFontFeatureTypeIdentifierKey : @(kStylisticAlternativesType), UIFontFeatureSelectorIdentifierKey : @(kStylisticAltTwentyOnSelector), - } + }, + @"case-sensitive" : @{ + UIFontFeatureTypeIdentifierKey : @(kCaseSensitiveLayoutType), + UIFontFeatureSelectorIdentifierKey : @(kCaseSensitiveLayoutOnSelector), + }, }; }); RCTFontVariantDescriptor *value = mapping[json]; diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTypefaceUtils.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTypefaceUtils.kt index e33daa691f31..a06d7f57f73f 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTypefaceUtils.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTypefaceUtils.kt @@ -90,6 +90,7 @@ public object ReactTypefaceUtils { "stylistic-eighteen" -> features.add("'ss18'") "stylistic-nineteen" -> features.add("'ss19'") "stylistic-twenty" -> features.add("'ss20'") + "case-sensitive" -> features.add("'case'") } } return features.joinToString(", ") diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextAttributeProps.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextAttributeProps.kt index 45c8bec4c2b8..c9543746326f 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextAttributeProps.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextAttributeProps.kt @@ -234,6 +234,7 @@ public class TextAttributeProps private constructor() { "stylistic-eighteen" -> features.add("'ss18'") "stylistic-nineteen" -> features.add("'ss19'") "stylistic-twenty" -> features.add("'ss20'") + "case-sensitive" -> features.add("'case'") } } } diff --git a/packages/react-native/ReactCommon/react/renderer/attributedstring/conversions.h b/packages/react-native/ReactCommon/react/renderer/attributedstring/conversions.h index 331e2019338a..06eba877e4f7 100644 --- a/packages/react-native/ReactCommon/react/renderer/attributedstring/conversions.h +++ b/packages/react-native/ReactCommon/react/renderer/attributedstring/conversions.h @@ -372,6 +372,8 @@ inline std::optional fontVariantFromCSSFontVariant(CSSFontVariant c return FontVariant::StylisticNineteen; case CSSFontVariant::StylisticTwenty: return FontVariant::StylisticTwenty; + case CSSFontVariant::CaseSensitive: + return FontVariant::CaseSensitive; case CSSFontVariant::CommonLigatures: case CSSFontVariant::NoCommonLigatures: case CSSFontVariant::DiscretionaryLigatures: @@ -442,6 +444,8 @@ parseProcessedFontVariant(const PropsParserContext & /*context*/, const RawValue result = (FontVariant)((int)result | (int)FontVariant::StylisticNineteen); } else if (item == "stylistic-twenty") { result = (FontVariant)((int)result | (int)FontVariant::StylisticTwenty); + } else if (item == "case-sensitive") { + result = (FontVariant)((int)result | (int)FontVariant::CaseSensitive); } else { LOG(ERROR) << "Unsupported FontVariant value: " << item; } @@ -564,6 +568,9 @@ inline std::string toString(const FontVariant &fontVariant) if (((int)fontVariant & (int)FontVariant::StylisticTwenty) != 0) { result += "stylistic-twenty" + separator; } + if (((int)fontVariant & (int)FontVariant::CaseSensitive) != 0) { + result += "case-sensitive" + separator; + } if (!result.empty()) { result.erase(result.length() - separator.length()); @@ -1237,6 +1244,9 @@ inline MapBuffer toMapBuffer(const FontVariant &fontVariant) if ((int)fontVariant & (int)FontVariant::StylisticTwenty) { builder.putString(index++, "stylistic-twenty"); } + if ((int)fontVariant & (int)FontVariant::CaseSensitive) { + builder.putString(index++, "case-sensitive"); + } return builder.build(); } diff --git a/packages/react-native/ReactCommon/react/renderer/attributedstring/primitives.h b/packages/react-native/ReactCommon/react/renderer/attributedstring/primitives.h index d23104518c74..eab5ff714266 100644 --- a/packages/react-native/ReactCommon/react/renderer/attributedstring/primitives.h +++ b/packages/react-native/ReactCommon/react/renderer/attributedstring/primitives.h @@ -62,7 +62,8 @@ enum class FontVariant : int { StylisticSeventeen = 1 << 22, StylisticEighteen = 1 << 23, StylisticNineteen = 1 << 24, - StylisticTwenty = 1 << 25 + StylisticTwenty = 1 << 25, + CaseSensitive = 1 << 26 }; enum class DynamicTypeRamp { @@ -132,7 +133,12 @@ enum class LineBreakMode { Tail // Truncate at tail of line: "abcd..." }; -enum class TextDecorationLineType { None, Underline, Strikethrough, UnderlineStrikethrough }; +enum class TextDecorationLineType { + None, + Underline, + Strikethrough, + UnderlineStrikethrough +}; enum class TextDecorationStyle { Solid, Double, Dotted, Dashed }; diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSFontVariant.h b/packages/react-native/ReactCommon/react/renderer/css/CSSFontVariant.h index b4749170fe43..6f9db1f48687 100644 --- a/packages/react-native/ReactCommon/react/renderer/css/CSSFontVariant.h +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSFontVariant.h @@ -26,7 +26,8 @@ enum class CSSFontVariant : std::underlying_type_t { CommonLigatures = to_underlying(CSSKeyword::CommonLigatures), NoCommonLigatures = to_underlying(CSSKeyword::NoCommonLigatures), DiscretionaryLigatures = to_underlying(CSSKeyword::DiscretionaryLigatures), - NoDiscretionaryLigatures = to_underlying(CSSKeyword::NoDiscretionaryLigatures), + NoDiscretionaryLigatures = + to_underlying(CSSKeyword::NoDiscretionaryLigatures), HistoricalLigatures = to_underlying(CSSKeyword::HistoricalLigatures), NoHistoricalLigatures = to_underlying(CSSKeyword::NoHistoricalLigatures), Contextual = to_underlying(CSSKeyword::Contextual), @@ -52,6 +53,7 @@ enum class CSSFontVariant : std::underlying_type_t { StylisticEighteen = to_underlying(CSSKeyword::StylisticEighteen), StylisticNineteen = to_underlying(CSSKeyword::StylisticNineteen), StylisticTwenty = to_underlying(CSSKeyword::StylisticTwenty), + CaseSensitive = to_underlying(CSSKeyword::CaseSensitive), }; static_assert(CSSDataType); diff --git a/packages/react-native/ReactCommon/react/renderer/css/CSSKeyword.h b/packages/react-native/ReactCommon/react/renderer/css/CSSKeyword.h index f75901853c86..2b132929e1d9 100644 --- a/packages/react-native/ReactCommon/react/renderer/css/CSSKeyword.h +++ b/packages/react-native/ReactCommon/react/renderer/css/CSSKeyword.h @@ -28,6 +28,7 @@ enum class CSSKeyword : uint8_t { Baseline, Block, Bottom, + CaseSensitive, Center, Clip, Column, @@ -150,6 +151,7 @@ CSS_DEFINE_KEYWORD(Auto, "auto") CSS_DEFINE_KEYWORD(Baseline, "baseline") CSS_DEFINE_KEYWORD(Block, "block") CSS_DEFINE_KEYWORD(Bottom, "bottom") +CSS_DEFINE_KEYWORD(CaseSensitive, "case-sensitive") CSS_DEFINE_KEYWORD(Center, "center") CSS_DEFINE_KEYWORD(Clip, "clip") CSS_DEFINE_KEYWORD(Column, "column") @@ -263,6 +265,7 @@ constexpr std::optional parseCSSKeyword(std::string_view ident) CSS_HANDLE_KEYWORD(Baseline) CSS_HANDLE_KEYWORD(Block) CSS_HANDLE_KEYWORD(Bottom) + CSS_HANDLE_KEYWORD(CaseSensitive) CSS_HANDLE_KEYWORD(Center) CSS_HANDLE_KEYWORD(Clip) CSS_HANDLE_KEYWORD(Column) @@ -361,7 +364,8 @@ constexpr std::optional parseCSSKeyword(std::string_view ident) template struct CSSDataTypeParser { - static constexpr auto consumePreservedToken(const CSSPreservedToken &token) -> std::optional + static constexpr auto consumePreservedToken(const CSSPreservedToken &token) + -> std::optional { if (token.type() == CSSTokenType::Ident) { return parseCSSKeyword(token.stringValue()); diff --git a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTFontProperties.h b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTFontProperties.h index efbfdf0d894e..9466627af681 100644 --- a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTFontProperties.h +++ b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTFontProperties.h @@ -44,6 +44,7 @@ typedef NS_OPTIONS(NSInteger, RCTFontVariant) { RCTFontVariantStylisticEighteen = 1 << 23, RCTFontVariantStylisticNineteen = 1 << 24, RCTFontVariantStylisticTwenty = 1 << 25, + RCTFontVariantCaseSensitive = 1 << 26, }; struct RCTFontProperties { diff --git a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTFontUtils.mm b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTFontUtils.mm index 9b04cadc22f5..2cc0b8234540 100644 --- a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTFontUtils.mm +++ b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTFontUtils.mm @@ -166,6 +166,10 @@ static RCTFontStyle RCTGetFontStyle(UIFont *font) UIFontFeatureTypeIdentifierKey : @(kStylisticAlternativesType), UIFontFeatureSelectorIdentifierKey : @(kStylisticAltTwentyOnSelector), }}, + {RCTFontVariantCaseSensitive, @{ + UIFontFeatureTypeIdentifierKey : @(kCaseSensitiveLayoutType), + UIFontFeatureSelectorIdentifierKey : @(kCaseSensitiveLayoutOnSelector), + }}, }; }); @@ -244,6 +248,9 @@ static RCTFontStyle RCTGetFontStyle(UIFont *font) if ((fontVariant & RCTFontVariantStylisticTwenty) != 0) { [fontFeatures addObject:mapping[RCTFontVariantStylisticTwenty]]; } + if ((fontVariant & RCTFontVariantCaseSensitive) != 0) { + [fontFeatures addObject:mapping[RCTFontVariantCaseSensitive]]; + } return fontFeatures; } diff --git a/packages/react-native/ReactNativeApi.d.ts b/packages/react-native/ReactNativeApi.d.ts index 261ef45f8c2f..60741264e10c 100644 --- a/packages/react-native/ReactNativeApi.d.ts +++ b/packages/react-native/ReactNativeApi.d.ts @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<> * * This file was generated by scripts/js-api/build-types/index.js. */ @@ -628,6 +628,7 @@ declare type ____FlattenStyleProp_Internal< ? any : ____FlattenStyleProp_Helper declare type ____FontVariant_Internal = + | "case-sensitive" | "common-ligatures" | "contextual" | "discretionary-ligatures" @@ -6067,7 +6068,7 @@ export { AlertOptions, // a0cdac0f AlertType, // 5ab91217 AndroidKeyboardEvent, // e03becc8 - Animated, // 0652b5d1 + Animated, // 91b43ef7 AppConfig, // ce4209a7 AppRegistry, // 5edf0524 AppState, // 12012be5 @@ -6117,7 +6118,7 @@ export { FlatList, // 8c50f04a FlatListProps, // e170f2c9 FocusEvent, // 62fc1eb8 - FontVariant, // 7c7558bb + FontVariant, // 8779dc78 GestureResponderEvent, // f693e9a5 GestureResponderHandlers, // cc70e4cb Handle, // 2d65285d @@ -6278,7 +6279,7 @@ export { StatusBarProps, // 06c98add StatusBarStyle, // 986b2051 StyleProp, // fa0e9b4a - StyleSheet, // e77dd046 + StyleSheet, // 614889ca SubmitBehavior, // c4ddf490 Switch, // 3434138b SwitchChangeEvent, // 63e9c50b @@ -6287,9 +6288,9 @@ export { TVViewPropsIOS, // 330ce7b5 TargetedEvent, // 16e98910 TaskProvider, // 266dedf2 - Text, // 717d25fe + Text, // 044dd5f4 TextContentType, // 239b3ecc - TextInput, // ed3a8375 + TextInput, // 68d39b8a TextInputAndroidProps, // 3f09ce49 TextInputChangeEvent, // 3ab11bb4 TextInputContentSizeChangeEvent, // f71f8571 @@ -6297,12 +6298,12 @@ export { TextInputFocusEvent, // 020507e6 TextInputIOSProps, // 0d05a855 TextInputKeyPressEvent, // 3924ad9b - TextInputProps, // 9b370db2 + TextInputProps, // 0f04d5b7 TextInputSelectionChangeEvent, // d4d10630 TextInputSubmitEditingEvent, // 22885c31 TextLayoutEvent, // 73ab173e - TextProps, // 68a1c0e8 - TextStyle, // bb9b7a58 + TextProps, // f62cde6d + TextStyle, // b327f541 ToastAndroid, // 88a8969a Touchable, // da3239ee TouchableHighlight, // 9d67503a diff --git a/packages/rn-tester/js/examples/Text/TextExample.ios.js b/packages/rn-tester/js/examples/Text/TextExample.ios.js index 68b4a103a87e..7a92312ec99d 100644 --- a/packages/rn-tester/js/examples/Text/TextExample.ios.js +++ b/packages/rn-tester/js/examples/Text/TextExample.ios.js @@ -1329,6 +1329,21 @@ const examples = [ 1111{'\n'} 2222{'\n'} + + (E-H) [E-H]+ Case sensitive (OFF) + + + (E-H) [E-H]+ Case sensitive (ON) + + + Note: the visual rendering differences for this font are only + discernible on-device, not in a simulator. + ); },