Traktor/myenv/Lib/site-packages/pygame/tests/font_test.py
2024-05-26 05:12:46 +02:00

750 lines
27 KiB
Python

# -*- coding: utf-8 -*-
from re import T
import sys
import os
import unittest
import pathlib
import platform
import pygame
from pygame import font as pygame_font # So font can be replaced with ftfont
FONTDIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "fixtures", "fonts")
def equal_images(s1, s2):
size = s1.get_size()
if s2.get_size() != size:
return False
w, h = size
for x in range(w):
for y in range(h):
if s1.get_at((x, y)) != s2.get_at((x, y)):
return False
return True
IS_PYPY = "PyPy" == platform.python_implementation()
@unittest.skipIf(IS_PYPY, "pypy skip known failure") # TODO
class FontModuleTest(unittest.TestCase):
def setUp(self):
pygame_font.init()
def tearDown(self):
pygame_font.quit()
def test_get_sdl_ttf_version(self):
def test_ver_tuple(ver):
self.assertIsInstance(ver, tuple)
self.assertEqual(len(ver), 3)
for i in ver:
self.assertIsInstance(i, int)
if pygame_font.__name__ != "pygame.ftfont":
compiled = pygame_font.get_sdl_ttf_version()
linked = pygame_font.get_sdl_ttf_version(linked=True)
test_ver_tuple(compiled)
test_ver_tuple(linked)
self.assertTrue(linked >= compiled)
def test_SysFont(self):
# Can only check that a font object is returned.
fonts = pygame_font.get_fonts()
if "arial" in fonts:
# Try to use arial font if it is there, rather than a random font
# which can be different depending on installed fonts on the system.
font_name = "arial"
else:
font_name = sorted(fonts)[0]
o = pygame_font.SysFont(font_name, 20)
self.assertTrue(isinstance(o, pygame_font.FontType))
o = pygame_font.SysFont(font_name, 20, italic=True)
self.assertTrue(isinstance(o, pygame_font.FontType))
o = pygame_font.SysFont(font_name, 20, bold=True)
self.assertTrue(isinstance(o, pygame_font.FontType))
o = pygame_font.SysFont("thisisnotafont", 20)
self.assertTrue(isinstance(o, pygame_font.FontType))
def test_get_default_font(self):
self.assertEqual(pygame_font.get_default_font(), "freesansbold.ttf")
def test_get_fonts_returns_something(self):
fnts = pygame_font.get_fonts()
self.assertTrue(fnts)
# to test if some files exist...
# def XXtest_has_file_osx_10_5_sdk(self):
# import os
# f = "/Developer/SDKs/MacOSX10.5.sdk/usr/X11/include/ft2build.h"
# self.assertEqual(os.path.exists(f), True)
# def XXtest_has_file_osx_10_4_sdk(self):
# import os
# f = "/Developer/SDKs/MacOSX10.4u.sdk/usr/X11R6/include/ft2build.h"
# self.assertEqual(os.path.exists(f), True)
def test_get_fonts(self):
fnts = pygame_font.get_fonts()
self.assertTrue(fnts, msg=repr(fnts))
for name in fnts:
# note, on ubuntu 2.6 they are all unicode strings.
self.assertTrue(isinstance(name, str), name)
# Font names can be comprised of only numeric characters, so
# just checking name.islower() will not work as expected here.
self.assertFalse(any(c.isupper() for c in name))
self.assertTrue(name.isalnum(), name)
def test_get_init(self):
self.assertTrue(pygame_font.get_init())
pygame_font.quit()
self.assertFalse(pygame_font.get_init())
def test_init(self):
pygame_font.init()
def test_match_font_all_exist(self):
fonts = pygame_font.get_fonts()
# Ensure all listed fonts are in fact available, and the returned file
# name is a full path.
for font in fonts:
path = pygame_font.match_font(font)
self.assertFalse(path is None)
self.assertTrue(os.path.isabs(path) and os.path.isfile(path))
def test_match_font_name(self):
"""That match_font accepts names of various types"""
font = pygame_font.get_fonts()[0]
font_path = pygame_font.match_font(font)
self.assertIsNotNone(font_path)
font_b = font.encode()
not_a_font = "thisisnotafont"
not_a_font_b = b"thisisnotafont"
good_font_names = [
# Check single name bytes.
font_b,
# Check string of comma-separated names.
",".join([not_a_font, font, not_a_font]),
# Check list of names.
[not_a_font, font, not_a_font],
# Check generator:
(name for name in [not_a_font, font, not_a_font]),
# Check comma-separated bytes.
b",".join([not_a_font_b, font_b, not_a_font_b]),
# Check list of bytes.
[not_a_font_b, font_b, not_a_font_b],
# Check mixed list of bytes and string.
[font, not_a_font, font_b, not_a_font_b],
]
for font_name in good_font_names:
self.assertEqual(pygame_font.match_font(font_name), font_path, font_name)
def test_not_match_font_name(self):
"""match_font return None when names of various types do not exist"""
not_a_font = "thisisnotafont"
not_a_font_b = b"thisisnotafont"
bad_font_names = [
not_a_font,
",".join([not_a_font, not_a_font, not_a_font]),
[not_a_font, not_a_font, not_a_font],
(name for name in [not_a_font, not_a_font, not_a_font]),
not_a_font_b,
b",".join([not_a_font_b, not_a_font_b, not_a_font_b]),
[not_a_font_b, not_a_font_b, not_a_font_b],
[not_a_font, not_a_font_b, not_a_font],
]
for font_name in bad_font_names:
self.assertIsNone(pygame_font.match_font(font_name), font_name)
def test_match_font_bold(self):
fonts = pygame_font.get_fonts()
# Look for a bold font.
self.assertTrue(any(pygame_font.match_font(font, bold=True) for font in fonts))
def test_match_font_italic(self):
fonts = pygame_font.get_fonts()
# Look for an italic font.
self.assertTrue(
any(pygame_font.match_font(font, italic=True) for font in fonts)
)
def test_issue_742(self):
"""that the font background does not crash."""
surf = pygame.Surface((320, 240))
font = pygame_font.Font(None, 24)
image = font.render("Test", 0, (255, 255, 255), (0, 0, 0))
self.assertIsNone(image.get_colorkey())
image.set_alpha(255)
surf.blit(image, (0, 0))
# not issue 742, but be sure to test that background color is
# correctly issued on this mode
self.assertEqual(surf.get_at((0, 0)), pygame.Color(0, 0, 0))
def test_issue_font_alphablit(self):
"""Check that blitting anti-aliased text doesn't
change the background blue"""
pygame.display.set_mode((600, 400))
font = pygame_font.Font(None, 24)
(color, text, center, pos) = ((160, 200, 250), "Music", (190, 170), "midright")
img1 = font.render(text, True, color)
img = pygame.Surface(img1.get_size(), depth=32)
pre_blit_corner_pixel = img.get_at((0, 0))
img.blit(img1, (0, 0))
post_blit_corner_pixel = img.get_at((0, 0))
self.assertEqual(pre_blit_corner_pixel, post_blit_corner_pixel)
def test_segfault_after_reinit(self):
"""Reinitialization of font module should not cause
segmentation fault"""
import gc
font = pygame_font.Font(None, 20)
pygame_font.quit()
pygame_font.init()
del font
gc.collect()
def test_quit(self):
pygame_font.quit()
@unittest.skipIf(IS_PYPY, "pypy skip known failure") # TODO
class FontTest(unittest.TestCase):
def setUp(self):
pygame_font.init()
def tearDown(self):
pygame_font.quit()
def test_render_args(self):
screen = pygame.display.set_mode((600, 400))
rect = screen.get_rect()
f = pygame_font.Font(None, 20)
screen.fill((10, 10, 10))
font_surface = f.render(" bar", True, (0, 0, 0), (255, 255, 255))
font_rect = font_surface.get_rect()
font_rect.topleft = rect.topleft
self.assertTrue(font_surface)
screen.blit(font_surface, font_rect, font_rect)
pygame.display.update()
self.assertEqual(tuple(screen.get_at((0, 0)))[:3], (255, 255, 255))
self.assertEqual(tuple(screen.get_at(font_rect.topleft))[:3], (255, 255, 255))
# If we don't have a real display, don't do this test.
# Transparent background doesn't seem to work without a read video card.
if os.environ.get("SDL_VIDEODRIVER") != "dummy":
screen.fill((10, 10, 10))
font_surface = f.render(" bar", True, (0, 0, 0), None)
font_rect = font_surface.get_rect()
font_rect.topleft = rect.topleft
self.assertTrue(font_surface)
screen.blit(font_surface, font_rect, font_rect)
pygame.display.update()
self.assertEqual(tuple(screen.get_at((0, 0)))[:3], (10, 10, 10))
self.assertEqual(tuple(screen.get_at(font_rect.topleft))[:3], (10, 10, 10))
screen.fill((10, 10, 10))
font_surface = f.render(" bar", True, (0, 0, 0))
font_rect = font_surface.get_rect()
font_rect.topleft = rect.topleft
self.assertTrue(font_surface)
screen.blit(font_surface, font_rect, font_rect)
pygame.display.update(rect)
self.assertEqual(tuple(screen.get_at((0, 0)))[:3], (10, 10, 10))
self.assertEqual(tuple(screen.get_at(font_rect.topleft))[:3], (10, 10, 10))
@unittest.skipIf(IS_PYPY, "pypy skip known failure") # TODO
class FontTypeTest(unittest.TestCase):
def setUp(self):
pygame_font.init()
def tearDown(self):
pygame_font.quit()
def test_default_parameters(self):
f = pygame_font.Font()
def test_get_ascent(self):
# Ckecking ascent would need a custom test font to do properly.
f = pygame_font.Font(None, 20)
ascent = f.get_ascent()
self.assertTrue(isinstance(ascent, int))
self.assertTrue(ascent > 0)
s = f.render("X", False, (255, 255, 255))
self.assertTrue(s.get_size()[1] > ascent)
def test_get_descent(self):
# Ckecking descent would need a custom test font to do properly.
f = pygame_font.Font(None, 20)
descent = f.get_descent()
self.assertTrue(isinstance(descent, int))
self.assertTrue(descent < 0)
def test_get_height(self):
# Ckecking height would need a custom test font to do properly.
f = pygame_font.Font(None, 20)
height = f.get_height()
self.assertTrue(isinstance(height, int))
self.assertTrue(height > 0)
s = f.render("X", False, (255, 255, 255))
self.assertTrue(s.get_size()[1] == height)
def test_get_linesize(self):
# Ckecking linesize would need a custom test font to do properly.
# Questions: How do linesize, height and descent relate?
f = pygame_font.Font(None, 20)
linesize = f.get_linesize()
self.assertTrue(isinstance(linesize, int))
self.assertTrue(linesize > 0)
def test_metrics(self):
# Ensure bytes decoding works correctly. Can only compare results
# with unicode for now.
f = pygame_font.Font(None, 20)
um = f.metrics(".")
bm = f.metrics(b".")
self.assertEqual(len(um), 1)
self.assertEqual(len(bm), 1)
self.assertIsNotNone(um[0])
self.assertEqual(um, bm)
u = "\u212A"
b = u.encode("UTF-16")[2:] # Keep byte order consistent. [2:] skips BOM
bm = f.metrics(b)
self.assertEqual(len(bm), 2)
try: # FIXME why do we do this try/except ?
um = f.metrics(u)
except pygame.error:
pass
else:
self.assertEqual(len(um), 1)
self.assertNotEqual(bm[0], um[0])
self.assertNotEqual(bm[1], um[0])
u = "\U00013000"
bm = f.metrics(u)
self.assertEqual(len(bm), 1)
self.assertIsNone(bm[0])
return # unfinished
# The documentation is useless here. How large a list?
# How do list positions relate to character codes?
# What about unicode characters?
# __doc__ (as of 2008-08-02) for pygame_font.Font.metrics:
# Font.metrics(text): return list
# Gets the metrics for each character in the passed string.
#
# The list contains tuples for each character, which contain the
# minimum X offset, the maximum X offset, the minimum Y offset, the
# maximum Y offset and the advance offset (bearing plus width) of the
# character. [(minx, maxx, miny, maxy, advance), (minx, maxx, miny,
# maxy, advance), ...]
self.fail()
def test_render(self):
f = pygame_font.Font(None, 20)
s = f.render("foo", True, [0, 0, 0], [255, 255, 255])
s = f.render("xxx", True, [0, 0, 0], [255, 255, 255])
s = f.render("", True, [0, 0, 0], [255, 255, 255])
s = f.render("foo", False, [0, 0, 0], [255, 255, 255])
s = f.render("xxx", False, [0, 0, 0], [255, 255, 255])
s = f.render("xxx", False, [0, 0, 0])
s = f.render(" ", False, [0, 0, 0])
s = f.render(" ", False, [0, 0, 0], [255, 255, 255])
# null text should be 0 pixel wide.
s = f.render("", False, [0, 0, 0], [255, 255, 255])
self.assertEqual(s.get_size()[0], 0)
# None text should be 0 pixel wide.
s = f.render(None, False, [0, 0, 0], [255, 255, 255])
self.assertEqual(s.get_size()[0], 0)
# Non-text should raise a TypeError.
self.assertRaises(TypeError, f.render, [], False, [0, 0, 0], [255, 255, 255])
self.assertRaises(TypeError, f.render, 1, False, [0, 0, 0], [255, 255, 255])
# is background transparent for antialiasing?
s = f.render(".", True, [255, 255, 255])
self.assertEqual(s.get_at((0, 0))[3], 0)
# is Unicode and bytes encoding correct?
# Cannot really test if the correct characters are rendered, but
# at least can assert the encodings differ.
su = f.render(".", False, [0, 0, 0], [255, 255, 255])
sb = f.render(b".", False, [0, 0, 0], [255, 255, 255])
self.assertTrue(equal_images(su, sb))
u = "\u212A"
b = u.encode("UTF-16")[2:] # Keep byte order consistent. [2:] skips BOM
sb = f.render(b, False, [0, 0, 0], [255, 255, 255])
try: # FIXME why do we do this try/except ?
su = f.render(u, False, [0, 0, 0], [255, 255, 255])
except pygame.error:
pass
else:
self.assertFalse(equal_images(su, sb))
# test for internal null bytes
self.assertRaises(ValueError, f.render, b"ab\x00cd", 0, [0, 0, 0])
self.assertRaises(ValueError, f.render, "ab\x00cd", 0, [0, 0, 0])
def test_render_ucs2_ucs4(self):
"""that it renders without raising if there is a new enough SDL_ttf."""
f = pygame_font.Font(None, 20)
# If the font module is SDL_ttf < 2.0.15 based, then it only supports UCS-2
# it will raise an exception for an out-of-range UCS-4 code point.
if hasattr(pygame_font, "UCS4"):
ucs_2 = "\uFFEE"
s = f.render(ucs_2, False, [0, 0, 0], [255, 255, 255])
ucs_4 = "\U00010000"
s = f.render(ucs_4, False, [0, 0, 0], [255, 255, 255])
def test_set_bold(self):
f = pygame_font.Font(None, 20)
self.assertFalse(f.get_bold())
f.set_bold(True)
self.assertTrue(f.get_bold())
f.set_bold(False)
self.assertFalse(f.get_bold())
def test_set_italic(self):
f = pygame_font.Font(None, 20)
self.assertFalse(f.get_italic())
f.set_italic(True)
self.assertTrue(f.get_italic())
f.set_italic(False)
self.assertFalse(f.get_italic())
def test_set_underline(self):
f = pygame_font.Font(None, 20)
self.assertFalse(f.get_underline())
f.set_underline(True)
self.assertTrue(f.get_underline())
f.set_underline(False)
self.assertFalse(f.get_underline())
def test_set_strikethrough(self):
if pygame_font.__name__ != "pygame.ftfont":
f = pygame_font.Font(None, 20)
self.assertFalse(f.get_strikethrough())
f.set_strikethrough(True)
self.assertTrue(f.get_strikethrough())
f.set_strikethrough(False)
self.assertFalse(f.get_strikethrough())
def test_bold_attr(self):
f = pygame_font.Font(None, 20)
self.assertFalse(f.bold)
f.bold = True
self.assertTrue(f.bold)
f.bold = False
self.assertFalse(f.bold)
def test_set_italic_property(self):
f = pygame_font.Font(None, 20)
self.assertFalse(f.italic)
f.italic = True
self.assertTrue(f.italic)
f.italic = False
self.assertFalse(f.italic)
def test_set_underline_property(self):
f = pygame_font.Font(None, 20)
self.assertFalse(f.underline)
f.underline = True
self.assertTrue(f.underline)
f.underline = False
self.assertFalse(f.underline)
def test_set_strikethrough_property(self):
if pygame_font.__name__ != "pygame.ftfont":
f = pygame_font.Font(None, 20)
self.assertFalse(f.strikethrough)
f.strikethrough = True
self.assertTrue(f.strikethrough)
f.strikethrough = False
self.assertFalse(f.strikethrough)
def test_size(self):
f = pygame_font.Font(None, 20)
text = "Xg"
size = f.size(text)
w, h = size
s = f.render(text, False, (255, 255, 255))
btext = text.encode("ascii")
self.assertIsInstance(w, int)
self.assertIsInstance(h, int)
self.assertEqual(s.get_size(), size)
self.assertEqual(f.size(btext), size)
text = "\u212A"
btext = text.encode("UTF-16")[2:] # Keep the byte order consistent.
bsize = f.size(btext)
size = f.size(text)
self.assertNotEqual(size, bsize)
def test_font_file_not_found(self):
# A per BUG reported by Bo Jangeborg on pygame-user mailing list,
# http://www.mail-archive.com/pygame-users@seul.org/msg11675.html
pygame_font.init()
self.assertRaises(
FileNotFoundError, pygame_font.Font, "some-fictional-font.ttf", 20
)
def test_load_from_file(self):
font_name = pygame_font.get_default_font()
font_path = os.path.join(
os.path.split(pygame.__file__)[0], pygame_font.get_default_font()
)
f = pygame_font.Font(font_path, 20)
def test_load_from_file_default(self):
font_name = pygame_font.get_default_font()
font_path = os.path.join(
os.path.split(pygame.__file__)[0], pygame_font.get_default_font()
)
f = pygame_font.Font(font_path)
def test_load_from_pathlib(self):
font_name = pygame_font.get_default_font()
font_path = os.path.join(
os.path.split(pygame.__file__)[0], pygame_font.get_default_font()
)
f = pygame_font.Font(pathlib.Path(font_path), 20)
f = pygame_font.Font(pathlib.Path(font_path))
def test_load_from_pathlib_default(self):
font_name = pygame_font.get_default_font()
font_path = os.path.join(
os.path.split(pygame.__file__)[0], pygame_font.get_default_font()
)
f = pygame_font.Font(pathlib.Path(font_path))
def test_load_from_file_obj(self):
font_name = pygame_font.get_default_font()
font_path = os.path.join(
os.path.split(pygame.__file__)[0], pygame_font.get_default_font()
)
with open(font_path, "rb") as f:
font = pygame_font.Font(f, 20)
def test_load_from_file_obj_default(self):
font_name = pygame_font.get_default_font()
font_path = os.path.join(
os.path.split(pygame.__file__)[0], pygame_font.get_default_font()
)
with open(font_path, "rb") as f:
font = pygame_font.Font(f)
def test_load_default_font_filename(self):
# In font_init, a special case is when the filename argument is
# identical to the default font file name.
f = pygame_font.Font(pygame_font.get_default_font(), 20)
def test_load_default_font_filename_default(self):
# In font_init, a special case is when the filename argument is
# identical to the default font file name.
f = pygame_font.Font(pygame_font.get_default_font())
def _load_unicode(self, path):
import shutil
fdir = str(FONTDIR)
temp = os.path.join(fdir, path)
pgfont = os.path.join(fdir, "test_sans.ttf")
shutil.copy(pgfont, temp)
try:
with open(temp, "rb") as f:
pass
except FileNotFoundError:
raise unittest.SkipTest("the path cannot be opened")
try:
pygame_font.Font(temp, 20)
finally:
os.remove(temp)
def test_load_from_file_unicode_0(self):
"""ASCII string as a unicode object"""
self._load_unicode("temp_file.ttf")
def test_load_from_file_unicode_1(self):
self._load_unicode("你好.ttf")
def test_load_from_file_bytes(self):
font_path = os.path.join(
os.path.split(pygame.__file__)[0], pygame_font.get_default_font()
)
filesystem_encoding = sys.getfilesystemencoding()
filesystem_errors = "replace" if sys.platform == "win32" else "surrogateescape"
try: # FIXME why do we do this try/except ?
font_path = font_path.decode(filesystem_encoding, filesystem_errors)
except AttributeError:
pass
bfont_path = font_path.encode(filesystem_encoding, filesystem_errors)
f = pygame_font.Font(bfont_path, 20)
def test_issue_3144(self):
fpath = os.path.join(FONTDIR, "PlayfairDisplaySemibold.ttf")
# issue in SDL_ttf 2.0.18 DLL on Windows
# tested to make us aware of any regressions
for size in (60, 40, 10, 20, 70, 45, 50, 10):
font = pygame_font.Font(fpath, size)
font.render("WHERE", True, "black")
def test_font_set_script(self):
if pygame_font.__name__ == "pygame.ftfont":
return # this ain't a pygame.ftfont thing!
font = pygame_font.Font(None, 16)
ttf_version = pygame_font.get_sdl_ttf_version()
if ttf_version >= (2, 20, 0):
self.assertRaises(TypeError, pygame.font.Font.set_script)
self.assertRaises(TypeError, pygame.font.Font.set_script, font)
self.assertRaises(TypeError, pygame.font.Font.set_script, "hey", "Deva")
self.assertRaises(TypeError, font.set_script, 1)
self.assertRaises(TypeError, font.set_script, ["D", "e", "v", "a"])
self.assertRaises(ValueError, font.set_script, "too long by far")
self.assertRaises(ValueError, font.set_script, "")
self.assertRaises(ValueError, font.set_script, "a")
font.set_script("Deva")
else:
self.assertRaises(pygame.error, font.set_script, "Deva")
@unittest.skipIf(IS_PYPY, "pypy skip known failure") # TODO
class VisualTests(unittest.TestCase):
__tags__ = ["interactive"]
screen = None
aborted = False
def setUp(self):
if self.screen is None:
pygame.init()
self.screen = pygame.display.set_mode((600, 200))
self.screen.fill((255, 255, 255))
pygame.display.flip()
self.f = pygame_font.Font(None, 32)
def abort(self):
if self.screen is not None:
pygame.quit()
self.aborted = True
def query(
self,
bold=False,
italic=False,
underline=False,
strikethrough=False,
antialiase=False,
):
if self.aborted:
return False
spacing = 10
offset = 20
y = spacing
f = self.f
screen = self.screen
screen.fill((255, 255, 255))
pygame.display.flip()
if not (bold or italic or underline or strikethrough or antialiase):
text = "normal"
else:
modes = []
if bold:
modes.append("bold")
if italic:
modes.append("italic")
if underline:
modes.append("underlined")
if strikethrough:
modes.append("strikethrough")
if antialiase:
modes.append("antialiased")
text = f"{'-'.join(modes)} (y/n):"
f.set_bold(bold)
f.set_italic(italic)
f.set_underline(underline)
if pygame_font.__name__ != "pygame.ftfont":
f.set_strikethrough(strikethrough)
s = f.render(text, antialiase, (0, 0, 0))
screen.blit(s, (offset, y))
y += s.get_size()[1] + spacing
f.set_bold(False)
f.set_italic(False)
f.set_underline(False)
if pygame_font.__name__ != "pygame.ftfont":
f.set_strikethrough(False)
s = f.render("(some comparison text)", False, (0, 0, 0))
screen.blit(s, (offset, y))
pygame.display.flip()
while True:
for evt in pygame.event.get():
if evt.type == pygame.KEYDOWN:
if evt.key == pygame.K_ESCAPE:
self.abort()
return False
if evt.key == pygame.K_y:
return True
if evt.key == pygame.K_n:
return False
if evt.type == pygame.QUIT:
self.abort()
return False
def test_bold(self):
self.assertTrue(self.query(bold=True))
def test_italic(self):
self.assertTrue(self.query(italic=True))
def test_underline(self):
self.assertTrue(self.query(underline=True))
def test_strikethrough(self):
if pygame_font.__name__ != "pygame.ftfont":
self.assertTrue(self.query(strikethrough=True))
def test_antialiase(self):
self.assertTrue(self.query(antialiase=True))
def test_bold_antialiase(self):
self.assertTrue(self.query(bold=True, antialiase=True))
def test_italic_underline(self):
self.assertTrue(self.query(italic=True, underline=True))
def test_bold_strikethrough(self):
if pygame_font.__name__ != "pygame.ftfont":
self.assertTrue(self.query(bold=True, strikethrough=True))
if __name__ == "__main__":
unittest.main()