diff --git a/change/react-native-windows-c9337072-335c-49fb-97c4-530cb46c136d.json b/change/react-native-windows-c9337072-335c-49fb-97c4-530cb46c136d.json new file mode 100644 index 00000000000..8a0b72f9ed4 --- /dev/null +++ b/change/react-native-windows-c9337072-335c-49fb-97c4-530cb46c136d.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "[Fabric] Implement writingDirection property for Text component", + "packageName": "react-native-windows", + "email": "54227869+anupriya13@users.noreply.github.com", + "dependentChangeType": "patch" +} \ No newline at end of file diff --git a/packages/playground/Samples/text.tsx b/packages/playground/Samples/text.tsx index a3a20d59fab..48639afd9d2 100644 --- a/packages/playground/Samples/text.tsx +++ b/packages/playground/Samples/text.tsx @@ -40,6 +40,32 @@ export default class Bootstrap extends React.Component { aliquip ex ea commodo consequat. + + {/* Writing Direction Examples */} + + Writing Direction Examples + + Left-to-Right (LTR): + + This text flows from left to right. Numbers: 123 456 789 + + + Right-to-Left (RTL): + + This text flows from right to left. Numbers: 123 456 789 + + + Auto/Natural: + + This text uses natural direction (defaults to LTR) + + ); } @@ -64,6 +90,36 @@ const styles = StyleSheet.create({ textAlign: 'center', margin: 10, }, + writingDirectionContainer: { + backgroundColor: '#E8F4FD', + padding: 15, + margin: 10, + borderRadius: 8, + width: 400, + }, + sectionTitle: { + fontSize: 18, + fontWeight: 'bold', + marginBottom: 15, + textAlign: 'center', + color: '#2C3E50', + }, + directionLabel: { + fontSize: 14, + fontWeight: '600', + marginTop: 10, + marginBottom: 5, + color: '#34495E', + }, + directionExample: { + fontSize: 16, + padding: 8, + backgroundColor: '#FFFFFF', + borderRadius: 4, + borderWidth: 1, + borderColor: '#BDC3C7', + marginBottom: 8, + }, }); AppRegistry.registerComponent('Bootstrap', () => Bootstrap); diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.cpp index c2093ef9bce..f2537f71016 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.cpp @@ -63,6 +63,10 @@ void ParagraphComponentView::updateProps( updateTextAlignment(newViewProps.textAttributes.alignment); } + if (oldViewProps.textAttributes.baseWritingDirection != newViewProps.textAttributes.baseWritingDirection) { + m_textLayout = nullptr; + } + if (oldViewProps.paragraphAttributes.ellipsizeMode != newViewProps.paragraphAttributes.ellipsizeMode) { m_textLayout = nullptr; } diff --git a/vnext/Microsoft.ReactNative/Fabric/platform/react/renderer/textlayoutmanager/WindowsTextLayoutManager.cpp b/vnext/Microsoft.ReactNative/Fabric/platform/react/renderer/textlayoutmanager/WindowsTextLayoutManager.cpp index b383fa72449..cfffa94c3d7 100644 --- a/vnext/Microsoft.ReactNative/Fabric/platform/react/renderer/textlayoutmanager/WindowsTextLayoutManager.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/platform/react/renderer/textlayoutmanager/WindowsTextLayoutManager.cpp @@ -158,6 +158,23 @@ void WindowsTextLayoutManager::GetTextLayout( } winrt::check_hresult(spTextFormat->SetTextAlignment(alignment)); + // Set reading direction based on baseWritingDirection + if (outerFragment.textAttributes.baseWritingDirection) { + DWRITE_READING_DIRECTION readingDirection = DWRITE_READING_DIRECTION_LEFT_TO_RIGHT; + + // Handle explicit RightToLeft + if (*outerFragment.textAttributes.baseWritingDirection == facebook::react::WritingDirection::RightToLeft) { + readingDirection = DWRITE_READING_DIRECTION_RIGHT_TO_LEFT; + } + // For all other cases (including Natural), use context-aware detection + else { + // For Natural direction, detect from text content + readingDirection = GetNaturalReadingDirection(attributedStringBox); + } + + winrt::check_hresult(spTextFormat->SetReadingDirection(readingDirection)); + } + // Get text with Object Replacement Characters for attachments auto str = GetTransformedText(attributedStringBox); winrt::check_hresult(Microsoft::ReactNative::DWriteFactory()->CreateTextLayout( @@ -547,4 +564,43 @@ winrt::hstring WindowsTextLayoutManager::GetTransformedText(const AttributedStri return result; } +DWRITE_READING_DIRECTION WindowsTextLayoutManager::GetNaturalReadingDirection(const AttributedStringBox &attributedStringBox) noexcept { + const auto &attributedString = attributedStringBox.getValue(); + + // Analyze the text content to determine the natural reading direction + // Look for the first strong directional character + for (const auto &fragment : attributedString.getFragments()) { + if (!fragment.isAttachment()) { + auto fragmentText = Microsoft::Common::Unicode::Utf8ToUtf16(fragment.string); + + for (wchar_t ch : fragmentText) { + // Check for strong RTL characters (Arabic, Hebrew ranges) + if ((ch >= 0x0590 && ch <= 0x05FF) || // Hebrew + (ch >= 0x0600 && ch <= 0x06FF) || // Arabic + (ch >= 0x0750 && ch <= 0x077F) || // Arabic Supplement + (ch >= 0x08A0 && ch <= 0x08FF) || // Arabic Extended-A + (ch >= 0xFB1D && ch <= 0xFB4F) || // Hebrew Presentation Forms + (ch >= 0xFB50 && ch <= 0xFDFF) || // Arabic Presentation Forms-A + (ch >= 0xFE70 && ch <= 0xFEFF)) { // Arabic Presentation Forms-B + return DWRITE_READING_DIRECTION_RIGHT_TO_LEFT; + } + // Check for strong LTR characters (Latin, Cyrillic, etc.) + else if ((ch >= 0x0041 && ch <= 0x005A) || // Latin uppercase + (ch >= 0x0061 && ch <= 0x007A) || // Latin lowercase + (ch >= 0x00C0 && ch <= 0x00D6) || // Latin extended + (ch >= 0x00D8 && ch <= 0x00F6) || // Latin extended + (ch >= 0x00F8 && ch <= 0x00FF) || // Latin extended + (ch >= 0x0100 && ch <= 0x017F) || // Latin Extended-A + (ch >= 0x0180 && ch <= 0x024F) || // Latin Extended-B + (ch >= 0x0400 && ch <= 0x04FF)) { // Cyrillic + return DWRITE_READING_DIRECTION_LEFT_TO_RIGHT; + } + } + } + } + + // If no strong directional characters found, use system default (LTR) + return DWRITE_READING_DIRECTION_LEFT_TO_RIGHT; +} + } // namespace facebook::react diff --git a/vnext/Microsoft.ReactNative/Fabric/platform/react/renderer/textlayoutmanager/WindowsTextLayoutManager.h b/vnext/Microsoft.ReactNative/Fabric/platform/react/renderer/textlayoutmanager/WindowsTextLayoutManager.h index 60a32b99562..e56ab41a1ad 100644 --- a/vnext/Microsoft.ReactNative/Fabric/platform/react/renderer/textlayoutmanager/WindowsTextLayoutManager.h +++ b/vnext/Microsoft.ReactNative/Fabric/platform/react/renderer/textlayoutmanager/WindowsTextLayoutManager.h @@ -58,6 +58,7 @@ class WindowsTextLayoutManager : public TextLayoutManager { private: static winrt::hstring GetTransformedText(const AttributedStringBox &attributedStringBox); + static DWRITE_READING_DIRECTION GetNaturalReadingDirection(const AttributedStringBox &attributedStringBox) noexcept; static void GetTextLayout( const AttributedStringBox &attributedStringBox, const ParagraphAttributes ¶graphAttributes,