diff --git a/PIL/ImageFont.py b/PIL/ImageFont.py index 8ec60fef9..288bc2954 100644 --- a/PIL/ImageFont.py +++ b/PIL/ImageFont.py @@ -30,6 +30,11 @@ from __future__ import print_function from PIL import Image import os, sys +try: + import warnings +except ImportError: + warnings = None + class _imagingft_not_installed: # module placeholder def __getattr__(self, id): @@ -40,6 +45,13 @@ try: except ImportError: core = _imagingft_not_installed() +if bytes is str: + def isStringType(t): + return isinstance(t, basestring) +else: + def isStringType(t): + return isinstance(t, str) + # FIXME: add support for pilfont2 format (see FontFile.py) # -------------------------------------------------------------------- @@ -129,9 +141,18 @@ class ImageFont: class FreeTypeFont: "FreeType font wrapper (requires _imagingft service)" - def __init__(self, file, size, index=0, encoding=""): + def __init__(self, font=None, size=10, index=0, encoding="", file=None): # FIXME: use service provider instead - self.font = core.getfont(file, size, index, encoding) + if file: + if warnings: + warnings.warn('file parameter deprecated, please use font parameter instead.', DeprecationWarning) + font = file + + if isStringType(font): + self.font = core.getfont(font, size, index, encoding) + else: + self.font_bytes = font.read() + self.font = core.getfont("", size, index, encoding, self.font_bytes) def getname(self): return self.font.family, self.font.style @@ -212,10 +233,16 @@ def load(filename): # @return A font object. # @exception IOError If the file could not be read. -def truetype(filename, size, index=0, encoding=""): +def truetype(font=None, size=10, index=0, encoding="", filename=None): "Load a truetype font file." + + if filename: + if warnings: + warnings.warn('filename parameter deprecated, please use font parameter instead.', DeprecationWarning) + font = filename + try: - return FreeTypeFont(filename, size, index, encoding) + return FreeTypeFont(font, size, index, encoding) except IOError: if sys.platform == "win32": # check the windows font repository @@ -223,8 +250,8 @@ def truetype(filename, size, index=0, encoding=""): # 1.5.2's os.environ.get() windir = os.environ.get("WINDIR") if windir: - filename = os.path.join(windir, "fonts", filename) - return FreeTypeFont(filename, size, index, encoding) + filename = os.path.join(windir, "fonts", font) + return FreeTypeFont(font, size, index, encoding) raise ## diff --git a/Tests/fonts/FreeMono.ttf b/Tests/fonts/FreeMono.ttf new file mode 100644 index 000000000..f88bcef9c Binary files /dev/null and b/Tests/fonts/FreeMono.ttf differ diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index 3c4e1f1b8..9a1a0811c 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -1,12 +1,61 @@ from tester import * from PIL import Image +from io import BytesIO + try: from PIL import ImageFont ImageFont.core.getfont # check if freetype is available except ImportError: skip() -def test_sanity(): +from PIL import ImageDraw +font_path = "Tests/fonts/FreeMono.ttf" +font_size=20 + +def test_sanity(): assert_match(ImageFont.core.freetype2_version, "\d+\.\d+\.\d+$") + +def test_font_with_name(): + assert_no_exception(lambda: ImageFont.truetype(font_path, font_size)) + assert_no_exception(lambda: _render(font_path)) + +def _font_as_bytes(): + with open(font_path, 'rb') as f: + font_bytes = BytesIO(f.read()) + return font_bytes + +def test_font_with_filelike(): + assert_no_exception(lambda: ImageFont.truetype(_font_as_bytes(), font_size)) + assert_no_exception(lambda: _render(_font_as_bytes())) + # Usage note: making two fonts from the same buffer fails. + #shared_bytes = _font_as_bytes() + #assert_no_exception(lambda: _render(shared_bytes)) + #assert_exception(Exception, lambda: _render(shared_bytes)) + +def test_font_with_open_file(): + with open(font_path, 'rb') as f: + assert_no_exception(lambda: _render(f)) + +def test_font_old_parameters(): + assert_warning(DeprecationWarning, lambda: ImageFont.truetype(filename=font_path, size=font_size)) + +def _render(font): + txt = "Hello World!" + ttf = ImageFont.truetype(font, font_size) + w, h = ttf.getsize(txt) + img = Image.new("RGB", (256, 64), "white") + d = ImageDraw.Draw(img) + d.text((10, 10), txt, font=ttf, fill='black') + + img.save('font.png') + return img + +def test_render_equal(): + img_path = _render(font_path) + with open(font_path, 'rb') as f: + font_filelike = BytesIO(f.read()) + img_filelike = _render(font_filelike) + + assert_image_equal(img_path, img_filelike) diff --git a/_imagingft.c b/_imagingft.c index e29e3a244..08d38da24 100644 --- a/_imagingft.c +++ b/_imagingft.c @@ -99,17 +99,20 @@ getfont(PyObject* self_, PyObject* args, PyObject* kw) FontObject* self; int error; - char* filename; + char* filename = NULL; int size; int index = 0; - unsigned char* encoding = NULL; + unsigned char* encoding; + unsigned char* font_bytes; + int font_bytes_size = 0; static char* kwlist[] = { - "filename", "size", "index", "encoding", NULL + "filename", "size", "index", "encoding", "font_bytes", NULL }; - if (!PyArg_ParseTupleAndKeywords(args, kw, "eti|is", kwlist, + if (!PyArg_ParseTupleAndKeywords(args, kw, "eti|iss#", kwlist, Py_FileSystemDefaultEncoding, &filename, - &size, &index, &encoding)) + &size, &index, &encoding, &font_bytes, + &font_bytes_size)) return NULL; if (!library) { @@ -124,8 +127,12 @@ getfont(PyObject* self_, PyObject* args, PyObject* kw) if (!self) return NULL; - error = FT_New_Face(library, filename, index, &self->face); - + if (filename && font_bytes_size <= 0) { + error = FT_New_Face(library, filename, index, &self->face); + } else { + error = FT_New_Memory_Face(library, (FT_Byte*)font_bytes, font_bytes_size, index, &self->face); + } + if (!error) error = FT_Set_Pixel_Sizes(self->face, 0, size);