mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-08-06 21:40:09 +03:00
Merge 4dc3dc1af2
into 8beb66443b
This commit is contained in:
commit
9d3425e620
|
@ -29,13 +29,15 @@ from __future__ import print_function
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from PIL._util import isDirectory, isPath
|
from PIL._util import isDirectory, isPath
|
||||||
import os, sys
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import warnings
|
import warnings
|
||||||
except ImportError:
|
except ImportError:
|
||||||
warnings = None
|
warnings = None
|
||||||
|
|
||||||
|
|
||||||
class _imagingft_not_installed:
|
class _imagingft_not_installed:
|
||||||
# module placeholder
|
# module placeholder
|
||||||
def __getattr__(self, id):
|
def __getattr__(self, id):
|
||||||
|
@ -91,7 +93,7 @@ class ImageFont:
|
||||||
if file.readline() != b"PILfont\n":
|
if file.readline() != b"PILfont\n":
|
||||||
raise SyntaxError("Not a PILfont file")
|
raise SyntaxError("Not a PILfont file")
|
||||||
d = file.readline().split(b";")
|
d = file.readline().split(b";")
|
||||||
self.info = [] # FIXME: should be a dictionary
|
self.info = [] # FIXME: should be a dictionary
|
||||||
while True:
|
while True:
|
||||||
s = file.readline()
|
s = file.readline()
|
||||||
if not s or s == b"DATA\n":
|
if not s or s == b"DATA\n":
|
||||||
|
@ -113,6 +115,7 @@ class ImageFont:
|
||||||
self.getsize = self.font.getsize
|
self.getsize = self.font.getsize
|
||||||
self.getmask = self.font.getmask
|
self.getmask = self.font.getmask
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Wrapper for FreeType fonts. Application code should use the
|
# Wrapper for FreeType fonts. Application code should use the
|
||||||
# <b>truetype</b> factory function to create font objects.
|
# <b>truetype</b> factory function to create font objects.
|
||||||
|
@ -131,7 +134,8 @@ class FreeTypeFont:
|
||||||
self.font = core.getfont(font, size, index, encoding)
|
self.font = core.getfont(font, size, index, encoding)
|
||||||
else:
|
else:
|
||||||
self.font_bytes = font.read()
|
self.font_bytes = font.read()
|
||||||
self.font = core.getfont("", size, index, encoding, self.font_bytes)
|
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
|
||||||
|
@ -151,9 +155,10 @@ class FreeTypeFont:
|
||||||
def getmask2(self, text, mode="", fill=Image.core.fill):
|
def getmask2(self, text, mode="", fill=Image.core.fill):
|
||||||
size, offset = self.font.getsize(text)
|
size, offset = self.font.getsize(text)
|
||||||
im = fill("L", size, 0)
|
im = fill("L", size, 0)
|
||||||
self.font.render(text, im.id, mode=="1")
|
self.font.render(text, im.id, mode == "1")
|
||||||
return im, offset
|
return im, offset
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Wrapper that creates a transposed font from any existing font
|
# Wrapper that creates a transposed font from any existing font
|
||||||
# object.
|
# object.
|
||||||
|
@ -168,7 +173,7 @@ class TransposedFont:
|
||||||
|
|
||||||
def __init__(self, font, orientation=None):
|
def __init__(self, font, orientation=None):
|
||||||
self.font = font
|
self.font = font
|
||||||
self.orientation = orientation # any 'transpose' argument, or None
|
self.orientation = orientation # any 'transpose' argument, or None
|
||||||
|
|
||||||
def getsize(self, text):
|
def getsize(self, text):
|
||||||
w, h = self.font.getsize(text)
|
w, h = self.font.getsize(text)
|
||||||
|
@ -207,7 +212,8 @@ def truetype(font=None, size=10, index=0, encoding="", filename=None):
|
||||||
|
|
||||||
:param filename: A truetype font file. Under Windows, if the file
|
:param filename: A truetype font file. Under Windows, if the file
|
||||||
is not found in this filename, the loader also looks in
|
is not found in this filename, the loader also looks in
|
||||||
Windows :file:`fonts/` directory.
|
Windows :file:`fonts/` and Linux :file:`XDG_DATA_DIRS`
|
||||||
|
directories.
|
||||||
:param size: The requested size, in points.
|
:param size: The requested size, in points.
|
||||||
:param index: Which font face to load (default is first available face).
|
:param index: Which font face to load (default is first available face).
|
||||||
:param encoding: Which font encoding to use (default is Unicode). Common
|
:param encoding: Which font encoding to use (default is Unicode). Common
|
||||||
|
@ -227,14 +233,18 @@ def truetype(font=None, size=10, index=0, encoding="", filename=None):
|
||||||
try:
|
try:
|
||||||
return FreeTypeFont(font, size, index, encoding)
|
return FreeTypeFont(font, size, index, encoding)
|
||||||
except IOError:
|
except IOError:
|
||||||
|
dir = None
|
||||||
if sys.platform == "win32":
|
if sys.platform == "win32":
|
||||||
# check the windows font repository
|
# check the windows font repository
|
||||||
# NOTE: must use uppercase WINDIR, to work around bugs in
|
# NOTE: must use uppercase WINDIR, to work around bugs in
|
||||||
# 1.5.2's os.environ.get()
|
# 1.5.2's os.environ.get()
|
||||||
windir = os.environ.get("WINDIR")
|
dir = os.environ.get("WINDIR")
|
||||||
if windir:
|
elif sys.platform == "linux":
|
||||||
filename = os.path.join(windir, "fonts", font)
|
# check the Linux font repository
|
||||||
return FreeTypeFont(filename, size, index, encoding)
|
dir = os.environ.get("XDG_DATA_DIRS")
|
||||||
|
if dir:
|
||||||
|
filename = os.path.join(dir, "fonts", font)
|
||||||
|
return FreeTypeFont(filename, size, index, encoding)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
@ -272,8 +282,8 @@ def load_default():
|
||||||
import base64
|
import base64
|
||||||
f = ImageFont()
|
f = ImageFont()
|
||||||
f._load_pilfont_data(
|
f._load_pilfont_data(
|
||||||
# courB08
|
# courB08
|
||||||
BytesIO(base64.decodestring(b'''
|
BytesIO(base64.decodestring(b'''
|
||||||
UElMZm9udAo7Ozs7OzsxMDsKREFUQQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
UElMZm9udAo7Ozs7OzsxMDsKREFUQQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
@ -395,7 +405,7 @@ w7IkEbzhVQAAAABJRU5ErkJggg==
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# create font data chunk for embedding
|
# create font data chunk for embedding
|
||||||
import base64, os, sys
|
import base64
|
||||||
font = "../Images/courB08"
|
font = "../Images/courB08"
|
||||||
print(" f._load_pilfont_data(")
|
print(" f._load_pilfont_data(")
|
||||||
print(" # %s" % os.path.basename(font))
|
print(" # %s" % os.path.basename(font))
|
||||||
|
|
|
@ -6,44 +6,54 @@ import os
|
||||||
|
|
||||||
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()
|
||||||
|
|
||||||
from PIL import ImageDraw
|
from PIL import ImageDraw
|
||||||
|
|
||||||
font_path = "Tests/fonts/FreeMono.ttf"
|
font_path = "Tests/fonts/FreeMono.ttf"
|
||||||
font_size=20
|
font_size = 20
|
||||||
|
|
||||||
|
|
||||||
def test_sanity():
|
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():
|
def test_font_with_name():
|
||||||
assert_no_exception(lambda: ImageFont.truetype(font_path, font_size))
|
assert_no_exception(lambda: ImageFont.truetype(font_path, font_size))
|
||||||
assert_no_exception(lambda: _render(font_path))
|
assert_no_exception(lambda: _render(font_path))
|
||||||
_clean()
|
_clean()
|
||||||
|
|
||||||
|
|
||||||
def _font_as_bytes():
|
def _font_as_bytes():
|
||||||
with open(font_path, 'rb') as f:
|
with open(font_path, 'rb') as f:
|
||||||
font_bytes = BytesIO(f.read())
|
font_bytes = BytesIO(f.read())
|
||||||
return font_bytes
|
return font_bytes
|
||||||
|
|
||||||
|
|
||||||
def test_font_with_filelike():
|
def test_font_with_filelike():
|
||||||
assert_no_exception(lambda: ImageFont.truetype(_font_as_bytes(), font_size))
|
assert_no_exception(
|
||||||
|
lambda: ImageFont.truetype(_font_as_bytes(), font_size))
|
||||||
assert_no_exception(lambda: _render(_font_as_bytes()))
|
assert_no_exception(lambda: _render(_font_as_bytes()))
|
||||||
# Usage note: making two fonts from the same buffer fails.
|
# Usage note: making two fonts from the same buffer fails.
|
||||||
#shared_bytes = _font_as_bytes()
|
# shared_bytes = _font_as_bytes()
|
||||||
#assert_no_exception(lambda: _render(shared_bytes))
|
# assert_no_exception(lambda: _render(shared_bytes))
|
||||||
#assert_exception(Exception, lambda: _render(shared_bytes))
|
# assert_exception(Exception, lambda: _render(shared_bytes))
|
||||||
_clean()
|
_clean()
|
||||||
|
|
||||||
|
|
||||||
def test_font_with_open_file():
|
def test_font_with_open_file():
|
||||||
with open(font_path, 'rb') as f:
|
with open(font_path, 'rb') as f:
|
||||||
assert_no_exception(lambda: _render(f))
|
assert_no_exception(lambda: _render(f))
|
||||||
_clean()
|
_clean()
|
||||||
|
|
||||||
|
|
||||||
def test_font_old_parameters():
|
def test_font_old_parameters():
|
||||||
assert_warning(DeprecationWarning, lambda: ImageFont.truetype(filename=font_path, size=font_size))
|
assert_warning(
|
||||||
|
DeprecationWarning,
|
||||||
|
lambda: ImageFont.truetype(filename=font_path, size=font_size))
|
||||||
|
|
||||||
|
|
||||||
def _render(font):
|
def _render(font):
|
||||||
txt = "Hello World!"
|
txt = "Hello World!"
|
||||||
|
@ -56,9 +66,11 @@ def _render(font):
|
||||||
img.save('font.png')
|
img.save('font.png')
|
||||||
return img
|
return img
|
||||||
|
|
||||||
|
|
||||||
def _clean():
|
def _clean():
|
||||||
os.unlink('font.png')
|
os.unlink('font.png')
|
||||||
|
|
||||||
|
|
||||||
def test_render_equal():
|
def test_render_equal():
|
||||||
img_path = _render(font_path)
|
img_path = _render(font_path)
|
||||||
with open(font_path, 'rb') as f:
|
with open(font_path, 'rb') as f:
|
||||||
|
@ -70,7 +82,7 @@ def test_render_equal():
|
||||||
|
|
||||||
|
|
||||||
def test_render_multiline():
|
def test_render_multiline():
|
||||||
im = Image.new(mode='RGB', size=(300,100))
|
im = Image.new(mode='RGB', size=(300, 100))
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
ttf = ImageFont.truetype(font_path, font_size)
|
ttf = ImageFont.truetype(font_path, font_size)
|
||||||
line_spacing = draw.textsize('A', font=ttf)[1] + 8
|
line_spacing = draw.textsize('A', font=ttf)[1] + 8
|
||||||
|
@ -80,14 +92,13 @@ def test_render_multiline():
|
||||||
draw.text((0, y), line, font=ttf)
|
draw.text((0, y), line, font=ttf)
|
||||||
y += line_spacing
|
y += line_spacing
|
||||||
|
|
||||||
|
|
||||||
target = 'Tests/images/multiline_text.png'
|
target = 'Tests/images/multiline_text.png'
|
||||||
target_img = Image.open(target)
|
target_img = Image.open(target)
|
||||||
|
|
||||||
# some versions of freetype have different horizontal spacing.
|
# some versions of freetype have different horizontal spacing.
|
||||||
# setting a tight epsilon, I'm showing the original test failure
|
# setting a tight epsilon, I'm showing the original test failure
|
||||||
# at epsilon = ~38.
|
# at epsilon = ~38.
|
||||||
assert_image_similar(im, target_img,.5)
|
assert_image_similar(im, target_img, .5)
|
||||||
|
|
||||||
|
|
||||||
def test_rotated_transposed_font():
|
def test_rotated_transposed_font():
|
||||||
|
@ -133,3 +144,8 @@ def test_unrotated_transposed_font():
|
||||||
assert_equal(box_size_a, box_size_b)
|
assert_equal(box_size_a, box_size_b)
|
||||||
|
|
||||||
|
|
||||||
|
def test_font_not_found():
|
||||||
|
assert_exception(IOError, lambda: ImageFont.truetype("/fake/font.ttf"))
|
||||||
|
|
||||||
|
|
||||||
|
# End of file
|
||||||
|
|
Loading…
Reference in New Issue
Block a user