App icon indicating copy to clipboard operation
App copied to clipboard

[$1000] Standardize on single fontFamily names across platforms

Open luacmartins opened this issue 2 years ago • 110 comments

Problem

Coming from this discussion, react-native-asset let's us easily link and manage assets, including fonts. However, it fails to provide a cross platform way to use font typeface modifiers such as fontWeight and fontStyle in combination with a custom fontFamily name, in both iOS and Android, which in turn results in keeping multiple font-families such as, ExpensifyNewKansas-Medium, ExpensifyNewKansas-MediumItalic instead of a single ExpensifyNewKansas font-family.

iOS seems to work out of the box as long as we use the postScript name for the font. However, android uses the font filename which results in the different fontFamilies above.

Why is this important

Ease of importing new fonts and keeping/maintinaing a single font type-face.

Solution

Update react-native-asset to support XML fonts in android and auto-group font families into XML fontFamilies.

cc @roryabraham

Upwork Automation - Do Not Edit
  • Upwork Job URL: https://www.upwork.com/jobs/~0165fabbe62a87ed20
  • Upwork Job ID: 1641731454588358656
  • Last Price Increase: 2023-03-31

luacmartins avatar Dec 21 '22 22:12 luacmartins

cc @Luke9389 for visibility

roryabraham avatar Dec 21 '22 22:12 roryabraham

nice!

grgia avatar Dec 23 '22 02:12 grgia

Love it! Thanks y'all

Luke9389 avatar Jan 04 '23 01:01 Luke9389

No update yet

roryabraham avatar Jan 04 '23 21:01 roryabraham

Sorry for the delay here. I'm going to make this a weekly, but my plan here is to:

  1. Create a couple simple demo React Native projects to show the difference between the "old" way of bundling fonts that we use today vs the "new" way (i.e: Android xml fonts)
  2. Then create an issue in https://github.com/unimonkiez/react-native-asset to show the "before" and "after" that explains what we would expect react-native-asset to do, given font assets in the top-level assets directory.
  3. Once that is done, we can re-group, but it's likely I would want to pass this off to an expert contractor (or even better, hire the react-native-asset maintainer directly) to do the actual implementation. I think I could implement it, but I just don't know when I'll have enough time.

roryabraham avatar Jan 12 '23 04:01 roryabraham

I think that sounds great @roryabraham. Thanks for the update

Luke9389 avatar Jan 12 '23 19:01 Luke9389

No update this week :(

roryabraham avatar Jan 21 '23 02:01 roryabraham

No update again

roryabraham avatar Feb 03 '23 09:02 roryabraham

Sorry, still no update here.

roryabraham avatar Feb 23 '23 05:02 roryabraham

No update, won't prioritize until after ECX at the earliest. If we could very clearly state our goal here we could pass this off to an agency, which has been my intention since the start

roryabraham avatar Mar 07 '23 20:03 roryabraham

I've held onto this for too long and I've been struggling to make time for this. Given that I have several other higher-priority initiatives, I'm going to drop this for now.

roryabraham avatar Mar 20 '23 16:03 roryabraham

Should we make this external? Other contributors can propose a fix to react-native-asset

luacmartins avatar Mar 20 '23 21:03 luacmartins

Asked callstack here

luacmartins avatar Mar 20 '23 22:03 luacmartins

Hi, I'm Fábio from Callstack and I would like to work on this issue.

fabioh8010 avatar Mar 21 '23 10:03 fabioh8010

Update: Regarding to this issue, I will start investigation today and will keep you updated here once I have a proposal.

fabioh8010 avatar Mar 22 '23 14:03 fabioh8010

Thanks for the update @fabioh8010!

luacmartins avatar Mar 22 '23 15:03 luacmartins

Update: Did some investigation and tests to validate the idea, I expect to provide a proposal on Monday.

fabioh8010 avatar Mar 24 '23 20:03 fabioh8010

Update: Proposal being reviewed internally, also exploring one alternative solution.

fabioh8010 avatar Mar 27 '23 15:03 fabioh8010

Proposal

Please re-state the problem that we are trying to solve in this issue.

iOS and Android have different ways of handling custom fonts on RN. While iOS has a more robust way to "query" the fonts available to use, in Android we have to rely on the font's file name to be able to use that custom font. In this way, it's difficult to have a cross-platform configuration that satisfies both platforms.

What is the root cause of that problem?

As stated in this issue, Android has a different logic to manage fonts related to iOS, requiring the developer to rely on the font file's name to be able to use the font properly in the application.

In Android, we have the ReactFontManager.java file that manages font typeface's cache and registry, and there is a logic to find the correct font file based on the fontFamily provided, an extension and the file extension itselt (.ttf or .otf). In fact, there is indeed a logic to determine if the font is bold or italic based on its name. For example, if you set fontFamily to Lato and fontWeight to bold and you have a font file called Lato_bold.ttf, the logic will correctly choose this file to use for the bold font you specified.

The problem is that this logic is flawed as it only works if the font file name have a specific and uncommon format, and it don't cover other font weights.

What changes do you think we should make in order to solve the problem?

We propose to go with the CLI improvement, making it execute the steps described here in an automatic way. We are going to do this in the following steps:

  1. When the CLI is run, search for the font files that were placed in the project.
  2. For each font file:
    1. Run fontname npm package or similar tool to get the font’s information.
    2. Get the font's information like font style and weight, by either:
      1. Reading the file metadata and extracting this info (if possible).
      2. Guessing the font style and weight by the font's file name.
  3. When all fonts are scanned, build a map with all different font families found.
  4. For each font family:
    1. Rename the corresponding font files and copy to android’s folder.
    2. Create a XML file with all variants for this font family (we can use fast-xml-parser npm package).
    3. Use regular expressions in the MainApplication.java file to find and insert the import and the addCustomFont() method to register that font family.
  5. Make a PR to react-native-asset library.
  6. When merged, use this tool in Expensify's project and make a PR with the changes.

What alternative solutions did you explore? (Optional)

We thought about making an improvement directly in the RN repo, improving ReactFontManager's logic to detect the fonts in a better way than the current one (using common suffixes for these fonts), but we ended up sticking with the CLI solution as we think it will provide a safer approach to use the fonts in a cross-platform way.

fabioh8010 avatar Mar 28 '23 18:03 fabioh8010

@fabioh8010 That sounds great to me 👍🏼

roryabraham avatar Mar 29 '23 11:03 roryabraham

I've contributed to react-native-asset before so can help review.

roryabraham avatar Mar 29 '23 11:03 roryabraham

Agreed! The steps above sound good! Let's get it started!

luacmartins avatar Mar 29 '23 11:03 luacmartins

Great! I'll start development tomorrow and will keep you updated here.

fabioh8010 avatar Mar 29 '23 16:03 fabioh8010

Job added to Upwork: https://www.upwork.com/jobs/~0165fabbe62a87ed20

MelvinBot avatar Mar 31 '23 09:03 MelvinBot

Triggered auto assignment to @sakluger (External), see https://stackoverflow.com/c/expensify/questions/8582 for more details.

MelvinBot avatar Mar 31 '23 09:03 MelvinBot

Triggered auto assignment to Contributor-plus team member for initial proposal review - @mollfpr (External)

MelvinBot avatar Mar 31 '23 09:03 MelvinBot

Current assignees @roryabraham and @luacmartins are eligible for the External assigner, not assigning anyone new.

MelvinBot avatar Mar 31 '23 09:03 MelvinBot

In order to achieve cross-platform compatibility for custom fontFamily names with fontWeight and fontStyle modifiers, we can follow the steps below:

Rename the font files to match the desired fontFamily name, for example, "ExpensifyNewKansas-Regular" for the regular weight.

In the react-native.config.js file, add the following code snippet:

module.exports = { assets: ['./src/assets/fonts/'], ios: {}, android: { sourceDir: './android', fontFamily: 'ExpensifyNewKansas', fontWeight: { Regular: '400', Medium: '500', Bold: '700', }, fontStyle: { Italic: 'italic', }, }, };

Here, we have added "fontFamily", "fontWeight", and "fontStyle" properties to the Android configuration.

Modify the styles as follows: const styles = StyleSheet.create({ textRegular: { fontFamily: Platform.OS === 'android' ? 'ExpensifyNewKansas-Regular' : 'ExpensifyNewKansas', fontWeight: Platform.OS === 'android' ? '400' : 'normal', fontStyle: 'normal', }, textMedium: { fontFamily: Platform.OS === 'android' ? 'ExpensifyNewKansas-Medium' : 'ExpensifyNewKansas', fontWeight: Platform.OS === 'android' ? '500' : 'bold', fontStyle: 'normal', }, textBoldItalic: { fontFamily: Platform.OS === 'android' ? 'ExpensifyNewKansas-Bold' : 'ExpensifyNewKansas-BoldItalic', fontWeight: Platform.OS === 'android' ? '700' : 'bold', fontStyle: 'italic', }, });

In the above code, we use the Platform module to differentiate between iOS and Android and set the appropriate font family, fontWeight, and fontStyle properties. This allows us to use a single fontFamily for all weight and style variations, including custom names with modifiers.

By following these steps, we can achieve cross-platform compatibility with custom fontFamily names in combination with fontWeight and fontStyle modifiers. I wish hope you are doing well [email protected]

luckchain007 avatar Mar 31 '23 12:03 luckchain007

📣 @luckchain007! 📣

Hey, it seems we don’t have your contributor details yet! You'll only have to do this once, and this is how we'll hire you on Upwork. Please follow these steps:

  1. Get the email address used to login to your Expensify account. If you don't already have an Expensify account, create one here. If you have multiple accounts (e.g. one for testing), please use your main account email.
  2. Get the link to your Upwork profile. It's necessary because we only pay via Upwork. You can access it by logging in, and then clicking on your name. It'll look like this. If you don't already have an account, sign up for one here.
  3. Copy the format below and paste it in a comment on this issue. Replace the placeholder text with your actual details.

Screen Shot 2022-11-16 at 4 42 54 PM

Format:

Contributor details
Your Expensify account email: <REPLACE EMAIL HERE>
Upwork Profile Link: <REPLACE LINK HERE>

MelvinBot avatar Mar 31 '23 12:03 MelvinBot

Or, While react-native-asset provides a convenient way to manage assets, including fonts, it does not offer a cross-platform solution for using font typeface modifiers with custom fontFamily names. This can result in having to create multiple font families, each with a different name, in order to use different font weights and styles.

In iOS, using the postScript name for the font works out of the box and allows you to use font typeface modifiers with a custom fontFamily name. However, in Android, the font filename is used instead of the postScript name, which can lead to the creation of multiple font families.

To achieve a consistent font family name across both iOS and Android, you can modify the font files to include the necessary information in their filenames. For example, you can add "-Regular", "-Medium", "-Bold", or "-Italic" to the font filename to indicate the font weight or style. This will ensure that the correct font is used on both platforms, without the need to create multiple font families.

Alternatively, you can use a third-party library, such as react-native-vector-icons, which offers cross-platform support for font typeface modifiers with custom fontFamily names. This library provides a wide range of icons, as well as support for custom fonts, and allows you to easily use font typeface modifiers with a custom fontFamily name on both iOS and Android.

luckchain007 avatar Mar 31 '23 12:03 luckchain007