react-native: Width measures of Text components using a custom font are wrong
React Native version:
System:
OS: macOS 10.14.5
CPU: (12) x64 Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz
Memory: 1002.17 MB / 32.00 GB
Shell: 5.3 - /bin/zsh
Binaries:
Node: 10.15.3 - ~/.nvm/versions/node/v10.15.3/bin/node
Yarn: 1.16.0 - /usr/local/bin/yarn
npm: 6.4.1 - ~/.nvm/versions/node/v10.15.3/bin/npm
Watchman: 4.9.0 - /usr/local/bin/watchman
SDKs:
iOS SDK:
Platforms: iOS 12.2, macOS 10.14, tvOS 12.2, watchOS 5.2
Android SDK:
API Levels: 23, 25, 26, 27, 28
Build Tools: 27.0.3, 28.0.2, 28.0.3, 29.0.0
System Images: android-28 | Google APIs Intel x86 Atom, android-28 | Google Play Intel x86 Atom, android-29 | Google Play Intel x86 Atom
Android NDK: 17.2.4988734
IDEs:
Android Studio: 3.4 AI-183.6156.11.34.5522156
Xcode: 10.2.1/10E1001 - /usr/bin/xcodebuild
npmPackages:
react: 16.8.6 => 16.8.6
react-native: 0.60.0 => 0.60.0
⚠️ This is not a regression from RN 0.60, can also be reproduced in RN 0.57.
On Android, some manufacturers — like Samsung, OnePlus, LG, HTC, and others — let you change the default font on your device without having a rooted device, and, of course, rooted devices can also do that.
The <Text>
component provided by React Native doesn’t seem to measure the layout correctly. Observed cases were when the default font is not Roboto and when a thick font weight is applied.
The result is that on these specifics devices, text components with a thicker font weight will be chopped off (seems to be wrapped in some cases?). It could be a dot missing, or even an entire word.
<Text style={{ fontWeight: 'bold' }}>
This looks great!
</Text>
Roboto (Default font) | OnePlus Slate |
---|---|
Workaround
To influence the layout measures of the text component, we can set a border of at least 2
on it. By doing this, the layout will be properly measured, but the text won’t be vertically centered.
From there, to vertically center the text, we can subtract the double of the border we previously set as a negative marginBottom
. (As seen in the screenshots above).
About this issue
- Original URL
- State: open
- Created 5 years ago
- Reactions: 25
- Comments: 33 (13 by maintainers)
Commits related to this issue
- Fix cut off text on OnePlus Trick is: DONT rely on fontWeight... (like in 1ac5a998a247df4d2869295df629bd463714a414) Instead use fontFamily. It seems fontWeight on android without an explicit fontFami... — committed to MoOx/LifeTime by MoOx 3 years ago
- React Native sync for revisions 0cac4d5...fd31724 Summary: - **[9fb581c7c](https://github.com/facebook/react/commit/9fb581c7c )**: Refactor: merge duplicate imports ([#25489](https://github.com/faceb... — committed to facebook/react-native by sammy-SC 2 years ago
- React Native sync for revisions 0cac4d5...fd31724 Summary: - **[9fb581c7c](https://github.com/facebook/react/commit/9fb581c7c )**: Refactor: merge duplicate imports ([#25489](https://github.com/faceb... — committed to OlimpiaZurek/react-native by sammy-SC 2 years ago
I discovered a “trick” to avoid this kind of issue: don’t rely on
fontWeight
on Android alone! Instead usefontFamily
. Android Roboto have only 6 variants, same for OnePlus Slate. I have spend a few hours trying to find an acceptable combo on stock Android (using a Pixel 3), ColorsOS (Oppo) and OxygenOS (OnePlus)You can safely (on Android) use the following name instead of weight
sans-serif-thin
(= 100/200 depending on android custom font)sans-serif-light
(=300)sans-serif
/sans-serif-regular
(400) (note that OnePlus Slate doesn’t handle sans-serif like sans-serif-regular, when used with a weight)sans-serif-medium
(500)sans-serif-bold
(600/700)sans-serif-black
(800/900)Note that
fontWeight
coupled with afontFamily
give better result on Android, if you want to rely on OS font.Here is a stylesheet you can use safely to think about “weight” and forget about the trick
Just to tag issues so people would find the workaround: #15114 #25258 #29259 #18258 #17629 #21729
Related, but I’ve noticed that because the wrapping calculation is wrong on custom fonts, sometimes you will get an extra line where the text renderer thinks the text will wrap but doesn’t. There is no way to get rid of it.
Example:
No reason to think this isn’t still an issue.
If someone can label this as “Backlog”, that seems like it’d be appropriate.
Not fixed AFAIK
Thanks @charpeni for the detailed report and debugging!
This issue has been around for a couple of years, as #15114. Some information that gradually accumulated there:
fontWeight
or using bold. This seems like a clue.This is an interesting workaround! It would be especially interesting to know whether this workaround keeps working even when the text is long and the border is still small.
That is, does it somehow cause the layout code to go down the right path to use the correct font metrics to make the box as wide as it needs to be? Or does it just add the border itself to the width, and then the text is able to eat into that when it turns out to be wider?
… Looking closely at your screenshots, actually, I have a new observation.
The last three green boxes – that is, each of the variations on a text box with bold text – have exactly the same width in the OnePlus Slate case as the Roboto case.
OTOH the first box, with unstyled text, has less width for OnePlus Slate than for Roboto. And in the OnePlus Slate case, it has less width than the (first) bold box.
So it looks as if in the bold case, the size isn’t getting based on the non-bold version of the same font… instead it seems to have the right dimensions for bold Roboto.
That would be interesting to confirm or disconfirm with more examples. If true, it may be a strong clue for whoever wants to go into the code to try to track down the actual cause.
For anyone running into this, I can reproduce this issue on any recent android emulator by enabling “Bold text” under
Accessibility > Text and display
, which seems like this is not just an issue for some brands, but an accessibility issue in general…The theory of the box around the text being incorrectly calculated seems pretty accurate to me.
@MoOx Not working for #29259
this is duplicate of https://github.com/facebook/react-native/issues/15114 please reopen that issue
Hello,
Thanks for the issue. I have been contributing to facebook/react-native for 4 years and specialize in the Text/TextInput components. I currently have 58 facebook/react-native PRs:
https://github.com/facebook/react-native/pulls?q=is%3Apr+author%3Afabriziobertoglio1987+
This is the suggested approach from the react-native core team (see this discussion):
I’m publishing several fixes for Text and TextInput component in a separate library react-native-improved.
The advantages would be:
What do you think about this? Should I keep working on this? Thanks
This issue has been reproduced and verified to be fixed in the pr #37271.
Hi @dumathane, it looks like this issue is clearly important to you. You could think about sponsoring / paying someone to fix it for you as nobody else is going to fix it apparently (this issue is already more than 2 years old).
New solution https://github.com/facebook/react-native/issues/15114#issuecomment-576313266 Just change textBreakStrategy to simple fix it