This commit is contained in:
@@ -0,0 +1,228 @@
|
||||
#!/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")
|
||||
Reference in New Issue
Block a user