Avoiding Fake Fonts in Android

Recently I experienced the problem of fake bold and italics using the font family in Android.

I would like to explain this problem and share its solution.

Font Family

Since API 26 was released, an ability to combine fonts into font families has appeared.

Font family is a set of fonts with their font styles and weights.

You can create a new font family as an XML resource and access it as a single item, instead of referencing each style and weight as separate resources.
In this way, the system will be able to choose the correct font depending on the text style that you use.

Example of the file:

<?xml version="1.0" encoding="utf-8"?>
<font-family xmlns:android="http://schemas.android.com/apk/res/android">
    <font
        android:fontStyle="normal"
        android:fontWeight="400"
        android:font="@font/lobster_regular" />
    <font
        android:fontStyle="italic"
        android:fontWeight="400"
        android:font="@font/lobster_italic" />
</font-family>

The attribute fontStyle determines individual font style – normal (normal) or cursive (italic).

fontWeight establishes font weight, a/k/a font saturation.

And of course, font sets a font which will be used at the given fontWeight and fontStyle.

Font Weight

This standard came from web development. Value is set from 100 to 900 with a 100 pitch.

The table below shows some common names of saturation:

ValueCommon name
100Thin (Hairline)
200Extra Light (Ultra Light)
300Light
400Normal (Regular)
500Medium
600Semibold (Demi Bold)
700Bold
800Extra Bold (Ultra Bold)
900Black (Heavy)
950Extra Black (Ultra Black)

Usually it is enough to specify only fonts for normal glyph (- 400) and standard bold one (- 700) in the font family file.

For details about font saturation, look here.

Fake Italic

When the system can’t find a suitable font for bold or cursive text, Android compensates it by stretching characters for fake bold and by tilting for fake italic.

I created an app which shows the differences between fake styles and the real ones using Lobster Two font as an example:

Using a fake font, you may notice that the text is more flattened. Even worse, this makes it difficult to read because of the smearing of letters and wrong tilt.

Solution

In this example I create lobster_two.xml font family file with fonts for normal, cursive, bold, and bold cursive text:

<?xml version="1.0" encoding="utf-8"?>
<font-family xmlns:app="http://schemas.android.com/apk/res-auto">

    <font
        app:fontStyle="normal"
        app:fontWeight="400"
        app:font="@font/lobster_two_normal" />

    <font
        app:fontStyle="italic"
        app:fontWeight="400"
        app:font="@font/lobster_two_italic" />

    <font
        app:fontStyle="normal"
        app:fontWeight="700"
        app:font="@font/lobster_two_bold" />

    <font
        app:fontStyle="italic"
        app:fontWeight="700"
        app:font="@font/lobster_two_bold_italic" />

</font-family>

I also created lobster_two_incomplete.xml with a font only for normal text:

<?xml version="1.0" encoding="utf-8"?>
<font-family xmlns:app="http://schemas.android.com/apk/res-auto">

    <font
        app:fontStyle="normal"
        app:fontWeight="400"
        app:font="@font/lobster_two_normal" />

</font-family>

And I was switching back and forth between them by pressing the switch.

When lobster_two_incomplete.xml was used instead of lobster_two.xml the font was artificially stretched and tilted.

To prevent this from happening, all sorts of styles in the font family file need to be defined, and this file always must be used as a font.

Using this in the code

// Correct
val typeFace = resources.getFont(R.font.lobster_two)
textView.setTypeface(typeFace, Typeface.BOLD)

// Incorrect
textView.typeface = resources.getFont(R.font.lobster_two_bold)

// Incorrect
val typeFace = resources.getFont(R.font.lobster_two_incomplete)
textView.setTypeface(typeFace, Typeface.BOLD)

// Incorrect
val typeFace = resources.getFont(R.font.lobster_two_normal)
textView.setTypeface(typeFace, Typeface.BOLD)

Using in xml:

// Correct
<TextView
    ...
    android:fontFamily="@font/lobster_two"
    android:textStyle="bold|italic" />

// Incorrect
<TextView
    ...
    android:fontFamily="@font/lobster_two_bold_italic" />

// Incorrect
<TextView
    ...
    android:fontFamily="@font/lobster_two_incomplete"
    android:textStyle="bold|italic" />

// Correct
<TextView
    ...
    android:fontFamily="@font/lobster_two"
    android:textStyle="bold" />

// Incorrect
<TextView
    ...
    android:fontFamily="@font/lobster_two_bold" />

// Incorrect
<TextView
    ...
    android:fontFamily="@font/lobster_two_normal"
    android:textStyle="bold" />