286 lines
10 KiB
Python
286 lines
10 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Unit tests for text rendering fixes.
|
|
Tests the fixes for text cropping and line length issues.
|
|
"""
|
|
|
|
import unittest
|
|
import os
|
|
from PIL import Image, ImageFont
|
|
|
|
from pyWebLayout.concrete.text import Text, Line
|
|
from pyWebLayout.style import Font, FontStyle, FontWeight
|
|
from pyWebLayout.style.layout import Alignment
|
|
|
|
|
|
class TestTextRenderingFix(unittest.TestCase):
|
|
"""Test cases for text rendering fixes"""
|
|
|
|
def setUp(self):
|
|
"""Set up test fixtures"""
|
|
self.font_style = Font(
|
|
font_path=None, # Use default font
|
|
font_size=16,
|
|
colour=(0, 0, 0, 255),
|
|
weight=FontWeight.NORMAL,
|
|
style=FontStyle.NORMAL
|
|
)
|
|
|
|
# Clean up any existing test images
|
|
self.test_images = []
|
|
|
|
def tearDown(self):
|
|
"""Clean up after tests"""
|
|
# Clean up test images
|
|
for img in self.test_images:
|
|
if os.path.exists(img):
|
|
os.remove(img)
|
|
|
|
def test_text_cropping_fix(self):
|
|
"""Test that text is no longer cropped at the beginning and end"""
|
|
# Test with text that might have overhang (like italic or characters with descenders)
|
|
test_texts = [
|
|
"Hello World!",
|
|
"Typography",
|
|
"gjpqy", # Characters with descenders
|
|
"AWVT", # Characters that might have overhang
|
|
"Italic Text"
|
|
]
|
|
|
|
for i, text_content in enumerate(test_texts):
|
|
with self.subTest(text=text_content):
|
|
text = Text(text_content, self.font_style)
|
|
|
|
# Verify dimensions are reasonable
|
|
self.assertGreater(text.width, 0, f"Text '{text_content}' should have positive width")
|
|
self.assertGreater(text.height, 0, f"Text '{text_content}' should have positive height")
|
|
|
|
# Render the text
|
|
rendered = text.render()
|
|
|
|
# Verify rendered image
|
|
self.assertIsInstance(rendered, Image.Image)
|
|
self.assertGreater(rendered.size[0], 0, "Rendered image should have positive width")
|
|
self.assertGreater(rendered.size[1], 0, "Rendered image should have positive height")
|
|
|
|
# Save for visual inspection
|
|
output_path = f"test_text_{i}_{text_content.replace(' ', '_').replace('!', '')}.png"
|
|
rendered.save(output_path)
|
|
self.test_images.append(output_path)
|
|
self.assertTrue(os.path.exists(output_path), f"Test image should be created for '{text_content}'")
|
|
|
|
def test_line_length_fix(self):
|
|
"""Test that lines are using the full available width properly"""
|
|
font_style = Font(
|
|
font_path=None,
|
|
font_size=14,
|
|
colour=(0, 0, 0, 255)
|
|
)
|
|
|
|
# Create a line with specific width
|
|
line_width = 300
|
|
line_height = 20
|
|
spacing = (5, 10) # min, max spacing
|
|
|
|
line = Line(
|
|
spacing=spacing,
|
|
origin=(0, 0),
|
|
size=(line_width, line_height),
|
|
font=font_style,
|
|
halign=Alignment.LEFT
|
|
)
|
|
|
|
# Add words to the line
|
|
words = ["This", "is", "a", "test", "of", "line", "length", "calculation"]
|
|
|
|
words_added = 0
|
|
for word in words:
|
|
result = line.add_word(word)
|
|
if result:
|
|
# Word didn't fit
|
|
break
|
|
else:
|
|
words_added += 1
|
|
|
|
# Assertions
|
|
self.assertGreater(words_added, 0, "Should have added at least one word")
|
|
self.assertGreaterEqual(line._current_width, 0, "Line width should be non-negative")
|
|
self.assertLessEqual(line._current_width, line_width, "Line width should not exceed maximum")
|
|
|
|
# Render the line
|
|
rendered_line = line.render()
|
|
self.assertIsInstance(rendered_line, Image.Image)
|
|
self.assertEqual(rendered_line.size, (line_width, line_height))
|
|
|
|
# Save for inspection
|
|
output_path = "test_line_length.png"
|
|
rendered_line.save(output_path)
|
|
self.test_images.append(output_path)
|
|
self.assertTrue(os.path.exists(output_path), "Line test image should be created")
|
|
|
|
def test_justification(self):
|
|
"""Test text justification to ensure proper spacing"""
|
|
font_style = Font(
|
|
font_path=None,
|
|
font_size=12,
|
|
colour=(0, 0, 0, 255)
|
|
)
|
|
|
|
alignments = [
|
|
(Alignment.LEFT, "left"),
|
|
(Alignment.CENTER, "center"),
|
|
(Alignment.RIGHT, "right"),
|
|
(Alignment.JUSTIFY, "justify")
|
|
]
|
|
|
|
for alignment, name in alignments:
|
|
with self.subTest(alignment=name):
|
|
line = Line(
|
|
spacing=(3, 8),
|
|
origin=(0, 0),
|
|
size=(250, 18),
|
|
font=font_style,
|
|
halign=alignment
|
|
)
|
|
|
|
# Add some words
|
|
words = ["Testing", "text", "alignment", "and", "spacing"]
|
|
for word in words:
|
|
line.add_word(word)
|
|
|
|
# Verify line has content
|
|
self.assertGreater(len(line.text_objects), 0, f"{name} alignment should have text objects")
|
|
|
|
# Render and verify
|
|
rendered = line.render()
|
|
self.assertIsInstance(rendered, Image.Image)
|
|
self.assertEqual(rendered.size, (250, 18))
|
|
|
|
# Save for inspection
|
|
output_path = f"test_alignment_{name}.png"
|
|
rendered.save(output_path)
|
|
self.test_images.append(output_path)
|
|
self.assertTrue(os.path.exists(output_path), f"Alignment test image should be created for {name}")
|
|
|
|
def test_text_dimensions_consistency(self):
|
|
"""Test that text dimensions are consistent between calculation and rendering"""
|
|
test_texts = ["Short", "Medium length text", "Very long text that might cause issues"]
|
|
|
|
for text_content in test_texts:
|
|
with self.subTest(text=text_content):
|
|
text = Text(text_content, self.font_style)
|
|
|
|
# Get calculated dimensions
|
|
calc_width = text.width
|
|
calc_height = text.height
|
|
calc_size = text.size
|
|
|
|
# Verify consistency
|
|
self.assertEqual(calc_size, (calc_width, calc_height))
|
|
self.assertGreater(calc_width, 0)
|
|
self.assertGreater(calc_height, 0)
|
|
|
|
# Render and check dimensions match expectation
|
|
rendered = text.render()
|
|
self.assertIsInstance(rendered, Image.Image)
|
|
# Note: rendered size might differ slightly due to margins/padding
|
|
self.assertGreaterEqual(rendered.size[0], calc_width - 10) # Allow small tolerance
|
|
self.assertGreaterEqual(rendered.size[1], calc_height - 10)
|
|
|
|
def test_different_font_sizes(self):
|
|
"""Test text rendering with different font sizes"""
|
|
font_sizes = [8, 12, 16, 20, 24]
|
|
test_text = "Sample Text"
|
|
|
|
for font_size in font_sizes:
|
|
with self.subTest(font_size=font_size):
|
|
font = Font(
|
|
font_path=None,
|
|
font_size=font_size,
|
|
colour=(0, 0, 0, 255)
|
|
)
|
|
|
|
text = Text(test_text, font)
|
|
|
|
# Larger fonts should generally produce larger text
|
|
self.assertGreater(text.width, 0)
|
|
self.assertGreater(text.height, 0)
|
|
|
|
# Render should work
|
|
rendered = text.render()
|
|
self.assertIsInstance(rendered, Image.Image)
|
|
|
|
def test_empty_text_handling(self):
|
|
"""Test handling of empty text"""
|
|
text = Text("", self.font_style)
|
|
|
|
# Should handle empty text gracefully
|
|
self.assertGreaterEqual(text.width, 0)
|
|
self.assertGreaterEqual(text.height, 0)
|
|
|
|
# Should be able to render
|
|
rendered = text.render()
|
|
self.assertIsInstance(rendered, Image.Image)
|
|
|
|
|
|
def test_line_multiple_words(self):
|
|
"""Test adding multiple words to a line"""
|
|
font_style = Font(font_path=None, font_size=12, colour=(0, 0, 0, 255))
|
|
|
|
line = Line(
|
|
spacing=(3, 8),
|
|
origin=(0, 0),
|
|
size=(200, 20),
|
|
font=font_style,
|
|
halign=Alignment.LEFT
|
|
)
|
|
|
|
words = ["One", "Two", "Three", "Four", "Five"]
|
|
added_words = []
|
|
|
|
for word in words:
|
|
result = line.add_word(word)
|
|
if result is None:
|
|
added_words.append(word)
|
|
else:
|
|
break
|
|
|
|
# Should have added at least some words
|
|
self.assertGreater(len(added_words), 0)
|
|
self.assertEqual(len(line.text_objects), len(added_words))
|
|
|
|
# Verify text objects contain correct text
|
|
for i, text_obj in enumerate(line.text_objects):
|
|
self.assertEqual(text_obj.text, added_words[i])
|
|
|
|
def test_line_spacing_constraints(self):
|
|
"""Test that line spacing respects min/max constraints"""
|
|
font_style = Font(font_path=None, font_size=12, colour=(0, 0, 0, 255))
|
|
|
|
min_spacing = 3
|
|
max_spacing = 10
|
|
|
|
line = Line(
|
|
spacing=(min_spacing, max_spacing),
|
|
origin=(0, 0),
|
|
size=(300, 20),
|
|
font=font_style,
|
|
halign=Alignment.JUSTIFY # Justify will test spacing limits
|
|
)
|
|
|
|
# Add multiple words
|
|
words = ["Test", "spacing", "constraints", "here"]
|
|
for word in words:
|
|
line.add_word(word)
|
|
|
|
# Render the line
|
|
rendered = line.render()
|
|
self.assertIsInstance(rendered, Image.Image)
|
|
|
|
# Line should respect spacing constraints (this is more of a system test)
|
|
self.assertGreater(len(line.text_objects), 1, "Should have multiple words for spacing test")
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|