diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.kt index 8f762006af5b..d906e9d78e6b 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.kt @@ -902,7 +902,12 @@ internal object TextLayoutManager { } } } - val enableStartOverhang = widthYogaMeasureMode == YogaMeasureMode.EXACTLY + // Bounds-based width is limited to text with RTL characters. An EXACTLY width usually comes + // from an earlier advance-based AT_MOST measurement, and LTR glyph bounds that are wider than + // their advances (some OEM fonts) would make a single line overflow that width and ellipsize. + val enableStartOverhang = + widthYogaMeasureMode == YogaMeasureMode.EXACTLY && + TextDirectionHeuristics.ANYRTL_LTR.isRtl(text, 0, text.length) val layout = buildLayout( text, diff --git a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views/text/TextLayoutManagerStartOverhangTest.kt b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views/text/TextLayoutManagerStartOverhangTest.kt index ecbdbf71a694..ee7001d3ba76 100644 --- a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views/text/TextLayoutManagerStartOverhangTest.kt +++ b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views/text/TextLayoutManagerStartOverhangTest.kt @@ -46,6 +46,29 @@ class TextLayoutManagerStartOverhangTest { assertThat(getBooleanLayoutProperty(layout, "getShiftDrawingOffsetForStartOverhang")).isFalse() } + @Test + @Config(sdk = [35]) + fun `EXACTLY mode keeps advance based width measurement for LTR text`() { + val layout = + createLayout( + YogaMeasureMode.EXACTLY, + text = SpannableString("Just listed"), + ellipsizeMode = TextUtils.TruncateAt.END, + maxNumberOfLines = 1, + ) + + assertThat(getBooleanLayoutProperty(layout, "getUseBoundsForWidth")).isFalse() + assertThat(getBooleanLayoutProperty(layout, "getShiftDrawingOffsetForStartOverhang")).isFalse() + } + + @Test + @Config(sdk = [35]) + fun `EXACTLY mode enables start overhang support for mixed LTR and RTL text`() { + val layout = createLayout(YogaMeasureMode.EXACTLY, text = SpannableString("Hello שלום")) + + assertThat(getBooleanLayoutProperty(layout, "getUseBoundsForWidth")).isTrue() + } + @Test @Config(sdk = [35]) fun `RTL right overhang is rounded up to reserve whole pixels`() {