Skip to content

FontManager.resolve_font silently returns the default face for any .bdf #517

Description

@ChuckBuilds

Summary

FontManager.resolve_font() cannot load a bitmap (.bdf) font at anything other than its own pixel size, and when it fails it returns the default face without signalling it. Callers cannot tell a successful load from a silent fallback, so a plugin offering a .bdf in its font picker appears to ignore the setting.

Why it is invisible

A .bdf exists at exactly one pixel size. FreeType raises OSError: invalid pixel size for any other, so ImageFont.truetype(path, size) fails whenever the requested size differs from the file's PIXEL_SIZE. resolve_font then falls back — and in current Pillow the default face is itself a FreeTypeFont, so the returned type gives no hint that anything went wrong.

common_fonts carries five_by_sevenassets/fonts/5x7.bdf, which is native 7px. Any caller asking for it at the usual 8 gets the default face back and no error.

Impact seen in plugins

This has now surfaced in five plugins in ledmatrix-plugins, each with its own copy of the loader:

Plugin Symptom
clock-simple 5x7.bdf and 4x6.bdf rendered byte-identical to the default
countdown five_by_seven fell back even though the plugin had its own working loader — the catalog has the family, so the plugin trusted the core's answer
news both .bdf faces fell back
tide-display 4x6.bdf worked and 5x7.bdf did not, purely because the default font_size of 6 happened to match one of them
mqtt-notifications, youtube-stats same loader, same behaviour

The countdown case is the one that argues for fixing it here: the plugin only consulted its own loader when the catalog missed a family, so a family the core claims-and-then-degrades is worse than one it does not know about.

Suggested fix

In the loader, on OSError, read PIXEL_SIZE from the .bdf header and retry at that size before giving up:

try:
    return ImageFont.truetype(path, size_px)
except OSError:
    native = _bdf_pixel_size(path)
    if native is None or native == size_px:
        raise
    return ImageFont.truetype(path, native)

That is the fix applied per-plugin in ChuckBuilds/ledmatrix-plugins#362, #369 and #370; doing it in FontManager would let those copies drop it.

Failing that, having resolve_font signal the fallback — a return value, or a warning naming the family — would at least make it diagnosable rather than looking like a plugin bug.

Environment

Pillow 11.3.0. The .bdf size constraint is a FreeType property rather than a Pillow-version one, so I would expect this to reproduce broadly, but I have only verified it here.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions