-
Notifications
You must be signed in to change notification settings - Fork 24.5k
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
Fix adjustsFontSizeToFit
for strings with a single character
#47082
Conversation
Let's make sure the CI is green before we import this 👍 |
Fixes the bug |
@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); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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:

The other two numbers end up as a StaticLayout
so they aren't affected.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done - ec2182b
(Apologise for the late review @j-piasecki) |
3234b8e
to
ec2182b
Compare
@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))) { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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.
ec2182b
to
fb3f1f0
Compare
@NickGerleman has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator. |
@NickGerleman merged this pull request in 47822e9. |
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? |
Summary:
Fixes #47045
On Android
adjustsFontSizeToFit
relies on two metrics:maximumNumberOfLines
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 ontruncatedGlyphRangeInLineFragmentForGlyphAtIndex
which seems to be returningNSNotFound
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 characterTest Plan:
Tested on the code from the issue: