fix: Text cut off issues when adjusting text size and font weight in system settings (#39581)

Summary:
Fix Text cut off issues when adjusting text size and font weight in system settings.

This pr fixed the problem that can be reproduced with the [snack](https://snack.expo.dev/fl5DSrLBJ) on Xiaomi devices with MIUI13 and MIUI14. The problem is shown as the image below: the number "999" is cut off and only "99" is rendered.
<img src="https://github.com/facebook/react-native/assets/23273745/64269ced-4060-4ab5-8233-8199e4f2acbd" width="20%" height="20%" alt="text-cutoff-when-scaling-miui14" />

The problem is produced with setting font scaling in system settings like the image below shows.

<img src="https://github.com/facebook/react-native/assets/23273745/c31d1bf2-d038-4536-b1a4-509050f2aa7c" width="20%" height="20%" alt="settings" />

This text cut off case can be avoided by setting [`allowFontScaling`](https://reactnative.cn/docs/text#allowfontscaling) to false. But this pr can make it no matter what value `allowFontScaling` is set.

The root cause of this case, according to MIUI developers, is that Misans typeface, as a variable font which will adjust the weight of different font axes with different font size, gets different widths when Text is measured and drawn for a bug in the framework of MIUI rom. They will fix this bug in next version while this pr fixed it in old versions.

## Changelog:

[ANDROID][FIXED]-Fix Text cut off issues when adjusting text size and font weight in system settings.

<!-- Help reviewers and the release process by writing your own changelog entry.

Pick one each for the category and type tags:

[ANDROID|GENERAL|IOS|INTERNAL] [BREAKING|ADDED|CHANGED|DEPRECATED|REMOVED|FIXED|SECURITY] - Message

For more details, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests

Pull Request resolved: https://github.com/facebook/react-native/pull/39581

Test Plan:
The effect after fixed is shown in the image below.

<img src="https://github.com/facebook/react-native/assets/23273745/1f93f47f-5cdf-4ee1-934a-6cb3b04309ea" width="20%" height="20%" alt="fixed" />

Reviewed By: NickGerleman

Differential Revision: D49509633

Pulled By: ryancat

fbshipit-source-id: fd93f14bdbced8026a45dc9e0299465962433de5
This commit is contained in:
lizhiqiang05
2023-09-25 20:07:06 -07:00
committed by Facebook GitHub Bot
parent 6118aff69d
commit babbc3e43c
2 changed files with 49 additions and 34 deletions
@@ -75,6 +75,16 @@ public abstract class ReactTextAnchorViewManager<T extends View, C extends React
view.setAdjustFontSizeToFit(adjustsFontSizeToFit);
}
@ReactProp(name = ViewProps.ALLOW_FONT_SCALING, defaultBoolean = true)
public void setAllowFontScaling(ReactTextView view, boolean allowFontScaling) {
view.setAllowFontScaling(allowFontScaling);
}
@ReactProp(name = ViewProps.MAX_FONT_SIZE_MULTIPLIER, defaultFloat = Float.NaN)
public void setMaxFontSizeMultiplier(ReactTextView view, float maxFontSizeMultiplier) {
view.setMaxFontSizeMultiplier(maxFontSizeMultiplier);
}
@ReactProp(name = ViewProps.FONT_SIZE)
public void setFontSize(ReactTextView view, float fontSize) {
view.setFontSize(fontSize);
@@ -58,8 +58,6 @@ public class ReactTextView extends AppCompatTextView implements ReactCompoundVie
private int mNumberOfLines;
private TextUtils.TruncateAt mEllipsizeLocation;
private boolean mAdjustsFontSizeToFit;
private float mFontSize = Float.NaN;
private float mLetterSpacing = Float.NaN;
private int mLinkifyMaskType;
private boolean mNotifyOnInlineViewLayout;
private boolean mTextIsSelectable;
@@ -67,6 +65,12 @@ public class ReactTextView extends AppCompatTextView implements ReactCompoundVie
private ReactViewBackgroundManager mReactBackgroundManager;
private Spannable mSpanned;
/**
* Used to collect some text size affecting attributes to fix some text cut-off issues when users
* adjust text size and font weight to the max value in system font settings.
*/
private TextAttributes mTextAttributes;
public ReactTextView(Context context) {
super(context);
@@ -98,6 +102,7 @@ public class ReactTextView extends AppCompatTextView implements ReactCompoundVie
mEllipsizeLocation = TextUtils.TruncateAt.END;
mSpanned = null;
mTextAttributes = new TextAttributes();
}
/* package */ void recycleView() {
@@ -587,29 +592,6 @@ public class ReactTextView extends AppCompatTextView implements ReactCompoundVie
mAdjustsFontSizeToFit = adjustsFontSizeToFit;
}
public void setFontSize(float fontSize) {
mFontSize =
mAdjustsFontSizeToFit
? (float) Math.ceil(PixelUtil.toPixelFromSP(fontSize))
: (float) Math.ceil(PixelUtil.toPixelFromDIP(fontSize));
applyTextAttributes();
}
public void setLetterSpacing(float letterSpacing) {
if (Float.isNaN(letterSpacing)) {
return;
}
float letterSpacingPixels = PixelUtil.toPixelFromDIP(letterSpacing);
// `letterSpacingPixels` and `getEffectiveFontSize` are both in pixels,
// yielding an accurate em value.
mLetterSpacing = letterSpacingPixels / mFontSize;
applyTextAttributes();
}
public void setEllipsizeLocation(TextUtils.TruncateAt ellipsizeLocation) {
mEllipsizeLocation = ellipsizeLocation;
}
@@ -625,6 +607,8 @@ public class ReactTextView extends AppCompatTextView implements ReactCompoundVie
? null
: mEllipsizeLocation;
setEllipsize(ellipsizeLocation);
applyTextAttributes();
}
@Override
@@ -680,16 +664,37 @@ public class ReactTextView extends AppCompatTextView implements ReactCompoundVie
return super.dispatchHoverEvent(event);
}
private void applyTextAttributes() {
// Workaround for an issue where text can be cut off with an ellipsis when
// using certain font sizes and padding. Sets the provided text size and
// letter spacing to ensure consistent rendering and prevent cut-off.
if (!Float.isNaN(mFontSize)) {
setTextSize(TypedValue.COMPLEX_UNIT_PX, mFontSize);
}
public void setLetterSpacing(float letterSpacing) {
mTextAttributes.setLetterSpacing(letterSpacing);
}
if (!Float.isNaN(mLetterSpacing)) {
super.setLetterSpacing(mLetterSpacing);
public void setAllowFontScaling(boolean allowFontScaling) {
if (mTextAttributes.getAllowFontScaling() != allowFontScaling) {
mTextAttributes.setAllowFontScaling(allowFontScaling);
}
}
public void setFontSize(float fontSize) {
mTextAttributes.setFontSize(fontSize);
}
public void setMaxFontSizeMultiplier(float maxFontSizeMultiplier) {
if (maxFontSizeMultiplier != mTextAttributes.getMaxFontSizeMultiplier()) {
mTextAttributes.setMaxFontSizeMultiplier(maxFontSizeMultiplier);
}
}
private void applyTextAttributes() {
// In general, the `getEffective*` functions return `Float.NaN` if the
// property hasn't been set.
// `getEffectiveFontSize` always returns a value so don't need to check for anything like
// `Float.NaN`.
setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextAttributes.getEffectiveFontSize());
float effectiveLetterSpacing = mTextAttributes.getEffectiveLetterSpacing();
if (!Float.isNaN(effectiveLetterSpacing)) {
super.setLetterSpacing(effectiveLetterSpacing);
}
}
}