Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix adjustsFontSizeToFit for strings with a single character #47082

Conversation

j-piasecki
Copy link
Contributor

Summary:

Fixes #47045

On Android adjustsFontSizeToFit relies on two metrics:

  • Text with line breaks results in more lines than maximumNumberOfLines
  • The overall height of the text is larger than the available height

None of these two was fulfilled when a single-character string had a higher width than the available one (a single character will not be broken into multiple lines). This PR adds exactly that as a third option to trigger the scaling algorithm - a single-character string that has a higher width than the available one.

On iOS adjustsFontSizeToFit relies on truncatedGlyphRangeInLineFragmentForGlyphAtIndex which seems to be returning NSNotFound when a single-character sting gets truncated. Similarly to Android, this PR adds an additional check to make sure that single-character strings actually fit inside the container.

Changelog:

[GENERAL] [FIXED] - Fixed adjustsFontSizeToFit not working for text with a single character

Test Plan:

Tested on the code from the issue:

Android (old arch) Android (new arch) iOS (old arch) iOS (new arch)
android_old android_new ios_old ios_new

@facebook-github-bot facebook-github-bot added 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. labels Oct 17, 2024
@cortinico
Copy link
Contributor

Let's make sure the CI is green before we import this 👍

@itsramiel
Copy link

Fixes the bug

@facebook-github-bot
Copy link
Contributor

@cortinico has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator.

@@ -600,6 +601,7 @@ private static Layout createLayout(
text.getSpanFlags(span));
text.removeSpan(span);
}
boring = BoringLayout.isBoring(text, paint);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quoting @javache:

This is quite of an expensive call

Also we call this just above at the beginning of the adjustSpannableFontToFit function. Why do you need call this again?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It updates the font metrics after applying the smaller font size. In this exact case, the layout height is larger than the available height and without the update to boring it ends up shrinking the text to the smallest allowed value:

image

The other two numbers end up as a StaticLayout so they aren't affected.

Copy link
Contributor

@NickGerleman NickGerleman Dec 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could probably guard this with a check to only recalculate BoringLayout metics if existing text was boring (adjusting font size won't change boringness, but will change the layout result). But I think needing to recreate it makes sense.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a check in fb3f1f0

@@ -75,6 +75,7 @@ public long measure(
"Spannable element has not been prepared in onBeforeLayout");

Layout layout = measureSpannedText(text, width, widthMode);
TextPaint paint = new TextPaint(sTextPaintInstance);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you amend all the changes to the paper implementation. This is quite intrusive and we'd rather just have the fix for Fabric only.

cc @NickGerleman

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done - ec2182b

@cortinico
Copy link
Contributor

(Apologise for the late review @j-piasecki)

@j-piasecki j-piasecki force-pushed the @jpiasecki/fix-font-size-adjustment-single-char branch from 3234b8e to ec2182b Compare November 7, 2024 11:04
@facebook-github-bot
Copy link
Contributor

@cortinico has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator.

@@ -630,7 +630,8 @@ private static Layout createLayout(
&& maximumNumberOfLines != 0
&& layout.getLineCount() > maximumNumberOfLines)
|| (heightYogaMeasureMode != YogaMeasureMode.UNDEFINED
&& layout.getHeight() > height))) {
&& layout.getHeight() > height)
|| (text.length() == 1 && layout.getPaint().measureText(text.toString()) > width))) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This throws away metric effecting formatting on the text, like the font, whether its italic, how its justified, etc. It also throws out initial font size, which we only set on the paint after this.

If none of the existing line accessors in layout can tell that we've overflowed, and we really needed to do a new layout (we should avoid if possible), we would want to layout the full text, either using existing BoringLayout (simple not wrapping layout) which can give full width, or recreating a new layout without width constraint to get natural width. Though doing another layout here is really not ideal if there is any accessor on the existing layout that will give us this measurement.

Existing layout has fillCharacterBounds() accessor to get glyph rects in the existing layout. At a glance, if none of the line accessors show where the single glyph lies, this would I think let us avoid relayout.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed it to getLineWidth in fb3f1f0. It seems like this value isn't clipped to the width of the layout.

@@ -600,6 +601,7 @@ private static Layout createLayout(
text.getSpanFlags(span));
text.removeSpan(span);
}
boring = BoringLayout.isBoring(text, paint);
Copy link
Contributor

@NickGerleman NickGerleman Dec 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could probably guard this with a check to only recalculate BoringLayout metics if existing text was boring (adjusting font size won't change boringness, but will change the layout result). But I think needing to recreate it makes sense.

@j-piasecki j-piasecki force-pushed the @jpiasecki/fix-font-size-adjustment-single-char branch from ec2182b to fb3f1f0 Compare December 5, 2024 09:17
@facebook-github-bot
Copy link
Contributor

@NickGerleman has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator.

@facebook-github-bot
Copy link
Contributor

@NickGerleman merged this pull request in 47822e9.

@facebook-github-bot facebook-github-bot added the Merged This PR has been merged. label Dec 9, 2024
@react-native-bot
Copy link
Collaborator

This pull request was successfully merged by @j-piasecki in 47822e9

When will my fix make it into a release? | How to file a pick request?

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. Merged This PR has been merged. 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.

Text with adjustFontSizeToFit and numberOfLine equal to 1 leads to text being cut off or having extra width
6 participants