From 1f5f086ac7ef410f2d195467056b1dbdaa306297 Mon Sep 17 00:00:00 2001 From: Andrew Ghostuhin Date: Tue, 9 Jun 2026 14:51:43 +0300 Subject: [PATCH] fix(android): measure inline text views in dp --- .../react/views/text/TextLayoutManager.kt | 15 +++-- .../TextLayoutManagerInlineViewSizeTest.kt | 61 +++++++++++++++++++ 2 files changed, 71 insertions(+), 5 deletions(-) create mode 100644 packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views/text/TextLayoutManagerInlineViewSizeTest.kt 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 4a900ffcf71..85ca66631bd 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 @@ -264,13 +264,15 @@ internal object TextLayoutManager { val reactTag = if (fragment.contains(FR_KEY_REACT_TAG)) fragment.getInt(FR_KEY_REACT_TAG) else View.NO_ID if (fragment.contains(FR_KEY_IS_ATTACHMENT) && fragment.getBoolean(FR_KEY_IS_ATTACHMENT)) { - val width = PixelUtil.toPixelFromSP(fragment.getDouble(FR_KEY_WIDTH)) - val height = PixelUtil.toPixelFromSP(fragment.getDouble(FR_KEY_HEIGHT)) ops.add( SetSpanOperation( sb.length - 1, sb.length, - TextInlineViewPlaceholderSpan(reactTag, width.toInt(), height.toInt()), + TextInlineViewPlaceholderSpan( + reactTag, + inlineViewSizeToPixels(fragment.getDouble(FR_KEY_WIDTH)), + inlineViewSizeToPixels(fragment.getDouble(FR_KEY_HEIGHT)), + ), ) ) } else if (end >= start) { @@ -484,8 +486,8 @@ internal object TextLayoutManager { spannable.setSpan( TextInlineViewPlaceholderSpan( fragment.reactTag, - PixelUtil.toPixelFromSP(fragment.width).toInt(), - PixelUtil.toPixelFromSP(fragment.height).toInt(), + inlineViewSizeToPixels(fragment.width), + inlineViewSizeToPixels(fragment.height), ), start, end, @@ -649,6 +651,9 @@ internal object TextLayoutManager { return spannable } + private fun inlineViewSizeToPixels(size: Double): Int = + ceil(PixelUtil.toPixelFromDIP(size).toDouble()).toInt() + @OptIn(UnstableReactNativeAPI::class) fun getOrCreateSpannableForText( assets: AssetManager, diff --git a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views/text/TextLayoutManagerInlineViewSizeTest.kt b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views/text/TextLayoutManagerInlineViewSizeTest.kt new file mode 100644 index 00000000000..6433d46d321 --- /dev/null +++ b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views/text/TextLayoutManagerInlineViewSizeTest.kt @@ -0,0 +1,61 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +@file:Suppress("DEPRECATION") + +package com.facebook.react.views.text + +import android.util.DisplayMetrics +import com.facebook.react.uimanager.DisplayMetricsHolder +import org.assertj.core.api.Assertions.assertThat +import org.junit.After +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner + +@RunWith(RobolectricTestRunner::class) +class TextLayoutManagerInlineViewSizeTest { + + @After + fun tearDown() { + DisplayMetricsHolder.setScreenDisplayMetrics(null) + } + + @Test + fun `inline view attachment width does not shrink with small font scale`() { + DisplayMetricsHolder.setScreenDisplayMetrics( + DisplayMetrics().apply { + density = 1f + scaledDensity = 0.85f + } + ) + + assertThat(invokeInlineViewSizeToPixels(155.0)).isEqualTo(155) + } + + @Test + fun `inline view attachment width is rounded up to the pixel grid`() { + DisplayMetricsHolder.setScreenDisplayMetrics( + DisplayMetrics().apply { + density = 1f + scaledDensity = 1f + } + ) + + assertThat(invokeInlineViewSizeToPixels(132.1)).isEqualTo(133) + } + + private fun invokeInlineViewSizeToPixels(size: Double): Int { + val method = + TextLayoutManager::class + .java + .getDeclaredMethod("inlineViewSizeToPixels", java.lang.Double.TYPE) + .apply { isAccessible = true } + + return method.invoke(TextLayoutManager, size) as Int + } +}