pyWebLayout/tests/test_multiline_rendering.py
Duncan Tourolle 4e65fe3e67
Some checks failed
Python CI / test (push) Failing after 4m50s
fixed some rendering issues
2025-06-07 21:11:37 +02:00

229 lines
8.0 KiB
Python

#!/usr/bin/env python3
"""
Test script to verify multi-line text rendering and line wrapping functionality.
"""
from PIL import Image, ImageDraw
from pyWebLayout.concrete.text import Text, Line
from pyWebLayout.style import Font, FontStyle, FontWeight
from pyWebLayout.style.layout import Alignment
import os
def create_multiline_test(sentence, target_lines, line_width, line_height, font_size=14):
"""
Test rendering a sentence across multiple lines
Args:
sentence: The sentence to render
target_lines: Expected number of lines
line_width: Width of each line in pixels
line_height: Height of each line in pixels
font_size: Font size to use
Returns:
tuple: (actual_lines_used, lines_list, combined_image)
"""
font_style = Font(
font_path=None,
font_size=font_size,
colour=(0, 0, 0, 255)
)
# Split sentence into words
words = sentence.split()
# Create lines and distribute words
lines = []
current_line = None
words_remaining = words.copy()
while words_remaining:
# Create a new line
current_line = Line(
spacing=(3, 8), # min, max spacing
origin=(0, len(lines) * line_height),
size=(line_width, line_height),
font=font_style,
halign=Alignment.LEFT
)
lines.append(current_line)
# Add words to current line until it's full
words_added_to_line = []
while words_remaining:
word = words_remaining[0]
result = current_line.add_word(word)
if result is None:
# Word fit in the line
words_added_to_line.append(word)
words_remaining.pop(0)
else:
# Word didn't fit, try next line
break
# If no words were added to this line, we have a problem
if not words_added_to_line:
print(f"ERROR: Word '{words_remaining[0]}' is too long for line width {line_width}")
break
# Create combined image showing all lines
total_height = len(lines) * line_height
combined_image = Image.new('RGBA', (line_width, total_height), (255, 255, 255, 255))
for i, line in enumerate(lines):
line_img = line.render()
y_pos = i * line_height
combined_image.paste(line_img, (0, y_pos), line_img)
# Add a subtle line border for visualization
draw = ImageDraw.Draw(combined_image)
draw.rectangle([(0, y_pos), (line_width-1, y_pos + line_height-1)], outline=(200, 200, 200), width=1)
return len(lines), lines, combined_image
def test_sentence_wrapping():
"""Test various sentences with different expected line counts"""
test_cases = [
{
"sentence": "This is a simple test sentence that should wrap to exactly two lines.",
"expected_lines": 2,
"line_width": 200,
"description": "Two-line sentence"
},
{
"sentence": "This is a much longer sentence that contains many more words and should definitely wrap across three lines when rendered with the specified width constraints.",
"expected_lines": 3,
"line_width": 180,
"description": "Three-line sentence"
},
{
"sentence": "Here we have an even longer sentence with significantly more content that will require four lines to properly display all the text when using the constrained width setting.",
"expected_lines": 4,
"line_width": 160,
"description": "Four-line sentence"
},
{
"sentence": "Short sentence.",
"expected_lines": 1,
"line_width": 300,
"description": "Single line sentence"
},
{
"sentence": "This sentence has some really long words like supercalifragilisticexpialidocious that might need hyphenation.",
"expected_lines": 3,
"line_width": 150,
"description": "Sentence with long words"
}
]
print("Testing multi-line sentence rendering...\n")
results = []
for i, test_case in enumerate(test_cases):
sentence = test_case["sentence"]
expected_lines = test_case["expected_lines"]
line_width = test_case["line_width"]
description = test_case["description"]
print(f"Test {i+1}: {description}")
print(f" Sentence: \"{sentence}\"")
print(f" Expected lines: {expected_lines}")
print(f" Line width: {line_width}px")
# Run the test
actual_lines, lines, combined_image = create_multiline_test(
sentence, expected_lines, line_width, 25, font_size=12
)
print(f" Actual lines: {actual_lines}")
# Show word distribution
for j, line in enumerate(lines):
words_in_line = [word.word.text for word in line.renderable_words]
print(f" Line {j+1}: {' '.join(words_in_line)}")
# Save the result
output_filename = f"test_multiline_{i+1}_{description.lower().replace(' ', '_').replace('-', '_')}.png"
combined_image.save(output_filename)
print(f" Saved as: {output_filename}")
# Check if it matches expectations
if actual_lines == expected_lines:
print(f" ✓ SUCCESS: Got expected {expected_lines} lines")
else:
print(f" ✗ MISMATCH: Expected {expected_lines} lines, got {actual_lines}")
results.append({
"test": description,
"expected": expected_lines,
"actual": actual_lines,
"success": actual_lines == expected_lines,
"filename": output_filename
})
print()
# Summary
print("="*60)
print("SUMMARY")
print("="*60)
successful_tests = sum(1 for r in results if r["success"])
total_tests = len(results)
print(f"Tests passed: {successful_tests}/{total_tests}")
print()
for result in results:
status = "✓ PASS" if result["success"] else "✗ FAIL"
print(f"{status} {result['test']}: {result['actual']}/{result['expected']} lines ({result['filename']})")
return results
def test_fixed_width_scenarios():
"""Test specific width scenarios to verify line utilization"""
print("\n" + "="*60)
print("TESTING FIXED WIDTH SCENARIOS")
print("="*60)
# Test with progressively narrower widths
sentence = "The quick brown fox jumps over the lazy dog near the riverbank."
widths = [300, 200, 150, 100, 80]
for width in widths:
print(f"\nTesting width: {width}px")
actual_lines, lines, combined_image = create_multiline_test(
sentence, None, width, 20, font_size=12
)
# Calculate utilization
for j, line in enumerate(lines):
words_in_line = [word.word.text for word in line.renderable_words]
line_text = ' '.join(words_in_line)
utilization = (line._current_width / width) * 100
print(f" Line {j+1}: \"{line_text}\" (width: {line._current_width}/{width}px, {utilization:.1f}% utilization)")
output_filename = f"test_width_{width}px.png"
combined_image.save(output_filename)
print(f" Saved as: {output_filename}")
if __name__ == "__main__":
print("Running multi-line text rendering verification tests...\n")
# Test sentence wrapping
results = test_sentence_wrapping()
# Test fixed width scenarios
test_fixed_width_scenarios()
print(f"\nAll tests completed. Check the generated PNG files for visual verification.")
print("Look for:")
print("- Proper line wrapping at expected breakpoints")
print("- Good utilization of available line width")
print("- No text cropping at line boundaries")
print("- Proper word spacing and alignment")