Trying to create a lightweight fallback font with a substitute all glyphs feature.


For a web app I need a way to prevent that a browser falls back to another font if my web font doesn't include a character. It seems the only way to do this is to add another font to the fontstack which includes "all" possible characters .

There are already existing fallback fonts, but those are more debug helpers as they show the codepoint as number, therefore they are much to heavy (>2MB).

The fallback font for my use case should just show something like a box to signal a missing character.

My idea was to generate a simple font with only one glyph and apply a feature file which will substitute all glyphs with this one.

My script for fontforge:

import fontforge
import fontTools.feaLib.builder as feaLibBuilder
from fontTools.ttLib import TTFont

font_name = 'maeh.ttf'
font = fontforge.font()
glyph = font.createChar(33, "theone")
pen = glyph.glyphPen()

for i in range(34, 99):
    glyph = font.createChar(i)

font.cidConvertTo('Adobe', 'Identity', 0)  # doesn't make a difference


font = TTFont(font_name)
feaLibBuilder.addOpenTypeFeatures(font, 'fallback.fea')"fea_"+font_name)

My feature file:

languagesystem DFLT dflt;

#@all=[A-Z]   this works 

feature liga {
    sub @all by theone;
} liga;

For testing I target not 'all' glyphs, only the range from 33-99.

But the above results in a

KeyError: ('cid00037', 'SingleSubst[0]', 'Lookup[0]', 'LookupList')

with changing numbers for cid00037.

If I use the out commented class definition A-Z from the Feature file it works, so this approach doesn't seem to be completely wrong. But how to create a class this way which includes 'all' possible 'glyphs'.

Why can't fonttools find the glyphs if I specify the range in CID notation? Is there another way to crate a class for the OpenType feature file which includes all glyphs?

I use 'liga' in the feature file as this seems to be the most supported tag. Is there a more appropriated one.



  • attar
    attar Posts: 209
    Maybe Adobe Blank 2 would be a useful starting point.
  • Kent Lew
    Kent Lew Posts: 905
    If I’m understanding what you’re attempting to do correctly, then I don’t think that using a GSUB feature will work. For GSUB features to work, the target of the substitution needs to be an existing glyph. You can’t target a raw unmapped codepoint.

    Wouldn’t it make more sense to build a CMAP table that maps all possible codepoints to a single glyph outline?
  • Shouldn't a font do this by default?

  • You could use a cmap table format 13.0 to create a substitution, and save space.

  • Jeff Kellem
    While designing the 1403 Vintage Mono Pro specimen site, I modified Adobe Blank and Adobe Blank 2 to do exactly that for debugging, as @Adrien Tétar suggested in this thread.

    Since then, Ken Lunde created Adobe NotDef which does the same thing. Set it as the fallback font immediately after your font. Ken wrote an introduction post about it.

  • Tobias Reinhardt
    Kent Lew, thank for your hint about using the Adobe Blank 2. It came in just about 5 minutes after I asked the question. I couldn't open the Adobe Blank 2 neither with fontforge nor with ttx, but I was able to work with the number 1.

    At Jeff Kellem, thank you for hint, because that is just what I was looking for and what I would have tried to accomplish with the Adobe Blank. But my result wouldn't have been as compact as this one and this way I will save time of course. And your link to the Blog helped to understand why the Blank font is structured as it is.
  • Kent Lew
    Kent Lew Posts: 905
    For the record, it was Adrien Tétar who suggested the Adobe Blank fonts, not me.
  • Tobias Reinhardt
    For anybody going down the same road: For some reason I wasn't able to convert the .otf of the Adobe NotDef to woff or woff2 with fontforge. Also all the online tools to create the web font files like fontsquirrel failed.

    To create the woff file I used sfnt2woff from the woff-tools package. For the woff2 file I used
  • Georg Seifert
    I do not wonder that all tools the try to process the fonts (open it in fontforge converts to the internal representation and fontsquirrel tries to do things, too). The internal font structure is very different than the tools expectations...
  • Dave Crossland
    I believe the font squirrel converter uses font forge in places
  • Thomas Phinney
    I'd expect well-made, currently updated font tools to open these fonts, but I would not be surprised if the results of opening and regenerating or converting were... non-optimal, especially by being less compact.

    As Georg says, these fonts have very unusual, carefully optimized (sometimes by hand) internal structures.
  • Michael Rafailyk
    Michael Rafailyk Posts: 143
    edited January 2022
    Alternatively, JavaScript can be used for this task. 10-20 lines of code weigh much less than a fallback font. Here are two versions for input elements, and after some correction, it is possible to use it for contenteditable div elements as well. A both versions use the regular expression pattern [a-zA-Z\'\s] allowed only the English alphabet, quotesingle, and space character.

    Blank solution (prevent to type/copypaste unacceptable characters):

    NotDef solution (replace unacceptable characters with .notdef glyph):