Let’s have some fun with Fonts: part 1

Mikołaj Kąkol
November 28, 2022 | Software development

This article is the first part of the series that will introduce the topic of fonts to you.

Do you remember Fun with Flags in The Big Bang Theory? Let’s have some Fun with Fonts. 

Jim Parsons and Will Wheaton in The Big Bang Theory || www.filmweb.pl

 ABC 

First of all, to avoid confusion, let’s define what is what. According to wiki: 

  • A typeface is the design of lettering that can include variations in size, weight (e.g. bold), slope (e.g. italic), width (e.g. condensed), and so on. Each of these variations of the typeface is a font. 
  • In metal typesetting, a font is a typeface’s particular size, weight, and style. Each font is a matched set of types, with a piece (a “sort”) for each glyph. A typeface consists of a range of such fonts that share an overall design.

    So, typeface describes how the letters should look and font is particular (exact size, weight, etc) implementation of a given typeface.  

  • Font Family is a group of fonts that implements the same typeface with different variations. 

Show me the code 

A single font family can exponentially have many fonts and scales by adding more weights or slopes. This approach has a few flaws in development. When adding a typeface to our project, we need to include about 12 fonts, which also increases the app size. 

private val fonts = listOf( 
    Font(R.font.roboto_thin, weight = FontWeight.Thin), 
    Font(R.font.roboto_thin_italic, weight = FontWeight.Thin, style = FontStyle.Italic), 
    Font(R.font.roboto_light, weight = FontWeight.Light), 
    Font(R.font.roboto_light_italic, weight = FontWeight.Light, style = FontStyle.Italic), 
    Font(R.font.roboto_regular), 
    Font(R.font.roboto_italic, style = FontStyle.Italic), 
    Font(R.font.roboto_medium, weight = FontWeight.Medium), 
    Font(R.font.roboto_medium_italic, weight = FontWeight.Medium, style = FontStyle.Italic), 
    Font(R.font.roboto_bold, weight = FontWeight.Bold), 
    Font(R.font.roboto_bold_italic, weight = FontWeight.Bold, style = FontStyle.Italic), 
    Font(R.font.roboto_black, weight = FontWeight.Black), 
    Font(R.font.roboto_black_italic, weight = FontWeight.Black, style = FontStyle.Italic), 
) 

We need to assign each font the proper weight and style. Let’s render it. 

@Composable 
fun AllFonts() = Column { 
    fonts.forEach { font -> 
        Text( 
            text = demoText, 
            fontFamily = FontFamily(font), 
        ) 
    } 
} 

We have all fonts required for possible design, but this is not the way how you use Font Family is supposed to be used, let’s achieve this again.  

private val robotoFontFamily = FontFamily(fonts) 
 

@Composable 
fun FontFamily() = Column { 
    val weights = listOf( 
        FontWeight.Thin, 
        FontWeight.Light, 
        FontWeight.Normal, 
        FontWeight.Medium, 
        FontWeight.Bold, 
        FontWeight.Black, 
    ) 
    weights.forEach { weight -> 
        Text( 
            text = demoText, 
            fontFamily = robotoFontFamily, 
            fontWeight = weight, 
        ) 
        Text( 
            text = demoText, 
            fontFamily = robotoFontFamily, 
            fontWeight = weight, 
            fontStyle = FontStyle.Italic, 
        ) 
    } 
} 

To my eyes, it looks the same. Now we might start to think if we really need italic fonts. Let’s check what would happen if we use an italic style without having italic font. 

@Composable 
fun NotEverythingItalicIsItalic() = Column { 
    val fontsNormal = fonts.filter { it.style == FontStyle.Normal } 
    val fontsItalic = fonts.filter { it.style == FontStyle.Italic } 
    fontsNormal.forEach { font -> 
        Text( 
            text = demoText, 
            fontFamily = FontFamily(font), 
            fontStyle = FontStyle.Italic, 
        ) 
    } 
    fontsItalic.forEach { font -> 
        Text( 
            text = demoText, 
            fontFamily = FontFamily(font), 
        ) 
    } 
} 

Again we see that text is actually slanted. So, it worked, italic fonts are not needed? Well, kind of, it depends on how lenient your designer is. If we look closely, we will see that the angle is different in the font slanted by Android Framework (HarfBuzz library most likely) than the original font.

@Composable 
fun NotEverythingBoldIsBold() = Column { 
    val regularFont = fonts.first { it.weight == FontWeight.Normal } 
    val boldFont = fonts.first { it.weight == FontWeight.Bold } 
    Text( 
        text = "$demoText regular", 
        fontFamily = FontFamily(regularFont), 
    ) 
    Text( 
        text = "$demoText regular bolded", 
        fontFamily = FontFamily(regularFont), 
        fontWeight = FontWeight.Bold, 
    ) 
    Text( 
        text = "$demoText bold", 
        fontFamily = FontFamily(boldFont), 
    ) 
} 


The same thing happened for bolding text. We can see that framework tried to make the font bolded, but it is not exactly what we would get using the correct font. 

That’s it 

That was easy. Now you know how to correctly define fonts and use them in the app. Next, we will check how we can make this process less cumbersome.

Do you want to know more? Check the second part of the article. 

If you want to meet us in person, click here and we’ll get in touch!