test both layout engines, if available

This commit is contained in:
wiredfool 2017-06-13 09:31:29 -07:00
parent b8c04de043
commit 39327332df

View File

@ -1,7 +1,6 @@
from helper import unittest, PillowTestCase from helper import unittest, PillowTestCase
from PIL import Image from PIL import Image, ImageDraw, ImageFont, features
from PIL import ImageDraw
from io import BytesIO from io import BytesIO
import os import os
import sys import sys
@ -12,466 +11,471 @@ FONT_SIZE = 20
TEST_TEXT = "hey you\nyou are awesome\nthis looks awkward" TEST_TEXT = "hey you\nyou are awesome\nthis looks awkward"
HAS_FREETYPE = features.check('freetype2')
HAS_RAQM = features.check('raqm')
try:
from PIL import ImageFont
ImageFont.core.getfont # check if freetype is available
class SimplePatcher(object): class SimplePatcher(object):
def __init__(self, parent_obj, attr_name, value): def __init__(self, parent_obj, attr_name, value):
self._parent_obj = parent_obj self._parent_obj = parent_obj
self._attr_name = attr_name self._attr_name = attr_name
self._saved = None self._saved = None
self._is_saved = False
self._value = value
def __enter__(self):
# Patch the attr on the object
if hasattr(self._parent_obj, self._attr_name):
self._saved = getattr(self._parent_obj, self._attr_name)
setattr(self._parent_obj, self._attr_name, self._value)
self._is_saved = True
else:
setattr(self._parent_obj, self._attr_name, self._value)
self._is_saved = False self._is_saved = False
self._value = value
def __enter__(self): def __exit__(self, type, value, traceback):
# Patch the attr on the object # Restore the original value
if hasattr(self._parent_obj, self._attr_name): if self._is_saved:
self._saved = getattr(self._parent_obj, self._attr_name) setattr(self._parent_obj, self._attr_name, self._saved)
setattr(self._parent_obj, self._attr_name, self._value) else:
self._is_saved = True delattr(self._parent_obj, self._attr_name)
else:
setattr(self._parent_obj, self._attr_name, self._value)
self._is_saved = False
def __exit__(self, type, value, traceback): @unittest.skipUnless(HAS_FREETYPE, "ImageFont not Available")
# Restore the original value class TestImageFont(PillowTestCase):
if self._is_saved: LAYOUT_ENGINE = ImageFont.LAYOUT_BASIC
setattr(self._parent_obj, self._attr_name, self._saved)
else:
delattr(self._parent_obj, self._attr_name)
class TestImageFont(PillowTestCase): def get_font(self):
return ImageFont.truetype(FONT_PATH, FONT_SIZE,
layout_engine=self.LAYOUT_ENGINE)
def test_sanity(self): def test_sanity(self):
self.assertRegexpMatches( self.assertRegexpMatches(
ImageFont.core.freetype2_version, r"\d+\.\d+\.\d+$") ImageFont.core.freetype2_version, r"\d+\.\d+\.\d+$")
def test_font_properties(self): def test_font_properties(self):
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) ttf = self.get_font()
self.assertEqual(ttf.path, FONT_PATH) self.assertEqual(ttf.path, FONT_PATH)
self.assertEqual(ttf.size, FONT_SIZE) self.assertEqual(ttf.size, FONT_SIZE)
ttf_copy = ttf.font_variant() ttf_copy = ttf.font_variant()
self.assertEqual(ttf_copy.path, FONT_PATH) self.assertEqual(ttf_copy.path, FONT_PATH)
self.assertEqual(ttf_copy.size, FONT_SIZE) self.assertEqual(ttf_copy.size, FONT_SIZE)
ttf_copy = ttf.font_variant(size=FONT_SIZE+1) ttf_copy = ttf.font_variant(size=FONT_SIZE+1)
self.assertEqual(ttf_copy.size, FONT_SIZE+1) self.assertEqual(ttf_copy.size, FONT_SIZE+1)
second_font_path = "Tests/fonts/DejaVuSans.ttf" second_font_path = "Tests/fonts/DejaVuSans.ttf"
ttf_copy = ttf.font_variant(font=second_font_path) ttf_copy = ttf.font_variant(font=second_font_path)
self.assertEqual(ttf_copy.path, second_font_path) self.assertEqual(ttf_copy.path, second_font_path)
def test_font_with_name(self): def test_font_with_name(self):
ImageFont.truetype(FONT_PATH, FONT_SIZE) self.get_font()
self._render(FONT_PATH) self._render(FONT_PATH)
self._clean() self._clean()
def _font_as_bytes(self): def _font_as_bytes(self):
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(self): def test_font_with_filelike(self):
ImageFont.truetype(self._font_as_bytes(), FONT_SIZE) ImageFont.truetype(self._font_as_bytes(), FONT_SIZE,
self._render(self._font_as_bytes()) layout_engine=self.LAYOUT_ENGINE)
# Usage note: making two fonts from the same buffer fails. self._render(self._font_as_bytes())
# shared_bytes = self._font_as_bytes() # Usage note: making two fonts from the same buffer fails.
# self._render(shared_bytes) # shared_bytes = self._font_as_bytes()
# self.assertRaises(Exception, lambda: _render(shared_bytes)) # self._render(shared_bytes)
self._clean() # self.assertRaises(Exception, lambda: _render(shared_bytes))
self._clean()
def test_font_with_open_file(self): def test_font_with_open_file(self):
with open(FONT_PATH, 'rb') as f: with open(FONT_PATH, 'rb') as f:
self._render(f) self._render(f)
self._clean() self._clean()
def _render(self, font): def _render(self, font):
txt = "Hello World!" txt = "Hello World!"
ttf = ImageFont.truetype(font, FONT_SIZE) ttf = ImageFont.truetype(font, FONT_SIZE,
ttf.getsize(txt) layout_engine=self.LAYOUT_ENGINE)
ttf.getsize(txt)
img = Image.new("RGB", (256, 64), "white") img = Image.new("RGB", (256, 64), "white")
d = ImageDraw.Draw(img) d = ImageDraw.Draw(img)
d.text((10, 10), txt, font=ttf, fill='black') d.text((10, 10), txt, font=ttf, fill='black')
img.save('font.png') img.save('font.png')
return img return img
def _clean(self): def _clean(self):
os.unlink('font.png') os.unlink('font.png')
def test_render_equal(self): def test_render_equal(self):
img_path = self._render(FONT_PATH) img_path = self._render(FONT_PATH)
with open(FONT_PATH, 'rb') as f: with open(FONT_PATH, 'rb') as f:
font_filelike = BytesIO(f.read()) font_filelike = BytesIO(f.read())
img_filelike = self._render(font_filelike) img_filelike = self._render(font_filelike)
self.assert_image_equal(img_path, img_filelike) self.assert_image_equal(img_path, img_filelike)
self._clean() self._clean()
def test_textsize_equal(self): def test_textsize_equal(self):
im = Image.new(mode='RGB', size=(300, 100))
draw = ImageDraw.Draw(im)
ttf = self.get_font()
txt = "Hello World!"
size = draw.textsize(txt, ttf)
draw.text((10, 10), txt, font=ttf)
draw.rectangle((10, 10, 10 + size[0], 10 + size[1]))
del draw
target = 'Tests/images/rectangle_surrounding_text.png'
target_img = Image.open(target)
# Epsilon ~.5 fails with FreeType 2.7
self.assert_image_similar(im, target_img, 2.5)
def test_render_multiline(self):
im = Image.new(mode='RGB', size=(300, 100))
draw = ImageDraw.Draw(im)
ttf = self.get_font()
line_spacing = draw.textsize('A', font=ttf)[1] + 4
lines = TEST_TEXT.split("\n")
y = 0
for line in lines:
draw.text((0, y), line, font=ttf)
y += line_spacing
target = 'Tests/images/multiline_text.png'
target_img = Image.open(target)
# some versions of freetype have different horizontal spacing.
# setting a tight epsilon, I'm showing the original test failure
# at epsilon = ~38.
self.assert_image_similar(im, target_img, 6.2)
def test_render_multiline_text(self):
ttf = self.get_font()
# Test that text() correctly connects to multiline_text()
# and that align defaults to left
im = Image.new(mode='RGB', size=(300, 100))
draw = ImageDraw.Draw(im)
draw.text((0, 0), TEST_TEXT, font=ttf)
target = 'Tests/images/multiline_text.png'
target_img = Image.open(target)
# Epsilon ~.5 fails with FreeType 2.7
self.assert_image_similar(im, target_img, 6.2)
# Test that text() can pass on additional arguments
# to multiline_text()
draw.text((0, 0), TEST_TEXT, fill=None, font=ttf, anchor=None,
spacing=4, align="left")
draw.text((0, 0), TEST_TEXT, None, ttf, None, 4, "left")
del draw
# Test align center and right
for align, ext in {"center": "_center",
"right": "_right"}.items():
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) draw.multiline_text((0, 0), TEST_TEXT, font=ttf, align=align)
txt = "Hello World!"
size = draw.textsize(txt, ttf)
draw.text((10, 10), txt, font=ttf)
draw.rectangle((10, 10, 10 + size[0], 10 + size[1]))
del draw del draw
target = 'Tests/images/rectangle_surrounding_text.png' target = 'Tests/images/multiline_text'+ext+'.png'
target_img = Image.open(target)
# Epsilon ~.5 fails with FreeType 2.7
self.assert_image_similar(im, target_img, 2.5)
def test_render_multiline(self):
im = Image.new(mode='RGB', size=(300, 100))
draw = ImageDraw.Draw(im)
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
line_spacing = draw.textsize('A', font=ttf)[1] + 4
lines = TEST_TEXT.split("\n")
y = 0
for line in lines:
draw.text((0, y), line, font=ttf)
y += line_spacing
target = 'Tests/images/multiline_text.png'
target_img = Image.open(target)
# some versions of freetype have different horizontal spacing.
# setting a tight epsilon, I'm showing the original test failure
# at epsilon = ~38.
self.assert_image_similar(im, target_img, 6.2)
def test_render_multiline_text(self):
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE)
# Test that text() correctly connects to multiline_text()
# and that align defaults to left
im = Image.new(mode='RGB', size=(300, 100))
draw = ImageDraw.Draw(im)
draw.text((0, 0), TEST_TEXT, font=ttf)
target = 'Tests/images/multiline_text.png'
target_img = Image.open(target) target_img = Image.open(target)
# Epsilon ~.5 fails with FreeType 2.7 # Epsilon ~.5 fails with FreeType 2.7
self.assert_image_similar(im, target_img, 6.2) self.assert_image_similar(im, target_img, 6.2)
# Test that text() can pass on additional arguments def test_unknown_align(self):
# to multiline_text() im = Image.new(mode='RGB', size=(300, 100))
draw.text((0, 0), TEST_TEXT, fill=None, font=ttf, anchor=None, draw = ImageDraw.Draw(im)
spacing=4, align="left") ttf = self.get_font()
draw.text((0, 0), TEST_TEXT, None, ttf, None, 4, "left")
del draw
# Test align center and right # Act/Assert
for align, ext in {"center": "_center", self.assertRaises(AssertionError,
"right": "_right"}.items(): lambda: draw.multiline_text((0, 0), TEST_TEXT,
im = Image.new(mode='RGB', size=(300, 100)) font=ttf,
draw = ImageDraw.Draw(im) align="unknown"))
draw.multiline_text((0, 0), TEST_TEXT, font=ttf, align=align)
del draw
target = 'Tests/images/multiline_text'+ext+'.png' def test_multiline_size(self):
target_img = Image.open(target) ttf = self.get_font()
im = Image.new(mode='RGB', size=(300, 100))
draw = ImageDraw.Draw(im)
# Epsilon ~.5 fails with FreeType 2.7 # Test that textsize() correctly connects to multiline_textsize()
self.assert_image_similar(im, target_img, 6.2) self.assertEqual(draw.textsize(TEST_TEXT, font=ttf),
draw.multiline_textsize(TEST_TEXT, font=ttf))
def test_unknown_align(self): # Test that textsize() can pass on additional arguments
im = Image.new(mode='RGB', size=(300, 100)) # to multiline_textsize()
draw = ImageDraw.Draw(im) draw.textsize(TEST_TEXT, font=ttf, spacing=4)
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) draw.textsize(TEST_TEXT, ttf, 4)
del draw
# Act/Assert def test_multiline_width(self):
self.assertRaises(AssertionError, ttf = self.get_font()
lambda: draw.multiline_text((0, 0), TEST_TEXT, im = Image.new(mode='RGB', size=(300, 100))
font=ttf, draw = ImageDraw.Draw(im)
align="unknown"))
def test_multiline_size(self): self.assertEqual(draw.textsize("longest line", font=ttf)[0],
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) draw.multiline_textsize("longest line\nline",
im = Image.new(mode='RGB', size=(300, 100)) font=ttf)[0])
draw = ImageDraw.Draw(im) del draw
# Test that textsize() correctly connects to multiline_textsize() def test_multiline_spacing(self):
self.assertEqual(draw.textsize(TEST_TEXT, font=ttf), ttf = self.get_font()
draw.multiline_textsize(TEST_TEXT, font=ttf))
# Test that textsize() can pass on additional arguments im = Image.new(mode='RGB', size=(300, 100))
# to multiline_textsize() draw = ImageDraw.Draw(im)
draw.textsize(TEST_TEXT, font=ttf, spacing=4) draw.multiline_text((0, 0), TEST_TEXT, font=ttf, spacing=10)
draw.textsize(TEST_TEXT, ttf, 4) del draw
del draw
def test_multiline_width(self): target = 'Tests/images/multiline_text_spacing.png'
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) target_img = Image.open(target)
im = Image.new(mode='RGB', size=(300, 100))
draw = ImageDraw.Draw(im)
self.assertEqual(draw.textsize("longest line", font=ttf)[0], # Epsilon ~.5 fails with FreeType 2.7
draw.multiline_textsize("longest line\nline", self.assert_image_similar(im, target_img, 6.2)
font=ttf)[0])
del draw
def test_multiline_spacing(self): def test_rotated_transposed_font(self):
ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) img_grey = Image.new("L", (100, 100))
draw = ImageDraw.Draw(img_grey)
word = "testing"
font = self.get_font()
im = Image.new(mode='RGB', size=(300, 100)) orientation = Image.ROTATE_90
draw = ImageDraw.Draw(im) transposed_font = ImageFont.TransposedFont(
draw.multiline_text((0, 0), TEST_TEXT, font=ttf, spacing=10) font, orientation=orientation)
del draw
target = 'Tests/images/multiline_text_spacing.png' # Original font
target_img = Image.open(target) draw.font = font
box_size_a = draw.textsize(word)
# Epsilon ~.5 fails with FreeType 2.7 # Rotated font
self.assert_image_similar(im, target_img, 6.2) draw.font = transposed_font
box_size_b = draw.textsize(word)
del draw
def test_rotated_transposed_font(self): # Check (w,h) of box a is (h,w) of box b
img_grey = Image.new("L", (100, 100)) self.assertEqual(box_size_a[0], box_size_b[1])
draw = ImageDraw.Draw(img_grey) self.assertEqual(box_size_a[1], box_size_b[0])
word = "testing"
font = ImageFont.truetype(FONT_PATH, FONT_SIZE)
orientation = Image.ROTATE_90 def test_unrotated_transposed_font(self):
transposed_font = ImageFont.TransposedFont( img_grey = Image.new("L", (100, 100))
font, orientation=orientation) draw = ImageDraw.Draw(img_grey)
word = "testing"
font = self.get_font()
# Original font orientation = None
draw.font = font transposed_font = ImageFont.TransposedFont(
box_size_a = draw.textsize(word) font, orientation=orientation)
# Rotated font # Original font
draw.font = transposed_font draw.font = font
box_size_b = draw.textsize(word) box_size_a = draw.textsize(word)
del draw
# Check (w,h) of box a is (h,w) of box b # Rotated font
self.assertEqual(box_size_a[0], box_size_b[1]) draw.font = transposed_font
self.assertEqual(box_size_a[1], box_size_b[0]) box_size_b = draw.textsize(word)
del draw
def test_unrotated_transposed_font(self): # Check boxes a and b are same size
img_grey = Image.new("L", (100, 100)) self.assertEqual(box_size_a, box_size_b)
draw = ImageDraw.Draw(img_grey)
word = "testing"
font = ImageFont.truetype(FONT_PATH, FONT_SIZE)
orientation = None def test_rotated_transposed_font_get_mask(self):
transposed_font = ImageFont.TransposedFont( # Arrange
font, orientation=orientation) text = "mask this"
font = self.get_font()
orientation = Image.ROTATE_90
transposed_font = ImageFont.TransposedFont(
font, orientation=orientation)
# Original font # Act
draw.font = font mask = transposed_font.getmask(text)
box_size_a = draw.textsize(word)
# Rotated font # Assert
draw.font = transposed_font self.assertEqual(mask.size, (13, 108))
box_size_b = draw.textsize(word)
del draw
# Check boxes a and b are same size def test_unrotated_transposed_font_get_mask(self):
self.assertEqual(box_size_a, box_size_b) # Arrange
text = "mask this"
font = self.get_font()
orientation = None
transposed_font = ImageFont.TransposedFont(
font, orientation=orientation)
def test_rotated_transposed_font_get_mask(self): # Act
# Arrange mask = transposed_font.getmask(text)
text = "mask this"
font = ImageFont.truetype(FONT_PATH, FONT_SIZE)
orientation = Image.ROTATE_90
transposed_font = ImageFont.TransposedFont(
font, orientation=orientation)
# Act # Assert
mask = transposed_font.getmask(text) self.assertEqual(mask.size, (108, 13))
# Assert def test_free_type_font_get_name(self):
self.assertEqual(mask.size, (13, 108)) # Arrange
font = self.get_font()
def test_unrotated_transposed_font_get_mask(self): # Act
# Arrange name = font.getname()
text = "mask this"
font = ImageFont.truetype(FONT_PATH, FONT_SIZE)
orientation = None
transposed_font = ImageFont.TransposedFont(
font, orientation=orientation)
# Act # Assert
mask = transposed_font.getmask(text) self.assertEqual(('FreeMono', 'Regular'), name)
# Assert def test_free_type_font_get_metrics(self):
self.assertEqual(mask.size, (108, 13)) # Arrange
font = self.get_font()
def test_free_type_font_get_name(self): # Act
# Arrange ascent, descent = font.getmetrics()
font = ImageFont.truetype(FONT_PATH, FONT_SIZE)
# Act # Assert
name = font.getname() self.assertIsInstance(ascent, int)
self.assertIsInstance(descent, int)
self.assertEqual((ascent, descent), (16, 4)) # too exact check?
# Assert def test_free_type_font_get_offset(self):
self.assertEqual(('FreeMono', 'Regular'), name) # Arrange
font = self.get_font()
text = "offset this"
def test_free_type_font_get_metrics(self): # Act
# Arrange offset = font.getoffset(text)
font = ImageFont.truetype(FONT_PATH, FONT_SIZE)
# Act # Assert
ascent, descent = font.getmetrics() self.assertEqual(offset, (0, 3))
# Assert def test_free_type_font_get_mask(self):
self.assertIsInstance(ascent, int) # Arrange
self.assertIsInstance(descent, int) font = self.get_font()
self.assertEqual((ascent, descent), (16, 4)) # too exact check? text = "mask this"
def test_free_type_font_get_offset(self): # Act
# Arrange mask = font.getmask(text)
font = ImageFont.truetype(FONT_PATH, FONT_SIZE)
text = "offset this"
# Act # Assert
offset = font.getoffset(text) self.assertEqual(mask.size, (108, 13))
# Assert def test_load_path_not_found(self):
self.assertEqual(offset, (0, 3)) # Arrange
filename = "somefilenamethatdoesntexist.ttf"
def test_free_type_font_get_mask(self): # Act/Assert
# Arrange self.assertRaises(IOError, lambda: ImageFont.load_path(filename))
font = ImageFont.truetype(FONT_PATH, FONT_SIZE)
text = "mask this"
# Act def test_default_font(self):
mask = font.getmask(text) # Arrange
txt = 'This is a "better than nothing" default font.'
im = Image.new(mode='RGB', size=(300, 100))
draw = ImageDraw.Draw(im)
# Assert target = 'Tests/images/default_font.png'
self.assertEqual(mask.size, (108, 13)) target_img = Image.open(target)
def test_load_path_not_found(self): # Act
# Arrange default_font = ImageFont.load_default()
filename = "somefilenamethatdoesntexist.ttf" draw.text((10, 10), txt, font=default_font)
del draw
# Act/Assert # Assert
self.assertRaises(IOError, lambda: ImageFont.load_path(filename)) self.assert_image_equal(im, target_img)
def test_default_font(self): def _test_fake_loading_font(self, path_to_fake, fontname):
# Arrange # Make a copy of FreeTypeFont so we can patch the original
txt = 'This is a "better than nothing" default font.' free_type_font = copy.deepcopy(ImageFont.FreeTypeFont)
im = Image.new(mode='RGB', size=(300, 100)) with SimplePatcher(ImageFont, '_FreeTypeFont', free_type_font):
draw = ImageDraw.Draw(im) def loadable_font(filepath, size, index, encoding, *args, **kwargs):
if filepath == path_to_fake:
target = 'Tests/images/default_font.png' return ImageFont._FreeTypeFont(FONT_PATH, size, index,
target_img = Image.open(target)
# Act
default_font = ImageFont.load_default()
draw.text((10, 10), txt, font=default_font)
del draw
# Assert
self.assert_image_equal(im, target_img)
def _test_fake_loading_font(self, path_to_fake, fontname):
# 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, *args, **kwargs):
if filepath == path_to_fake:
return ImageFont._FreeTypeFont(FONT_PATH, size, index,
encoding, *args, **kwargs)
return ImageFont._FreeTypeFont(filepath, size, index,
encoding, *args, **kwargs) encoding, *args, **kwargs)
with SimplePatcher(ImageFont, 'FreeTypeFont', loadable_font): return ImageFont._FreeTypeFont(filepath, size, index,
font = ImageFont.truetype(fontname) encoding, *args, **kwargs)
# Make sure it's loaded with SimplePatcher(ImageFont, 'FreeTypeFont', loadable_font):
name = font.getname() font = ImageFont.truetype(fontname)
self.assertEqual(('FreeMono', 'Regular'), name) # Make sure it's loaded
name = font.getname()
self.assertEqual(('FreeMono', 'Regular'), name)
@unittest.skipIf(sys.platform.startswith('win32'), @unittest.skipIf(sys.platform.startswith('win32'),
"requires Unix or MacOS") "requires Unix or MacOS")
def test_find_linux_font(self): def test_find_linux_font(self):
# A lot of mocking here - this is more for hitting code and # A lot of mocking here - this is more for hitting code and
# catching syntax like errors # catching syntax like errors
font_directory = '/usr/local/share/fonts' font_directory = '/usr/local/share/fonts'
with SimplePatcher(sys, 'platform', 'linux'): with SimplePatcher(sys, 'platform', 'linux'):
patched_env = copy.deepcopy(os.environ) patched_env = copy.deepcopy(os.environ)
patched_env['XDG_DATA_DIRS'] = '/usr/share/:/usr/local/share/' patched_env['XDG_DATA_DIRS'] = '/usr/share/:/usr/local/share/'
with SimplePatcher(os, 'environ', patched_env): with SimplePatcher(os, 'environ', patched_env):
def fake_walker(path):
if path == font_directory:
return [(path, [], [
'Arial.ttf', 'Single.otf', 'Duplicate.otf',
'Duplicate.ttf'], )]
return [(path, [], ['some_random_font.ttf'], )]
with SimplePatcher(os, 'walk', fake_walker):
# Test that the font loads both with and without the
# extension
self._test_fake_loading_font(
font_directory+'/Arial.ttf', 'Arial.ttf')
self._test_fake_loading_font(
font_directory+'/Arial.ttf', 'Arial')
# Test that non-ttf fonts can be found without the
# extension
self._test_fake_loading_font(
font_directory+'/Single.otf', 'Single')
# Test that ttf fonts are preferred if the extension is
# not specified
self._test_fake_loading_font(
font_directory+'/Duplicate.ttf', 'Duplicate')
@unittest.skipIf(sys.platform.startswith('win32'),
"requires Unix or MacOS")
def test_find_macos_font(self):
# Like the linux test, more cover hitting code rather than testing
# correctness.
font_directory = '/System/Library/Fonts'
with SimplePatcher(sys, 'platform', 'darwin'):
def fake_walker(path): def fake_walker(path):
if path == font_directory: if path == font_directory:
return [(path, [], return [(path, [], [
['Arial.ttf', 'Single.otf', 'Arial.ttf', 'Single.otf', 'Duplicate.otf',
'Duplicate.otf', 'Duplicate.ttf'], )] 'Duplicate.ttf'], )]
return [(path, [], ['some_random_font.ttf'], )] return [(path, [], ['some_random_font.ttf'], )]
with SimplePatcher(os, 'walk', fake_walker): with SimplePatcher(os, 'walk', fake_walker):
# Test that the font loads both with and without the
# extension
self._test_fake_loading_font( self._test_fake_loading_font(
font_directory+'/Arial.ttf', 'Arial.ttf') font_directory+'/Arial.ttf', 'Arial.ttf')
self._test_fake_loading_font( self._test_fake_loading_font(
font_directory+'/Arial.ttf', 'Arial') font_directory+'/Arial.ttf', 'Arial')
# Test that non-ttf fonts can be found without the
# extension
self._test_fake_loading_font( self._test_fake_loading_font(
font_directory+'/Single.otf', 'Single') font_directory+'/Single.otf', 'Single')
# Test that ttf fonts are preferred if the extension is
# not specified
self._test_fake_loading_font( self._test_fake_loading_font(
font_directory+'/Duplicate.ttf', 'Duplicate') font_directory+'/Duplicate.ttf', 'Duplicate')
def test_imagefont_getters(self): @unittest.skipIf(sys.platform.startswith('win32'),
# Arrange "requires Unix or MacOS")
t = ImageFont.truetype(FONT_PATH, FONT_SIZE) def test_find_macos_font(self):
# Like the linux test, more cover hitting code rather than testing
# correctness.
font_directory = '/System/Library/Fonts'
with SimplePatcher(sys, 'platform', 'darwin'):
def fake_walker(path):
if path == font_directory:
return [(path, [],
['Arial.ttf', 'Single.otf',
'Duplicate.otf', 'Duplicate.ttf'], )]
return [(path, [], ['some_random_font.ttf'], )]
with SimplePatcher(os, 'walk', fake_walker):
self._test_fake_loading_font(
font_directory+'/Arial.ttf', 'Arial.ttf')
self._test_fake_loading_font(
font_directory+'/Arial.ttf', 'Arial')
self._test_fake_loading_font(
font_directory+'/Single.otf', 'Single')
self._test_fake_loading_font(
font_directory+'/Duplicate.ttf', 'Duplicate')
# Act / Assert def test_imagefont_getters(self):
self.assertEqual(t.getmetrics(), (16, 4)) # Arrange
self.assertEqual(t.font.ascent, 16) t = self.get_font()
self.assertEqual(t.font.descent, 4)
self.assertEqual(t.font.height, 20) # Act / Assert
self.assertEqual(t.font.x_ppem, 20) self.assertEqual(t.getmetrics(), (16, 4))
self.assertEqual(t.font.y_ppem, 20) self.assertEqual(t.font.ascent, 16)
self.assertEqual(t.font.glyphs, 4177) self.assertEqual(t.font.descent, 4)
self.assertEqual(t.getsize('A'), (12, 16)) self.assertEqual(t.font.height, 20)
self.assertEqual(t.getsize('AB'), (24, 16)) self.assertEqual(t.font.x_ppem, 20)
self.assertEqual(t.getsize('M'), (12, 16)) self.assertEqual(t.font.y_ppem, 20)
self.assertEqual(t.getsize('y'), (12, 20)) self.assertEqual(t.font.glyphs, 4177)
self.assertEqual(t.getsize('a'), (12, 16)) self.assertEqual(t.getsize('A'), (12, 16))
self.assertEqual(t.getsize('AB'), (24, 16))
self.assertEqual(t.getsize('M'), (12, 16))
self.assertEqual(t.getsize('y'), (12, 20))
self.assertEqual(t.getsize('a'), (12, 16))
except ImportError: @unittest.skipUnless(HAS_RAQM, "Raqm not Available")
class TestImageFont(PillowTestCase): class TestImageFont_RaqmLayout(TestImageFont):
def test_skip(self): LAYOUT_ENGINE = ImageFont.LAYOUT_RAQM
self.skipTest("ImportError")
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()