Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added byte support to FreeTypeFont #8141

Merged
merged 2 commits into from
Jun 25, 2024
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
17 changes: 17 additions & 0 deletions Tests/test_imagefont.py
Original file line number Diff line number Diff line change
Expand Up @@ -1096,6 +1096,23 @@ def test_too_many_characters(font: ImageFont.FreeTypeFont) -> None:
imagefont.getmask("A" * 1_000_001)


def test_bytes(font: ImageFont.FreeTypeFont) -> None:
assert font.getlength(b"test") == font.getlength("test")

assert font.getbbox(b"test") == font.getbbox("test")

assert_image_equal(
Image.Image()._new(font.getmask(b"test")),
Image.Image()._new(font.getmask("test")),
)

assert_image_equal(
Image.Image()._new(font.getmask2(b"test")[0]),
Image.Image()._new(font.getmask2("test")[0]),
)
assert font.getmask2(b"test")[1] == font.getmask2("test")[1]


@pytest.mark.parametrize(
"test_file",
[
Expand Down
8 changes: 4 additions & 4 deletions src/PIL/ImageFont.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ def getmetrics(self) -> tuple[int, int]:
return self.font.ascent, self.font.descent

def getlength(
self, text: str, mode="", direction=None, features=None, language=None
self, text: str | bytes, mode="", direction=None, features=None, language=None
) -> float:
"""
Returns length (in pixels with 1/64 precision) of given text when rendered
Expand Down Expand Up @@ -354,7 +354,7 @@ def getlength(

def getbbox(
self,
text: str,
text: str | bytes,
mode: str = "",
direction: str | None = None,
features: list[str] | None = None,
Expand Down Expand Up @@ -511,7 +511,7 @@ def getmask(

def getmask2(
self,
text: str,
text: str | bytes,
mode="",
direction=None,
features=None,
Expand Down Expand Up @@ -730,7 +730,7 @@ def getbbox(self, text, *args, **kwargs):
return 0, 0, height, width
return 0, 0, width, height

def getlength(self, text: str, *args, **kwargs) -> float:
def getlength(self, text: str | bytes, *args, **kwargs) -> float:
if self.orientation in (Image.Transpose.ROTATE_90, Image.Transpose.ROTATE_270):
msg = "text length is undefined for text rotated by 90 or 270 degrees"
raise ValueError(msg)
Expand Down
4 changes: 2 additions & 2 deletions src/PIL/_imagingft.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class Font:
def glyphs(self) -> int: ...
def render(
self,
string: str,
string: str | bytes,
fill,
mode=...,
dir=...,
Expand All @@ -51,7 +51,7 @@ class Font:
/,
) -> tuple[tuple[int, int], tuple[int, int]]: ...
def getlength(
self, string: str, mode=..., dir=..., features=..., lang=..., /
self, string: str | bytes, mode=..., dir=..., features=..., lang=..., /
) -> float: ...
def getvarnames(self) -> list[bytes]: ...
def getvaraxes(self) -> list[_Axis] | None: ...
Expand Down
64 changes: 30 additions & 34 deletions src/_imagingft.c
Original file line number Diff line number Diff line change
Expand Up @@ -233,18 +233,6 @@
return (PyObject *)self;
}

static int
font_getchar(PyObject *string, int index, FT_ULong *char_out) {
if (PyUnicode_Check(string)) {
if (index >= PyUnicode_GET_LENGTH(string)) {
return 0;
}
*char_out = PyUnicode_READ_CHAR(string, index);
return 1;
}
return 0;
}

#ifdef HAVE_RAQM

static size_t
Expand All @@ -266,28 +254,34 @@
goto failed;
}

Py_ssize_t size;
int set_text;
if (PyUnicode_Check(string)) {
Py_UCS4 *text = PyUnicode_AsUCS4Copy(string);
Py_ssize_t size = PyUnicode_GET_LENGTH(string);
size = PyUnicode_GET_LENGTH(string);
if (!text || !size) {
/* return 0 and clean up, no glyphs==no size,
and raqm fails with empty strings */
goto failed;
}
int set_text = raqm_set_text(rq, text, size);
set_text = raqm_set_text(rq, text, size);
PyMem_Free(text);
if (!set_text) {
PyErr_SetString(PyExc_ValueError, "raqm_set_text() failed");
} else {
char *buffer;
PyBytes_AsStringAndSize(string, &buffer, &size);
if (!buffer || !size) {
/* return 0 and clean up, no glyphs==no size,
and raqm fails with empty strings */
goto failed;
}
if (lang) {
if (!raqm_set_language(rq, lang, start, size)) {
PyErr_SetString(PyExc_ValueError, "raqm_set_language() failed");
goto failed;
}
}
} else {
PyErr_SetString(PyExc_TypeError, "expected string");
set_text = raqm_set_text_utf8(rq, buffer, size);
}
if (!set_text) {
PyErr_SetString(PyExc_ValueError, "raqm_set_text() failed");
goto failed;

Check warning on line 281 in src/_imagingft.c

View check run for this annotation

Codecov / codecov/patch

src/_imagingft.c#L280-L281

Added lines #L280 - L281 were not covered by tests
}
if (lang && !raqm_set_language(rq, lang, start, size)) {
PyErr_SetString(PyExc_ValueError, "raqm_set_language() failed");

Check warning on line 284 in src/_imagingft.c

View check run for this annotation

Codecov / codecov/patch

src/_imagingft.c#L284

Added line #L284 was not covered by tests
goto failed;
}

Expand Down Expand Up @@ -405,28 +399,25 @@
GlyphInfo **glyph_info,
int mask,
int color) {
int error, load_flags;
int error, load_flags, i;
char *buffer = NULL;
FT_ULong ch;
Py_ssize_t count;
FT_GlyphSlot glyph;
FT_Bool kerning = FT_HAS_KERNING(self->face);
FT_UInt last_index = 0;
int i;

if (features != Py_None || dir != NULL || lang != NULL) {
PyErr_SetString(
PyExc_KeyError,
"setting text direction, language or font features is not supported "
"without libraqm");
}
if (!PyUnicode_Check(string)) {
PyErr_SetString(PyExc_TypeError, "expected string");
return 0;
}

count = 0;
while (font_getchar(string, count, &ch)) {
count++;
if (PyUnicode_Check(string)) {
count = PyUnicode_GET_LENGTH(string);
} else {
PyBytes_AsStringAndSize(string, &buffer, &count);
}
if (count == 0) {
return 0;
Expand All @@ -445,7 +436,12 @@
if (color) {
load_flags |= FT_LOAD_COLOR;
}
for (i = 0; font_getchar(string, i, &ch); i++) {
for (i = 0; i < count; i++) {
if (buffer) {
ch = buffer[i];
} else {
ch = PyUnicode_READ_CHAR(string, i);
}
(*glyph_info)[i].index = FT_Get_Char_Index(self->face, ch);
error = FT_Load_Glyph(self->face, (*glyph_info)[i].index, load_flags);
if (error) {
Expand Down
Loading