Skip to content

Fix Android adjustsFontSizeToFit shrinking text that fits - #56815

Open
danyalahmed1995 wants to merge 1 commit into
react:mainfrom
danyalahmed1995:fix/issue-56799-android-adjusts-font-size-rounding
Open

danyalahmed1995 wants to merge 1 commit into
react:mainfrom
danyalahmed1995:fix/issue-56799-android-adjusts-font-size-rounding

Conversation

@danyalahmed1995

@danyalahmed1995 danyalahmed1995 commented May 13, 2026 •

Copy link
Copy Markdown

Summary

Fixes an Android Text regression where adjustsFontSizeToFit={true} could shrink text even when the original size already fits.

The Android font-size fitting path now performs an initial layout check before entering the binary-search resize loop. If the original layout already satisfies the same max-lines, height, and single-character-width constraints used by the fitting logic, it returns without mutating TextPaint or ReactAbsoluteSizeSpan values.

Fixes #56799.

Test Plan

  • Added a focused Robolectric test covering the no-resize case for adjustsFontSizeToFit.
  • Verified that ReactAbsoluteSizeSpan(22) and TextPaint.textSize = 22f remain unchanged when the text has enough space and no max-line pressure.
  • Ran the focused Android text layout test successfully.

Changelog:

[Android] [Fixed] - Prevent adjustsFontSizeToFit from shrinking text when the original Android Text layout already fits.

@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 May 13, 2026
@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 May 13, 2026
@danyalahmed1995
danyalahmed1995 force-pushed the fix/issue-56799-android-adjusts-font-size-rounding branch from 87cc0bd to c5db280 Compare May 14, 2026 18:30
@danyalahmed1995
danyalahmed1995 force-pushed the fix/issue-56799-android-adjusts-font-size-rounding branch from c5db280 to 9c161e0 Compare May 14, 2026 18:53
@meta-codesync

meta-codesync Bot commented May 18, 2026

Copy link
Copy Markdown

@CalixTang has imported this pull request. If you are a Meta employee, you can view this in D105609185.

@danyalahmed1995

Copy link
Copy Markdown
Author

The public check output only says the internal Meta linter/build tests failed and links to Phabricator, so I can’t see the actual failure details from GitHub.

Locally/publicly the PR diff is now clean and focused. If the internal failure is caused by this change, could someone share the relevant error snippet? I’m happy to adjust the PR once I can see what is failing.

image

@dfnerio

dfnerio commented Nov 1, 2026

Copy link
Copy Markdown

Any updates here?

@Elehiggle

Copy link
Copy Markdown

46 px still draws at 40 px on 0.89.0-rc.0. This PR needs a rebase: its createLayout call and its test call lack arguments main now requires (checked at 25d63d4).

Setup: template app on 0.88.1 and 0.89.0-rc.0, release build, API 36 arm64 emulator, 420 dpi, font scale 1, 2208×1840. Each row draws Hxg 00:00 three ways in cells wide enough for it, so nothing should shrink. Sizes are in px, read by matching drawn widths against the plain column. Of the 51 sizes from 20 to 70, these change:

Version Props Sizes that shrink
0.88.1, 0.89.0-rc.0 adjustsFontSizeToFit 30→29, 31→29, 33→30, 34→31, 37→32, 46→40, 53→52, 54→50, 55→52, 61→57, 64→62, 65→62, 67→63
0.89.0-rc.0 adjustsFontSizeToFit, minimumFontScale={0.8} 30→28, 53→52, 54→52, 55→52, 56→53, 61→58, 64→62, 65→62

The list depends on density; only 420 dpi was measured.

The cause is the toInt() in the resize loop. In 32-bit floats 46f * (28f / 46f) is 27.999998, which truncates to 27. Later steps scale from that smaller size, so 46 ends at 40. A replay of the loop's arithmetic matches all 51 sizes in each table row.

From the diff, this PR's early return should skip the loop for these rows. There was no patched build here; the reporter of #56799 wrote: "I tested the provided patch and it fixes this bug." For text that has to shrink, roundToInt() kept the searched size in the same replay (20–120 px, densities 2, 2.625 and 3, a simulated fit limit, one font size). That is arithmetic only.

Mixed sizes that have to shrink still drift, because each step rescales sizes that are already rounded. In the replay at 420 dpi, with roundToInt(), 45 and 17 px limited to 41 end at 41 and 18, where the ratio gives 15.5 (toInt() gives 12). Scaling from the original span sizes would avoid that.

#59792 and #59800 (open) change the width and height limits, including exceedsHeight, which this PR moves, but not the toInt() lines in the loop. The replay matches with every fit check passing, so they should not change these rows.

On main, text that already fits is not shrunk from #58492 uses 40 px, which survives the truncation. With the same 0.5 scale the replay gives 30→28 and 60→57, so testing several sizes would catch this.

Repro (needs a screen about 816 dp wide)
npx @react-native-community/cli@latest init FitRepro --version 0.89.0-rc.0 --skip-install --skip-git-init --pm npm
cd FitRepro && npm install
# replace App.tsx with the file below
cd android && ./gradlew :app:assembleRelease -PreactNativeArchitectures=arm64-v8a
adb install app/build/outputs/apk/release/app-release.apk
import React from 'react';
import {PixelRatio, Text, View} from 'react-native';

const SAMPLE = 'Hxg 00:00';
const density = PixelRatio.get();
// RN applies ceil(fontSize * density), so this lands on the wanted pixel size.
const fontSizeFor = (px: number) => (px - 0.5) / density;
const range = (a: number, b: number) => Array.from({length: b - a + 1}, (_, i) => a + i);
const COLUMNS = [range(20, 52), range(53, 70)];
const cell = {width: 130};

function Row({px}: {px: number}) {
  const style = {fontSize: fontSizeFor(px), color: 'black'};
  return (
    <View style={{flexDirection: 'row'}}>
      <View style={cell}><Text style={style} numberOfLines={1}>{SAMPLE}</Text></View>
      <View style={cell}><Text style={style} numberOfLines={1} adjustsFontSizeToFit>{SAMPLE}</Text></View>
      <View style={cell}><Text style={style} numberOfLines={1} adjustsFontSizeToFit minimumFontScale={0.8}>{SAMPLE}</Text></View>
    </View>
  );
}

export default function App() {
  return (
    <View style={{flex: 1, backgroundColor: 'white', flexDirection: 'row', paddingTop: 40, paddingLeft: 16}}>
      {COLUMNS.map(sizes => (
        <View key={sizes[0]} style={{width: 400}}>
          {sizes.map(px => <Row key={px} px={px} />)}
        </View>
      ))}
    </View>
  );
}

Claude Code ran these checks and drafted this comment.

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] Using adjustsFontSizeToFit=true sometimes causes font size to be too small.

3 participants