Skip to content

Fix Android Text measured one line taller than drawn at fractional widths - #59299

Closed
hcbylmz wants to merge 1 commit into
react:mainfrom
hcbylmz:fix/android-text-at-most-fractional-width
Closed

hcbylmz wants to merge 1 commit into
react:mainfrom
hcbylmz:fix/android-text-at-most-fractional-width

Conversation

@hcbylmz

@hcbylmz hcbylmz commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

Summary:

Fixes #59174.

On Android, TextLayoutManager.createLayout() wraps text at floor(width) when Yoga measures with AT_MOST, but at ceil(width) when the text is drawn with EXACTLY. When the text width falls in (floor(width), width], measuring wraps it to one more line than drawing does. The view keeps the taller measured height, so an empty line shows below the text. The issue has a real case: a 467.17 px text in a 467.99997 px column (density 2.1).

#56651 changed EXACTLY to ceil and kept AT_MOST on floor, so the reported size never goes over the AT_MOST constraint. This PR keeps that rule and changes where the text wraps:

  • AT_MOST now wraps at ceil(width), the same width EXACTLY uses, so measure and draw break lines the same way.
  • calculateWidth() returns min(layout.width, width) for AT_MOST, so the width reported to Yoga still never goes over the constraint. The layout can be up to a subpixel wider than width. Drawing already does the same with ceil(width).

The issue suggests floor(width + 0.01f). That fixes float rounding cases like 467.99997, but not real fractional widths like 467.5, where the same mismatch happens. The new test uses such a width.

Thanks to @YigitGok for the root cause analysis in the issue.

Changelog:

[ANDROID] [FIXED] - Fix Text measured one line taller than it is drawn when its width is a fractional pixel value

Test Plan:

Added TextLayoutManagerAtMostFractionalWidthTest (Robolectric, GraphicsMode.NATIVE so text is measured with real fonts). It builds the layout for the same fractional width with AT_MOST and with EXACTLY and checks that both have the same line count.

  • Without the fix: fails. With width 163.898 px and text width 163.797 px, AT_MOST gives 2 lines (layout width 163) and EXACTLY gives 1 line (layout width 164).
  • With the fix: passes.
./gradlew :packages:react-native:ReactAndroid:testDebugUnitTest

All 765 ReactAndroid unit tests pass, including TextLayoutManagerAbsoluteLayoutWithFractionalPixelTest from #56651.

🤖 Generated with Claude Code

…dths

createLayout() wrapped text at floor(width) for AT_MOST but at ceil(width)
for EXACTLY. Text whose width falls in (floor(width), width] was measured
with one more line than it was drawn with, which left an empty line
below it.

AT_MOST now wraps at ceil(width), like EXACTLY. calculateWidth() clamps
the reported width to the AT_MOST width, so the size returned to Yoga
still never exceeds the constraint.

Fixes react#59174

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@meta-cla

meta-cla Bot commented Oct 21, 2026

Copy link
Copy Markdown

Hi @hcbylmz!

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!

@hcbylmz

hcbylmz commented Oct 21, 2026

Copy link
Copy Markdown
Contributor Author

Closing: on-device testing showed this change breaks flex-start columns. I'll share the findings in #59174 instead.

@hcbylmz hcbylmz closed this Oct 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Android] Text measured as 2 lines but drawn as 1 (empty line below) when width is a fractional pixel: floor() in AT_MOST vs ceil() in EXACTLY

1 participant