mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-01-26 09:14:27 +03:00
added ability to set language for text rendering
This commit is contained in:
parent
906917b748
commit
8624efd283
BIN
Tests/images/test_language.png
Normal file
BIN
Tests/images/test_language.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 837 B |
|
@ -130,3 +130,16 @@ class TestImagecomplextext(PillowTestCase):
|
|||
target_img = Image.open(target)
|
||||
|
||||
self.assert_image_similar(im, target_img, .5)
|
||||
|
||||
def test_language(self):
|
||||
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
|
||||
|
||||
im = Image.new(mode='RGB', size=(300, 100))
|
||||
draw = ImageDraw.Draw(im)
|
||||
draw.text((0, 0), 'абвг', font=ttf, fill=500,
|
||||
language='sr')
|
||||
|
||||
target = 'Tests/images/test_language.png'
|
||||
target_img = Image.open(target)
|
||||
|
||||
self.assert_image_similar(im, target_img, .5)
|
|
@ -255,7 +255,7 @@ Methods
|
|||
|
||||
Draw a shape.
|
||||
|
||||
.. py:method:: PIL.ImageDraw.ImageDraw.text(xy, text, fill=None, font=None, anchor=None, spacing=0, align="left", direction=None, features=None)
|
||||
.. py:method:: PIL.ImageDraw.ImageDraw.text(xy, text, fill=None, font=None, anchor=None, spacing=0, align="left", direction=None, language=None, features=None)
|
||||
|
||||
Draws the string at the given position.
|
||||
|
||||
|
@ -274,6 +274,16 @@ Methods
|
|||
|
||||
.. versionadded:: 4.2.0
|
||||
|
||||
:param language: Language of the text. Different languages may use
|
||||
different glyph shapes or ligatures. This parameter tells
|
||||
the font which language the text is in, and to apply the
|
||||
correct substitutions as appropriate, if available.
|
||||
It should be a `BCP47 language code
|
||||
<https://www.w3.org/International/articles/language-tags/>`
|
||||
Requires libraqm.
|
||||
|
||||
.. versionadded:: 6.0.0
|
||||
|
||||
:param features: A list of OpenType font features to be used during text
|
||||
layout. This is usually used to turn on optional
|
||||
font features that are not enabled by default,
|
||||
|
@ -316,7 +326,7 @@ Methods
|
|||
|
||||
.. versionadded:: 4.2.0
|
||||
|
||||
.. py:method:: PIL.ImageDraw.ImageDraw.textsize(text, font=None, spacing=4, direction=None, features=None)
|
||||
.. py:method:: PIL.ImageDraw.ImageDraw.textsize(text, font=None, spacing=4, direction=None, language=None, features=None)
|
||||
|
||||
Return the size of the given string, in pixels.
|
||||
|
||||
|
@ -330,7 +340,15 @@ Methods
|
|||
Requires libraqm.
|
||||
|
||||
.. versionadded:: 4.2.0
|
||||
:param language: Language of the text. Different languages may use
|
||||
different glyph shapes or ligatures. This parameter tells
|
||||
the font which language the text is in, and to apply the
|
||||
correct substitutions as appropriate, if available.
|
||||
It should be a `BCP47 language code
|
||||
<https://www.w3.org/International/articles/language-tags/>`
|
||||
Requires libraqm.
|
||||
|
||||
.. versionadded:: 6.0.0
|
||||
:param features: A list of OpenType font features to be used during text
|
||||
layout. This is usually used to turn on optional
|
||||
font features that are not enabled by default,
|
||||
|
|
|
@ -47,11 +47,11 @@ Functions
|
|||
Methods
|
||||
-------
|
||||
|
||||
.. py:method:: PIL.ImageFont.ImageFont.getsize(text)
|
||||
.. py:method:: PIL.ImageFont.ImageFont.getsize(text, direction=None, language=None, features=[])
|
||||
|
||||
:return: (width, height)
|
||||
|
||||
.. py:method:: PIL.ImageFont.ImageFont.getmask(text, mode='', direction=None, features=[])
|
||||
.. py:method:: PIL.ImageFont.ImageFont.getmask(text, mode='', direction=None, language=None, features=[])
|
||||
|
||||
Create a bitmap for the text.
|
||||
|
||||
|
@ -72,6 +72,16 @@ Methods
|
|||
|
||||
.. versionadded:: 4.2.0
|
||||
|
||||
:param language: Language of the text. Different languages may use
|
||||
different glyph shapes or ligatures. This parameter tells
|
||||
the font which language the text is in, and to apply the
|
||||
correct substitutions as appropriate, if available.
|
||||
It should be a `BCP47 language code
|
||||
<https://www.w3.org/International/articles/language-tags/>`
|
||||
Requires libraqm.
|
||||
|
||||
.. versionadded:: 6.0.0
|
||||
|
||||
:param features: A list of OpenType font features to be used during text
|
||||
layout. This is usually used to turn on optional
|
||||
font features that are not enabled by default,
|
||||
|
|
|
@ -158,17 +158,17 @@ class FreeTypeFont(object):
|
|||
def getmetrics(self):
|
||||
return self.font.ascent, self.font.descent
|
||||
|
||||
def getsize(self, text, direction=None, features=None):
|
||||
size, offset = self.font.getsize(text, direction, features)
|
||||
def getsize(self, text, direction=None, language=None, features=None):
|
||||
size, offset = self.font.getsize(text, direction, language, features)
|
||||
return (size[0] + offset[0], size[1] + offset[1])
|
||||
|
||||
def getsize_multiline(self, text, direction=None,
|
||||
def getsize_multiline(self, text, direction=None, language=None,
|
||||
spacing=4, features=None):
|
||||
max_width = 0
|
||||
lines = self._multiline_split(text)
|
||||
line_spacing = self.getsize('A')[1] + spacing
|
||||
for line in lines:
|
||||
line_width, line_height = self.getsize(line, direction, features)
|
||||
line_width, line_height = self.getsize(line, direction, language, features)
|
||||
max_width = max(max_width, line_width)
|
||||
|
||||
return max_width, len(lines)*line_spacing - spacing
|
||||
|
@ -181,10 +181,10 @@ class FreeTypeFont(object):
|
|||
features=features)[0]
|
||||
|
||||
def getmask2(self, text, mode="", fill=Image.core.fill, direction=None,
|
||||
features=None, *args, **kwargs):
|
||||
size, offset = self.font.getsize(text, direction, features)
|
||||
language=None, features=None, *args, **kwargs):
|
||||
size, offset = self.font.getsize(text, direction, language, features)
|
||||
im = fill("L", size, 0)
|
||||
self.font.render(text, im.id, mode == "1", direction, features)
|
||||
self.font.render(text, im.id, mode == "1", direction, language, features)
|
||||
return im, offset
|
||||
|
||||
def font_variant(self, font=None, size=None, index=None, encoding=None,
|
||||
|
|
|
@ -87,6 +87,10 @@ typedef bool (*t_raqm_set_text_utf8) (raqm_t *rq,
|
|||
size_t len);
|
||||
typedef bool (*t_raqm_set_par_direction) (raqm_t *rq,
|
||||
raqm_direction_t dir);
|
||||
typedef bool (*t_raqm_set_language) (raqm_t *rq,
|
||||
const char *lang,
|
||||
size_t start,
|
||||
size_t len);
|
||||
typedef bool (*t_raqm_add_font_feature) (raqm_t *rq,
|
||||
const char *feature,
|
||||
int len);
|
||||
|
@ -106,6 +110,7 @@ typedef struct {
|
|||
t_raqm_set_text set_text;
|
||||
t_raqm_set_text_utf8 set_text_utf8;
|
||||
t_raqm_set_par_direction set_par_direction;
|
||||
t_raqm_set_language set_language;
|
||||
t_raqm_add_font_feature add_font_feature;
|
||||
t_raqm_set_freetype_face set_freetype_face;
|
||||
t_raqm_layout layout;
|
||||
|
@ -160,6 +165,7 @@ setraqm(void)
|
|||
p_raqm.set_text = (t_raqm_set_text)dlsym(p_raqm.raqm, "raqm_set_text");
|
||||
p_raqm.set_text_utf8 = (t_raqm_set_text_utf8)dlsym(p_raqm.raqm, "raqm_set_text_utf8");
|
||||
p_raqm.set_par_direction = (t_raqm_set_par_direction)dlsym(p_raqm.raqm, "raqm_set_par_direction");
|
||||
p_raqm.set_language = (t_raqm_set_language)dlsym(p_raqm.raqm, "raqm_set_language");
|
||||
p_raqm.add_font_feature = (t_raqm_add_font_feature)dlsym(p_raqm.raqm, "raqm_add_font_feature");
|
||||
p_raqm.set_freetype_face = (t_raqm_set_freetype_face)dlsym(p_raqm.raqm, "raqm_set_freetype_face");
|
||||
p_raqm.layout = (t_raqm_layout)dlsym(p_raqm.raqm, "raqm_layout");
|
||||
|
@ -176,6 +182,7 @@ setraqm(void)
|
|||
p_raqm.set_text &&
|
||||
p_raqm.set_text_utf8 &&
|
||||
p_raqm.set_par_direction &&
|
||||
p_raqm.set_language &&
|
||||
p_raqm.add_font_feature &&
|
||||
p_raqm.set_freetype_face &&
|
||||
p_raqm.layout &&
|
||||
|
@ -190,6 +197,7 @@ setraqm(void)
|
|||
p_raqm.set_text = (t_raqm_set_text)GetProcAddress(p_raqm.raqm, "raqm_set_text");
|
||||
p_raqm.set_text_utf8 = (t_raqm_set_text_utf8)GetProcAddress(p_raqm.raqm, "raqm_set_text_utf8");
|
||||
p_raqm.set_par_direction = (t_raqm_set_par_direction)GetProcAddress(p_raqm.raqm, "raqm_set_par_direction");
|
||||
p_raqm.set_language = (t_raqm_set_language)GetProcAddress(p_raqm.raqm, "raqm_set_language");
|
||||
p_raqm.add_font_feature = (t_raqm_add_font_feature)GetProcAddress(p_raqm.raqm, "raqm_add_font_feature");
|
||||
p_raqm.set_freetype_face = (t_raqm_set_freetype_face)GetProcAddress(p_raqm.raqm, "raqm_set_freetype_face");
|
||||
p_raqm.layout = (t_raqm_layout)GetProcAddress(p_raqm.raqm, "raqm_layout");
|
||||
|
@ -205,6 +213,7 @@ setraqm(void)
|
|||
p_raqm.set_text &&
|
||||
p_raqm.set_text_utf8 &&
|
||||
p_raqm.set_par_direction &&
|
||||
p_raqm.set_language &&
|
||||
p_raqm.add_font_feature &&
|
||||
p_raqm.set_freetype_face &&
|
||||
p_raqm.layout &&
|
||||
|
@ -332,7 +341,7 @@ font_getchar(PyObject* string, int index, FT_ULong* char_out)
|
|||
}
|
||||
|
||||
static size_t
|
||||
text_layout_raqm(PyObject* string, FontObject* self, const char* dir,
|
||||
text_layout_raqm(PyObject* string, FontObject* self, const char* dir, const char* lang,
|
||||
PyObject *features ,GlyphInfo **glyph_info, int mask)
|
||||
{
|
||||
int i = 0;
|
||||
|
@ -341,6 +350,7 @@ text_layout_raqm(PyObject* string, FontObject* self, const char* dir,
|
|||
raqm_glyph_t *glyphs = NULL;
|
||||
raqm_glyph_t_01 *glyphs_01 = NULL;
|
||||
raqm_direction_t direction;
|
||||
size_t start = 0;
|
||||
|
||||
rq = (*p_raqm.create)();
|
||||
if (rq == NULL) {
|
||||
|
@ -360,6 +370,13 @@ text_layout_raqm(PyObject* string, FontObject* self, const char* dir,
|
|||
PyErr_SetString(PyExc_ValueError, "raqm_set_text() failed");
|
||||
goto failed;
|
||||
}
|
||||
if (lang) {
|
||||
if (!(*p_raqm.set_language)(rq, lang, start, size)) {
|
||||
PyErr_SetString(PyExc_ValueError, "raqm_set_language() failed");
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#if PY_VERSION_HEX < 0x03000000
|
||||
else if (PyString_Check(string)) {
|
||||
|
@ -498,7 +515,7 @@ failed:
|
|||
}
|
||||
|
||||
static size_t
|
||||
text_layout_fallback(PyObject* string, FontObject* self, const char* dir,
|
||||
text_layout_fallback(PyObject* string, FontObject* self, const char* dir, const char* lang,
|
||||
PyObject *features ,GlyphInfo **glyph_info, int mask)
|
||||
{
|
||||
int error, load_flags;
|
||||
|
@ -509,8 +526,8 @@ text_layout_fallback(PyObject* string, FontObject* self, const char* dir,
|
|||
FT_UInt last_index = 0;
|
||||
int i;
|
||||
|
||||
if (features != Py_None || dir != NULL) {
|
||||
PyErr_SetString(PyExc_KeyError, "setting text direction or font features is not supported without libraqm");
|
||||
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 PY_VERSION_HEX >= 0x03000000
|
||||
if (!PyUnicode_Check(string)) {
|
||||
|
@ -564,15 +581,15 @@ text_layout_fallback(PyObject* string, FontObject* self, const char* dir,
|
|||
}
|
||||
|
||||
static size_t
|
||||
text_layout(PyObject* string, FontObject* self, const char* dir,
|
||||
text_layout(PyObject* string, FontObject* self, const char* dir, const char* lang,
|
||||
PyObject *features, GlyphInfo **glyph_info, int mask)
|
||||
{
|
||||
size_t count;
|
||||
|
||||
if (p_raqm.raqm && self->layout_engine == LAYOUT_RAQM) {
|
||||
count = text_layout_raqm(string, self, dir, features, glyph_info, mask);
|
||||
count = text_layout_raqm(string, self, dir, lang, features, glyph_info, mask);
|
||||
} else {
|
||||
count = text_layout_fallback(string, self, dir, features, glyph_info, mask);
|
||||
count = text_layout_fallback(string, self, dir, lang, features, glyph_info, mask);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
@ -584,6 +601,7 @@ font_getsize(FontObject* self, PyObject* args)
|
|||
FT_Face face;
|
||||
int xoffset, yoffset;
|
||||
const char *dir = NULL;
|
||||
const char *lang = NULL;
|
||||
size_t count;
|
||||
GlyphInfo *glyph_info = NULL;
|
||||
PyObject *features = Py_None;
|
||||
|
@ -591,14 +609,14 @@ font_getsize(FontObject* self, PyObject* args)
|
|||
/* calculate size and bearing for a given string */
|
||||
|
||||
PyObject* string;
|
||||
if (!PyArg_ParseTuple(args, "O|zO:getsize", &string, &dir, &features))
|
||||
if (!PyArg_ParseTuple(args, "O|zzO:getsize", &string, &dir, &lang, &features))
|
||||
return NULL;
|
||||
|
||||
face = NULL;
|
||||
xoffset = yoffset = 0;
|
||||
y_max = y_min = 0;
|
||||
|
||||
count = text_layout(string, self, dir, features, &glyph_info, 0);
|
||||
count = text_layout(string, self, dir, lang, features, &glyph_info, 0);
|
||||
if (PyErr_Occurred()) {
|
||||
return NULL;
|
||||
}
|
||||
|
@ -691,16 +709,17 @@ font_render(FontObject* self, PyObject* args)
|
|||
int temp;
|
||||
int xx, x0, x1;
|
||||
const char *dir = NULL;
|
||||
const char *lang = NULL;
|
||||
size_t count;
|
||||
GlyphInfo *glyph_info;
|
||||
PyObject *features = NULL;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "On|izO:render", &string, &id, &mask, &dir, &features)) {
|
||||
if (!PyArg_ParseTuple(args, "On|izzO:render", &string, &id, &mask, &dir, &lang, &features)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
glyph_info = NULL;
|
||||
count = text_layout(string, self, dir, features, &glyph_info, mask);
|
||||
count = text_layout(string, self, dir, lang, features, &glyph_info, mask);
|
||||
if (PyErr_Occurred()) {
|
||||
return NULL;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user