#!/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.text for word in line.text_objects] 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.text for word in line.text_objects] 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")