LineHeight is working weird on specific fonts (Android Only)
Please provide all the information requested. Issues that do not follow this format are likely to stall.
Description
Multiline texts don't have same line height on specific fonts. (NotoSansCJK= Source Hans Sans, NotoSerifCJK=Source Hans Serif)
It happens on first line.

React Native version:
info Fetching system and libraries information...
System:
OS: macOS 10.15.5
CPU: (12) x64 Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
Memory: 7.08 GB / 32.00 GB
Shell: 5.7.1 - /bin/zsh
Binaries:
Node: 14.4.0 - /usr/local/bin/node
Yarn: 1.22.4 - /usr/local/bin/yarn
npm: 6.14.4 - /usr/local/bin/npm
Watchman: 4.9.0 - /usr/local/bin/watchman
Managers:
CocoaPods: 1.9.2 - /usr/local/bin/pod
SDKs:
iOS SDK:
Platforms: iOS 13.5, DriverKit 19.0, macOS 10.15, tvOS 13.4, watchOS 6.2
Android SDK:
API Levels: 27, 28, 29
Build Tools: 28.0.3, 29.0.2, 29.0.3
System Images: android-29 | Google APIs Intel x86 Atom_64, android-29 | Google Play Intel x86 Atom_64
Android NDK: Not Found
IDEs:
Android Studio: 4.0 AI-193.6911.18.40.6514223
Xcode: 11.5/11E608c - /usr/bin/xcodebuild
Languages:
Java: 1.8.0_252 - /usr/bin/javac
Python: 2.7.16 - /usr/bin/python
npmPackages:
@react-native-community/cli: Not Found
react: 16.11.0 => 16.11.0
react-native: 0.62.2 => 0.62.2
npmGlobalPackages:
*react-native*: Not Found
Steps To Reproduce
- Set lineHeight properly. (not too small, large)
- and set fontFamily to custom font which has problem. (NotoSansCJK, NotoSerifCJK)
Expected Results
Every lines have same line height.
like this:

Snack, code example, screenshot, or link to a repository:
Android Native(No React Native) is fine. This font is NotoSansKR-Regular :

iOS is fine:

Text Style:
{
padding: 10,
fontSize: 12,
lineHeight: 28,
}
source code here. https://github.com/toy0605/FontTestApp
and this is real device(not emulator) screenshot.

it's galaxy s9+ (android 10)
you saying that this bug reproduces only on galaxy s9+ (android 10) ? Does not reproduce on emulator api 28

you saying that this bug reproduces only on galaxy s9+ (android 10) ? Does not reproduce on emulator api 28
No. It doesn't reproduce only on Galaxy S9+. It happens on emulator, too. Could you tell me which you tested on emulator?
I tested on Nexus 5 API 28, Nexus 5 API 29 Emulator. This is my emulator version.

and I changed System language(English(United States) to 한국어(대한민국))
Same issue here, even it was fixed according to https://github.com/facebook/react-native/issues/7546#issuecomment-246342692 , I am using react-native 0.63.2, line height of the first line of the ttf font I am using differs from all the other liens. And it is only on Android. I tested on Pixel 2 API R.

The same question,Have you solved it
I believe @fabriziobertoglio1987 is working on a PR similar to this? Can you confirm and link the PR/issues?
Hey there, it looks like there has been no activity on this issue recently. Has the issue been fixed, or does it still require the community's attention? This issue may be closed if no further activity occurs. You may also label this issue as a "Discussion" or add it to the "Backlog" and I will leave it open. Thank you for your contributions.
I am also running into this issue. Any solutions?
Reproducible example with the font HelveticaNowDisplay https://snack.expo.io/@ronicesarrc/custom-font-example
Unfortunately the issue still persists for me in 0.65.1. But I found a super strange workaround for this issue and would be curious if that also works for you. Maybe it also helps finding out what the underlying issue is.
Starting Point
<Text>aaa...</Text>
fontSize: 16,
lineHeight: 24
Strange workaround
Add an empty nested Text and give it lineHeight +1 of the parent.
<Text>aaa...<Text style={{ lineHeight: 24 + 1 }} /></Text>
fontSize: 16,
lineHeight: 24
Summary
I created a component that does the job:
import { Platform, Text as RNText } from 'react-native';
export const Text = ({ style, children, ...rest }) => (
<RNText style={style} {...rest}>
{children}
{Platform.OS === 'android' && <RNText style={{ lineHeight: style.lineHeight + 0.001 }} />}
</RNText>
);
@emin93 Thanks for the workaround!
I noticed that lineHeight + 1 seems to add a noticeable amount of space to the text node (I'm using react-native-capsize to trim space above capital letters and below the baseline).
It looks like literally any non-zero difference to the parent line height also fixes it, e.g.lineHeight + 0.001 also works for me and doesn't introduce unwanted space.
@markdalgleish Ah that's good to know, thanks!
I also noticed that this bug can affect text nodes on Android when when numberOfLines causes text to truncate, even when it's just a single line of text. Luckily the workaround posted earlier fixes this issue too, but decided to share for completeness.
Hey there, it looks like there has been no activity on this issue recently. Has the issue been fixed, or does it still require the community's attention? This issue may be closed if no further activity occurs. You may also label this issue as a "Discussion" or add it to the "Backlog" and I will leave it open. Thank you for your contributions.
Not stale.
Unfortunately the issue still persists for me in
0.65.1. But I found a super strange workaround for this issue and would be curious if that also works for you. Maybe it also helps finding out what the underlying issue is.Starting Point
<Text>aaa...</Text> fontSize: 16, lineHeight: 24![]()
Strange workaround
Add an empty nested
Textand give it lineHeight +1 of the parent.<Text>aaa...<Text style={{ lineHeight: 24 + 1 }} /></Text> fontSize: 16, lineHeight: 24![]()
Summary
I created a component that does the job:
import { Platform, Text as RNText } from 'react-native'; export const Text = ({ style, children, ...rest }) => ( <RNText style={style} {...rest}> {children} {Platform.OS === 'android' && <RNText style={{ lineHeight: style.lineHeight + 0.001 }} />} </RNText> );
Thanks @emin93 , it's working with your suggested changes.
The issue still persists for me in 0.64.3. However, the strange workaround from @emin93 works.
Reporting back after a while - this issue is still present. While the provided workaround might solve the issue with the first two lines, it unfortunately is not a "one size fits all" solution. When applied on single line texts it causes the vertical alignment to be off. includeFontPadding and textAlignVertical don't help in that case either.
I'm also seeing this issue when trying to correctly align the placeholder text inside the Android-specific SearchBar component from React Native Elements. Unfortunately, the workaround mentioned doesn't work for me, and the placeholder remains too high, so I've had to resort to the crude paddingTop: 3 to give it the nudge it needs. This is with the Poppins font, specifically Poppins_400Regular.
Can confirm, this is still a bug (using GrotaRounded custom font for instance) on Android. But @emin93 weird workaround works indeed!
I believe this is caused by
https://github.com/facebook/react-native/blob/main/ReactAndroid/src/main/java/com/facebook/react/views/text/CustomLineHeightSpan.java#L25
There is some complex logic that tries to center the font in an appropriate way if the line size is below a certain amount. It can happen if the font has unusual metadata, like the capheight being very tall.
I don't think this happens on IOS or web, at least I don't see it happening in practice nor can I find any code that does it.
Adding magic offsets to line height will only make it work for that specific font, at that specific size and line height.
I believe this is caused by
https://github.com/facebook/react-native/blob/main/ReactAndroid/src/main/java/com/facebook/react/views/text/CustomLineHeightSpan.java#L25
There is some complex logic that tries to center the font in an appropriate way if the line size is below a certain amount. It can happen if the font has unusual metadata, like the capheight being very tall.
I don't think this happens on IOS or web, at least I don't see it happening in practice nor can I find any code that does it.
Adding magic offsets to line height will only make it work for that specific font, at that specific size and line height.
This file has moved to https://github.com/facebook/react-native/blob/main/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/CustomLineHeightSpan.java
CustomLineHeightSpan claims to exist because of https://github.com/facebook/react-native/issues/7546 which seems to have been already fixed, so this workaround might not be needed anymore
This issue still persists. I tried the workaround suggested by @emin93, but it didn't work. To fix it, I added a marginTop specifically for Android. However, it feels somewhat dirty.
const style = getTextStyle(type);
const color = getTextColor();
const lineHeightFix = {marginTop: IS_ANDROID ? 6 : 0};
return (
<Text style={[style, {color}, propStyle, lineHeightFix]} {...otherProps}>
{children}
</Text>
);
I'm facing this problem too (version: react-native 0.70.9)
For most of the fonts you should apply ascender/descender metrics to make it render correctly at first and last lines. this tool might help you understand https://vertical-metrics.netlify.app/
Though there are broken fonts (made by font designers) where you should add workarounds 😄
based on ascender/descender data you can dynamically calculate your padding top
Though maybe someone has better approach, for me nothing worked except doing all calculations on my own
Unfortunately the issue still persists for me in
0.65.1. But I found a super strange workaround for this issue and would be curious if that also works for you. Maybe it also helps finding out what the underlying issue is.Starting Point
<Text>aaa...</Text> fontSize: 16, lineHeight: 24![]()
Strange workaround
Add an empty nested
Textand give it lineHeight +1 of the parent.<Text>aaa...<Text style={{ lineHeight: 24 + 1 }} /></Text> fontSize: 16, lineHeight: 24![]()
Summary
I created a component that does the job:
import { Platform, Text as RNText } from 'react-native'; export const Text = ({ style, children, ...rest }) => ( <RNText style={style} {...rest}> {children} {Platform.OS === 'android' && <RNText style={{ lineHeight: style.lineHeight + 0.001 }} />} </RNText> );
This solution is awesome, but not work for me. It worked when I place {children} after {Platform.OS ........} like below
import {Platform, StyleSheet, Text as RNText} from 'react-native';
const Text = ({style, children, ...rest}) => (
<RNText style={style} {...rest}>
{Platform.OS === 'android' && (
<RNText
style={
StyleSheet.flatten(style)?.lineHeight
? {lineHeight: StyleSheet.flatten(style).lineHeight + 0.001}
: {}
}
/>
)}
{children}
</RNText>
);
For most of the fonts you should apply ascender/descender metrics to make it render correctly at first and last lines. this tool might help you understand https://vertical-metrics.netlify.app/
Though there are broken fonts (made by font designers) where you should add workarounds 😄
based on ascender/descender data you can dynamically calculate your padding top
Though maybe someone has better approach, for me nothing worked except doing all calculations on my own
Thank you so much! I ran into this issue with custom font after switching to target SDK 33 and this app was immensely useful in showing what to fix in the font to make it behave 'normally'. I ended up rebuilding the font with proper location of glyphs.
The solution from @emin93 is nice! But I found that solution can't fix the paragragh problem. The lines still stick together after a newline. If you are facing same issue like me, I have another solution that is based on @emin93's solution.
export const Text = ({ style, children, ...rest }) => (
<RNText style={style} {...rest}>
{children}
{Platform.OS === 'android' && <RNText style={{ lineHeight: style.lineHeight + 0.001 }} />}
</RNText>
);
const ParagraphText = ({paragraph}): JSX.Element => {
const textStrings = useMemo(() => paragraph?.split(/\r?\n/), [paragraph]);
const textStringsComponents = useMemo(
() =>
textStrings?.map((text, index) => (
<>
<Text>
{text}
</Text>
{!!textStrings && index !== textStrings.length - 1 && (
<Text>
{'\n'}
</Text>
)}
</>
)),
[color, content, textStrings, variant, weight]
);
const transformedChildren = useMemo(
() => (Platform.OS === 'android' ? textStringsComponents : paragraph),
[paragraph, textStringsComponents]
);
return (
<Text>
{transformedChildren}
</Text>
);
Do you still experience this issue? If yes, I will publish the fix in the react-native-improved package https://github.com/fabriziobertoglio1987/react-native-improved. Thanks a lot