added layout engine switch

This commit is contained in:
wiredfool 2017-06-13 09:03:23 -07:00
parent f371ca07f4
commit b8c04de043
3 changed files with 47 additions and 18 deletions

View File

@ -41,6 +41,9 @@ try:
except ImportError: except ImportError:
core = _imagingft_not_installed() core = _imagingft_not_installed()
LAYOUT_BASIC = 0
LAYOUT_RAQM = 1
# FIXME: add support for pilfont2 format (see FontFile.py) # FIXME: add support for pilfont2 format (see FontFile.py)
# -------------------------------------------------------------------- # --------------------------------------------------------------------
@ -115,7 +118,8 @@ class ImageFont(object):
class FreeTypeFont(object): class FreeTypeFont(object):
"FreeType font wrapper (requires _imagingft service)" "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 # FIXME: use service provider instead
self.path = font self.path = font
@ -123,12 +127,21 @@ class FreeTypeFont(object):
self.index = index self.index = index
self.encoding = encoding 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): if isPath(font):
self.font = core.getfont(font, size, index, encoding) self.font = core.getfont(font, size, index, encoding, layout_engine=layout_engine)
else: else:
self.font_bytes = font.read() self.font_bytes = font.read()
self.font = core.getfont( self.font = core.getfont(
"", size, index, encoding, self.font_bytes) "", size, index, encoding, self.font_bytes, layout_engine)
def getname(self): def getname(self):
return self.font.family, self.font.style return self.font.family, self.font.style
@ -152,7 +165,8 @@ class FreeTypeFont(object):
self.font.render(text, im.id, mode == "1", direction, features) self.font.render(text, im.id, mode == "1", direction, features)
return im, offset 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, Create a copy of this FreeTypeFont object,
using any specified arguments to override the settings. 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, return FreeTypeFont(font=self.path if font is None else font,
size=self.size if size is None else size, size=self.size if size is None else size,
index=self.index if index is None else index, index=self.index if index is None else index,
encoding=self.encoding if encoding is None else encoding=self.encoding if encoding is None else encoding,
encoding) layout_engine=self.layout_engine if layout_engine is None else layout_engine
)
class TransposedFont(object): class TransposedFont(object):
@ -212,7 +227,8 @@ def load(filename):
return f 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. Load a TrueType or OpenType font file, and create a font object.
This function loads a font object from the given file, and creates 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), Symbol), "ADOB" (Adobe Standard), "ADBE" (Adobe Expert),
and "armn" (Apple Roman). See the FreeType documentation and "armn" (Apple Roman). See the FreeType documentation
for more information. for more information.
:param layout_engine: Which layout engine to use, if available:
`ImageFont.LAYOUT_BASIC` or `ImageFont.LAYOUT_RAQM`.
:return: A font object. :return: A font object.
:exception IOError: If the file could not be read. :exception IOError: If the file could not be read.
""" """
try: try:
return FreeTypeFont(font, size, index, encoding) return FreeTypeFont(font, size, index, encoding, layout_engine)
except IOError: except IOError:
ttf_filename = os.path.basename(font) ttf_filename = os.path.basename(font)
@ -266,16 +284,16 @@ def truetype(font=None, size=10, index=0, encoding=""):
for walkfilename in walkfilenames: for walkfilename in walkfilenames:
if ext and walkfilename == ttf_filename: if ext and walkfilename == ttf_filename:
fontpath = os.path.join(walkroot, walkfilename) 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: elif not ext and os.path.splitext(walkfilename)[0] == ttf_filename:
fontpath = os.path.join(walkroot, walkfilename) fontpath = os.path.join(walkroot, walkfilename)
if os.path.splitext(fontpath)[1] == '.ttf': 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: if not ext and first_font_with_a_different_extension is None:
first_font_with_a_different_extension = fontpath first_font_with_a_different_extension = fontpath
if first_font_with_a_different_extension: if first_font_with_a_different_extension:
return FreeTypeFont(first_font_with_a_different_extension, size, return FreeTypeFont(first_font_with_a_different_extension, size,
index, encoding) index, encoding, layout_engine)
raise raise

View File

@ -379,12 +379,12 @@ try:
# Make a copy of FreeTypeFont so we can patch the original # Make a copy of FreeTypeFont so we can patch the original
free_type_font = copy.deepcopy(ImageFont.FreeTypeFont) free_type_font = copy.deepcopy(ImageFont.FreeTypeFont)
with SimplePatcher(ImageFont, '_FreeTypeFont', free_type_font): 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: if filepath == path_to_fake:
return ImageFont._FreeTypeFont(FONT_PATH, size, index, return ImageFont._FreeTypeFont(FONT_PATH, size, index,
encoding) encoding, *args, **kwargs)
return ImageFont._FreeTypeFont(filepath, size, index, return ImageFont._FreeTypeFont(filepath, size, index,
encoding) encoding, *args, **kwargs)
with SimplePatcher(ImageFont, 'FreeTypeFont', loadable_font): with SimplePatcher(ImageFont, 'FreeTypeFont', loadable_font):
font = ImageFont.truetype(fontname) font = ImageFont.truetype(fontname)
# Make sure it's loaded # Make sure it's loaded

View File

@ -45,6 +45,9 @@
#include <raqm.h> #include <raqm.h>
#endif #endif
#define LAYOUT_FALLBACK 0
#define LAYOUT_RAQM 1
typedef struct typedef struct
{ {
int index, x_offset, x_advance, y_offset; int index, x_offset, x_advance, y_offset;
@ -67,6 +70,7 @@ typedef struct {
PyObject_HEAD PyObject_HEAD
FT_Face face; FT_Face face;
unsigned char *font_bytes; unsigned char *font_bytes;
int layout_engine;
} FontObject; } FontObject;
static PyTypeObject Font_Type; static PyTypeObject Font_Type;
@ -100,11 +104,13 @@ getfont(PyObject* self_, PyObject* args, PyObject* kw)
char* filename = NULL; char* filename = NULL;
int size; int size;
int index = 0; int index = 0;
int layout_engine = 0;
unsigned char* encoding; unsigned char* encoding;
unsigned char* font_bytes; unsigned char* font_bytes;
int font_bytes_size = 0; int font_bytes_size = 0;
static char* kwlist[] = { static char* kwlist[] = {
"filename", "size", "index", "encoding", "font_bytes", NULL "filename", "size", "index", "encoding", "font_bytes",
"layout_engine", NULL
}; };
if (!library) { if (!library) {
@ -115,10 +121,10 @@ getfont(PyObject* self_, PyObject* args, PyObject* kw)
return NULL; return NULL;
} }
if (!PyArg_ParseTupleAndKeywords(args, kw, "eti|iss#", kwlist, if (!PyArg_ParseTupleAndKeywords(args, kw, "eti|iss#i", kwlist,
Py_FileSystemDefaultEncoding, &filename, Py_FileSystemDefaultEncoding, &filename,
&size, &index, &encoding, &font_bytes, &size, &index, &encoding, &font_bytes,
&font_bytes_size)) { &font_bytes_size, &layout_engine)) {
return NULL; return NULL;
} }
@ -130,6 +136,7 @@ getfont(PyObject* self_, PyObject* args, PyObject* kw)
} }
self->face = NULL; self->face = NULL;
self->layout_engine = layout_engine;
if (filename && font_bytes_size <= 0) { if (filename && font_bytes_size <= 0) {
self->font_bytes = NULL; self->font_bytes = NULL;
@ -409,7 +416,11 @@ text_layout(PyObject* string, FontObject* self, const char* dir,
{ {
size_t count; size_t count;
#ifdef HAVE_RAQM #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 #else
count = text_layout_fallback(string, self, dir, features, glyph_info, mask); count = text_layout_fallback(string, self, dir, features, glyph_info, mask);
#endif #endif