pyWebLayout/examples/11_table_text_wrapping_demo.py
Duncan Tourolle 8e720d4037
Some checks failed
Python CI / test (3.10) (push) Successful in 2m17s
Python CI / test (3.13) (push) Has been cancelled
Python CI / test (3.12) (push) Has been cancelled
fix table in cell wrapping
2025-11-10 15:15:03 +01:00

313 lines
9.9 KiB
Python

#!/usr/bin/env python3
"""
Table Text Wrapping Example
This example demonstrates the line wrapping functionality in table cells:
- Tables with long text that wraps across multiple lines
- Automatic word wrapping within cell boundaries
- Hyphenation support for long words
- Multiple paragraphs per cell
- Comparison of narrow vs. wide columns
Shows how the Line-based text layout system handles text overflow in tables.
"""
from pyWebLayout.io.readers.html_extraction import parse_html_string
from pyWebLayout.layout.document_layouter import DocumentLayouter
from pyWebLayout.style.page_style import PageStyle
from pyWebLayout.concrete.table import TableStyle
from pyWebLayout.concrete.page import Page
import sys
from pathlib import Path
from PIL import Image
# Add pyWebLayout to path
sys.path.insert(0, str(Path(__file__).parent.parent))
def create_narrow_columns_example():
"""Create a table with narrow columns to show aggressive wrapping."""
print(" - Narrow columns with text wrapping")
html = """
<table>
<thead>
<tr>
<th>Feature</th>
<th>Description</th>
<th>Benefits</th>
</tr>
</thead>
<tbody>
<tr>
<td>Automatic Line Wrapping</td>
<td>Text automatically wraps to fit within the available cell width, creating multiple lines as needed.</td>
<td>Improves readability and prevents horizontal overflow in tables.</td>
</tr>
<tr>
<td>Hyphenation Support</td>
<td>Long words are intelligently hyphenated using pyphen library or brute-force splitting when necessary.</td>
<td>Handles extraordinarily long words that wouldn't fit on a single line.</td>
</tr>
<tr>
<td>Multi-paragraph Cells</td>
<td>Each cell can contain multiple paragraphs or headings, all properly wrapped.</td>
<td>Allows rich content within table cells.</td>
</tr>
</tbody>
</table>
"""
return html, "Text Wrapping in Narrow Columns"
def create_mixed_content_example():
"""Create a table with both short and long content."""
print(" - Mixed content lengths")
html = """
<table>
<caption>Product Comparison</caption>
<thead>
<tr>
<th>Product</th>
<th>Short Description</th>
<th>Detailed Features</th>
</tr>
</thead>
<tbody>
<tr>
<td>Widget Pro</td>
<td>Premium</td>
<td>Advanced functionality with enterprise-grade reliability, comprehensive warranty coverage, and dedicated customer support available around the clock.</td>
</tr>
<tr>
<td>Widget Lite</td>
<td>Basic</td>
<td>Essential features for everyday use with straightforward operation and minimal learning curve.</td>
</tr>
<tr>
<td>Widget Max</td>
<td>Ultimate</td>
<td>Everything from Widget Pro plus additional customization options, API integration capabilities, and advanced analytics dashboard.</td>
</tr>
</tbody>
</table>
"""
return html, "Mixed Short and Long Content"
def create_technical_documentation_example():
"""Create a table like technical documentation."""
print(" - Technical documentation style")
html = """
<table>
<thead>
<tr>
<th>API Method</th>
<th>Parameters</th>
<th>Description</th>
<th>Return Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>render_table()</td>
<td>table, origin, width, draw, style</td>
<td>Renders a table with automatic text wrapping in cells. Uses the Line class for intelligent word placement and hyphenation.</td>
<td>Rendered table with calculated height and width properties.</td>
</tr>
<tr>
<td>add_word()</td>
<td>word, pretext</td>
<td>Attempts to add a word to the current line. If it doesn't fit, tries hyphenation strategies including pyphen and brute-force splitting.</td>
<td>Tuple of (success, overflow_text) indicating whether word was added and any remaining text.</td>
</tr>
<tr>
<td>calculate_spacing()</td>
<td>text_objects, width, min_spacing, max_spacing</td>
<td>Determines optimal spacing between words to achieve proper justification within the specified constraints.</td>
<td>Calculated spacing value and position offset for alignment.</td>
</tr>
</tbody>
</table>
"""
return html, "Technical Documentation Table"
def create_news_article_example():
"""Create a table with article-style content."""
print(" - News article layout")
html = """
<table>
<thead>
<tr>
<th>Date</th>
<th>Headline</th>
<th>Summary</th>
</tr>
</thead>
<tbody>
<tr>
<td>2024-01-15</td>
<td>New Text Wrapping Feature</td>
<td>PyWebLayout now supports automatic line wrapping in table cells, bringing sophisticated text layout capabilities to table rendering. The implementation leverages the existing Line class infrastructure.</td>
</tr>
<tr>
<td>2024-01-10</td>
<td>Hyphenation Improvements</td>
<td>Enhanced hyphenation algorithms now include both dictionary-based pyphen hyphenation and intelligent brute-force splitting for edge cases.</td>
</tr>
<tr>
<td>2024-01-05</td>
<td>Performance Optimization</td>
<td>Table rendering performance improved through better caching and reduced font object creation overhead.</td>
</tr>
</tbody>
</table>
"""
return html, "News Article Layout"
def render_table_example(html, title, style_variant=0):
"""Render a single table example."""
from pyWebLayout.style import Font
from pyWebLayout.abstract.block import Table
# Parse HTML
base_font = Font(font_size=12)
blocks = parse_html_string(html, base_font=base_font)
# Find the table block
table = None
for block in blocks:
if isinstance(block, Table):
table = block
break
if not table:
print(f" Warning: No table found in {title}")
return None
# Create page style
page_style = PageStyle(
padding=(20, 20, 20, 20),
background_color=(255, 255, 255)
)
# Create page
page_size = (900, 600)
page = Page(size=page_size, style=page_style)
# Create table style variants
table_styles = [
# Default style
TableStyle(
border_width=1,
border_color=(0, 0, 0),
cell_padding=(8, 8, 8, 8),
header_bg_color=(240, 240, 240),
cell_bg_color=(255, 255, 255)
),
# Blue header style
TableStyle(
border_width=2,
border_color=(70, 130, 180),
cell_padding=(10, 10, 10, 10),
header_bg_color=(176, 196, 222),
cell_bg_color=(245, 250, 255)
),
# Minimal style
TableStyle(
border_width=1,
border_color=(200, 200, 200),
cell_padding=(6, 6, 6, 6),
header_bg_color=(250, 250, 250),
cell_bg_color=(255, 255, 255)
),
]
table_style = table_styles[style_variant % len(table_styles)]
# Create layouter and render table
layouter = DocumentLayouter(page)
layouter.layout_table(table, style=table_style)
# Get the rendered canvas
_ = page.draw # Ensure canvas exists
img = page._canvas
return img
def combine_examples(examples):
"""Combine multiple example images into one."""
images = []
titles = []
for html, title in examples:
img = render_table_example(html, title)
if img:
images.append(img)
titles.append(title)
if not images:
return None
# Calculate combined image size
max_width = max(img.width for img in images)
total_height = sum(img.height for img in images) + 40 * len(images) # Extra space between images
# Create combined image
combined = Image.new('RGB', (max_width, total_height), color=(255, 255, 255))
# Paste images
y_offset = 20
for img in images:
combined.paste(img, (0, y_offset))
y_offset += img.height + 40
return combined
def main():
"""Run the table text wrapping example."""
print("\nTable Text Wrapping Example")
print("=" * 50)
# Create examples
print("\n Creating table examples...")
examples = [
create_narrow_columns_example(),
create_mixed_content_example(),
create_technical_documentation_example(),
create_news_article_example(),
]
print("\n Rendering table examples...")
combined_image = combine_examples(examples)
if combined_image:
# Save the output
output_dir = Path(__file__).parent.parent / 'docs' / 'images'
output_dir.mkdir(parents=True, exist_ok=True)
output_path = output_dir / 'example_11_table_text_wrapping.png'
combined_image.save(str(output_path))
print("\n✓ Example completed!")
print(f" Output saved to: {output_path}")
print(f" Image size: {combined_image.width}x{combined_image.height} pixels")
print(f" Created {len(examples)} table examples with text wrapping")
else:
print("\n✗ Failed to generate examples")
if __name__ == '__main__':
main()