Skip to content

Commit e02c21b

Browse files
TorinAsakurameta-codesync[bot]
authored andcommitted
Fix Android inline Text views with small font scale
Summary: Fixes #50916. Closes torin-asakura/workspace#126. Android inline views inside `<Text>` receive their measured layout size from Fabric as layout units/DIP. TextLayoutManager was converting those attachment dimensions with `PixelUtil.toPixelFromSP(...)`, so a device font scale below 1.0 shrank the inline view placeholder width. In the #50916 RNTester reproducer, a measured `Row cutoff` attachment around 155px became about 132px at `font_scale=0.85`, clipping the rendered text to `Row`. This changes inline text attachment dimensions to use DIP conversion and rounds them up to the pixel grid in both spannable construction paths. ## Changelog: [ANDROID] [FIXED] - Keep inline views inside Text from shrinking with Android system font scale X-link: #57132 Differential Revision: D108030457 Pulled By: cipolleschi
1 parent 22cfb5c commit e02c21b

2 files changed

Lines changed: 71 additions & 5 deletions

File tree

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.kt

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -266,13 +266,15 @@ internal object TextLayoutManager {
266266
val reactTag =
267267
if (fragment.contains(FR_KEY_REACT_TAG)) fragment.getInt(FR_KEY_REACT_TAG) else View.NO_ID
268268
if (fragment.contains(FR_KEY_IS_ATTACHMENT) && fragment.getBoolean(FR_KEY_IS_ATTACHMENT)) {
269-
val width = PixelUtil.toPixelFromSP(fragment.getDouble(FR_KEY_WIDTH))
270-
val height = PixelUtil.toPixelFromSP(fragment.getDouble(FR_KEY_HEIGHT))
271269
ops.add(
272270
SetSpanOperation(
273271
sb.length - 1,
274272
sb.length,
275-
TextInlineViewPlaceholderSpan(reactTag, width.toInt(), height.toInt()),
273+
TextInlineViewPlaceholderSpan(
274+
reactTag,
275+
inlineViewSizeToPixels(fragment.getDouble(FR_KEY_WIDTH)),
276+
inlineViewSizeToPixels(fragment.getDouble(FR_KEY_HEIGHT)),
277+
),
276278
),
277279
)
278280
} else if (end >= start) {
@@ -490,8 +492,8 @@ internal object TextLayoutManager {
490492
spannable.setSpan(
491493
TextInlineViewPlaceholderSpan(
492494
fragment.reactTag,
493-
PixelUtil.toPixelFromSP(fragment.width).toInt(),
494-
PixelUtil.toPixelFromSP(fragment.height).toInt(),
495+
inlineViewSizeToPixels(fragment.width),
496+
inlineViewSizeToPixels(fragment.height),
495497
),
496498
start,
497499
end,
@@ -658,6 +660,9 @@ internal object TextLayoutManager {
658660
return spannable
659661
}
660662

663+
private fun inlineViewSizeToPixels(size: Double): Int =
664+
ceil(PixelUtil.toPixelFromDIP(size).toDouble()).toInt()
665+
661666
@OptIn(UnstableReactNativeAPI::class)
662667
fun getOrCreateSpannableForText(
663668
assets: AssetManager,
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
@file:Suppress("DEPRECATION")
9+
10+
package com.facebook.react.views.text
11+
12+
import android.util.DisplayMetrics
13+
import com.facebook.react.uimanager.DisplayMetricsHolder
14+
import org.assertj.core.api.Assertions.assertThat
15+
import org.junit.After
16+
import org.junit.Test
17+
import org.junit.runner.RunWith
18+
import org.robolectric.RobolectricTestRunner
19+
20+
@RunWith(RobolectricTestRunner::class)
21+
class TextLayoutManagerInlineViewSizeTest {
22+
23+
@After
24+
fun tearDown() {
25+
DisplayMetricsHolder.setScreenDisplayMetrics(null)
26+
}
27+
28+
@Test
29+
fun `inline view attachment width does not shrink with small font scale`() {
30+
DisplayMetricsHolder.setScreenDisplayMetrics(
31+
DisplayMetrics().apply {
32+
density = 1f
33+
scaledDensity = 0.85f
34+
},
35+
)
36+
37+
assertThat(invokeInlineViewSizeToPixels(155.0)).isEqualTo(155)
38+
}
39+
40+
@Test
41+
fun `inline view attachment width is rounded up to the pixel grid`() {
42+
DisplayMetricsHolder.setScreenDisplayMetrics(
43+
DisplayMetrics().apply {
44+
density = 1f
45+
scaledDensity = 1f
46+
},
47+
)
48+
49+
assertThat(invokeInlineViewSizeToPixels(132.1)).isEqualTo(133)
50+
}
51+
52+
private fun invokeInlineViewSizeToPixels(size: Double): Int {
53+
val method =
54+
TextLayoutManager::class
55+
.java
56+
.getDeclaredMethod("inlineViewSizeToPixels", java.lang.Double.TYPE)
57+
.apply { isAccessible = true }
58+
59+
return method.invoke(TextLayoutManager, size) as Int
60+
}
61+
}

0 commit comments

Comments
 (0)