229 lines
8.0 KiB
Python
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")
|