Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions Tests/test_imagefont.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import shutil
import sys
from io import BytesIO
from pathlib import Path

import pytest
from packaging.version import parse as parse_version
Expand Down Expand Up @@ -76,8 +77,9 @@ def _render(font, layout_engine):
return img


def test_font_with_name(layout_engine):
_render(FONT_PATH, layout_engine)
@pytest.mark.parametrize("font", (FONT_PATH, Path(FONT_PATH)))
def test_font_with_name(layout_engine, font):
_render(font, layout_engine)


def test_font_with_filelike(layout_engine):
Expand Down
14 changes: 13 additions & 1 deletion src/PIL/ImageFont.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,15 @@
# See the README file for information on usage and redistribution.
#

from __future__ import annotations

import base64
import os
import sys
import warnings
from enum import IntEnum
from io import BytesIO
from pathlib import Path

from . import Image
from ._util import is_directory, is_path
Expand Down Expand Up @@ -185,7 +188,14 @@ def getlength(self, text, *args, **kwargs):
class FreeTypeFont:
"""FreeType font wrapper (requires _imagingft service)"""

def __init__(self, font=None, size=10, index=0, encoding="", layout_engine=None):
def __init__(
self,
font: bytes | str | Path | None = None,
size: float = 10,
index: int = 0,
encoding: str = "",
layout_engine: int = None,
) -> None:
# FIXME: use service provider instead

self.path = font
Expand Down Expand Up @@ -213,6 +223,8 @@ def load_from_bytes(f):
)

if is_path(font):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm looking at _util.is_path, it checks isinstance(f, (bytes, str, Path)), so bytes are also handled as paths, but next I see: else: load_from_bytes(font).

Do we really want consider bytes as paths? I believe this is obsolete in Python3

Copy link
Member Author

@radarhere radarhere Nov 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#258 was the original request for bytes as paths.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume load_from_bytes is unreachable here, so we need to remove it, or provide a way to reach it

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's reachable. The logic hasn't changed since load_from_bytes() was added in #3785.

If it is a path, then pass the path to the C layer. If we're on Windows and the path is not ASCII, then open a file pointer to the path and read the contents of the file pointer with load_from_bytes().
If it is not a path, then it is a file pointer, read the contents of the file pointer with load_from_bytes().

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I see. I believe there is misnaming here, since f is not a bytes, it's file object.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh right, I remember figuring that out after being confused in the past.
Should typing.BinaryIO be added to the type annotation then?

Copy link
Member Author

@radarhere radarhere Dec 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, I've pushed a commit. Testing in PyCharm, I find it also includes io.BytesIO

Copy link
Contributor

@nulano nulano Dec 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@radarhere I see you've used typing.IO. This causes a mypy error, see #7642.

Testing with both PyCharm and mypy, I find that a io.BytesIO value is accepted for a typing.BinaryIO type hint (which seems correct here), so I'm not sure I understand your last message.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I was concerned about strings being used, but looking now, that doesn't appear to be a problem.

if isinstance(font, Path):
font = str(font)
if sys.platform == "win32":
font_bytes_path = font if isinstance(font, bytes) else font.encode()
try:
Expand Down