I recently completed a project which involved the Python Imaging Library (PIL) and its ability to load bitmap fonts, which must be presented in PIL format. I have a script, I have a PIL font, and I have an error…

$ grep ImageFont.load inkywhat-writer.py 
font = ImageFont.load("tom-thumb.pil")
$ ./inkywhat-writer.py 
Traceback (most recent call last):
File "./inkywhat-writer.py", line 14, in <module>
font = ImageFont.load("tom-thumb.pil")
File "/usr/lib/python2.7/dist-packages/PIL/ImageFont.py", line 249, in load
f._load_pilfont(filename)
File "/usr/lib/python2.7/dist-packages/PIL/ImageFont.py", line 81, in _load_pilfont
raise IOError("cannot find glyph data file")
IOError: cannot find glyph data file

Hmm. The PIL file is in the same directory as the script, so it should work. A quick DuckDuckGo of the error turns up little to help, beyond someone asking for help with the same error in 2003… with no response.

Turns out that a PIL file isn’t all you need: while it contains information about the font, it doesn’t actually contain the bitmap font itself – the “glyph data file” Python is complaining about not being able to find.

The key part of the PIL library itself is the ImageFont.py file:

 with open(filename, "rb") as fp:
        with open(filename, "rb") as fp:
            for ext in (".png", ".gif", ".pbm"):
                try:
                    fullname = os.path.splitext(filename)[0] + ext
                    image = Image.open(fullname)
                except Exception:
                    pass
                else:
                    if image and image.mode in ("1", "L"):
                        break
            else:
                raise IOError("cannot find glyph data file")

What this chunk of code is doing is taking the filename of the font you’re trying to load – in my case “tom-thumb.pil” – and looking for a bitmap graphic of the same name with a .png, .gif, or .pbm extension: Portable Network Graphic, Graphics Interchange Format, or Portable BitMap images. If it can’t find it, you get the “cannot find glyph data file” error – which is, perhaps, not the clearest for the actual error it’s encountering.

The fix, then, is to make sure that you have both the PIL file and the corresponding bitmap:

$ ls tom-thumb*
tom-thumb.pbm tom-thumb.pil

Providing you’ve got both files, they have the same filename apart from the extension, and the extension’s in lower-case characters only, you’re golden: the PIL library will load the font and your program continues as normal.

If you’ve got BDF or PCF format bitmap font and need to covert it to PIL and PBM for use with the PIL library, the following handy-dandy Python script from Peter Samuel Anttila is what you need. Save it, execute it, and any BDF or PCF file in the current directory will be converted to PIL and PBM.

#!/usr/bin/env python
# Author: Peter Samuel Anttila
# License: The Unlicense <http://unlicense.org, October 16 2018>
from PIL import BdfFontFile
from PIL import PcfFontFile
import os
import glob

font_file_paths = []
current_dir_path = os.path.dirname(os.path.abspath(__file__))
font_file_paths.extend(glob.glob(current_dir_path+"/*.bdf"))
font_file_paths.extend(glob.glob(current_dir_path+"/*.pcf"))

for font_file_path in font_file_paths:    
    try:
        with open(font_file_path,'rb') as fp:
            # despite what the syntax suggests, .save(font_file_path) won't 
            # overwrite your .bdf files, it just creates new .pil and .pdm
            # files in the same folder
            if font_file_path.lower().endswith('.bdf'):
                p = BdfFontFile.BdfFontFile(fp)
                p.save(font_file_path)
            elif font_file_path.lower().endswith('.pcf'):
                p = PcfFontFile.PcfFontFile(fp)
                p.save(font_file_path)
            else:
                # sanity catch-all
                print("Unrecognized extension.")
    except (SyntaxError,IOError) as err:
        print("File at '"+str(font_file_path)+"' could not be processed.")
        print("Error: " +str(err))