Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/react-native/ReactAndroid/api/ReactAndroid.api
Original file line number Diff line number Diff line change
Expand Up @@ -7390,7 +7390,7 @@ public class com/facebook/react/views/text/ReactTextView : androidx/appcompat/wi
public fun setIncludeFontPadding (Z)V
public fun setLetterSpacing (F)V
public fun setLinkifyMask (I)V
public fun setMinimumFontSize (F)V
public fun setMinimumFontScale (F)V
public fun setNotifyOnInlineViewLayout (Z)V
public fun setNumberOfLines (I)V
public fun setOverflow (Ljava/lang/String;)V
Expand Down Expand Up @@ -7606,7 +7606,7 @@ public class com/facebook/react/views/text/TextLayoutManager {
public static final field PA_KEY_INCLUDE_FONT_PADDING S
public static final field PA_KEY_MAXIMUM_FONT_SIZE S
public static final field PA_KEY_MAX_NUMBER_OF_LINES S
public static final field PA_KEY_MINIMUM_FONT_SIZE S
public static final field PA_KEY_MINIMUM_FONT_SCALE S
public static final field PA_KEY_TEXT_BREAK_STRATEGY S
public fun <init> ()V
public static fun adjustSpannableFontToFit (Landroid/text/Spannable;FLcom/facebook/yoga/YogaMeasureMode;FLcom/facebook/yoga/YogaMeasureMode;DIZIILandroid/text/Layout$Alignment;)V
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public class ReactTextView extends AppCompatTextView implements ReactCompoundVie
private TextUtils.TruncateAt mEllipsizeLocation;
private boolean mAdjustsFontSizeToFit;
private float mFontSize;
private float mMinimumFontSize;
private float mMinimumFontScale;
private float mLetterSpacing;
private int mLinkifyMaskType;
private boolean mNotifyOnInlineViewLayout;
Expand Down Expand Up @@ -99,7 +99,7 @@ private void initView() {
mShouldAdjustSpannableFontSize = false;
mEllipsizeLocation = TextUtils.TruncateAt.END;
mFontSize = Float.NaN;
mMinimumFontSize = Float.NaN;
mMinimumFontScale = 0.f;
mLetterSpacing = 0.f;

mSpanned = null;
Expand Down Expand Up @@ -371,7 +371,7 @@ protected void onDraw(Canvas canvas) {
YogaMeasureMode.EXACTLY,
getHeight(),
YogaMeasureMode.EXACTLY,
mMinimumFontSize,
mMinimumFontScale,
mNumberOfLines,
getIncludeFontPadding(),
getBreakStrategy(),
Expand Down Expand Up @@ -623,8 +623,8 @@ public void setFontSize(float fontSize) {
applyTextAttributes();
}

public void setMinimumFontSize(float minimumFontSize) {
mMinimumFontSize = minimumFontSize;
public void setMinimumFontScale(float minimumFontScale) {
mMinimumFontScale = minimumFontScale;
mShouldAdjustSpannableFontSize = true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,11 +154,11 @@ private Object getReactTextUpdate(ReactTextView view, ReactStylesDiffMap props,
view.setSpanned(spanned);

try {
float minimumFontSize =
(float) paragraphAttributes.getDouble(TextLayoutManager.PA_KEY_MINIMUM_FONT_SIZE);
view.setMinimumFontSize(minimumFontSize);
float minimumFontScale =
(float) paragraphAttributes.getDouble(TextLayoutManager.PA_KEY_MINIMUM_FONT_SCALE);
view.setMinimumFontScale(minimumFontScale);
} catch (IllegalArgumentException e) {
// T190482857: We see rare crash with MapBuffer without PA_KEY_MINIMUM_FONT_SIZE entry
// T190482857: We see rare crash with MapBuffer without PA_KEY_MINIMUM_FONT_SCALE entry
FLog.e(
TAG,
"Paragraph Attributes: %s",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,7 @@ public class TextLayoutManager {
public static final short PA_KEY_ADJUST_FONT_SIZE_TO_FIT = 3;
public static final short PA_KEY_INCLUDE_FONT_PADDING = 4;
public static final short PA_KEY_HYPHENATION_FREQUENCY = 5;
public static final short PA_KEY_MINIMUM_FONT_SIZE = 6;
public static final short PA_KEY_MAXIMUM_FONT_SIZE = 7;
public static final short PA_KEY_MINIMUM_FONT_SCALE = 6;

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

Expand Down Expand Up @@ -428,7 +427,7 @@ public static void adjustSpannableFontToFit(
YogaMeasureMode widthYogaMeasureMode,
float height,
YogaMeasureMode heightYogaMeasureMode,
double minimumFontSizeAttr,
double minimumFontScaleAttr,
int maximumNumberOfLines,
boolean includeFontPadding,
int textBreakStrategy,
Expand All @@ -446,18 +445,15 @@ public static void adjustSpannableFontToFit(
hyphenationFrequency,
alignment);

// Minimum font size is 4pts to match the iOS implementation.
int minimumFontSize =
(int)
(Double.isNaN(minimumFontSizeAttr) ? PixelUtil.toPixelFromDIP(4) : minimumFontSizeAttr);

// Find the largest font size used in the spannable to use as a starting point.
int currentFontSize = minimumFontSize;
// Find the largest font size used in the spanable to use as a starting point.
int currentFontSize = 0;
ReactAbsoluteSizeSpan[] spans = text.getSpans(0, text.length(), ReactAbsoluteSizeSpan.class);
for (ReactAbsoluteSizeSpan span : spans) {
currentFontSize = Math.max(currentFontSize, span.getSize());
}

// Minimum font size is 4pts to match the iOS implementation.
int minimumFontSize = (int) Math.max(minimumFontScaleAttr * currentFontSize, PixelUtil.toPixelFromDIP(4));
int initialFontSize = currentFontSize;
while (currentFontSize > minimumFontSize
&& ((maximumNumberOfLines != ReactConstants.UNSET
Expand Down Expand Up @@ -534,18 +530,18 @@ public static long measureText(
Layout.Alignment alignment = getTextAlignment(attributedString, text);

if (adjustFontSizeToFit) {
double minimumFontSize =
paragraphAttributes.contains(PA_KEY_MINIMUM_FONT_SIZE)
? paragraphAttributes.getDouble(PA_KEY_MINIMUM_FONT_SIZE)
: Double.NaN;
double minimumFontScale =
paragraphAttributes.contains(PA_KEY_MINIMUM_FONT_SCALE)
? paragraphAttributes.getDouble(PA_KEY_MINIMUM_FONT_SCALE)
: 0.0;

adjustSpannableFontToFit(
text,
width,
widthYogaMeasureMode,
height,
heightYogaMeasureMode,
minimumFontSize,
minimumFontScale,
maximumNumberOfLines,
includeFontPadding,
textBreakStrategy,
Expand Down Expand Up @@ -743,18 +739,18 @@ public static WritableArray measureLines(
Layout.Alignment alignment = getTextAlignment(attributedString, text);

if (adjustFontSizeToFit) {
double minimumFontSize =
paragraphAttributes.contains(PA_KEY_MINIMUM_FONT_SIZE)
? paragraphAttributes.getDouble(PA_KEY_MINIMUM_FONT_SIZE)
: Double.NaN;
double minimumFontScale =
paragraphAttributes.contains(PA_KEY_MINIMUM_FONT_SCALE)
? paragraphAttributes.getDouble(PA_KEY_MINIMUM_FONT_SCALE)
: 0.0;

adjustSpannableFontToFit(
text,
width,
YogaMeasureMode.EXACTLY,
height,
YogaMeasureMode.UNDEFINED,
minimumFontSize,
minimumFontScale,
maximumNumberOfLines,
includeFontPadding,
textBreakStrategy,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@ bool ParagraphAttributes::operator==(const ParagraphAttributes& rhs) const {
rhs.adjustsFontSizeToFit,
rhs.includeFontPadding,
rhs.android_hyphenationFrequency) &&
floatEquality(minimumFontSize, rhs.minimumFontSize) &&
floatEquality(maximumFontSize, rhs.maximumFontSize);
floatEquality(minimumFontScale, rhs.minimumFontScale);
}

bool ParagraphAttributes::operator!=(const ParagraphAttributes& rhs) const {
Expand All @@ -46,8 +45,7 @@ SharedDebugStringConvertibleList ParagraphAttributes::getDebugProps() const {
debugStringConvertibleItem("ellipsizeMode", ellipsizeMode),
debugStringConvertibleItem("textBreakStrategy", textBreakStrategy),
debugStringConvertibleItem("adjustsFontSizeToFit", adjustsFontSizeToFit),
debugStringConvertibleItem("minimumFontSize", minimumFontSize),
debugStringConvertibleItem("maximumFontSize", maximumFontSize),
debugStringConvertibleItem("minimumFontScale", minimumFontScale),
debugStringConvertibleItem("includeFontPadding", includeFontPadding),
debugStringConvertibleItem(
"android_hyphenationFrequency", android_hyphenationFrequency)};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,10 @@ class ParagraphAttributes : public DebugStringConvertible {
HyphenationFrequency android_hyphenationFrequency{};

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

bool operator==(const ParagraphAttributes&) const;
bool operator!=(const ParagraphAttributes&) const;
Expand All @@ -93,8 +92,7 @@ struct hash<facebook::react::ParagraphAttributes> {
attributes.ellipsizeMode,
attributes.textBreakStrategy,
attributes.adjustsFontSizeToFit,
attributes.minimumFontSize,
attributes.maximumFontSize,
attributes.minimumFontScale,
attributes.includeFontPadding,
attributes.android_hyphenationFrequency);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -762,18 +762,12 @@ inline ParagraphAttributes convertRawProp(
"adjustsFontSizeToFit",
sourceParagraphAttributes.adjustsFontSizeToFit,
defaultParagraphAttributes.adjustsFontSizeToFit);
paragraphAttributes.minimumFontSize = convertRawProp(
paragraphAttributes.minimumFontScale = convertRawProp(
context,
rawProps,
"minimumFontSize",
sourceParagraphAttributes.minimumFontSize,
defaultParagraphAttributes.minimumFontSize);
paragraphAttributes.maximumFontSize = convertRawProp(
context,
rawProps,
"maximumFontSize",
sourceParagraphAttributes.maximumFontSize,
defaultParagraphAttributes.maximumFontSize);
"minimumFontScale",
sourceParagraphAttributes.minimumFontScale,
defaultParagraphAttributes.minimumFontScale);
paragraphAttributes.includeFontPadding = convertRawProp(
context,
rawProps,
Expand Down Expand Up @@ -864,8 +858,7 @@ constexpr static MapBuffer::Key PA_KEY_TEXT_BREAK_STRATEGY = 2;
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_MINIMUM_FONT_SCALE = 6;

inline MapBuffer toMapBuffer(const ParagraphAttributes& paragraphAttributes) {
auto builder = MapBufferBuilder();
Expand All @@ -884,9 +877,7 @@ inline MapBuffer toMapBuffer(const ParagraphAttributes& paragraphAttributes) {
PA_KEY_HYPHENATION_FREQUENCY,
toString(paragraphAttributes.android_hyphenationFrequency));
builder.putDouble(
PA_KEY_MINIMUM_FONT_SIZE, paragraphAttributes.minimumFontSize);
builder.putDouble(
PA_KEY_MAXIMUM_FONT_SIZE, paragraphAttributes.maximumFontSize);
PA_KEY_MINIMUM_FONT_SCALE, paragraphAttributes.minimumFontScale);

return builder.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,14 +98,8 @@ void ParagraphProps::setProp(
paDefaults,
value,
paragraphAttributes,
minimumFontSize,
"minimumFontSize");
REBUILD_FIELD_SWITCH_CASE(
paDefaults,
value,
paragraphAttributes,
maximumFontSize,
"maximumFontSize");
minimumFontScale,
"minimumFontScale");
REBUILD_FIELD_SWITCH_CASE(
paDefaults,
value,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,14 +145,8 @@ void BaseTextInputProps::setProp(
paDefaults,
value,
paragraphAttributes,
minimumFontSize,
"minimumFontSize");
REBUILD_FIELD_SWITCH_CASE(
paDefaults,
value,
paragraphAttributes,
maximumFontSize,
"maximumFontSize");
minimumFontScale,
"minimumFontScale");
REBUILD_FIELD_SWITCH_CASE(
paDefaults,
value,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,22 @@ - (LinesMeasurements)getLinesForAttributedString:(facebook::react::AttributedStr
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
Expand All @@ -188,8 +204,8 @@ - (NSTextStorage *)_textStorageAndLayoutManagerWithAttributesString:(NSAttribute
[textStorage addLayoutManager:layoutManager];

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];
CGFloat minimumFontSize = MAX(paragraphAttributes.minimumFontScale * maximumFontSize, 4.0);
[textStorage scaleFontSizeToFitSize:size minimumFontSize:minimumFontSize maximumFontSize:maximumFontSize];
}

Expand Down
16 changes: 16 additions & 0 deletions packages/rn-tester/js/examples/Text/TextExample.android.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,22 @@ class AdjustingFontSize extends React.Component<
style={{fontSize: 36, marginVertical: 6}}>
Truncated text is baaaaad.
</Text>

<Text
numberOfLines={1}
adjustsFontSizeToFit={true}
style={{fontSize: 16, marginVertical: 6}}>
It doesn't grow
</Text>

<Text
numberOfLines={1}
adjustsFontSizeToFit={true}
minimumFontScale={0.5}
style={{fontSize: 40, marginVertical: 6}}>
Can limit how small the text becomes with minimumFontScale
</Text>

<Text
numberOfLines={1}
adjustsFontSizeToFit={true}
Expand Down
16 changes: 16 additions & 0 deletions packages/rn-tester/js/examples/Text/TextExample.ios.js
Original file line number Diff line number Diff line change
Expand Up @@ -211,13 +211,29 @@ class AdjustingFontSize extends React.Component<
style={{fontSize: 36, marginVertical: 6}}>
Truncated text is baaaaad.
</Text>

<Text
numberOfLines={1}
adjustsFontSizeToFit={true}
style={{fontSize: 40, marginVertical: 6}}>
Shrinking to fit available space is much better!
</Text>

<Text
numberOfLines={1}
adjustsFontSizeToFit={true}
style={{fontSize: 16, marginVertical: 6}}>
It doesn't grow
</Text>

<Text
numberOfLines={1}
adjustsFontSizeToFit={true}
minimumFontScale={0.5}
style={{fontSize: 40, marginVertical: 6}}>
Can limit how small the text becomes with minimumFontScale
</Text>

<Text
adjustsFontSizeToFit={true}
numberOfLines={1}
Expand Down