Merge pull request #209 from wiredfool/pr202

Fonts from Filelike Objects
This commit is contained in:
wiredfool 2013-04-25 22:14:10 -07:00
commit 44d97759af
4 changed files with 97 additions and 14 deletions

View File

@ -30,6 +30,11 @@ from __future__ import print_function
from PIL import Image from PIL import Image
import os, sys import os, sys
try:
import warnings
except ImportError:
warnings = None
class _imagingft_not_installed: class _imagingft_not_installed:
# module placeholder # module placeholder
def __getattr__(self, id): def __getattr__(self, id):
@ -40,6 +45,13 @@ try:
except ImportError: except ImportError:
core = _imagingft_not_installed() 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) # FIXME: add support for pilfont2 format (see FontFile.py)
# -------------------------------------------------------------------- # --------------------------------------------------------------------
@ -129,9 +141,18 @@ class ImageFont:
class FreeTypeFont: class FreeTypeFont:
"FreeType font wrapper (requires _imagingft service)" "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 # 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): def getname(self):
return self.font.family, self.font.style return self.font.family, self.font.style
@ -212,10 +233,16 @@ def load(filename):
# @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.
def truetype(filename, size, index=0, encoding=""): def truetype(font=None, size=10, index=0, encoding="", filename=None):
"Load a truetype font file." "Load a truetype font file."
if filename:
if warnings:
warnings.warn('filename parameter deprecated, please use font parameter instead.', DeprecationWarning)
font = filename
try: try:
return FreeTypeFont(filename, size, index, encoding) return FreeTypeFont(font, size, index, encoding)
except IOError: except IOError:
if sys.platform == "win32": if sys.platform == "win32":
# check the windows font repository # check the windows font repository
@ -223,8 +250,8 @@ def truetype(filename, size, index=0, encoding=""):
# 1.5.2's os.environ.get() # 1.5.2's os.environ.get()
windir = os.environ.get("WINDIR") windir = os.environ.get("WINDIR")
if windir: if windir:
filename = os.path.join(windir, "fonts", filename) filename = os.path.join(windir, "fonts", font)
return FreeTypeFont(filename, size, index, encoding) return FreeTypeFont(font, size, index, encoding)
raise raise
## ##

BIN
Tests/fonts/FreeMono.ttf Normal file

Binary file not shown.

View File

@ -1,12 +1,61 @@
from tester import * from tester import *
from PIL import Image from PIL import Image
from io import BytesIO
try: try:
from PIL import ImageFont from PIL import ImageFont
ImageFont.core.getfont # check if freetype is available ImageFont.core.getfont # check if freetype is available
except ImportError: except ImportError:
skip() 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+$") 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)

View File

@ -99,17 +99,20 @@ getfont(PyObject* self_, PyObject* args, PyObject* kw)
FontObject* self; FontObject* self;
int error; int error;
char* filename; char* filename = NULL;
int size; int size;
int index = 0; int index = 0;
unsigned char* encoding = NULL; unsigned char* encoding;
unsigned char* font_bytes;
int font_bytes_size = 0;
static char* kwlist[] = { 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, Py_FileSystemDefaultEncoding, &filename,
&size, &index, &encoding)) &size, &index, &encoding, &font_bytes,
&font_bytes_size))
return NULL; return NULL;
if (!library) { if (!library) {
@ -124,8 +127,12 @@ getfont(PyObject* self_, PyObject* args, PyObject* kw)
if (!self) if (!self)
return NULL; 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) if (!error)
error = FT_Set_Pixel_Sizes(self->face, 0, size); error = FT_Set_Pixel_Sizes(self->face, 0, size);