From 326e23d3e6f333c00d2e8f1793f4f6aeca7b7ae9 Mon Sep 17 00:00:00 2001 From: shamsa Date: Wed, 27 Jan 2016 09:09:26 +0400 Subject: [PATCH] fallback code --- PIL/ImageDraw.py | 5 +- PIL/ImageFont.py | 4 +- _imagingft.c | 191 ++++++++++++++++++++++++++++++++++++----------- setup.py | 18 +++-- 4 files changed, 164 insertions(+), 54 deletions(-) diff --git a/PIL/ImageDraw.py b/PIL/ImageDraw.py index 3512a3826..5902405a4 100644 --- a/PIL/ImageDraw.py +++ b/PIL/ImageDraw.py @@ -203,7 +203,8 @@ class ImageDraw(object): return text.split(split_character) def text(self, xy, text, fill=None, font=None, anchor=None, - *args, **kwargs, direction=None, features=[]): + *args, **kwargs, direction=None, features=None): + if self._multiline_check(text): return self.multiline_text(xy, text, fill, font, anchor, *args, **kwargs, direction=direction, features=features) @@ -225,7 +226,7 @@ class ImageDraw(object): self.draw.draw_bitmap(xy, mask, ink) def multiline_text(self, xy, text, fill=None, font=None, anchor=None, - spacing=4, align="left", direction=None, features=[]): + spacing=4, align="left", direction=None, features=None): widths = [] max_width = 0 lines = self._multiline_split(text) diff --git a/PIL/ImageFont.py b/PIL/ImageFont.py index 7e61e9c0d..3f25a92b6 100644 --- a/PIL/ImageFont.py +++ b/PIL/ImageFont.py @@ -143,10 +143,10 @@ class FreeTypeFont(object): def getoffset(self, text): return self.font.getsize(text)[1] - def getmask(self, text, mode="", direction=None, features=[]): + def getmask(self, text, mode="", direction=None, features=None): return self.getmask2(text, mode, direction=direction, features=features)[0] - def getmask2(self, text, mode="", fill=Image.core.fill, direction=None, features=[]): + def getmask2(self, text, mode="", fill=Image.core.fill, direction=None, features=None): size, offset = self.font.getsize(text) im = fill("L", size, 0) self.font.render(text, im.id, mode == "1", direction, features) diff --git a/_imagingft.c b/_imagingft.c index 55bfa860e..e1130b016 100644 --- a/_imagingft.c +++ b/_imagingft.c @@ -41,6 +41,23 @@ #define FT_ERRORDEF( e, v, s ) { e, s }, #define FT_ERROR_START_LIST { #define FT_ERROR_END_LIST { 0, 0 } }; +#ifdef HAVE_RAQM +#include +#else +typedef enum +{ + RAQM_DIRECTION_DEFAULT, + RAQM_DIRECTION_RTL, + RAQM_DIRECTION_LTR, + RAQM_DIRECTION_TTB +} raqm_direction_t; +#endif + +typedef struct +{ + int index, x_offset, x_advance, y_offset; + unsigned int cluster; +} GlyphInfo; struct { int code; @@ -48,7 +65,6 @@ struct { } ft_errors[] = #include FT_ERRORS_H -#include "raqm.h" /* -------------------------------------------------------------------- */ /* font objects */ @@ -322,31 +338,17 @@ font_getabc(FontObject* self, PyObject* args) return Py_BuildValue("ddd", a, b, c); } -static PyObject* -font_render(FontObject* self, PyObject* args) -{ - int i, x, y; - Imaging im; - int index, error, ascender; - int load_flags; - unsigned char *source; - FT_GlyphSlot glyph; - /* render string into given buffer (the buffer *must* have - the right size, or this will crash) */ - PyObject* string; - Py_ssize_t id; - int mask = 0; - int temp; - int xx, x0, x1; - const char *dir = NULL; - raqm_direction_t direction; - raqm_t *rq; - size_t count; - raqm_glyph_t *glyph_info; - PyObject *features = NULL; - if (!PyArg_ParseTuple(args, "On|izO:render", &string, &id, &mask, &dir, &features)) - return NULL; +static size_t +text_layout(PyObject* string, FontObject* self, const char* dir, + PyObject *features ,GlyphInfo **glyph_info, int mask) +{ +#ifdef HAVE_RAQM + int i = 0; + raqm_t *rq; + size_t count = 0; + raqm_glyph_t *glyphs; + raqm_direction_t direction; rq = raqm_create(); if (rq == NULL) { @@ -354,7 +356,6 @@ font_render(FontObject* self, PyObject* args) goto failed; } - if (PyUnicode_Check(string)) { Py_UNICODE *text = PyUnicode_AS_UNICODE(string); Py_ssize_t size = PyUnicode_GET_SIZE(string); @@ -397,7 +398,7 @@ font_render(FontObject* self, PyObject* args) goto failed; } - if (features) { + if (features != Py_None) { int len; PyObject *seq = PySequence_Fast(features, "expected a sequence"); if (!seq) { @@ -420,8 +421,10 @@ font_render(FontObject* self, PyObject* args) goto failed; } - if (!PyUnicode_Check(item)) { + if (PyUnicode_Check(item)) { bytes = PyUnicode_AsUTF8String(item); + if (bytes == NULL) + goto failed; feature = PyBytes_AS_STRING(bytes); size = PyBytes_GET_SIZE(bytes); } @@ -448,12 +451,121 @@ font_render(FontObject* self, PyObject* args) goto failed; } - glyph_info = raqm_get_glyphs(rq, &count); - if (glyph_info == NULL) { + glyphs = raqm_get_glyphs(rq, &count); + if (glyphs == NULL) { PyErr_SetString(PyExc_ValueError, "raqm_get_glyphs() failed."); + count = 0; goto failed; } + (*glyph_info) = PyMem_New(GlyphInfo, count); + if ((*glyph_info) == NULL) { + PyErr_SetString(PyExc_MemoryError, "PyMem_New() failed"); + count = 0; + goto failed; + } + + for (i = 0; i < count; i++) { + (*glyph_info)[i].index = glyphs[i].index; + (*glyph_info)[i].x_offset = glyphs[i].x_offset; + (*glyph_info)[i].x_advance = glyphs[i].x_advance; + (*glyph_info)[i].y_offset = glyphs[i].y_offset; + (*glyph_info)[i].cluster = glyphs[i].cluster; + } + +failed: + raqm_destroy (rq); + return count; + +#else + if (features != Py_None || dir != NULL) + PyErr_SetString(PyExc_KeyError, "Raqm is missing."); + + int error, load_flags; + FT_ULong ch; + Py_ssize_t count; + FT_GlyphSlot glyph; + FT_Bool kerning = FT_HAS_KERNING(self->face); + FT_UInt last_index = 0; +#if PY_VERSION_HEX >= 0x03000000 + if (!PyUnicode_Check(string)) { +#else + if (!PyUnicode_Check(string) && !PyString_Check(string)) { +#endif + PyErr_SetString(PyExc_TypeError, "expected string"); + return 0; + } + + count = 0; + while (font_getchar(string, count, &ch)) + count++; + if (count == 0) + return 0; + + (*glyph_info) = PyMem_New(GlyphInfo, count); + if ((*glyph_info) == NULL) { + PyErr_SetString(PyExc_MemoryError, "PyMem_New() failed"); + return 0; + } + + load_flags = FT_LOAD_RENDER|FT_LOAD_NO_BITMAP; + if (mask) + load_flags |= FT_LOAD_TARGET_MONO; + int i; + for (i = 0; font_getchar(string, i, &ch); 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) { + geterror(error); + return 0; + } + glyph = self->face->glyph; + (*glyph_info)[i].x_offset=0; + (*glyph_info)[i].y_offset=0; + if (kerning && last_index && (*glyph_info)[i].index) { + FT_Vector delta; + if (FT_Get_Kerning(self->face, last_index, (*glyph_info)[i].index, + ft_kerning_default,&delta) == 0) + (*glyph_info)[i-1].x_advance += PIXEL(delta.x); + } + + (*glyph_info)[i].x_advance = glyph->metrics.horiAdvance; + last_index = (*glyph_info)[i].index; + (*glyph_info)[i].cluster = ch; + } + return count; +#endif +} + +static PyObject* +font_render(FontObject* self, PyObject* args) +{ + int i, x, y; + Imaging im; + int index, error, ascender; + int load_flags; + unsigned char *source; + FT_GlyphSlot glyph; + /* render string into given buffer (the buffer *must* have + the right size, or this will crash) */ + PyObject* string; + Py_ssize_t id; + int mask = 0; + int temp; + int xx, x0, x1; + const char *dir = NULL; + size_t count; + GlyphInfo *glyph_info; + PyObject *features = NULL; + + if (!PyArg_ParseTuple(args, "On|izO:render", &string, &id, &mask, &dir, &features)) + return NULL; + + glyph_info = NULL; + count = text_layout(string, self, dir, features, &glyph_info, mask); + if (count == 0) + return NULL; + im = (Imaging) id; /* Note: bitmap fonts within ttf fonts do not work, see #891/pr#960 */ load_flags = FT_LOAD_RENDER|FT_LOAD_NO_BITMAP; @@ -464,10 +576,9 @@ font_render(FontObject* self, PyObject* args) for (i = 0; i < count; i++) { index = glyph_info[i].index; error = FT_Load_Glyph(self->face, index, load_flags); - if (error) { - geterror(error); - goto failed; - } + if (error) + return geterror(error); + glyph = self->face->glyph; temp = (glyph->bitmap.rows - glyph->bitmap_top); temp -= PIXEL(glyph_info[i].y_offset); @@ -481,10 +592,8 @@ font_render(FontObject* self, PyObject* args) index = glyph_info[i].index; error = FT_Load_Glyph(self->face, index, load_flags); - if (error){ - geterror(error); - goto failed; - } + if (error) + return geterror(error); glyph = self->face->glyph; @@ -537,12 +646,8 @@ font_render(FontObject* self, PyObject* args) } x += PIXEL(glyph_info[i].x_advance); } - + PyMem_Del(glyph_info); Py_RETURN_NONE; - -failed: - raqm_destroy(rq); - return NULL; } static void diff --git a/setup.py b/setup.py index 1b302952d..86269f5ed 100755 --- a/setup.py +++ b/setup.py @@ -519,8 +519,10 @@ class pil_build_ext(build_ext): if feature.want('raqm'): if _find_include_file(self, "raqm.h"): - if _find_library_file(self, "raqm"): - feature.raqm = "raqm" + if _find_library_file(self, "raqm") and \ + _find_library_file(self, "harfbuzz") and \ + _find_library_file(self, "fribidi"): + feature.raqm = ["raqm", "harfbuzz", "fribidi"] if feature.want('lcms'): _dbg('Looking for lcms') @@ -600,12 +602,14 @@ class pil_build_ext(build_ext): # additional libraries if feature.freetype: - exts.append(Extension("PIL._imagingft", - ["_imagingft.c"], - libraries=["freetype"])) - if feature.freetype and feature.raqm: + libs = ["freetype"] + defs = [] + if feature.raqm: + libs.extend(feature.raqm) + defs.append(('HAVE_RAQM', None)) exts.append(Extension( - "PIL._imagingft", ["_imagingft.c"], libraries=["freetype", "fribidi" , "harfbuzz", "raqm"])) + "PIL._imagingft", ["_imagingft.c"], libraries=libs, + define_macros=defs)) if feature.lcms: extra = []