Skip to content

Commit 20300a3

Browse files
committed
Added abstract BaseImageFont class
1 parent 0ef81c3 commit 20300a3

3 files changed

Lines changed: 51 additions & 58 deletions

File tree

src/PIL/ImageDraw.py

Lines changed: 21 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,15 @@
3636
from collections.abc import Sequence
3737
from typing import cast
3838

39-
from . import Image, ImageColor, ImageText
39+
from . import Image, ImageColor, ImageFont, ImageText
4040

4141
TYPE_CHECKING = False
4242
if TYPE_CHECKING:
4343
from collections.abc import Callable
4444
from types import ModuleType
4545
from typing import Any, AnyStr
4646

47-
from . import ImageDraw2, ImageFont
47+
from . import ImageDraw2
4848
from ._typing import Coords, _Ink
4949

5050
# experimental access to the outline API
@@ -59,9 +59,7 @@
5959

6060

6161
class ImageDraw:
62-
font: (
63-
ImageFont.ImageFont | ImageFont.FreeTypeFont | ImageFont.TransposedFont | None
64-
) = None
62+
font: ImageFont.BaseImageFont | None = None
6563

6664
def __init__(self, im: Image.Image, mode: str | None = None) -> None:
6765
"""
@@ -105,7 +103,7 @@ def __init__(self, im: Image.Image, mode: str | None = None) -> None:
105103

106104
def getfont(
107105
self,
108-
) -> ImageFont.ImageFont | ImageFont.FreeTypeFont | ImageFont.TransposedFont:
106+
) -> ImageFont.BaseImageFont:
109107
"""
110108
Get the current default font.
111109
@@ -130,9 +128,7 @@ def getfont(
130128
self.font = ImageFont.load_default()
131129
return self.font
132130

133-
def _getfont(
134-
self, font_size: float | None
135-
) -> ImageFont.ImageFont | ImageFont.FreeTypeFont | ImageFont.TransposedFont:
131+
def _getfont(self, font_size: float | None) -> ImageFont.BaseImageFont:
136132
if font_size is not None:
137133
from . import ImageFont
138134

@@ -540,12 +536,7 @@ def text(
540536
xy: tuple[float, float],
541537
text: AnyStr | ImageText.Text[AnyStr],
542538
fill: _Ink | None = None,
543-
font: (
544-
ImageFont.ImageFont
545-
| ImageFont.FreeTypeFont
546-
| ImageFont.TransposedFont
547-
| None
548-
) = None,
539+
font: ImageFont.BaseImageFont | None = None,
549540
anchor: str | None = None,
550541
spacing: float = 4,
551542
align: str = "left",
@@ -600,26 +591,26 @@ def draw_text(ink: int, stroke_width: float = 0) -> None:
600591
x = int(line.x)
601592
y = int(line.y)
602593
start = (math.modf(line.x)[0], math.modf(line.y)[0])
603-
try:
604-
mask, offset = image_text.font.getmask2( # type: ignore[union-attr,misc]
594+
if isinstance(image_text.font, ImageFont.FreeTypeFont):
595+
mask, offset = image_text.font.getmask2(
605596
line.text,
606597
mode,
607-
direction=direction,
608-
features=features,
609-
language=language,
610-
stroke_width=stroke_width,
598+
direction,
599+
features,
600+
language,
601+
stroke_width,
602+
line.anchor,
603+
ink,
604+
start,
611605
stroke_filled=True,
612-
anchor=line.anchor,
613-
ink=ink,
614-
start=start,
615606
*args,
616607
**kwargs,
617608
)
618609
x += offset[0]
619610
y += offset[1]
620-
except AttributeError:
611+
else:
621612
try:
622-
mask = image_text.font.getmask( # type: ignore[misc]
613+
mask = image_text.font.getmask(
623614
line.text,
624615
mode,
625616
direction,
@@ -664,12 +655,7 @@ def multiline_text(
664655
xy: tuple[float, float],
665656
text: AnyStr,
666657
fill: _Ink | None = None,
667-
font: (
668-
ImageFont.ImageFont
669-
| ImageFont.FreeTypeFont
670-
| ImageFont.TransposedFont
671-
| None
672-
) = None,
658+
font: ImageFont.BaseImageFont | None = None,
673659
anchor: str | None = None,
674660
spacing: float = 4,
675661
align: str = "left",
@@ -702,12 +688,7 @@ def multiline_text(
702688
def textlength(
703689
self,
704690
text: AnyStr,
705-
font: (
706-
ImageFont.ImageFont
707-
| ImageFont.FreeTypeFont
708-
| ImageFont.TransposedFont
709-
| None
710-
) = None,
691+
font: ImageFont.BaseImageFont | None = None,
711692
direction: str | None = None,
712693
features: list[str] | None = None,
713694
language: str | None = None,
@@ -734,12 +715,7 @@ def textbbox(
734715
self,
735716
xy: tuple[float, float],
736717
text: AnyStr,
737-
font: (
738-
ImageFont.ImageFont
739-
| ImageFont.FreeTypeFont
740-
| ImageFont.TransposedFont
741-
| None
742-
) = None,
718+
font: ImageFont.BaseImageFont | None = None,
743719
anchor: str | None = None,
744720
spacing: float = 4,
745721
align: str = "left",
@@ -767,12 +743,7 @@ def multiline_textbbox(
767743
self,
768744
xy: tuple[float, float],
769745
text: AnyStr,
770-
font: (
771-
ImageFont.ImageFont
772-
| ImageFont.FreeTypeFont
773-
| ImageFont.TransposedFont
774-
| None
775-
) = None,
746+
font: ImageFont.BaseImageFont | None = None,
776747
anchor: str | None = None,
777748
spacing: float = 4,
778749
align: str = "left",

src/PIL/ImageFont.py

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
from __future__ import annotations
2929

30+
import abc
3031
import base64
3132
import os
3233
import sys
@@ -91,7 +92,27 @@ def _string_length_check(text: str | bytes | bytearray) -> None:
9192
# --------------------------------------------------------------------
9293

9394

94-
class ImageFont:
95+
class BaseImageFont(abc.ABC):
96+
"""Used by ImageDraw and ImageText"""
97+
98+
@abc.abstractmethod
99+
def getbbox(
100+
self, text: str | bytes | bytearray, *args: Any, **kwargs: Any
101+
) -> tuple[float, float, float, float]:
102+
pass
103+
104+
@abc.abstractmethod
105+
def getmask(
106+
self, text: str | bytes, mode: str = "", *args: Any, **kwargs: Any
107+
) -> Image.core.ImagingCore:
108+
pass
109+
110+
@abc.abstractmethod
111+
def getlength(self, text: str | bytes, *args: Any, **kwargs: Any) -> float:
112+
pass
113+
114+
115+
class ImageFont(BaseImageFont):
95116
"""PIL font wrapper"""
96117

97118
font: ImagingFont
@@ -215,7 +236,7 @@ def getlength(
215236
# <b>truetype</b> factory function to create font objects.
216237

217238

218-
class FreeTypeFont:
239+
class FreeTypeFont(BaseImageFont):
219240
"""FreeType font wrapper (requires _imagingft service)"""
220241

221242
font: Font
@@ -723,7 +744,7 @@ def set_variation_by_axes(self, axes: list[float]) -> None:
723744
self.font.setvaraxes(axes)
724745

725746

726-
class TransposedFont:
747+
class TransposedFont(BaseImageFont):
727748
"""Wrapper for writing rotated or mirrored text"""
728749

729750
def __init__(

src/PIL/ImageText.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77
from . import ImageFont
88
from ._typing import _Ink
99

10-
Font = ImageFont.ImageFont | ImageFont.FreeTypeFont | ImageFont.TransposedFont
11-
1210

1311
class _Line(NamedTuple):
1412
x: float
@@ -27,7 +25,7 @@ def __init__(
2725
text: Text[AnyStr],
2826
width: int,
2927
height: int | None = None,
30-
font: Font | None = None,
28+
font: ImageFont.BaseImageFont | None = None,
3129
) -> None:
3230
self.text: Text[AnyStr] = text
3331
self.width = width
@@ -97,7 +95,7 @@ class Text(Generic[AnyStr]):
9795
def __init__(
9896
self,
9997
text: AnyStr,
100-
font: Font | None = None,
98+
font: ImageFont.BaseImageFont | None = None,
10199
mode: str = "RGB",
102100
spacing: float = 4,
103101
direction: str | None = None,
@@ -449,7 +447,10 @@ def _split(
449447
return parts
450448

451449
def _get_bbox(
452-
self, text: str | bytes, font: Font | None = None, anchor: str | None = None
450+
self,
451+
text: str | bytes,
452+
font: ImageFont.BaseImageFont | None = None,
453+
anchor: str | None = None,
453454
) -> tuple[float, float, float, float]:
454455
return (font or self.font).getbbox(
455456
text,

0 commit comments

Comments
 (0)