Remove the unused maximumFontSize paragraph attribute (#58529)

Summary:
Fabric's `ParagraphAttributes` carries a `maximumFontSize` field, but it is not exposed by the `<Text>` or `<TextInput>` APIs, so normal JS usage leaves it as `NaN`. On iOS, `RCTTextLayoutManager` falls back to a 96pt maximum when it is `NaN`. On Android, the C++ side serializes it into MapBuffer key 7, but `TextLayoutManager` never reads that key.

This PR removes the attribute: the `ParagraphAttributes` field, its raw-prop parsing in `conversions.h`, the paragraph and text input prop entries, and MapBuffer key 7 on both the C++ and Kotlin sides.

- **iOS**: `RCTTextLayoutManager` now passes the largest font size in the attributed string to `scaleFontSizeToFitSize:` as the maximum instead of 96pt. `scaleFontSizeToFitSize:` returns early when the text already fits; otherwise `scaleFontSizeWithRatio:` applies `MIN(pointSize * ratio, maximumFontSize)` on each pass of a bisection over the ratio. With 96pt, fonts above 96pt were clamped to 96pt on every pass, and when that clamp alone made the text fit, the search went on to ratios above 1.0 and grew the remaining fonts up to 96pt. With the largest font size as the maximum the clamp is a no-op, the ratio-1.0 pass reproduces the original text, which is known not to fit, and every later pass shrinks. Fonts above 96pt are now scaled proportionally rather than capped, and text is never grown. Text at 96pt or smaller is unaffected.
- **Android**: `PA_KEY_MAXIMUM_FONT_SIZE` is removed. Nothing reads it, so there is no behavior change.

Split out of https://github.com/react/react-native/issues/58492. https://github.com/react/react-native/issues/58492 builds on this and reuses the largest-font-size helper for `minimumFontScale`.

## Changelog:

[GENERAL] [REMOVED] - Remove the unused `maximumFontSize` paragraph attribute

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

Test Plan:
**Unit tests**

- Updated `ParagraphAttributesTest.cpp` for the removed field.
- Regenerated the C++ API snapshots (`yarn cxx-api-build`).

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

All 38 `com.facebook.react.views.text` tests pass.

Reviewed By: cipolleschi

Differential Revision: D120127100

Pulled By: javache

fbshipit-source-id: 44e7de2c21c95500e721d5c6f4b4e207da369713
This commit is contained in:
Conner Reimers
2026-09-16 05:18:05 -07:00
committed by meta-codesync[bot]
parent b9adf95f39
commit 7e02fc7e68
19 changed files with 21 additions and 60 deletions
@@ -94,7 +94,6 @@ internal object TextLayoutManager {
const val PA_KEY_INCLUDE_FONT_PADDING: Int = 4
const val PA_KEY_HYPHENATION_FREQUENCY: Int = 5
const val PA_KEY_MINIMUM_FONT_SIZE: Int = 6
const val PA_KEY_MAXIMUM_FONT_SIZE: Int = 7
const val PA_KEY_TEXT_ALIGN_VERTICAL: Int = 8
const val PA_KEY_TEXT_WIDTH_MODE: Int = 9
@@ -34,7 +34,6 @@ bool ParagraphAttributes::operator==(const ParagraphAttributes& rhs) const {
rhs.android_hyphenationFrequency,
rhs.textAlignVertical) &&
floatEquality(minimumFontSize, rhs.minimumFontSize) &&
floatEquality(maximumFontSize, rhs.maximumFontSize) &&
floatEquality(minimumFontScale, rhs.minimumFontScale);
}
@@ -64,10 +63,6 @@ SharedDebugStringConvertibleList ParagraphAttributes::getDebugProps() const {
"minimumFontSize",
minimumFontSize,
paragraphAttributes.minimumFontSize),
debugStringConvertibleItem(
"maximumFontSize",
maximumFontSize,
paragraphAttributes.maximumFontSize),
debugStringConvertibleItem(
"includeFontPadding",
includeFontPadding,
@@ -68,11 +68,9 @@ class ParagraphAttributes : public DebugStringConvertible {
HyphenationFrequency android_hyphenationFrequency{};
/*
* In case of font size adjustment enabled, defines minimum and maximum
* font sizes.
* In case of font size adjustment enabled, defines the minimum font size.
*/
Float minimumFontSize{std::numeric_limits<Float>::quiet_NaN()};
Float maximumFontSize{std::numeric_limits<Float>::quiet_NaN()};
/*
* Specifies the smallest possible scale a font can reach when
@@ -110,7 +108,6 @@ struct hash<facebook::react::ParagraphAttributes> {
attributes.textWidthMode,
attributes.adjustsFontSizeToFit,
attributes.minimumFontSize,
attributes.maximumFontSize,
attributes.includeFontPadding,
attributes.android_hyphenationFrequency,
attributes.minimumFontScale,
@@ -1091,12 +1091,6 @@ inline ParagraphAttributes convertRawProp(
"minimumFontSize",
sourceParagraphAttributes.minimumFontSize,
defaultParagraphAttributes.minimumFontSize);
paragraphAttributes.maximumFontSize = convertRawProp(
context,
rawProps,
"maximumFontSize",
sourceParagraphAttributes.maximumFontSize,
defaultParagraphAttributes.maximumFontSize);
paragraphAttributes.includeFontPadding = convertRawProp(
context,
rawProps,
@@ -1200,7 +1194,6 @@ constexpr static MapBuffer::Key PA_KEY_ADJUST_FONT_SIZE_TO_FIT = 3;
constexpr static MapBuffer::Key PA_KEY_INCLUDE_FONT_PADDING = 4;
constexpr static MapBuffer::Key PA_KEY_HYPHENATION_FREQUENCY = 5;
constexpr static MapBuffer::Key PA_KEY_MINIMUM_FONT_SIZE = 6;
constexpr static MapBuffer::Key PA_KEY_MAXIMUM_FONT_SIZE = 7;
constexpr static MapBuffer::Key PA_KEY_TEXT_ALIGN_VERTICAL = 8;
constexpr static MapBuffer::Key PA_KEY_TEXT_WIDTH_MODE = 9;
@@ -1218,7 +1211,6 @@ inline MapBuffer toMapBuffer(const ParagraphAttributes &paragraphAttributes)
builder.putString(PA_KEY_TEXT_ALIGN_VERTICAL, toString(*paragraphAttributes.textAlignVertical));
}
builder.putDouble(PA_KEY_MINIMUM_FONT_SIZE, paragraphAttributes.minimumFontSize);
builder.putDouble(PA_KEY_MAXIMUM_FONT_SIZE, paragraphAttributes.maximumFontSize);
return builder.build();
}
@@ -11,7 +11,7 @@
namespace facebook::react {
// The three Float fields default to NaN, and NaN != NaN under IEEE-754.
// The two Float fields default to NaN, and NaN != NaN under IEEE-754.
// operator== must special-case NaN via floatEquality so two freshly
// default-constructed ParagraphAttributes compare equal.
TEST(
@@ -31,12 +31,10 @@ TEST(
testOperatorEqualsFloatFieldsUseEpsilonComparison) {
ParagraphAttributes a{};
a.minimumFontSize = 12.0f;
a.maximumFontSize = 48.0f;
a.minimumFontScale = 0.5f;
auto b = a;
b.minimumFontSize = a.minimumFontSize + 0.001f;
b.maximumFontSize = a.maximumFontSize + 0.001f;
b.minimumFontScale = a.minimumFontScale + 0.001f;
EXPECT_TRUE(a == b);
@@ -46,8 +44,8 @@ TEST(
}
// floatEquality returns true only when *both* operands are NaN or when
// *neither* is. A NaN-vs-finite mismatch in any of the three float fields
// must therefore make the instances unequal, even though both operands are
// *neither* is. A NaN-vs-finite mismatch in either float field must
// therefore make the instances unequal, even though both operands are
// "invalid" font sizes.
TEST(
ParagraphAttributesTest,
@@ -104,12 +104,6 @@ void BaseParagraphProps::setProp(
paragraphAttributes,
minimumFontSize,
"minimumFontSize");
REBUILD_FIELD_SWITCH_CASE(
paDefaults,
value,
paragraphAttributes,
maximumFontSize,
"maximumFontSize");
REBUILD_FIELD_SWITCH_CASE(
paDefaults,
value,
@@ -128,12 +128,6 @@ folly::dynamic HostPlatformParagraphProps::getDiffProps(
result["minimumFontSize"] = paragraphAttributes.minimumFontSize;
}
if (!floatEquality(
paragraphAttributes.maximumFontSize,
oldProps->paragraphAttributes.maximumFontSize)) {
result["maximumFontSize"] = paragraphAttributes.maximumFontSize;
}
if (paragraphAttributes.includeFontPadding !=
oldProps->paragraphAttributes.includeFontPadding) {
result["includeFontPadding"] = paragraphAttributes.includeFontPadding;
@@ -179,12 +179,6 @@ void BaseTextInputProps::setProp(
paragraphAttributes,
minimumFontSize,
"minimumFontSize");
REBUILD_FIELD_SWITCH_CASE(
paDefaults,
value,
paragraphAttributes,
maximumFontSize,
"maximumFontSize");
REBUILD_FIELD_SWITCH_CASE(
paDefaults,
value,
@@ -395,12 +395,6 @@ folly::dynamic AndroidTextInputProps::getDiffProps(
result["minimumFontSize"] = paragraphAttributes.minimumFontSize;
}
if (!floatEquality(
paragraphAttributes.maximumFontSize,
oldProps->paragraphAttributes.maximumFontSize)) {
result["maximumFontSize"] = paragraphAttributes.maximumFontSize;
}
if (paragraphAttributes.includeFontPadding !=
oldProps->paragraphAttributes.includeFontPadding) {
result["includeFontPadding"] = paragraphAttributes.includeFontPadding;
@@ -415,6 +415,22 @@ static NSLineBreakMode RCTNSLineBreakModeFromEllipsizeMode(EllipsizeMode ellipsi
return paragraphLines;
}
- (CGFloat)_maximumFontSizeInAttributedString:(NSAttributedString *)attributedString
{
__block CGFloat maximumFontSize = 0.0;
[attributedString enumerateAttribute:NSFontAttributeName
inRange:NSMakeRange(0, attributedString.length)
options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired
usingBlock:^(id _Nullable value, NSRange range, BOOL *_Nonnull stop) {
CGFloat fontSize = ((UIFont *)value).pointSize;
if (fontSize > maximumFontSize) {
maximumFontSize = fontSize;
}
}];
return maximumFontSize;
}
- (NSTextStorage *)_textStorageAndLayoutManagerWithAttributesString:(NSAttributedString *)attributedString
paragraphAttributes:(ParagraphAttributes)paragraphAttributes
size:(CGSize)size
@@ -439,7 +455,7 @@ static NSLineBreakMode RCTNSLineBreakModeFromEllipsizeMode(EllipsizeMode ellipsi
if (paragraphAttributes.adjustsFontSizeToFit) {
CGFloat minimumFontSize = !isnan(paragraphAttributes.minimumFontSize) ? paragraphAttributes.minimumFontSize : 4.0;
CGFloat maximumFontSize = !isnan(paragraphAttributes.maximumFontSize) ? paragraphAttributes.maximumFontSize : 96.0;
CGFloat maximumFontSize = [self _maximumFontSizeInAttributedString:attributedString];
[textStorage scaleFontSizeToFitSize:size minimumFontSize:minimumFontSize maximumFontSize:maximumFontSize];
}
@@ -508,7 +508,6 @@ static constexpr facebook::react::MapBuffer::Key facebook::react::PA_KEY_ADJUST_
static constexpr facebook::react::MapBuffer::Key facebook::react::PA_KEY_ELLIPSIZE_MODE;
static constexpr facebook::react::MapBuffer::Key facebook::react::PA_KEY_HYPHENATION_FREQUENCY;
static constexpr facebook::react::MapBuffer::Key facebook::react::PA_KEY_INCLUDE_FONT_PADDING;
static constexpr facebook::react::MapBuffer::Key facebook::react::PA_KEY_MAXIMUM_FONT_SIZE;
static constexpr facebook::react::MapBuffer::Key facebook::react::PA_KEY_MAX_NUMBER_OF_LINES;
static constexpr facebook::react::MapBuffer::Key facebook::react::PA_KEY_MINIMUM_FONT_SIZE;
static constexpr facebook::react::MapBuffer::Key facebook::react::PA_KEY_TEXT_ALIGN_VERTICAL;
@@ -4017,7 +4016,6 @@ class facebook::react::ParagraphAttributes : public facebook::react::DebugString
public bool includeFontPadding;
public bool operator==(const facebook::react::ParagraphAttributes& rhs) const;
public facebook::react::EllipsizeMode ellipsizeMode;
public facebook::react::Float maximumFontSize;
public facebook::react::Float minimumFontScale;
public facebook::react::Float minimumFontSize;
public facebook::react::HyphenationFrequency android_hyphenationFrequency;
@@ -508,7 +508,6 @@ static constexpr facebook::react::MapBuffer::Key facebook::react::PA_KEY_ADJUST_
static constexpr facebook::react::MapBuffer::Key facebook::react::PA_KEY_ELLIPSIZE_MODE;
static constexpr facebook::react::MapBuffer::Key facebook::react::PA_KEY_HYPHENATION_FREQUENCY;
static constexpr facebook::react::MapBuffer::Key facebook::react::PA_KEY_INCLUDE_FONT_PADDING;
static constexpr facebook::react::MapBuffer::Key facebook::react::PA_KEY_MAXIMUM_FONT_SIZE;
static constexpr facebook::react::MapBuffer::Key facebook::react::PA_KEY_MAX_NUMBER_OF_LINES;
static constexpr facebook::react::MapBuffer::Key facebook::react::PA_KEY_MINIMUM_FONT_SIZE;
static constexpr facebook::react::MapBuffer::Key facebook::react::PA_KEY_TEXT_ALIGN_VERTICAL;
@@ -3871,7 +3870,6 @@ class facebook::react::ParagraphAttributes : public facebook::react::DebugString
public bool includeFontPadding;
public bool operator==(const facebook::react::ParagraphAttributes& rhs) const;
public facebook::react::EllipsizeMode ellipsizeMode;
public facebook::react::Float maximumFontSize;
public facebook::react::Float minimumFontScale;
public facebook::react::Float minimumFontSize;
public facebook::react::HyphenationFrequency android_hyphenationFrequency;
@@ -508,7 +508,6 @@ static constexpr facebook::react::MapBuffer::Key facebook::react::PA_KEY_ADJUST_
static constexpr facebook::react::MapBuffer::Key facebook::react::PA_KEY_ELLIPSIZE_MODE;
static constexpr facebook::react::MapBuffer::Key facebook::react::PA_KEY_HYPHENATION_FREQUENCY;
static constexpr facebook::react::MapBuffer::Key facebook::react::PA_KEY_INCLUDE_FONT_PADDING;
static constexpr facebook::react::MapBuffer::Key facebook::react::PA_KEY_MAXIMUM_FONT_SIZE;
static constexpr facebook::react::MapBuffer::Key facebook::react::PA_KEY_MAX_NUMBER_OF_LINES;
static constexpr facebook::react::MapBuffer::Key facebook::react::PA_KEY_MINIMUM_FONT_SIZE;
static constexpr facebook::react::MapBuffer::Key facebook::react::PA_KEY_TEXT_ALIGN_VERTICAL;
@@ -4014,7 +4013,6 @@ class facebook::react::ParagraphAttributes : public facebook::react::DebugString
public bool includeFontPadding;
public bool operator==(const facebook::react::ParagraphAttributes& rhs) const;
public facebook::react::EllipsizeMode ellipsizeMode;
public facebook::react::Float maximumFontSize;
public facebook::react::Float minimumFontScale;
public facebook::react::Float minimumFontSize;
public facebook::react::HyphenationFrequency android_hyphenationFrequency;
@@ -6202,7 +6202,6 @@ class facebook::react::ParagraphAttributes : public facebook::react::DebugString
public bool includeFontPadding;
public bool operator==(const facebook::react::ParagraphAttributes& rhs) const;
public facebook::react::EllipsizeMode ellipsizeMode;
public facebook::react::Float maximumFontSize;
public facebook::react::Float minimumFontScale;
public facebook::react::Float minimumFontSize;
public facebook::react::HyphenationFrequency android_hyphenationFrequency;
@@ -6088,7 +6088,6 @@ class facebook::react::ParagraphAttributes : public facebook::react::DebugString
public bool includeFontPadding;
public bool operator==(const facebook::react::ParagraphAttributes& rhs) const;
public facebook::react::EllipsizeMode ellipsizeMode;
public facebook::react::Float maximumFontSize;
public facebook::react::Float minimumFontScale;
public facebook::react::Float minimumFontSize;
public facebook::react::HyphenationFrequency android_hyphenationFrequency;
@@ -6199,7 +6199,6 @@ class facebook::react::ParagraphAttributes : public facebook::react::DebugString
public bool includeFontPadding;
public bool operator==(const facebook::react::ParagraphAttributes& rhs) const;
public facebook::react::EllipsizeMode ellipsizeMode;
public facebook::react::Float maximumFontSize;
public facebook::react::Float minimumFontScale;
public facebook::react::Float minimumFontSize;
public facebook::react::HyphenationFrequency android_hyphenationFrequency;
@@ -2626,7 +2626,6 @@ class facebook::react::OperatorAnimatedNode : public facebook::react::ValueAnima
}
class facebook::react::ParagraphAttributes : public facebook::react::DebugStringConvertible {
public Float maximumFontSize;
public Float minimumFontScale;
public Float minimumFontSize;
public bool adjustsFontSizeToFit;
@@ -2520,7 +2520,6 @@ class facebook::react::OperatorAnimatedNode : public facebook::react::ValueAnima
}
class facebook::react::ParagraphAttributes : public facebook::react::DebugStringConvertible {
public Float maximumFontSize;
public Float minimumFontScale;
public Float minimumFontSize;
public bool adjustsFontSizeToFit;
@@ -2623,7 +2623,6 @@ class facebook::react::OperatorAnimatedNode : public facebook::react::ValueAnima
}
class facebook::react::ParagraphAttributes : public facebook::react::DebugStringConvertible {
public Float maximumFontSize;
public Float minimumFontScale;
public Float minimumFontSize;
public bool adjustsFontSizeToFit;