diff --git a/packages/react-native/Libraries/Components/View/ReactNativeStyleAttributes.js b/packages/react-native/Libraries/Components/View/ReactNativeStyleAttributes.js index ac54183bb554..ba4e727d86c3 100644 --- a/packages/react-native/Libraries/Components/View/ReactNativeStyleAttributes.js +++ b/packages/react-native/Libraries/Components/View/ReactNativeStyleAttributes.js @@ -67,6 +67,10 @@ export const transformOriginAttribute: AnyAttributeType = nativeCSSParsing ? true : {process: processTransformOrigin}; +export const fontVariantAttribute: AnyAttributeType = nativeCSSParsing + ? true + : {process: processFontVariant}; + const ReactNativeStyleAttributes: {[string]: AnyAttributeType, ...} = { /** * Layout @@ -248,7 +252,7 @@ const ReactNativeStyleAttributes: {[string]: AnyAttributeType, ...} = { fontFamily: true, fontSize: true, fontStyle: true, - fontVariant: {process: processFontVariant}, + fontVariant: fontVariantAttribute, fontWeight: true, includeFontPadding: true, letterSpacing: true, diff --git a/packages/react-native/ReactCommon/react/renderer/attributedstring/conversions.h b/packages/react-native/ReactCommon/react/renderer/attributedstring/conversions.h index 5635a692b17b..a509abfab571 100644 --- a/packages/react-native/ReactCommon/react/renderer/attributedstring/conversions.h +++ b/packages/react-native/ReactCommon/react/renderer/attributedstring/conversions.h @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -21,6 +22,8 @@ #include #include #include +#include +#include #include #ifdef RN_SERIALIZABLE_STATE @@ -317,7 +320,73 @@ inline std::string toString(const FontStyle &fontStyle) return "normal"; } -inline void fromRawValue(const PropsParserContext &context, const RawValue &value, FontVariant &result) +inline std::optional fontVariantFromCSSFontVariant(CSSFontVariant cssVariant) +{ + switch (cssVariant) { + case CSSFontVariant::SmallCaps: + return FontVariant::SmallCaps; + case CSSFontVariant::OldstyleNums: + return FontVariant::OldstyleNums; + case CSSFontVariant::LiningNums: + return FontVariant::LiningNums; + case CSSFontVariant::TabularNums: + return FontVariant::TabularNums; + case CSSFontVariant::ProportionalNums: + return FontVariant::ProportionalNums; + case CSSFontVariant::StylisticOne: + return FontVariant::StylisticOne; + case CSSFontVariant::StylisticTwo: + return FontVariant::StylisticTwo; + case CSSFontVariant::StylisticThree: + return FontVariant::StylisticThree; + case CSSFontVariant::StylisticFour: + return FontVariant::StylisticFour; + case CSSFontVariant::StylisticFive: + return FontVariant::StylisticFive; + case CSSFontVariant::StylisticSix: + return FontVariant::StylisticSix; + case CSSFontVariant::StylisticSeven: + return FontVariant::StylisticSeven; + case CSSFontVariant::StylisticEight: + return FontVariant::StylisticEight; + case CSSFontVariant::StylisticNine: + return FontVariant::StylisticNine; + case CSSFontVariant::StylisticTen: + return FontVariant::StylisticTen; + case CSSFontVariant::StylisticEleven: + return FontVariant::StylisticEleven; + case CSSFontVariant::StylisticTwelve: + return FontVariant::StylisticTwelve; + case CSSFontVariant::StylisticThirteen: + return FontVariant::StylisticThirteen; + case CSSFontVariant::StylisticFourteen: + return FontVariant::StylisticFourteen; + case CSSFontVariant::StylisticFifteen: + return FontVariant::StylisticFifteen; + case CSSFontVariant::StylisticSixteen: + return FontVariant::StylisticSixteen; + case CSSFontVariant::StylisticSeventeen: + return FontVariant::StylisticSeventeen; + case CSSFontVariant::StylisticEighteen: + return FontVariant::StylisticEighteen; + case CSSFontVariant::StylisticNineteen: + return FontVariant::StylisticNineteen; + case CSSFontVariant::StylisticTwenty: + return FontVariant::StylisticTwenty; + case CSSFontVariant::CommonLigatures: + case CSSFontVariant::NoCommonLigatures: + case CSSFontVariant::DiscretionaryLigatures: + case CSSFontVariant::NoDiscretionaryLigatures: + case CSSFontVariant::HistoricalLigatures: + case CSSFontVariant::NoHistoricalLigatures: + case CSSFontVariant::Contextual: + case CSSFontVariant::NoContextual: + return std::nullopt; + } +} + +inline void +parseProcessedFontVariant(const PropsParserContext & /*context*/, const RawValue &value, FontVariant &result) { result = FontVariant::Default; react_native_expect(value.hasType>()); @@ -376,7 +445,6 @@ inline void fromRawValue(const PropsParserContext &context, const RawValue &valu result = (FontVariant)((int)result | (int)FontVariant::StylisticTwenty); } else { LOG(ERROR) << "Unsupported FontVariant value: " << item; - react_native_expect(false); } } } else { @@ -384,6 +452,40 @@ inline void fromRawValue(const PropsParserContext &context, const RawValue &valu } } +inline void parseUnprocessedFontVariantString(const std::string &value, FontVariant &result) +{ + auto fontVariantList = parseCSSProperty(value); + if (!std::holds_alternative(fontVariantList)) { + result = FontVariant::Default; + return; + } + + result = FontVariant::Default; + for (const auto &cssVariant : std::get(fontVariantList)) { + if (auto fv = fontVariantFromCSSFontVariant(cssVariant)) { + result = (FontVariant)((int)result | (int)*fv); + } + } +} + +inline void parseUnprocessedFontVariant(const PropsParserContext &context, const RawValue &value, FontVariant &result) +{ + if (value.hasType()) { + parseUnprocessedFontVariantString((std::string)value, result); + } else { + parseProcessedFontVariant(context, value, result); + } +} + +inline void fromRawValue(const PropsParserContext &context, const RawValue &value, FontVariant &result) +{ + if (ReactNativeFeatureFlags::enableNativeCSSParsing()) { + parseUnprocessedFontVariant(context, value, result); + } else { + parseProcessedFontVariant(context, value, result); + } +} + inline std::string toString(const FontVariant &fontVariant) { auto result = std::string{}; diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/tests/ConversionsTest.cpp b/packages/react-native/ReactCommon/react/renderer/components/view/tests/ConversionsTest.cpp index c784ecfd3ed7..e3c716415c5a 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/view/tests/ConversionsTest.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/view/tests/ConversionsTest.cpp @@ -7,6 +7,7 @@ #include +#include #include #include #include @@ -454,4 +455,29 @@ TEST(ConversionsTest, unprocessed_transform_origin_rawvalue_string_with_z) { EXPECT_EQ(result.z, 15.0f); } +TEST(ConversionsTest, unprocessed_font_variant_string_single) { + FontVariant result{}; + parseUnprocessedFontVariantString("small-caps", result); + + EXPECT_EQ((int)result, (int)FontVariant::SmallCaps); +} + +TEST(ConversionsTest, unprocessed_font_variant_string_multiple) { + FontVariant result{}; + parseUnprocessedFontVariantString( + "small-caps oldstyle-nums tabular-nums", result); + + EXPECT_EQ( + (int)result, + (int)FontVariant::SmallCaps | (int)FontVariant::OldstyleNums | + (int)FontVariant::TabularNums); +} + +TEST(ConversionsTest, unprocessed_font_variant_string_invalid) { + FontVariant result{}; + parseUnprocessedFontVariantString("not-a-variant", result); + + EXPECT_EQ((int)result, (int)FontVariant::Default); +} + } // namespace facebook::react