Merge pull request #8141 from radarhere/freetypefont_bytes

This commit is contained in:
Hugo van Kemenade 2024-06-25 06:11:20 -06:00 committed by GitHub
commit 563f45c355
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 53 additions and 40 deletions

View File

@ -1097,6 +1097,23 @@ def test_too_many_characters(font: ImageFont.FreeTypeFont) -> None:
imagefont.getmask("A" * 1_000_001) 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( @pytest.mark.parametrize(
"test_file", "test_file",
[ [

View File

@ -283,7 +283,7 @@ class FreeTypeFont:
return self.font.ascent, self.font.descent return self.font.ascent, self.font.descent
def getlength( def getlength(
self, text: str, mode="", direction=None, features=None, language=None self, text: str | bytes, mode="", direction=None, features=None, language=None
) -> float: ) -> float:
""" """
Returns length (in pixels with 1/64 precision) of given text when rendered Returns length (in pixels with 1/64 precision) of given text when rendered
@ -358,7 +358,7 @@ class FreeTypeFont:
def getbbox( def getbbox(
self, self,
text: str, text: str | bytes,
mode: str = "", mode: str = "",
direction: str | None = None, direction: str | None = None,
features: list[str] | None = None, features: list[str] | None = None,
@ -515,7 +515,7 @@ class FreeTypeFont:
def getmask2( def getmask2(
self, self,
text: str, text: str | bytes,
mode="", mode="",
direction=None, direction=None,
features=None, features=None,
@ -734,7 +734,7 @@ class TransposedFont:
return 0, 0, height, width return 0, 0, height, width
return 0, 0, width, height 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): 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" msg = "text length is undefined for text rotated by 90 or 270 degrees"
raise ValueError(msg) raise ValueError(msg)

View File

@ -27,7 +27,7 @@ class Font:
def glyphs(self) -> int: ... def glyphs(self) -> int: ...
def render( def render(
self, self,
string: str, string: str | bytes,
fill, fill,
mode=..., mode=...,
dir=..., dir=...,
@ -51,7 +51,7 @@ class Font:
/, /,
) -> tuple[tuple[int, int], tuple[int, int]]: ... ) -> tuple[tuple[int, int], tuple[int, int]]: ...
def getlength( def getlength(
self, string: str, mode=..., dir=..., features=..., lang=..., / self, string: str | bytes, mode=..., dir=..., features=..., lang=..., /
) -> float: ... ) -> float: ...
def getvarnames(self) -> list[bytes]: ... def getvarnames(self) -> list[bytes]: ...
def getvaraxes(self) -> list[_Axis] | None: ... def getvaraxes(self) -> list[_Axis] | None: ...

View File

@ -233,18 +233,6 @@ getfont(PyObject *self_, PyObject *args, PyObject *kw) {
return (PyObject *)self; 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 #ifdef HAVE_RAQM
static size_t static size_t
@ -266,28 +254,34 @@ text_layout_raqm(
goto failed; goto failed;
} }
Py_ssize_t size;
int set_text;
if (PyUnicode_Check(string)) { if (PyUnicode_Check(string)) {
Py_UCS4 *text = PyUnicode_AsUCS4Copy(string); Py_UCS4 *text = PyUnicode_AsUCS4Copy(string);
Py_ssize_t size = PyUnicode_GET_LENGTH(string); size = PyUnicode_GET_LENGTH(string);
if (!text || !size) { if (!text || !size) {
/* return 0 and clean up, no glyphs==no size, /* return 0 and clean up, no glyphs==no size,
and raqm fails with empty strings */ and raqm fails with empty strings */
goto failed; goto failed;
} }
int set_text = raqm_set_text(rq, text, size); set_text = raqm_set_text(rq, text, size);
PyMem_Free(text); PyMem_Free(text);
if (!set_text) { } else {
PyErr_SetString(PyExc_ValueError, "raqm_set_text() failed"); 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; goto failed;
} }
if (lang) { set_text = raqm_set_text_utf8(rq, buffer, size);
if (!raqm_set_language(rq, lang, start, size)) { }
PyErr_SetString(PyExc_ValueError, "raqm_set_language() failed"); if (!set_text) {
goto failed; PyErr_SetString(PyExc_ValueError, "raqm_set_text() failed");
} goto failed;
} }
} else { if (lang && !raqm_set_language(rq, lang, start, size)) {
PyErr_SetString(PyExc_TypeError, "expected string"); PyErr_SetString(PyExc_ValueError, "raqm_set_language() failed");
goto failed; goto failed;
} }
@ -405,13 +399,13 @@ text_layout_fallback(
GlyphInfo **glyph_info, GlyphInfo **glyph_info,
int mask, int mask,
int color) { int color) {
int error, load_flags; int error, load_flags, i;
char *buffer = NULL;
FT_ULong ch; FT_ULong ch;
Py_ssize_t count; Py_ssize_t count;
FT_GlyphSlot glyph; FT_GlyphSlot glyph;
FT_Bool kerning = FT_HAS_KERNING(self->face); FT_Bool kerning = FT_HAS_KERNING(self->face);
FT_UInt last_index = 0; FT_UInt last_index = 0;
int i;
if (features != Py_None || dir != NULL || lang != NULL) { if (features != Py_None || dir != NULL || lang != NULL) {
PyErr_SetString( PyErr_SetString(
@ -419,14 +413,11 @@ text_layout_fallback(
"setting text direction, language or font features is not supported " "setting text direction, language or font features is not supported "
"without libraqm"); "without libraqm");
} }
if (!PyUnicode_Check(string)) {
PyErr_SetString(PyExc_TypeError, "expected string");
return 0;
}
count = 0; if (PyUnicode_Check(string)) {
while (font_getchar(string, count, &ch)) { count = PyUnicode_GET_LENGTH(string);
count++; } else {
PyBytes_AsStringAndSize(string, &buffer, &count);
} }
if (count == 0) { if (count == 0) {
return 0; return 0;
@ -445,7 +436,12 @@ text_layout_fallback(
if (color) { if (color) {
load_flags |= FT_LOAD_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); (*glyph_info)[i].index = FT_Get_Char_Index(self->face, ch);
error = FT_Load_Glyph(self->face, (*glyph_info)[i].index, load_flags); error = FT_Load_Glyph(self->face, (*glyph_info)[i].index, load_flags);
if (error) { if (error) {