From b8c04de043ce71fa8859c83b31b9fdda02fe4bc8 Mon Sep 17 00:00:00 2001 From: wiredfool Date: Tue, 13 Jun 2017 09:03:23 -0700 Subject: [PATCH] added layout engine switch --- PIL/ImageFont.py | 40 +++++++++++++++++++++++++++++----------- Tests/test_imagefont.py | 6 +++--- _imagingft.c | 19 +++++++++++++++---- 3 files changed, 47 insertions(+), 18 deletions(-) diff --git a/PIL/ImageFont.py b/PIL/ImageFont.py index 3aad8c076..6b90182ef 100644 --- a/PIL/ImageFont.py +++ b/PIL/ImageFont.py @@ -41,6 +41,9 @@ try: except ImportError: core = _imagingft_not_installed() +LAYOUT_BASIC = 0 +LAYOUT_RAQM = 1 + # FIXME: add support for pilfont2 format (see FontFile.py) # -------------------------------------------------------------------- @@ -115,7 +118,8 @@ class ImageFont(object): class FreeTypeFont(object): "FreeType font wrapper (requires _imagingft service)" - def __init__(self, font=None, size=10, index=0, encoding=""): + def __init__(self, font=None, size=10, index=0, encoding="", + layout_engine=None): # FIXME: use service provider instead self.path = font @@ -123,12 +127,21 @@ class FreeTypeFont(object): self.index = index self.encoding = encoding + if layout_engine not in (LAYOUT_BASIC, LAYOUT_RAQM): + layout_engine = LAYOUT_BASIC + if core.HAVE_RAQM: + layout_engine = LAYOUT_RAQM + if layout_engine == LAYOUT_RAQM and not core.HAVE_RAQM: + layout_engine = LAYOUT_BASIC + + self.layout_engine = layout_engine + if isPath(font): - self.font = core.getfont(font, size, index, encoding) + self.font = core.getfont(font, size, index, encoding, layout_engine=layout_engine) else: self.font_bytes = font.read() self.font = core.getfont( - "", size, index, encoding, self.font_bytes) + "", size, index, encoding, self.font_bytes, layout_engine) def getname(self): return self.font.family, self.font.style @@ -152,7 +165,8 @@ class FreeTypeFont(object): self.font.render(text, im.id, mode == "1", direction, features) return im, offset - def font_variant(self, font=None, size=None, index=None, encoding=None): + def font_variant(self, font=None, size=None, index=None, encoding=None, + layout_engine=None): """ Create a copy of this FreeTypeFont object, using any specified arguments to override the settings. @@ -165,8 +179,9 @@ class FreeTypeFont(object): return FreeTypeFont(font=self.path if font is None else font, size=self.size if size is None else size, index=self.index if index is None else index, - encoding=self.encoding if encoding is None else - encoding) + encoding=self.encoding if encoding is None else encoding, + layout_engine=self.layout_engine if layout_engine is None else layout_engine + ) class TransposedFont(object): @@ -212,7 +227,8 @@ def load(filename): return f -def truetype(font=None, size=10, index=0, encoding=""): +def truetype(font=None, size=10, index=0, encoding="", + layout_engine=None): """ Load a TrueType or OpenType font file, and create a font object. This function loads a font object from the given file, and creates @@ -230,12 +246,14 @@ def truetype(font=None, size=10, index=0, encoding=""): Symbol), "ADOB" (Adobe Standard), "ADBE" (Adobe Expert), and "armn" (Apple Roman). See the FreeType documentation for more information. + :param layout_engine: Which layout engine to use, if available: + `ImageFont.LAYOUT_BASIC` or `ImageFont.LAYOUT_RAQM`. :return: A font object. :exception IOError: If the file could not be read. """ try: - return FreeTypeFont(font, size, index, encoding) + return FreeTypeFont(font, size, index, encoding, layout_engine) except IOError: ttf_filename = os.path.basename(font) @@ -266,16 +284,16 @@ def truetype(font=None, size=10, index=0, encoding=""): for walkfilename in walkfilenames: if ext and walkfilename == ttf_filename: fontpath = os.path.join(walkroot, walkfilename) - return FreeTypeFont(fontpath, size, index, encoding) + return FreeTypeFont(fontpath, size, index, encoding, layout_engine) elif not ext and os.path.splitext(walkfilename)[0] == ttf_filename: fontpath = os.path.join(walkroot, walkfilename) if os.path.splitext(fontpath)[1] == '.ttf': - return FreeTypeFont(fontpath, size, index, encoding) + return FreeTypeFont(fontpath, size, index, encoding, layout_engine) if not ext and first_font_with_a_different_extension is None: first_font_with_a_different_extension = fontpath if first_font_with_a_different_extension: return FreeTypeFont(first_font_with_a_different_extension, size, - index, encoding) + index, encoding, layout_engine) raise diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index 5207dce38..350b0ad13 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -379,12 +379,12 @@ try: # Make a copy of FreeTypeFont so we can patch the original free_type_font = copy.deepcopy(ImageFont.FreeTypeFont) with SimplePatcher(ImageFont, '_FreeTypeFont', free_type_font): - def loadable_font(filepath, size, index, encoding): + def loadable_font(filepath, size, index, encoding, *args, **kwargs): if filepath == path_to_fake: return ImageFont._FreeTypeFont(FONT_PATH, size, index, - encoding) + encoding, *args, **kwargs) return ImageFont._FreeTypeFont(filepath, size, index, - encoding) + encoding, *args, **kwargs) with SimplePatcher(ImageFont, 'FreeTypeFont', loadable_font): font = ImageFont.truetype(fontname) # Make sure it's loaded diff --git a/_imagingft.c b/_imagingft.c index 48f8e8791..cd17758e4 100644 --- a/_imagingft.c +++ b/_imagingft.c @@ -45,6 +45,9 @@ #include #endif +#define LAYOUT_FALLBACK 0 +#define LAYOUT_RAQM 1 + typedef struct { int index, x_offset, x_advance, y_offset; @@ -67,6 +70,7 @@ typedef struct { PyObject_HEAD FT_Face face; unsigned char *font_bytes; + int layout_engine; } FontObject; static PyTypeObject Font_Type; @@ -100,11 +104,13 @@ getfont(PyObject* self_, PyObject* args, PyObject* kw) char* filename = NULL; int size; int index = 0; + int layout_engine = 0; unsigned char* encoding; unsigned char* font_bytes; int font_bytes_size = 0; static char* kwlist[] = { - "filename", "size", "index", "encoding", "font_bytes", NULL + "filename", "size", "index", "encoding", "font_bytes", + "layout_engine", NULL }; if (!library) { @@ -115,10 +121,10 @@ getfont(PyObject* self_, PyObject* args, PyObject* kw) return NULL; } - if (!PyArg_ParseTupleAndKeywords(args, kw, "eti|iss#", kwlist, + if (!PyArg_ParseTupleAndKeywords(args, kw, "eti|iss#i", kwlist, Py_FileSystemDefaultEncoding, &filename, &size, &index, &encoding, &font_bytes, - &font_bytes_size)) { + &font_bytes_size, &layout_engine)) { return NULL; } @@ -130,6 +136,7 @@ getfont(PyObject* self_, PyObject* args, PyObject* kw) } self->face = NULL; + self->layout_engine = layout_engine; if (filename && font_bytes_size <= 0) { self->font_bytes = NULL; @@ -409,7 +416,11 @@ text_layout(PyObject* string, FontObject* self, const char* dir, { size_t count; #ifdef HAVE_RAQM - count = text_layout_raqm(string, self, dir, features, glyph_info, mask); + if (self->layout_engine == LAYOUT_RAQM) { + count = text_layout_raqm(string, self, dir, features, glyph_info, mask); + } else { + count = text_layout_fallback(string, self, dir, features, glyph_info, mask); + } #else count = text_layout_fallback(string, self, dir, features, glyph_info, mask); #endif