Skip to content

Fix Android text measured narrower than drawn when EmojiCompat replaces an emoji - #59851

Open
KAMRONBEK wants to merge 1 commit into
react:mainfrom
KAMRONBEK:fix/android-emojicompat-measure
Open

KAMRONBEK wants to merge 1 commit into
react:mainfrom
KAMRONBEK:fix/android-emojicompat-measure

Conversation

@KAMRONBEK

Copy link
Copy Markdown

Summary:

Fixes #59850

On Android, text with an emoji the system font lacks (🫩 U+1FAE9 on Android 15, for example) is measured narrower than it's drawn, so the next view overlaps it. ReactTextView and ReactEditText are AppCompat views, and AppCompat runs their text through EmojiCompat, which replaces that emoji with an EmojiSpan from the downloadable font. TextLayoutManager measured the string without that span.

This runs EmojiCompat.process() on the Spannable in createSpannableFromAttributedString, right after onPostProcessSpannable, so measurement sees the same spans the view draws. It only does anything when EmojiCompat is configured and has finished loading, and with the default config it only adds spans for emoji the system font can't draw.

emoji2 1.3.0 is already on the runtime classpath through appcompat 1.7.0, but not on ReactAndroid's compile classpath. I added it as a direct dependency at that same version so the resolved version doesn't change. Internal builds will probably need the same dependency.

Known limits:

  • Text measured before EmojiCompat finishes loading keeps the old width. In my test a row mounted in the first frame stayed at 81.09 dp. Fixing that needs a re-measure when loading finishes, which I left out.
  • With prepared layout on (main's default), the measured layout is also the drawn one, so 🫩 now draws from the emoji font instead of tofu.

Changelog:

[ANDROID] [FIXED] - Fix text being measured narrower than drawn when EmojiCompat replaces an emoji the system font lacks

Test Plan:

Applied the same change to 0.88.1, built React Native from source in a fresh release app, and ran it on an ASUS Zenfone 10 (Android 15, Google Play services). Widths in dp, rows mounted 3 s after launch:

Row Before After
inline end 84.36 84.36
inline🫨 end 109.09 109.09
inline🫩 end 93.09, " end" hidden 109.09
wwww 🫩 😠 103.27, 😠 missing 119.27
🫩🫩🫩 26.18, one visible 74.18
TextInput a🫩b 30.91, tofu 46.91

With main's two text flags forced on in that app, selectable select🫩 end went from 99.64 (wrapped, clipped) to 115.64.

Before After

Repro app: https://github.com/KAMRONBEK/rn-emojicompat-repro

./gradlew :packages:react-native:ReactAndroid:testDebugUnitTest \
  --tests 'com.facebook.react.views.text.*' --tests 'com.facebook.react.views.textinput.*'

145 tests, 0 failures. ktfmt 0.59.0 with the format-kotlin flags passes on both changed Kotlin files.

Not run: the full test suite, yarn lint, RNTester, an app built from main, other devices.

…es an emoji

ReactTextView and ReactEditText extend AppCompat views, and AppCompat runs
their text through EmojiCompat. When the system font has no glyph for an
emoji (U+1FAE9 on Android 15, for example), EmojiCompat swaps it for a span
drawn from the downloadable emoji font. TextLayoutManager measured the same
text with the system font, so the drawn text came out wider than its box and
ran into whatever was laid out after it. On the phone I tested, each U+1FAE9
was measured at about 8.7dp and drawn at about 24.7dp.

Run EmojiCompat on every Spannable TextLayoutManager builds, once EmojiCompat
has loaded, so measurement sees the same spans the view draws. emoji2 is
already on the runtime classpath through appcompat 1.7.0; this adds it as a
direct dependency at the same version (1.3.0) so ReactAndroid can compile
against it.
@meta-cla

meta-cla Bot commented Nov 7, 2026

Copy link
Copy Markdown

Hi @KAMRONBEK!

Thank you for your pull request and welcome to our community.

Action Required

In order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you.

Process

In order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA.

Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with CLA signed. The tagging process may take up to 1 hour after signing. Please give it that time before contacting us about it.

If you have received this in error or have any questions, please contact us at cla@meta.com. Thanks!

@meta-cla

meta-cla Bot commented Nov 7, 2026

Copy link
Copy Markdown

Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Meta Open Source project. Thanks!

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Nov 7, 2026
@meta-cla

meta-cla Bot commented Nov 7, 2026

Copy link
Copy Markdown

Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Meta Open Source project. Thanks!

@facebook-github-tools facebook-github-tools Bot added the Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. label Nov 7, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Android] Text measured narrower than drawn when EmojiCompat replaces an emoji the system font lacks (🫩 on Android 15)

1 participant