8000 Merge pull request #26403 from QuLogic/font-typing · matplotlib/matplotlib@ccad5ef · GitHub
[go: up one dir, main page]

Skip to content

Commit ccad5ef

Browse files
authored
Merge pull request #26403 from QuLogic/font-typing
Update type hints for font manager and extension
2 parents 4b1508f + 94146a1 commit ccad5ef

File tree

8 files changed

+139
-97
lines changed
  • tests
  • src
  • 8 files changed

    +139
    -97
    lines changed

    ci/mypy-stubtest-allowlist.txt

    Lines changed: 1 addition & 1 deletion
    Original file line numberDiff line numberDiff line change
    @@ -5,7 +5,7 @@ matplotlib.pylab.*
    55
    matplotlib._.*
    66
    matplotlib.rcsetup._listify_validator
    77
    matplotlib.rcsetup._validate_linestyle
    8-
    matplotlib.ft2font.*
    8+
    matplotlib.ft2font.Glyph
    99
    matplotlib.testing.jpl_units.*
    1010
    matplotlib.sphinxext.*
    1111

    lib/matplotlib/font_manager.py

    Lines changed: 4 additions & 1 deletion
    Original file line numberDiff line numberDiff line change
    @@ -40,6 +40,7 @@
    4040
    import subprocess
    4141
    import sys
    4242
    import threading
    43+
    from typing import Union
    4344

    4445
    import matplotlib as mpl
    4546
    from matplotlib import _api, _afm, cbook, ft2font
    @@ -315,7 +316,7 @@ def _fontentry_helper_repr_html(fontent):
    315316
    ('name', str, dataclasses.field(default='')),
    316317
    ('style', str, dataclasses.field(default='normal')),
    317318
    ('variant', str, dataclasses.field(default='normal')),
    318-
    ('weight', str, dataclasses.field(default='normal')),
    319+
    ('weight', Union[str, int], dataclasses.field(default='normal')),
    319320
    ('stretch', str, dataclasses.field(default='normal')),
    320321
    ('size', str, dataclasses.field(default='medium')),
    321322
    ],
    @@ -464,6 +465,8 @@ def afmFontProperty(fontpath, font):
    464465
    465466
    Parameters
    466467
    ----------
    468+
    fontpath : str
    469+
    The filename corresponding to *font*.
    467470
    font : AFM
    468471
    The AFM font file from which information will be extracted.
    469472

    lib/matplotlib/font_manager.pyi

    Lines changed: 20 additions & 17 deletions
    Original file line numberDiff line numberDiff line change
    @@ -20,19 +20,19 @@ X11FontDirectories: list[str]
    2020
    OSXFontDirectories: list[str]
    2121

    2222
    def get_fontext_synonyms(fontext: str) -> list[str]: ...
    23-
    def list_fonts(directory: str, extensions: Iterable[str]): ...
    23+
    def list_fonts(directory: str, extensions: Iterable[str]) -> list[str]: ...
    2424
    def win32FontDirectory() -> str: ...
    2525
    def _get_fontconfig_fonts() -> list[Path]: ...
    2626
    def findSystemFonts(
    27-
    fontpaths: Iterable[str] | None = ..., fontext: str = ...
    27+
    fontpaths: Iterable[str | os.PathLike | Path] | None = ..., fontext: str = ...
    2828
    ) -> list[str]: ...
    2929
    @dataclass
    3030
    class FontEntry:
    3131
    fname: str = ...
    3232
    name: str = ...
    3333
    style: str = ...
    3434
    variant: str = ...
    35-
    weight: str = ...
    35+
    weight: str | int = ...
    3636
    stretch: str = ...
    3737
    size: str = ...
    3838

    @@ -42,41 +42,44 @@ def afmFontProperty(fontpath: str, font: AFM) -> FontEntry: ...
    4242
    class FontProperties:
    4343
    def __init__(
    4444
    self,
    45-
    family: str | None = ...,
    45+
    family: str | Iterable[str] | None = ...,
    4646
    style: Literal["normal", "italic", "oblique"] | None = ...,
    4747
    variant: Literal["normal", "small-caps"] | None = ...,
    4848
    weight: int | str | None = ...,
    4949
    stretch: int | str | None = ...,
    5050
    size: float | str | None = ...,
    51-
    fname: str | None = ...,
    51+
    fname: str | os.PathLike | Path | None = ...,
    5252
    math_fontfamily: str | None = ...,
    5353
    ) -> None: ...
    5454
    def __hash__(self) -> int: ...
    5555
    def __eq__(self, other: object) -> bool: ...
    56-
    def get_family(self) -> str: ...
    56+
    def get_family(self) -> list[str]: ...
    5757
    def get_name(self) -> str: ...
    5858
    def get_style(self) -> Literal["normal", "italic", "oblique"]: ...
    5959
    def get_variant(self) -> Literal["normal", "small-caps"]: ...
    6060
    def get_weight(self) -> int | str: ...
    6161
    def get_stretch(self) -> int | str: ...
    6262
    def get_size(self) -> float: ...
    63-
    def get_file(self) -> str: ...
    63+
    def get_file(self) -> str | bytes | None: ...
    6464
    def get_fontconfig_pattern(self) -> dict[str, list[Any]]: ...
    65-
    def set_family(self, family: str | Iterable[str]) -> None: ...
    66-
    def set_style(self, style: Literal["normal", "italic", "oblique"]) -> None: ...
    67-
    def set_variant(self, variant: Literal["normal", "small-caps"]) -> None: ...
    68-
    def set_weight(self, weight: int | str) -> None: ...
    69-
    def set_stretch(self, stretch: int | str) -> None: ...
    70-
    def set_size(self, size: float | str) -> None: ...
    65+
    def set_family(self, family: str | Iterable[str] | None) -> None: ...
    66+
    def set_style(
    67+
    self, style: Literal["normal", "italic", "oblique"] | None
    68+
    ) -> None: ...
    69+
    def set_variant(self, variant: Literal["normal", "small-caps"] | None) -> None: ...
    70+
    def set_weight(self, weight: int | str | None) -> None: ...
    71+
    def set_stretch(self, stretch: int | str | None) -> None: ...
    72+
    def set_size(self, size: float | str | None) -> None: ...
    7173
    def set_file(self, file: str | os.PathLike | Path | None) -> None: ...
    7274
    def set_fontconfig_pattern(self, pattern: str) -> None: ...
    7375
    def get_math_fontfamily(self) -> str: ...
    7476
    def set_math_fontfamily(self, fontfamily: str | None) -> None: ...
    7577
    def copy(self) -> FontProperties: ...
    76-
    def set_name(self, family: str) -> None: ...
    77-
    def get_slant(self) -> Literal["normal", "italic", "oblique"]: ...
    78-
    def set_slant(self, style: Literal["normal", "italic", "oblique"]) -> None: ...
    79-
    def get_size_in_points(self) -> float: ...
    78+
    # Aliases
    79+
    set_name = set_family
    80+
    get_slant = get_style
    81+
    set_slant = set_style
    82+
    get_size_in_points = get_size
    8083

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

    lib/matplotlib/ft2font.pyi

    Lines changed: 84 additions & 53 deletions
    Original file line numberDiff line numberDiff line change
    @@ -1,8 +1,10 @@
    1-
    # This is generated from a compiled module, and as such is very generic
    2-
    # This could be more specific. Docstrings for this module are light
    1+
    from typing import BinaryIO, Literal
    32

    4-
    from typing import Any
    3+
    import numpy as np
    4+
    from numpy.typing import NDArray
    55

    6+
    __freetype_build_type__: str
    7+
    __freetype_version__: str
    68
    BOLD: int
    79
    EXTERNAL_STREAM: int
    810
    FAST_GLYPHS: int
    @@ -41,54 +43,83 @@ SFNT: int
    4143
    VERTICAL: int
    4244

    4345
    class FT2Font:
    44-
    ascender: Any
    45-
    bbox: Any
    46-
    descender: Any
    47-
    face_flags: Any
    48-
    family_name: Any
    49-
    fname: Any
    50-
    height: Any
    51-
    max_advance_height: Any
    52-
    max_advance_width: Any
    53-
    num_charmaps: Any
    54-
    num_faces: Any
    55-
    num_fixed_sizes: Any
    56-
    num_glyphs: Any
    57-
    postscript_name: Any
    58-
    scalable: Any
    59-
    style_flags: Any
    60-
    style_name: Any
    61-
    underline_position: Any
    62-
    underline_thickness: Any
    63-
    units_per_EM: Any
    64-
    def __init__(self, *args, **kwargs) -> None: ...
    65-
    def _get_fontmap(self, *args, **kwargs) -> Any: ...
    66-
    def clear(self, *args, **kwargs) -> Any: ...
    67-
    def draw_glyph_to_bitmap(self, *args, **kwargs) -> Any: ...
    68-
    def draw_glyphs_to_bitmap(self, *args, **kwargs) -> Any: ...
    69-
    def get_bitmap_offset(self, *args, **kwargs) -> Any: ...
    70-
    def get_char_index(self, *args, **kwargs) -> Any: ...
    71-
    def get_charmap(self, *args, **kwargs) -> Any: ...
    72-
    def get_descent(self, *args, **kwargs) -> Any: ...
    73-
    def get_glyph_name(self, *args, **kwargs) -> Any: ...
    74-
    def get_image(self, *args, **kwargs) -> Any: ...
    75-
    def get_kerning(self, *args, **kwargs) -> Any: ...
    76-
    def get_name_index(self, *args, **kwargs) -> Any: ...
    77-
    def get_num_glyphs(self, *args, **kwargs) -> Any: ...
    78-
    def get_path(self, *args, **kwargs) -> Any: ...
    79-
    def get_ps_font_info(self, *args, **kwargs) -> Any: ...
    80-
    def get_sfnt(self, *args, **kwargs) -> Any: ...
    81-
    def get_sfnt_table(self, *args, **kwargs) -> Any: ...
    82-
    def get_width_height(self, *args, **kwargs) -> Any: ...
    83-
    def get_xys(self, *args, **kwargs) -> Any: ...
    84-
    def load_char(self, *args, **kwargs) -> Any: ...
    85-
    def load_glyph(self, *args, **kwargs) -> Any: ...
    86-
    def select_charmap(self, *args, **kwargs) -> Any: ...
    87-
    def set_charmap(self, *args, **kwargs) -> Any: ...
    88-
    def set_size(self, *args, **kwargs) -> Any: ...
    89-
    def set_text(self, *args, **kwargs) -> Any: ...
    46+
    ascender: int
    47+
    bbox: tuple[int, int, int, int]
    48+
    descender: int
    49+
    face_flags: int
    50+
    family_name: str
    51+
    fname: str
    52+
    height: int
    53+
    max_advance_height: int
    54+
    max_advance_width: int
    55+
    num_charmaps: int
    56+
    num_faces: int
    57+
    num_fixed_sizes: int
    58+
    num_glyphs: int
    59+
    postscript_name: str
    60+
    scalable: bool
    61+
    style_flags: int
    62+
    style_name: str
    63+
    underline_position: int
    64+
    underline_thickness: int
    65+
    units_per_EM: int
    9066

    91-
    class FT2Image:
    92-
    def __init__(self, *args, **kwargs) -> None: ...
    93-
    def draw_rect(self, *args, **kwargs) -> Any: ...
    94-
    def draw_rect_filled(self, *args, **kwargs) -> Any: ...
    67+
    def __init__(
    68+
    self,
    69+
    filename: str | BinaryIO,
    70+
    hinting_factor: int = ...,
    71+
    *,
    72+
    _fallback_list: list[FT2Font] | None = ...,
    73+
    _kerning_factor: int = ...
    74+
    ) -> None: ...
    75+
    def _get_fontmap(self, string: str) -> dict[str, FT2Font]: ...
    76+
    def clear(self) -> None: ...
    77+
    def draw_glyph_to_bitmap(
    78+
    self, image: FT2Image, x: float, y: float, glyph: Glyph, antialiased: bool = ...
    79+
    ) -> None: ...
    80+
    def draw_glyphs_to_bitmap(self, antialiased: bool = ...) -> None: ...
    81+
    def get_bitmap_offset(self) -> tuple[int, int]: ...
    82+
    def get_char_index(self, codepoint: int) -> int: ...
    83+
    def get_charmap(self) -> dict[int, int]: ...
    84+
    def get_descent(self) -> int: ...
    85+
    def get_glyph_name(self, index: int) -> str: ...
    86+
    def get_image(self) -> NDArray[np.uint8]: ...
    87+
    def get_kerning(self, left: int, right: int, mode: int) -> int: ...
    88+
    def get_name_index(self, name: str) -> int: ...
    89+
    def get_num_glyphs(self) -> int: ...
    90+
    def get_path(self) -> tuple[NDArray[np.float64], NDArray[np.int8]]: ...
    91+
    def get_ps_font_info(
    92+
    self,
    93+
    ) -> tuple[str, str, str, str, str, int, int, int, int]: ...
    94+
    def get_sfnt(self) -> dict[tuple[int, int, int, int], bytes]: ...
    95+
    def get_sfnt_table(
    96+
    self, name: Literal["head", "maxp", "OS/2", "hhea", "vhea", "post", "pclt"]
    97+
    ) -> dict[str, tuple[int, int, int, int] | tuple[int, int] | int | bytes]: ...
    98+
    def get_width_height(self) -> tuple[int, int]: ...
    99+
    def get_xys(self, antialiased: bool = ...) -> NDArray[np.float64]: ...
    100+
    def load_char(self, charcode: int, flags: int = ...) -> Glyph: ...
    101+
    def load_glyph(self, glyphindex: int, flags: int = ...) -> Glyph: ...
    102+
    def select_charmap(self, i: int) -> None: ...
    103+
    def set_charmap(self, i: int) -> None: ...
    104+
    def set_size(self, ptsize: float, dpi: float) -> None: ...
    105+
    def set_text(
    106+
    self, string: str, angle: float = ..., flags: int = ...
    107+
    ) -> NDArray[np.float64]: ...
    108+
    109+
    class FT2Image: # TODO: When updating mypy>=1.4, subclass from Buffer.
    110+
    def __init__(self, width: float, height: float) -> None: ...
    111+
    def draw_rect(self, x0: float, y0: float, x1: float, y1: float) -> None: ...
    112+
    def draw_rect_filled(self, x0: float, y0: float, x1: float, y1: float) -> None: ...
    113+
    114+
    class Glyph:
    115+
    width: int
    116+
    height: int
    117+
    horiBearingX: int
    118+
    horiBearingY: int
    119+
    horiAdvance: int
    120+
    linearHoriAdvance: int
    121+
    vertBearingX: int
    122+
    vertBearingY: int
    123+
    vertAdvance: int
    124+
    125+
    def bbox(self) -> tuple[int, int, int, int]: ...

    lib/matplotlib/tests/test_font_manager.py

    Lines changed: 4 additions & 4 deletions
    Original file line numberDiff line numberDiff line change
    @@ -25,11 +25,11 @@ def test_font_priority():
    2525
    with rc_context(rc={
    2626
    'font.sans-serif':
    2727
    ['cmmi10', 'Bitstream Vera Sans']}):
    28-
    font = findfont(FontProperties(family=["sans-serif"]))
    29-
    assert Path(font).name == 'cmmi10.ttf'
    28+
    fontfile = findfont(FontProperties(family=["sans-serif"]))
    29+
    assert Path(fontfile).name == 'cmmi10.ttf'
    3030

    3131
    # Smoketest get_charmap, which isn't used internally anymore
    32-
    font = get_font(font)
    32+
    font = get_font(fontfile)
    3333
    cmap = font.get_charmap()
    3434
    assert len(cmap) == 131
    3535
    assert cmap[8729] == 30
    @@ -148,7 +148,7 @@ def test_find_invalid(tmpdir):
    148148
    # Not really public, but get_font doesn't expose non-filename constructor.
    149149
    from matplotlib.ft2font import FT2Font
    150150
    with pytest.raises(TypeError, match='font file or a binary-mode file'):
    151-
    FT2Font(StringIO())
    151+
    FT2Font(StringIO()) # type: ignore[arg-type]
    152152

    153153

    154154
    @pytest.mark.skipif(sys.platform != 'linux' or not has_fclist,

    lib/matplotlib/tests/test_ft2font.py

    Lines changed: 2 additions & 2 deletions
    Original file line numberDiff line numberDiff line change
    @@ -14,12 +14,12 @@ def test_fallback_errors():
    1414

    1515
    with pytest.raises(TypeError, match="Fallback list must be a list"):
    1616
    # failing to be a list will fail before the 0
    17-
    ft2font.FT2Font(file_name, _fallback_list=(0,))
    17+
    ft2font.FT2Font(file_name, _fallback_list=(0,)) # type: ignore[arg-type]
    1818

    1919
    with pytest.raises(
    2020
    TypeError, match="Fallback fonts must be FT2Font objects."
    2121
    ):
    22-
    ft2font.FT2Font(file_name, _fallback_list=[0])
    22+
    ft2font.FT2Font(file_name, _fallback_list=[0]) # type: ignore[list-item]
    2323

    2424

    2525
    def test_ft2font_positive_hinting_factor():

    lib/matplotlib/tests/test_mathtext.py

    Lines changed: 1 addition & 1 deletion
    Original file line numberDiff line numberDiff line change
    @@ -187,7 +187,7 @@
    187187
    font_tests: list[None | str] = []
    188188
    for fonts, chars in font_test_specs:
    189189
    if fonts is None:
    190-
    font_tests.extend([None] * chars) # type: ignore
    190+
    font_tests.extend([None] * chars)
    191191
    else:
    192192
    wrapper = ''.join([
    193193
    ' '.join(fonts),

    src/ft2font_wrapper.cpp

    Lines changed: 23 additions & 18 deletions
    Original file line numberDiff line numberDiff line change
    @@ -331,46 +331,51 @@ const char *PyFT2Font_init__doc__ =
    331331
    "Parameters\n"
    332332
    "----------\n"
    333333
    "filename : str or file-like\n"
    334-
    " The source of the font data in a format (ttf or ttc) that FreeType can read\n\n"
    334+
    " The source of the font data in a format (ttf or ttc) that FreeType can read\n"
    335+
    "\n"
    335336
    "hinting_factor : int, optional\n"
    336337
    " Must be positive. Used to scale the hinting in the x-direction\n"
    337338
    "_fallback_list : list of FT2Font, optional\n"
    338-
    " A list of FT2Font objects used to find missing glyphs.\n\n"
    339+
    " A list of FT2Font objects used to find missing glyphs.\n"
    340+
    "\n"
    339341
    " .. warning::\n"
    340-
    " This API is both private and provisional: do not use it directly\n\n"
    342+
    " This API is both private and provisional: do not use it directly\n"
    343+
    "\n"
    341344
    "_kerning_factor : int, optional\n"
    342-
    " Used to adjust the degree of kerning.\n\n"
    345+
    " Used to adjust the degree of kerning.\n"
    346+
    "\n"
    343347
    " .. warning::\n"
    344-
    " This API is private: do not use it directly\n\n"
    348+
    " This API is private: do not use it directly\n"
    349+
    "\n"
    345350
    "Attributes\n"
    346351
    "----------\n"
    347-
    "num_faces\n"
    352+
    "num_faces : int\n"
    348353
    " Number of faces in file.\n"
    349354
    "face_flags, style_flags : int\n"
    350355
    " Face and style flags; see the ft2font constants.\n"
    351-
    "num_glyphs\n"
    356+
    "num_glyphs : int\n"
    352357
    " Number of glyphs in the face.\n"
    353-
    "family_name, style_name\n"
    358+
    "family_name, style_name : str\n"
    354359
    " Face family and style name.\n"
    355-
    "num_fixed_sizes\n"
    360+
    "num_fixed_sizes : int\n"
    356361
    " Number of bitmap in the face.\n"
    357-
    "scalable\n"
    362+
    "scalable : bool\n"
    358363
    " Whether face is scalable; attributes after this one are only\n"
    359364
    " defined for scalable faces.\n"
    360-
    "bbox\n"
    365+
    "bbox : tuple[int, int, int, int]\n"
    361366
    " Face global bounding box (xmin, ymin, xmax, ymax).\n"
    362-
    "units_per_EM\n"
    367+
    "units_per_EM : int\n"
    363368
    " Number of font units covered by the EM.\n"
    364-
    "ascender, descender\n"
    369+
    "ascender, descender : int\n"
    365370
    " Ascender and descender in 26.6 units.\n"
    366-
    "height\n"
    371+
    "height : int\n"
    367372
    " Height in 26.6 units; used to compute a default line spacing\n"
    368373
    " (baseline-to-baseline distance).\n"
    369-
    "max_advance_width, max_advance_height\n"
    374+
    "max_advance_width, max_advance_height : int\n"
    370375
    " Maximum horizontal and vertical cursor advance for all glyphs.\n"
    371-
    "underline_position, underline_thickness\n"
    376+
    "underline_position, underline_thickness : int\n"
    372377
    " Vertical position and thickness of the underline bar.\n"
    373-
    "postscript_name\n"
    378+
    "postscript_name : str\n"
    374379
    " PostScript name of the font.\n";
    375380

    376381
    static int PyFT2Font_init(PyFT2Font *self, PyObject *args, PyObject *kwds)
    @@ -627,7 +632,7 @@ static PyObject *PyFT2Font_get_fontmap(PyFT2Font *self, PyObject *args, PyObject
    627632

    628633

    629634
    const char *PyFT2Font_set_text__doc__ =
    630-
    "set_text(self, string, angle, flags=32)\n"
    635+
    "set_text(self, string, angle=0.0, flags=32)\n"
    631636
    "--\n\n"
    632637
    "Set the text *string* and *angle*.\n"
    633638
    "*flags* can be a bitwise-or of the LOAD_XXX constants;\n"

    0 commit comments

    Comments
     (0)
    0