Skip to content

Commit e86f9db

Browse files
Fix adjustsFontSizeToFit on iOS Fabric
1 parent a023cc1 commit e86f9db

10 files changed

Lines changed: 55 additions & 69 deletions

File tree

packages/react-native/ReactAndroid/api/ReactAndroid.api

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7755,7 +7755,7 @@ public class com/facebook/react/views/text/ReactTextView : androidx/appcompat/wi
77557755
public fun setIncludeFontPadding (Z)V
77567756
public fun setLetterSpacing (F)V
77577757
public fun setLinkifyMask (I)V
7758-
public fun setMinimumFontSize (F)V
7758+
public fun setMinimumFontScale (F)V
77597759
public fun setNotifyOnInlineViewLayout (Z)V
77607760
public fun setNumberOfLines (I)V
77617761
public fun setOverflow (Ljava/lang/String;)V
@@ -7971,9 +7971,8 @@ public class com/facebook/react/views/text/TextLayoutManager {
79717971
public static final field PA_KEY_ELLIPSIZE_MODE S
79727972
public static final field PA_KEY_HYPHENATION_FREQUENCY S
79737973
public static final field PA_KEY_INCLUDE_FONT_PADDING S
7974-
public static final field PA_KEY_MAXIMUM_FONT_SIZE S
79757974
public static final field PA_KEY_MAX_NUMBER_OF_LINES S
7976-
public static final field PA_KEY_MINIMUM_FONT_SIZE S
7975+
public static final field PA_KEY_MINIMUM_FONT_SCALE S
79777976
public static final field PA_KEY_TEXT_BREAK_STRATEGY S
79787977
public fun <init> ()V
79797978
public static fun adjustSpannableFontToFit (Landroid/text/Spannable;FLcom/facebook/yoga/YogaMeasureMode;FLcom/facebook/yoga/YogaMeasureMode;DIZIILandroid/text/Layout$Alignment;)V

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextView.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ public class ReactTextView extends AppCompatTextView implements ReactCompoundVie
7272
private TextUtils.TruncateAt mEllipsizeLocation;
7373
private boolean mAdjustsFontSizeToFit;
7474
private float mFontSize;
75-
private float mMinimumFontSize;
75+
private float mMinimumFontScale;
7676
private float mLetterSpacing;
7777
private int mLinkifyMaskType;
7878
private boolean mNotifyOnInlineViewLayout;
@@ -108,7 +108,7 @@ private void initView() {
108108
mShouldAdjustSpannableFontSize = false;
109109
mEllipsizeLocation = TextUtils.TruncateAt.END;
110110
mFontSize = Float.NaN;
111-
mMinimumFontSize = Float.NaN;
111+
mMinimumFontScale = 0.f;
112112
mLetterSpacing = 0.f;
113113
mOverflow = Overflow.VISIBLE;
114114
mSpanned = null;
@@ -385,7 +385,7 @@ protected void onDraw(Canvas canvas) {
385385
YogaMeasureMode.EXACTLY,
386386
getHeight(),
387387
YogaMeasureMode.EXACTLY,
388-
mMinimumFontSize,
388+
mMinimumFontScale,
389389
mNumberOfLines,
390390
getIncludeFontPadding(),
391391
getBreakStrategy(),
@@ -653,8 +653,8 @@ public void setFontSize(float fontSize) {
653653
applyTextAttributes();
654654
}
655655

656-
public void setMinimumFontSize(float minimumFontSize) {
657-
mMinimumFontSize = minimumFontSize;
656+
public void setMinimumFontScale(float minimumFontScale) {
657+
minimumFontScale = minimumFontScale;
658658
mShouldAdjustSpannableFontSize = true;
659659
}
660660

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewManager.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -158,11 +158,11 @@ private Object getReactTextUpdate(ReactTextView view, ReactStylesDiffMap props,
158158
view.setSpanned(spanned);
159159

160160
try {
161-
float minimumFontSize =
162-
(float) paragraphAttributes.getDouble(TextLayoutManager.PA_KEY_MINIMUM_FONT_SIZE);
163-
view.setMinimumFontSize(minimumFontSize);
161+
float minimumFontScale =
162+
(float) paragraphAttributes.getDouble(TextLayoutManager.PA_KEY_MINIMUM_FONT_SCALE);
163+
view.setMinimumFontScale(minimumFontScale);
164164
} catch (IllegalArgumentException e) {
165-
// T190482857: We see rare crash with MapBuffer without PA_KEY_MINIMUM_FONT_SIZE entry
165+
// T190482857: We see rare crash with MapBuffer without PA_KEY_MINIMUM_FONT_SCALE entry
166166
FLog.e(
167167
TAG,
168168
"Paragraph Attributes: %s",

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManager.java

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,7 @@ public class TextLayoutManager {
7878
public static final short PA_KEY_ADJUST_FONT_SIZE_TO_FIT = 3;
7979
public static final short PA_KEY_INCLUDE_FONT_PADDING = 4;
8080
public static final short PA_KEY_HYPHENATION_FREQUENCY = 5;
81-
public static final short PA_KEY_MINIMUM_FONT_SIZE = 6;
82-
public static final short PA_KEY_MAXIMUM_FONT_SIZE = 7;
81+
public static final short PA_KEY_MINIMUM_FONT_SCALE = 6;
8382

8483
private static final boolean ENABLE_MEASURE_LOGGING = ReactBuildConfig.DEBUG && false;
8584

@@ -460,18 +459,18 @@ public static Layout createLayout(
460459
Layout.Alignment alignment = getTextAlignment(attributedString, text);
461460

462461
if (adjustFontSizeToFit) {
463-
double minimumFontSize =
464-
paragraphAttributes.contains(PA_KEY_MINIMUM_FONT_SIZE)
465-
? paragraphAttributes.getDouble(PA_KEY_MINIMUM_FONT_SIZE)
466-
: Double.NaN;
462+
double minimumFontScale =
463+
paragraphAttributes.contains(PA_KEY_MINIMUM_FONT_SCALE)
464+
? paragraphAttributes.getDouble(PA_KEY_MINIMUM_FONT_SCALE)
465+
: 0.0;
467466

468467
adjustSpannableFontToFit(
469468
text,
470469
width,
471470
YogaMeasureMode.EXACTLY,
472471
height,
473472
YogaMeasureMode.UNDEFINED,
474-
minimumFontSize,
473+
minimumFontScale,
475474
maximumNumberOfLines,
476475
includeFontPadding,
477476
textBreakStrategy,
@@ -496,7 +495,7 @@ public static void adjustSpannableFontToFit(
496495
YogaMeasureMode widthYogaMeasureMode,
497496
float height,
498497
YogaMeasureMode heightYogaMeasureMode,
499-
double minimumFontSizeAttr,
498+
double minimumFontScaleAttr,
500499
int maximumNumberOfLines,
501500
boolean includeFontPadding,
502501
int textBreakStrategy,
@@ -514,18 +513,15 @@ public static void adjustSpannableFontToFit(
514513
hyphenationFrequency,
515514
alignment);
516515

517-
// Minimum font size is 4pts to match the iOS implementation.
518-
int minimumFontSize =
519-
(int)
520-
(Double.isNaN(minimumFontSizeAttr) ? PixelUtil.toPixelFromDIP(4) : minimumFontSizeAttr);
521-
522516
// Find the largest font size used in the spannable to use as a starting point.
523-
int currentFontSize = minimumFontSize;
517+
int currentFontSize = 0;
524518
ReactAbsoluteSizeSpan[] spans = text.getSpans(0, text.length(), ReactAbsoluteSizeSpan.class);
525519
for (ReactAbsoluteSizeSpan span : spans) {
526520
currentFontSize = Math.max(currentFontSize, span.getSize());
527521
}
528522

523+
// Minimum font size is 4pts to match the iOS implementation.
524+
int minimumFontSize = (int) Math.max(minimumFontScaleAttr * currentFontSize, PixelUtil.toPixelFromDIP(4));
529525
int initialFontSize = currentFontSize;
530526
while (currentFontSize > minimumFontSize
531527
&& ((maximumNumberOfLines != ReactConstants.UNSET

packages/react-native/ReactCommon/react/renderer/attributedstring/ParagraphAttributes.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,7 @@ bool ParagraphAttributes::operator==(const ParagraphAttributes& rhs) const {
2929
rhs.adjustsFontSizeToFit,
3030
rhs.includeFontPadding,
3131
rhs.android_hyphenationFrequency) &&
32-
floatEquality(minimumFontSize, rhs.minimumFontSize) &&
33-
floatEquality(maximumFontSize, rhs.maximumFontSize);
32+
floatEquality(minimumFontScale, rhs.minimumFontScale);
3433
}
3534

3635
bool ParagraphAttributes::operator!=(const ParagraphAttributes& rhs) const {
@@ -46,8 +45,7 @@ SharedDebugStringConvertibleList ParagraphAttributes::getDebugProps() const {
4645
debugStringConvertibleItem("ellipsizeMode", ellipsizeMode),
4746
debugStringConvertibleItem("textBreakStrategy", textBreakStrategy),
4847
debugStringConvertibleItem("adjustsFontSizeToFit", adjustsFontSizeToFit),
49-
debugStringConvertibleItem("minimumFontSize", minimumFontSize),
50-
debugStringConvertibleItem("maximumFontSize", maximumFontSize),
48+
debugStringConvertibleItem("minimumFontScale", minimumFontScale),
5149
debugStringConvertibleItem("includeFontPadding", includeFontPadding),
5250
debugStringConvertibleItem(
5351
"android_hyphenationFrequency", android_hyphenationFrequency)};

packages/react-native/ReactCommon/react/renderer/attributedstring/ParagraphAttributes.h

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,10 @@ class ParagraphAttributes : public DebugStringConvertible {
6464
HyphenationFrequency android_hyphenationFrequency{};
6565

6666
/*
67-
* In case of font size adjustment enabled, defines minimum and maximum
68-
* font sizes.
67+
* Specifies smallest possible scale a font can reach when adjustsFontSizeToFit
68+
* is enabled. (values 0.01-1.0).
6969
*/
70-
Float minimumFontSize{std::numeric_limits<Float>::quiet_NaN()};
71-
Float maximumFontSize{std::numeric_limits<Float>::quiet_NaN()};
70+
Float minimumFontScale{0.0};
7271

7372
bool operator==(const ParagraphAttributes&) const;
7473
bool operator!=(const ParagraphAttributes&) const;
@@ -93,8 +92,7 @@ struct hash<facebook::react::ParagraphAttributes> {
9392
attributes.ellipsizeMode,
9493
attributes.textBreakStrategy,
9594
attributes.adjustsFontSizeToFit,
96-
attributes.minimumFontSize,
97-
attributes.maximumFontSize,
95+
attributes.minimumFontScale,
9896
attributes.includeFontPadding,
9997
attributes.android_hyphenationFrequency);
10098
}

packages/react-native/ReactCommon/react/renderer/attributedstring/conversions.h

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -815,18 +815,12 @@ inline ParagraphAttributes convertRawProp(
815815
"adjustsFontSizeToFit",
816816
sourceParagraphAttributes.adjustsFontSizeToFit,
817817
defaultParagraphAttributes.adjustsFontSizeToFit);
818-
paragraphAttributes.minimumFontSize = convertRawProp(
818+
paragraphAttributes.minimumFontScale = convertRawProp(
819819
context,
820820
rawProps,
821-
"minimumFontSize",
822-
sourceParagraphAttributes.minimumFontSize,
823-
defaultParagraphAttributes.minimumFontSize);
824-
paragraphAttributes.maximumFontSize = convertRawProp(
825-
context,
826-
rawProps,
827-
"maximumFontSize",
828-
sourceParagraphAttributes.maximumFontSize,
829-
defaultParagraphAttributes.maximumFontSize);
821+
"minimumFontScale",
822+
sourceParagraphAttributes.minimumFontScale,
823+
defaultParagraphAttributes.minimumFontScale);
830824
paragraphAttributes.includeFontPadding = convertRawProp(
831825
context,
832826
rawProps,
@@ -917,8 +911,7 @@ constexpr static MapBuffer::Key PA_KEY_TEXT_BREAK_STRATEGY = 2;
917911
constexpr static MapBuffer::Key PA_KEY_ADJUST_FONT_SIZE_TO_FIT = 3;
918912
constexpr static MapBuffer::Key PA_KEY_INCLUDE_FONT_PADDING = 4;
919913
constexpr static MapBuffer::Key PA_KEY_HYPHENATION_FREQUENCY = 5;
920-
constexpr static MapBuffer::Key PA_KEY_MINIMUM_FONT_SIZE = 6;
921-
constexpr static MapBuffer::Key PA_KEY_MAXIMUM_FONT_SIZE = 7;
914+
constexpr static MapBuffer::Key PA_KEY_MINIMUM_FONT_SCALE = 6;
922915

923916
inline MapBuffer toMapBuffer(const ParagraphAttributes& paragraphAttributes) {
924917
auto builder = MapBufferBuilder();
@@ -937,9 +930,7 @@ inline MapBuffer toMapBuffer(const ParagraphAttributes& paragraphAttributes) {
937930
PA_KEY_HYPHENATION_FREQUENCY,
938931
toString(paragraphAttributes.android_hyphenationFrequency));
939932
builder.putDouble(
940-
PA_KEY_MINIMUM_FONT_SIZE, paragraphAttributes.minimumFontSize);
941-
builder.putDouble(
942-
PA_KEY_MAXIMUM_FONT_SIZE, paragraphAttributes.maximumFontSize);
933+
PA_KEY_MINIMUM_FONT_SCALE, paragraphAttributes.minimumFontScale);
943934

944935
return builder.build();
945936
}

packages/react-native/ReactCommon/react/renderer/components/text/ParagraphProps.cpp

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -98,14 +98,8 @@ void ParagraphProps::setProp(
9898
paDefaults,
9999
value,
100100
paragraphAttributes,
101-
minimumFontSize,
102-
"minimumFontSize");
103-
REBUILD_FIELD_SWITCH_CASE(
104-
paDefaults,
105-
value,
106-
paragraphAttributes,
107-
maximumFontSize,
108-
"maximumFontSize");
101+
minimumFontScale,
102+
"minimumFontScale");
109103
REBUILD_FIELD_SWITCH_CASE(
110104
paDefaults,
111105
value,

packages/react-native/ReactCommon/react/renderer/components/textinput/BaseTextInputProps.cpp

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -145,14 +145,8 @@ void BaseTextInputProps::setProp(
145145
paDefaults,
146146
value,
147147
paragraphAttributes,
148-
minimumFontSize,
149-
"minimumFontSize");
150-
REBUILD_FIELD_SWITCH_CASE(
151-
paDefaults,
152-
value,
153-
paragraphAttributes,
154-
maximumFontSize,
155-
"maximumFontSize");
148+
minimumFontScale,
149+
"minimumFontScale");
156150
REBUILD_FIELD_SWITCH_CASE(
157151
paDefaults,
158152
value,

packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,22 @@ - (LinesMeasurements)getLinesForAttributedString:(facebook::react::AttributedStr
228228
return paragraphLines;
229229
}
230230

231+
- (CGFloat)_maximumFontSizeInAttributedString:(NSAttributedString *)attributedString
232+
{
233+
__block CGFloat maximumFontSize = 0.0;
234+
[attributedString enumerateAttribute:NSFontAttributeName
235+
inRange:NSMakeRange(0, attributedString.length)
236+
options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired
237+
usingBlock:^(id _Nullable value, NSRange range, BOOL *_Nonnull stop) {
238+
CGFloat fontSize = ((UIFont *)value).pointSize;
239+
if (fontSize > maximumFontSize) {
240+
maximumFontSize = fontSize;
241+
}
242+
}];
243+
244+
return maximumFontSize;
245+
}
246+
231247
- (NSTextStorage *)_textStorageAndLayoutManagerWithAttributesString:(NSAttributedString *)attributedString
232248
paragraphAttributes:(ParagraphAttributes)paragraphAttributes
233249
size:(CGSize)size
@@ -251,8 +267,8 @@ - (NSTextStorage *)_textStorageAndLayoutManagerWithAttributesString:(NSAttribute
251267
[textStorage addLayoutManager:layoutManager];
252268

253269
if (paragraphAttributes.adjustsFontSizeToFit) {
254-
CGFloat minimumFontSize = !isnan(paragraphAttributes.minimumFontSize) ? paragraphAttributes.minimumFontSize : 4.0;
255-
CGFloat maximumFontSize = !isnan(paragraphAttributes.maximumFontSize) ? paragraphAttributes.maximumFontSize : 96.0;
270+
CGFloat maximumFontSize = [self _maximumFontSizeInAttributedString:attributedString];
271+
CGFloat minimumFontSize = MAX(paragraphAttributes.minimumFontScale * maximumFontSize, 4.0);
256272
[textStorage scaleFontSizeToFitSize:size minimumFontSize:minimumFontSize maximumFontSize:maximumFontSize];
257273
}
258274

0 commit comments

Comments
 (0)