facebook / react-native

A framework for building native applications using React
https://reactnative.dev
MIT License
119.05k stars 24.32k forks source link

[0.73.1] fontWeight not apply while using custom font #42116

Open flixyudh opened 10 months ago

flixyudh commented 10 months ago

Description

I just tried to use custom font in my project template, and i realize when i want to use fontWeight as a reference of my custom font family it's not applicable, is it expected or am i doing something wrong while linking my custom font?

I'm expecting fontWeight should applicable with font as google fonts declared

  1. Thin 100
  2. Thin 100 Italic
  3. ExtraLight 200
  4. ...
Screen Shot 2024-01-02 at 00 08 25

Steps to reproduce

  1. Download font from google
  2. Extract downloaded font and copy font into project directory (without pt prefix)
  3. linking the fonts with react-native-asset
  4. Paste this code in App.[js/tsx]
import React from 'react';
import { Text, View } from 'react-native';

const TextExample = props => {
  const fontListRegularWithFontWeight = [
    {fontFamily: 'DMSans-Regular', fontWeight: '100'},
    {fontFamily: 'DMSans-Regular', fontWeight: '200'},
    {fontFamily: 'DMSans-Regular', fontWeight: '300'},
    {fontFamily: 'DMSans-Regular', fontWeight: '400'},
    {fontFamily: 'DMSans-Regular', fontWeight: '500'},
    {fontFamily: 'DMSans-Regular', fontWeight: '600'},
    {fontFamily: 'DMSans-Regular', fontWeight: '700'},
    {fontFamily: 'DMSans-Regular', fontWeight: '800'},
    {fontFamily: 'DMSans-Regular', fontWeight: '900'},
  ];

  const fontListWithCustomFontOnly = [
    {fontFamily: 'DMSans-Thin'},
    {fontFamily: 'DMSans-ExtraLight'},
    {fontFamily: 'DMSans-Light'},
    {fontFamily: 'DMSans-Regular'},
    {fontFamily: 'DMSans-Medium'},
    {fontFamily: 'DMSans-SemiBold'},
    {fontFamily: 'DMSans-Bold'},
    {fontFamily: 'DMSans-ExtraBold'},
    {fontFamily: 'DMSans-Black'},
  ];

  return (
    <View style={{flex: 1}}>
      <Text style={{textAlign: 'center'}}>Custom Font Regular with fontWeight</Text>
      <View style={{borderWidth: 1, margin: 14, padding: 14, marginBottom: 24, alignItems: 'center'}}>
        {fontListRegularWithFontWeight.map((x, i) => (
          <Text key={x.fontFamily + i} style={x}>
            {JSON.stringify(x)}
          </Text>
        ))}
      </View>

      <Text style={{textAlign: 'center'}}>Custom Font With Exact Font Name</Text>
      <View style={{borderWidth: 1, margin: 14, padding: 14, alignItems: 'center'}}>
        {fontListWithCustomFontOnly.map((x, i) => (
          <Text key={x.fontFamily + i} style={x}>
            {JSON.stringify(x)}
          </Text>
        ))}
      </View>
    </View>
  );
};

export default TextExample;

React Native Version

0.73.1

Affected Platforms

Runtime - Android

Output of npx react-native info

info Fetching system and libraries information...
System:
  OS: macOS 12.5
  CPU: (8) x64 Intel(R) Core(TM) i5-1038NG7 CPU @ 2.00GHz
  Memory: 679.42 MB / 16.00 GB
  Shell:
    version: 5.8.1
    path: /bin/zsh
Binaries:
  Node:
    version: 18.19.0
    path: /usr/local/bin/node
  Yarn:
    version: 1.22.19
    path: ~/.yarn/bin/yarn
  npm:
    version: 10.2.3
    path: /usr/local/bin/npm
  Watchman:
    version: 2023.12.04.00
    path: /usr/local/bin/watchman
Managers:
  CocoaPods: Not Found
SDKs:
  iOS SDK:
    Platforms:
      - DriverKit 21.4
      - iOS 16.0
      - macOS 12.3
      - tvOS 16.0
      - watchOS 9.0
  Android SDK:
    API Levels:
      - "27"
      - "28"
      - "29"
      - "30"
      - "31"
      - "33"
      - "34"
    Build Tools:
      - 28.0.3
      - 29.0.2
      - 29.0.3
      - 30.0.2
      - 30.0.3
      - 31.0.0
      - 33.0.0
      - 33.0.1
      - 33.0.2
      - 34.0.0
    System Images:
      - android-23 | Intel x86 Atom_64
      - android-27 | Google APIs ARM 64 v8a
      - android-28 | Google APIs Intel x86 Atom
      - android-29 | Google APIs Intel x86 Atom
      - android-30 | Google APIs Intel x86 Atom
      - android-30 | Google Play ARM 64 v8a
      - android-34 | Google APIs Intel x86_64 Atom
    Android NDK: Not Found
IDEs:
  Android Studio: 2022.3 AI-223.8836.35.2231.10406996
  Xcode:
    version: 14.0.1/14A400
    path: /usr/bin/xcodebuild
Languages:
  Java:
    version: 17.0.9
    path: /usr/bin/javac
  Ruby:
    version: 2.7.4
    path: /Users/administrator_1/.rvm/rubies/ruby-2.7.4/bin/ruby
npmPackages:
  "@react-native-community/cli": Not Found
  react:
    installed: 18.2.0
    wanted: 18.2.0
  react-native:
    installed: 0.73.1
    wanted: 0.73.1
  react-native-macos: Not Found
npmGlobalPackages:
  "*react-native*": Not Found
Android:
  hermesEnabled: true
  newArchEnabled: true
iOS:
  hermesEnabled: Not found
  newArchEnabled: false

### Stacktrace or Logs

```text
noneed

Reproducer

https://snack.expo.dev/w667bnl74

Screenshots and Videos

Screen Shot 2024-01-02 at 00 14 50
Linuhusainnk commented 10 months ago

Use fontFamily name instead of fontWeight

ex : use FamilyName-Medium instead of familyName + 500

flixyudh commented 10 months ago

@Linuhusainnk I'm not asking how to use it, I'm asking why fontWeight is not apply when using custom font? the font itself support all fontWeight from 100-900

tarakagilefoways commented 10 months ago

Please check below things are done properly

  1. iOS -> Info Plist
  2. Android > SRC > MAIN > ASSETS > FONTS > all files are placed.
  3. Styles must have fontFamily : "FontName-FontWeightName , e.g. fontFamily : "FiraSans-Regular"

and to answer your question please check https://github.com/facebook/react-native/issues/26193

Frans-L commented 10 months ago

As far as I have understood, this issue has unfortunately existed for years for Android.

Here's a great workaround of how you can make the fontWeight property work properly by utilising Android's XML font feature. Note that this workaround doesn't work with EXPO without ejecting.

Stack overflow answer: https://stackoverflow.com/a/70247374 Step-by-step guide: https://github.com/jsamr/react-native-font-demo

Patrick-web commented 10 months ago

Use fontFamily name instead of fontWeight

ex : use FamilyName-Medium instead of familyName + 500

I use this workaround

function mapFontweightToFontFamily(weight: FontWeight, fontPrefix = "Mulish") {
    switch (weight) {
        case "extralight":
            return `${fontPrefix}ExtraLight`;
        case "light":
            return `${fontPrefix}Light`;
        case "regular":
            return `${fontPrefix}`;
        case "semibold":
            return `${fontPrefix}SemiBold`;
        case "bold":
            return `${fontPrefix}Bold`;
        case "extrabold":
            return `${fontPrefix}ExtraBold`;
        case "black":
            return `${fontPrefix}Black`;
        default:
            return `${fontPrefix}`;
    }
}
fabOnReact commented 9 months ago

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

imbaky commented 9 months ago

I can confirm that I am also running into this issue and the problem with the proposed workaround is that you need to add these font files to your app which wouldn't be needed when using fontWeight.

flixyudh commented 9 months ago

@fabriziobertoglio1987 yes, the issue still persist.

i don't really understand why we need react-native-improved to fix this issue, is it not possible to fix into react-native core, so you need to create a patch to support fontWeight with custom font? or the patch itself is a temporary solution while we waiting RN Team fixed the issue?

i think it should be fixed in react-native source, since Text itself is a part of react-native core component.

flixyudh commented 9 months ago

@fabOnReact @cortinico can we override the ReactTypefaceUtils.java to something like this ?

ReactTypefaceUtils.java Based on 0.73.1:

ReactTypefaceUtils [CHANGES] ```diff --- com/facebook/react/views/text/ReactTypefaceUtils.java +++ com/facebook/react/views/text/ReactTypefaceUtils.java @@ -179,6 +179,35 @@ return TextUtils.join(", ", features); } + public static String FontFamilyMappingWithWeight(String fontFamilyName, int fontWeight){ + if(fontFamilyName.toLowerCase().contains("regular") && fontWeight > 0){ + String extractFontFamily = fontFamilyName.split("-")[0]; + + switch (fontWeight) { + case 100: + return extractFontFamily+"-Thin"; + case 200: + return extractFontFamily+"-ExtraLight"; + case 300: + return extractFontFamily+"-Light"; + case 400: + return extractFontFamily+"-Regular"; + case 500: + return extractFontFamily+"-Medium"; + case 600: + return extractFontFamily+"-SemiBold"; + case 700: + return extractFontFamily+"-Bold"; + case 800: + return extractFontFamily+"-ExtraBold"; + case 900: + return extractFontFamily+"-Black"; + } + } + + return fontFamilyName; + } + public static Typeface applyStyles( @Nullable Typeface typeface, int style, @@ -190,8 +219,9 @@ if (fontFamilyName == null) { return typefaceStyle.apply(typeface == null ? Typeface.DEFAULT : typeface); } else { + String FontFamilyRealName = FontFamilyMappingWithWeight(fontFamilyName, weight); return ReactFontManager.getInstance() - .getTypeface(fontFamilyName, typefaceStyle, assetManager); + .getTypeface(FontFamilyRealName, typefaceStyle, assetManager); } } } ```

I've tried to compile then run it, and the result quite expected except the fontWeight 700, 800, 900 seems not affected

Current Result with <Text/> Component :

Current Result with <TextInput/> Component :

fabOnReact commented 9 months ago

You can over ride everything. Even private apis. You can check my project sourcecode to understand what i mean

Just define new class that inherits from that java api

My project has many examples of this

Private api needs to be reimplemented in child class.. but few lines of code

krisidmisso commented 7 months ago

@cortinico Im not a java expert but this doesnt feel right com/facebook/react/views/text/ReactTypefaceUtils.java:

image

The RN upgrade has made the android version ugly

cortinico commented 7 months ago

Im not a java expert but this doesnt feel right com/facebook/react/views/text/ReactTypefaceUtils.java:

Can you clarify why it doens't feel right @krisidmisso

krisidmisso commented 7 months ago

@cortinico

Again, I dont understand java but this issue is boring and it challenges us in every project. I have bookmarked this solution https://stackoverflow.com/questions/38815234/how-to-add-fonts-for-different-font-weights-for-react-native-android-project but I dont think this should be the best way to achieve font styles

I dont remember having this issue with old RN versions

cortinico commented 7 months ago

I dont remember having this issue with old RN versions

  1. Are you able to bisect in which version of RN this got introduced?
  2. Also can we please create a repro with this template: https://github.com/react-native-community/reproducer-react-native
VladimirAtGlossgenius commented 1 month ago

Hello Everyone! I'm running into the same problem and trying to wrap my head around the issue. A couple of things don't add up for me.

image

Font weight on Android for custom fonts only applies regular weight for 100 - 600 and bold for 700 - 900. For default font family however it works fine for all the ranges.

If it's Android's limitation and the only way to make it work is to make an XML font definition as described in this comment: https://github.com/facebook/react-native/issues/42116#issuecomment-1876854067 then does it mean the Android system font has separate fonts defined for Thin, ExtraLight, Light, Regular, Medium, SemiBold, Bold ,ExtraBold and Black?

In that case it doesn't make sense to me because I couldn't find the place where RN converts 100- 900 values to Android font names but default RN text still works.

Please let me know if I'm missing something and how I can help 🙏