🌐 AI搜索 & 代理 主页
Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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: 3 additions & 3 deletions lib/matplotlib/font_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -1611,10 +1611,10 @@ def get_font(font_filepaths, hinting_factor=None):

Parameters
----------
font_filepaths : Iterable[str, Path, bytes], str, Path, bytes
font_filepaths : Iterable[str, bytes, os.PathLike], str, bytes, os.PathLike
Relative or absolute paths to the font files to be used.

If a single string, bytes, or `pathlib.Path`, then it will be treated
If a single string, bytes, or `os.PathLike`, then it will be treated
as a list with that entry only.

If more than one filepath is passed, then the returned FT2Font object
Expand All @@ -1626,7 +1626,7 @@ def get_font(font_filepaths, hinting_factor=None):
`.ft2font.FT2Font`

"""
if isinstance(font_filepaths, (str, Path, bytes)):
if isinstance(font_filepaths, (str, bytes, os.PathLike)):
paths = (_cached_realpath(font_filepaths),)
else:
paths = tuple(_cached_realpath(fname) for fname in font_filepaths)
Expand Down
14 changes: 7 additions & 7 deletions lib/matplotlib/font_manager.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def list_fonts(directory: str, extensions: Iterable[str]) -> list[str]: ...
def win32FontDirectory() -> str: ...
def _get_fontconfig_fonts() -> list[Path]: ...
def findSystemFonts(
fontpaths: Iterable[str | os.PathLike | Path] | None = ..., fontext: str = ...
fontpaths: Iterable[str | os.PathLike] | None = ..., fontext: str = ...
) -> list[str]: ...
@dataclass
class FontEntry:
Expand All @@ -50,7 +50,7 @@ class FontProperties:
weight: int | str | None = ...,
stretch: int | str | None = ...,
size: float | str | None = ...,
fname: str | os.PathLike | Path | None = ...,
fname: str | os.PathLike | None = ...,
math_fontfamily: str | None = ...,
) -> None: ...
def __hash__(self) -> int: ...
Expand All @@ -72,7 +72,7 @@ class FontProperties:
def set_weight(self, weight: int | str | None) -> None: ...
def set_stretch(self, stretch: int | str | None) -> None: ...
def set_size(self, size: float | str | None) -> None: ...
def set_file(self, file: str | os.PathLike | Path | None) -> None: ...
def set_file(self, file: str | os.PathLike | None) -> None: ...
def set_fontconfig_pattern(self, pattern: str) -> None: ...
def get_math_fontfamily(self) -> str: ...
def set_math_fontfamily(self, fontfamily: str | None) -> None: ...
Expand All @@ -83,8 +83,8 @@ class FontProperties:
set_slant = set_style
get_size_in_points = get_size

def json_dump(data: FontManager, filename: str | Path | os.PathLike) -> None: ...
def json_load(filename: str | Path | os.PathLike) -> FontManager: ...
def json_dump(data: FontManager, filename: str | os.PathLike) -> None: ...
def json_load(filename: str | os.PathLike) -> FontManager: ...

class FontManager:
__version__: str
Expand All @@ -93,7 +93,7 @@ class FontManager:
afmlist: list[FontEntry]
ttflist: list[FontEntry]
def __init__(self, size: float | None = ..., weight: str = ...) -> None: ...
def addfont(self, path: str | Path | os.PathLike) -> None: ...
def addfont(self, path: str | os.PathLike) -> None: ...
@property
def defaultFont(self) -> dict[str, str]: ...
def get_default_weight(self) -> str: ...
Expand All @@ -120,7 +120,7 @@ class FontManager:

def is_opentype_cff_font(filename: str) -> bool: ...
def get_font(
font_filepaths: Iterable[str | Path | bytes] | str | Path | bytes,
font_filepaths: Iterable[str | bytes | os.PathLike] | str | bytes | os.PathLike,
hinting_factor: int | None = ...,
) -> ft2font.FT2Font: ...

Expand Down
5 changes: 3 additions & 2 deletions lib/matplotlib/ft2font.pyi
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from enum import Enum, Flag
from os import PathLike
import sys
from typing import BinaryIO, Literal, NewType, TypeAlias, TypedDict, cast, final, overload
from typing_extensions import Buffer # < Py 3.12
Expand Down Expand Up @@ -194,7 +195,7 @@ class _SfntPcltDict(TypedDict):
class FT2Font(Buffer):
def __init__(
self,
filename: str | BinaryIO,
filename: str | bytes | PathLike | BinaryIO,
hinting_factor: int = ...,
*,
_fallback_list: list[FT2Font] | None = ...,
Expand Down Expand Up @@ -256,7 +257,7 @@ class FT2Font(Buffer):
@property
def family_name(self) -> str: ...
@property
def fname(self) -> str: ...
def fname(self) -> str | bytes: ...
@property
def height(self) -> int: ...
@property
Expand Down
33 changes: 27 additions & 6 deletions lib/matplotlib/tests/test_font_manager.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from io import BytesIO, StringIO
from io import BytesIO
import gc
import multiprocessing
import os
Expand Down Expand Up @@ -137,6 +137,32 @@ def test_find_noto():
fig.savefig(BytesIO(), format=fmt)


def test_find_valid():
class PathLikeClass:
def __init__(self, filename):
self.filename = filename

def __fspath__(self):
return self.filename

file_str = findfont('DejaVu Sans')
file_bytes = os.fsencode(file_str)

font = get_font(file_str)
assert font.fname == file_str
font = get_font(file_bytes)
assert font.fname == file_bytes
font = get_font(PathLikeClass(file_str))
assert font.fname == file_str
font = get_font(PathLikeClass(file_bytes))
assert font.fname == file_bytes

# Note, fallbacks are not currently accessible.
font = get_font([file_str, file_bytes,
PathLikeClass(file_str), PathLikeClass(file_bytes)])
assert font.fname == file_str


def test_find_invalid(tmp_path):

with pytest.raises(FileNotFoundError):
Expand All @@ -148,11 +174,6 @@ def test_find_invalid(tmp_path):
with pytest.raises(FileNotFoundError):
get_font(bytes(tmp_path / 'non-existent-font-name.ttf'))

# Not really public, but get_font doesn't expose non-filename constructor.
Copy link
Member

Choose a reason for hiding this comment

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

note to future reviewers, this was removed as redundent with other test

from matplotlib.ft2font import FT2Font
with pytest.raises(TypeError, match='font file or a binary-mode file'):
FT2Font(StringIO()) # type: ignore[arg-type]


@pytest.mark.skipif(sys.platform != 'linux' or not has_fclist,
reason='only Linux with fontconfig installed')
Expand Down
22 changes: 22 additions & 0 deletions lib/matplotlib/tests/test_ft2font.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import itertools
import io
import os
from pathlib import Path
from typing import cast

Expand Down Expand Up @@ -134,6 +135,27 @@ def test_ft2font_stix_bold_attrs():
assert font.bbox == (4, -355, 1185, 2095)


def test_ft2font_valid_args():
class PathLikeClass:
def __init__(self, filename):
self.filename = filename

def __fspath__(self):
return self.filename

file_str = fm.findfont('DejaVu Sans')
file_bytes = os.fsencode(file_str)

font = ft2font.FT2Font(file_str)
assert font.fname == file_str
font = ft2font.FT2Font(file_bytes)
assert font.fname == file_bytes
font = ft2font.FT2Font(PathLikeClass(file_str))
assert font.fname == file_str
font = ft2font.FT2Font(PathLikeClass(file_bytes))
assert font.fname == file_bytes


def test_ft2font_invalid_args(tmp_path):
# filename argument.
with pytest.raises(TypeError, match='to a font file or a binary-mode file object'):
Expand Down
13 changes: 8 additions & 5 deletions src/ft2font_wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,7 @@ close_file_callback(FT_Stream stream)
const char *PyFT2Font_init__doc__ = R"""(
Parameters
----------
filename : str or file-like
filename : str, bytes, os.PathLike, or io.BinaryIO
The source of the font data in a format (ttf or ttc) that FreeType can read.

hinting_factor : int, optional
Expand Down Expand Up @@ -488,7 +488,10 @@ PyFT2Font_init(py::object filename, long hinting_factor = 8,
open_args.flags = FT_OPEN_STREAM;
open_args.stream = &self->stream;

if (py::isinstance<py::bytes>(filename) || py::isinstance<py::str>(filename)) {
auto PathLike = py::module_::import("os").attr("PathLike");
if (py::isinstance<py::bytes>(filename) || py::isinstance<py::str>(filename) ||
py::isinstance(filename, PathLike))
{
self->py_file = py::module_::import("io").attr("open")(filename, "rb");
self->stream.close = &close_file_callback;
} else {
Expand All @@ -511,13 +514,13 @@ PyFT2Font_init(py::object filename, long hinting_factor = 8,
return self;
}

static py::str
static py::object
PyFT2Font_fname(PyFT2Font *self)
{
if (self->stream.close) { // Called passed a filename to the constructor.
if (self->stream.close) { // User passed a filename to the constructor.
return self->py_file.attr("name");
} else {
return py::cast<py::str>(self->py_file);
return self->py_file;
}
}

Expand Down
Loading