OpenType Font and Font Family

TL;DR

Font family name (Name Id 1 in name table of the font) in a OpenType font file should not be used as the font-family property in CSS if the font is not one of regular, italic, bold, and bold italic style. Instead, the Typographic Family Name or Preferred Font Family (Name ID 16 in name table of the font) should be used if including them in CSS.

Background and Problem

Recently, I was tasked to investigate an interesting problem found in our custom font implementation. Font is something I have no prior knowledge about and this investigation really helped me learn a lot more about font and by writing it down, I hope I can help developers who may face similar problems.

Our system allows user to upload any font as long as they have the permission to use the font commercially. Recently, we realized that some of the fonts uploaded by user were not displayed correctly. More specifically the font-family attribute of these said fonts were incorrectly extracted.

As an example, an user uploaded a font called fontA-SemiBold, the extracted font-family name from the font was fontA-SemiBold whereas the correct name of for the font-family should be fontA. And in this case. The incorrect font name resulted in incorrect rendering in the front-end as the CSS rendering is suppose to have the correct font-family name.

Investigation

After looking at several of the uploaded fonts and going through the code, I decided it is necessary to create a small POC using the library we chose in our application. I was able to reproduce the issue that we face with the user uploaded font -- the library yield an incorrect font-family name. This leads me to think whether the problem lies within the library itself. So I immediately used another font I found online. To my surprise, this time the library was able to extract the correct font-family.

Having done the POC, and finished this small experiment, I'm a bit at lost. How come the library sometimes work and sometimes failed to work?

Later, I realized this is because the font I chose happen to be Roboto-Bold.

Findings and learnings

To dig deeper into this, I went through the OpenType font specification on Microsoft website. The relevant part for the documentation is the name table for a font, so I read through all of it and here's what I found and learnt:

  • A font file contains a name table that stores the information about the font, this might include license for the font, font family name, urls etc. The library we used use this specific table to extract information about a font.
  • A font family name (name ID 1 in the name table) for a font can only be shared among at most four basic styles of font faces -- they are regular, italic, bold, and bold italic. That is why the library returns the correct font family name for the Roboto-Bold I chose.
  • For styles that fall outside of these four styles, such as Semi-Bold or Black, they are treated as a separate font family and will have a separate font family name. For example, the font family name for Roboto-Black will be Roboto-Black rather than Roboto.
  • A Typographic Family Name (or Preferred Family) -- Name Id 16 in the name table has no constraints in the number of font faces, but is only present when the font face falls outside of the basic four styles.
  • In CSS, the font-family property is expected to be the shared font name. For example, if using Roboto-Black font in CSS, the font-family property should be Roboto rather than Roboto-Black
  • The difference between the font family name definition between CSS and the OpenType means that it is best to always use the Typographic Family Name if it presents rather than the Font Family name if the HTML/CSS is generated on the fly.

Solution

As we have a better understanding on the font family specification now, we need to find ways to retrieve it from the font table.

Fontkit was the library of choice and therefore I will only discuss how to get the correct font family name in fontkit. Unfortunately, fontkit does not have a direct way of accessing the Typographic Family Name. Through reading its source code, I found the way to go is to use an undocumented API -- font.getName('preferredName'), note that the preferredName is the same as Typographic Family Name, and it corresponds to name ID 16 in the font name table.

Bonus: Using fonttools to dump and inspect font tables

Out of curiosity, and to verify Microsoft's documentation I found a tool called fonttools which can be used to dump tables in a ttf/otf file to ttx format. This format is essentially an XML, you can open it with any text editor and inspect the content.

Installation of this tool can be done through pip: pip install --user fonttools

Once installation's finished, you can dump all the tables for a font file using ttx <fontfileName>. Note that this will result in an very large XML -- roughly 120k lines.

As we are only interested in the name table, the following command can be used to dump that table only: ttx -t name <fontFileName>

Here's an example of the dump:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<?xml version="1.0" encoding="UTF-8"?>
<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="4.26">

<name>
<namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
Copyright 2011 Google Inc. All Rights Reserved.
</namerecord>
<namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
Roboto Black
</namerecord>
<namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
Regular
</namerecord>
<namerecord nameID="3" platformID="3" platEncID="1" langID="0x409">
Roboto Black
</namerecord>
<namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
Roboto Black
</namerecord>
<namerecord nameID="5" platformID="3" platEncID="1" langID="0x409">
Version 2.137; 2017
</namerecord>
<namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
Roboto-Black
</namerecord>
<namerecord nameID="7" platformID="3" platEncID="1" langID="0x409">
Roboto is a trademark of Google.
</namerecord>
<namerecord nameID="9" platformID="3" platEncID="1" langID="0x409">
Google
</namerecord>
<namerecord nameID="11" platformID="3" platEncID="1" langID="0x409">
Google.com
</namerecord>
<namerecord nameID="12" platformID="3" platEncID="1" langID="0x409">
Christian Robertson
</namerecord>
<namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
Licensed under the Apache License, Version 2.0
</namerecord>
<namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
http://www.apache.org/licenses/LICENSE-2.0
</namerecord>
<namerecord nameID="16" platformID="3" platEncID="1" langID="0x409">
Roboto
</namerecord>
<namerecord nameID="17" platformID="3" platEncID="1" langID="0x409">
Black
</namerecord>
</name>

</ttFont>

References and further readings

https://docs.microsoft.com/en-us/typography/opentype/spec/name
https://github.com/foliojs/fontkit
https://github.com/fonttools/fonttools