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 beRoboto-Black
rather thanRoboto
. - 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 usingRoboto-Black
font in CSS, the font-family property should beRoboto
rather thanRoboto-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 |
|
References and further readings
https://docs.microsoft.com/en-us/typography/opentype/spec/name
https://github.com/foliojs/fontkit
https://github.com/fonttools/fonttools