Skip to content
Merged
1 change: 1 addition & 0 deletions Tests/test_imagefont.py
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,7 @@ def loadable_font(
# catching syntax like errors
monkeypatch.setattr(sys, "platform", platform)
if platform == "linux":
monkeypatch.setenv("XDG_DATA_HOME", "/home/__pillow__/.local/share")
monkeypatch.setenv("XDG_DATA_DIRS", "/usr/share/:/usr/local/share/")

def fake_walker(path: str) -> list[tuple[str, list[str], list[str]]]:
Expand Down
8 changes: 8 additions & 0 deletions docs/releasenotes/10.4.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@ The ``hints`` parameter in :py:meth:`~PIL.ImageDraw.getdraw()` has been deprecat
API Changes
===========

ImageFont.truetype
^^^^^^^^^^^^^^^^^^

:py:func:`~PIL.ImageFont.truetype` now searches for fonts in the directory indicated
by the ``XDG_DATA_HOME`` envirnorment variable in addition to those specified in
``XDG_DATA_DIRS``. Additionally, the default for ``XDG_DATA_DIRS`` has been updated
to match the freedesktop spec.

TODO
^^^^

Expand Down
32 changes: 22 additions & 10 deletions src/PIL/ImageFont.py
Original file line number Diff line number Diff line change
Expand Up @@ -775,10 +775,15 @@ def truetype(

:param font: A filename or file-like object containing a TrueType font.
If the file is not found in this filename, the loader may also
search in other directories, such as the :file:`fonts/`
directory on Windows or :file:`/Library/Fonts/`,
:file:`/System/Library/Fonts/` and :file:`~/Library/Fonts/` on
macOS.
search in other directories, such as:

* The :file:`fonts/` directory on Windows,
* :file:`/Library/Fonts/`, :file:`/System/Library/Fonts/`
and :file:`~/Library/Fonts/` on macOS.
* :file:`~/.local/share/fonts`, :file:`/usr/local/share/fonts`,
and :file:`/usr/share/fonts` on Linux; or those specified by
the ``XDG_DATA_HOME`` and ``XDG_DATA_DIRS`` environment variables
for user-installed and system-wide fonts, respectively.

:param size: The requested size, in pixels.
:param index: Which font face to load (default is first available face).
Expand Down Expand Up @@ -837,12 +842,19 @@ def freetype(font: StrOrBytesPath | BinaryIO | None) -> FreeTypeFont:
if windir:
dirs.append(os.path.join(windir, "fonts"))
elif sys.platform in ("linux", "linux2"):
lindirs = os.environ.get("XDG_DATA_DIRS")
if not lindirs:
# According to the freedesktop spec, XDG_DATA_DIRS should
# default to /usr/share
lindirs = "/usr/share"
dirs += [os.path.join(lindir, "fonts") for lindir in lindirs.split(":")]
data_home = os.environ.get("XDG_DATA_HOME")
if not data_home:
# The freedesktop spec defines the following default directory for
# when XDG_DATA_HOME is unset or empty. This user-level directory
# takes precedence over system-level directories.
data_home = os.path.expanduser("~/.local/share")
dirs.append(os.path.join(data_home, "fonts"))

data_dirs = os.environ.get("XDG_DATA_DIRS")
if not data_dirs:
# Similarly, defaults are defined for the system-level directories
data_dirs = "/usr/local/share:/usr/share"
dirs += [os.path.join(ddir, "fonts") for ddir in data_dirs.split(":")]
elif sys.platform == "darwin":
dirs += [
"/Library/Fonts",
Expand Down