359 lines
9.6 KiB
Python
359 lines
9.6 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Table Rendering Example
|
|
|
|
This example demonstrates rendering HTML tables:
|
|
- Simple tables with headers
|
|
- Tables with multiple rows and columns
|
|
- Tables with colspan and borders
|
|
- Tables with formatted content
|
|
- Tables parsed from HTML using DocumentLayouter
|
|
|
|
Shows the HTML-first rendering pipeline.
|
|
"""
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
from PIL import Image, ImageDraw
|
|
|
|
# Add pyWebLayout to path
|
|
sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
|
|
from pyWebLayout.concrete.page import Page
|
|
from pyWebLayout.concrete.table import TableStyle
|
|
from pyWebLayout.style.page_style import PageStyle
|
|
from pyWebLayout.layout.document_layouter import DocumentLayouter
|
|
from pyWebLayout.io.readers.html_extraction import parse_html_string
|
|
from pyWebLayout.style import Font
|
|
from pyWebLayout.abstract.block import Table
|
|
|
|
|
|
def create_simple_table_example():
|
|
"""Create a simple table from HTML."""
|
|
print(" - Simple data table")
|
|
|
|
html = """
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Name</th>
|
|
<th>Age</th>
|
|
<th>City</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td>Alice</td>
|
|
<td>28</td>
|
|
<td>Paris</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Bob</td>
|
|
<td>34</td>
|
|
<td>London</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Charlie</td>
|
|
<td>25</td>
|
|
<td>Tokyo</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
"""
|
|
|
|
return html, "Simple Table"
|
|
|
|
|
|
def create_styled_table_example():
|
|
"""Create a table with custom styling."""
|
|
print(" - Styled table")
|
|
|
|
html = """
|
|
<table>
|
|
<caption>Monthly Sales Report</caption>
|
|
<thead>
|
|
<tr>
|
|
<th>Month</th>
|
|
<th>Revenue</th>
|
|
<th>Expenses</th>
|
|
<th>Profit</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td>January</td>
|
|
<td>$50,000</td>
|
|
<td>$30,000</td>
|
|
<td>$20,000</td>
|
|
</tr>
|
|
<tr>
|
|
<td>February</td>
|
|
<td>$55,000</td>
|
|
<td>$32,000</td>
|
|
<td>$23,000</td>
|
|
</tr>
|
|
<tr>
|
|
<td>March</td>
|
|
<td>$60,000</td>
|
|
<td>$35,000</td>
|
|
<td>$25,000</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
"""
|
|
|
|
return html, "Styled Table"
|
|
|
|
|
|
def create_complex_table_example():
|
|
"""Create a table with colspan."""
|
|
print(" - Complex table with colspan")
|
|
|
|
html = """
|
|
<table>
|
|
<caption>Product Specifications</caption>
|
|
<thead>
|
|
<tr>
|
|
<th>Product</th>
|
|
<th>Features</th>
|
|
<th>Price</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td>Laptop</td>
|
|
<td>16GB RAM, 512GB SSD</td>
|
|
<td>$1,299</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Monitor</td>
|
|
<td>27 inch, 4K Resolution</td>
|
|
<td>$599</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Keyboard</td>
|
|
<td>Mechanical, RGB</td>
|
|
<td>$129</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
"""
|
|
|
|
return html, "Complex Table"
|
|
|
|
|
|
def create_data_table_example():
|
|
"""Create a table with numerical data."""
|
|
print(" - Data table")
|
|
|
|
html = """
|
|
<table>
|
|
<caption>Test Results</caption>
|
|
<thead>
|
|
<tr>
|
|
<th>Test</th>
|
|
<th>Score</th>
|
|
<th>Status</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td>Unit Tests</td>
|
|
<td>98%</td>
|
|
<td>Pass</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Integration</td>
|
|
<td>95%</td>
|
|
<td>Pass</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Performance</td>
|
|
<td>87%</td>
|
|
<td>Pass</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
"""
|
|
|
|
return html, "Data Table"
|
|
|
|
|
|
def render_table_example(html: str, title: str, style_variant: int = 0, page_size=(500, 400)):
|
|
"""Render a table from HTML to an image using DocumentLayouter."""
|
|
# Create page with varying backgrounds
|
|
bg_colors = [
|
|
(255, 255, 255), # White
|
|
(250, 255, 250), # Light green tint
|
|
(255, 250, 245), # Light orange tint
|
|
(245, 250, 255), # Light blue tint
|
|
]
|
|
|
|
page_style = PageStyle(
|
|
border_width=2,
|
|
border_color=(200, 200, 200),
|
|
padding=(20, 20, 20, 20),
|
|
background_color=bg_colors[style_variant % len(bg_colors)]
|
|
)
|
|
|
|
page = Page(size=page_size, style=page_style)
|
|
|
|
# Parse HTML to get table
|
|
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
|
|
|
|
# Table styles with different themes
|
|
table_styles = [
|
|
# Style 0: Classic blue header
|
|
TableStyle(
|
|
border_width=1,
|
|
border_color=(80, 80, 80),
|
|
cell_padding=(8, 10, 8, 10),
|
|
header_bg_color=(70, 130, 180), # Steel blue
|
|
cell_bg_color=(255, 255, 255),
|
|
alternate_row_color=(240, 248, 255) # Alice blue
|
|
),
|
|
# Style 1: Green theme
|
|
TableStyle(
|
|
border_width=2,
|
|
border_color=(34, 139, 34), # Forest green
|
|
cell_padding=(10, 12, 10, 12),
|
|
header_bg_color=(144, 238, 144), # Light green
|
|
cell_bg_color=(255, 255, 255),
|
|
alternate_row_color=(240, 255, 240) # Honeydew
|
|
),
|
|
# Style 2: Minimal style
|
|
TableStyle(
|
|
border_width=0,
|
|
border_color=(200, 200, 200),
|
|
cell_padding=(6, 8, 6, 8),
|
|
header_bg_color=(245, 245, 245),
|
|
cell_bg_color=(255, 255, 255),
|
|
alternate_row_color=None # No alternating
|
|
),
|
|
# Style 3: Bold borders
|
|
TableStyle(
|
|
border_width=3,
|
|
border_color=(0, 0, 0),
|
|
cell_padding=(10, 10, 10, 10),
|
|
header_bg_color=(255, 215, 0), # Gold
|
|
cell_bg_color=(255, 255, 255),
|
|
alternate_row_color=(255, 250, 205) # Lemon chiffon
|
|
),
|
|
]
|
|
|
|
table_style = table_styles[style_variant % len(table_styles)]
|
|
|
|
if table:
|
|
# Create DocumentLayouter
|
|
layouter = DocumentLayouter(page)
|
|
|
|
# Use DocumentLayouter to layout the table
|
|
layouter.layout_table(table, style=table_style)
|
|
|
|
# Get the rendered canvas
|
|
_ = page.draw # Ensure canvas exists
|
|
image = page._canvas
|
|
else:
|
|
# No table found - create empty page
|
|
_ = page.draw # Ensure canvas exists
|
|
draw = ImageDraw.Draw(page._canvas)
|
|
draw.text((page.border_size + 10, page.border_size + 50),
|
|
"No table found in HTML",
|
|
fill=(200, 0, 0))
|
|
image = page._canvas
|
|
|
|
return image
|
|
|
|
|
|
def combine_examples(examples):
|
|
"""Combine multiple table examples into a grid."""
|
|
print("\n Rendering table examples...")
|
|
|
|
images = []
|
|
for i, (html, title) in enumerate(examples):
|
|
img = render_table_example(html, title, style_variant=i)
|
|
images.append(img)
|
|
|
|
# Create grid (2x2)
|
|
padding = 15
|
|
cols = 2
|
|
rows = 2
|
|
|
|
img_width = images[0].size[0]
|
|
img_height = images[0].size[1]
|
|
|
|
total_width = cols * img_width + (cols + 1) * padding
|
|
total_height = rows * img_height + (rows + 1) * padding + 50 # Extra for main title
|
|
|
|
combined = Image.new('RGB', (total_width, total_height), (240, 240, 240))
|
|
draw = ImageDraw.Draw(combined)
|
|
|
|
# Add main title
|
|
from PIL import ImageFont
|
|
try:
|
|
main_font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 20)
|
|
except:
|
|
main_font = ImageFont.load_default()
|
|
|
|
title_text = "Table Rendering Examples"
|
|
bbox = draw.textbbox((0, 0), title_text, font=main_font)
|
|
text_width = bbox[2] - bbox[0]
|
|
title_x = (total_width - text_width) // 2
|
|
draw.text((title_x, 15), title_text, fill=(50, 50, 50), font=main_font)
|
|
|
|
# Place images
|
|
y_offset = 50 + padding
|
|
for row in range(rows):
|
|
x_offset = padding
|
|
for col in range(cols):
|
|
idx = row * cols + col
|
|
if idx < len(images):
|
|
combined.paste(images[idx], (x_offset, y_offset))
|
|
x_offset += img_width + padding
|
|
y_offset += img_height + padding
|
|
|
|
return combined
|
|
|
|
|
|
def main():
|
|
"""Demonstrate table rendering."""
|
|
print("Table Rendering Example")
|
|
print("=" * 50)
|
|
|
|
# Create table examples
|
|
print("\n Creating table examples...")
|
|
examples = [
|
|
create_simple_table_example(),
|
|
create_styled_table_example(),
|
|
create_complex_table_example(),
|
|
create_data_table_example()
|
|
]
|
|
|
|
# Render and combine
|
|
combined_image = combine_examples(examples)
|
|
|
|
# Save output
|
|
output_dir = Path("docs/images")
|
|
output_dir.mkdir(parents=True, exist_ok=True)
|
|
output_path = output_dir / "example_04_table_rendering.png"
|
|
combined_image.save(output_path)
|
|
|
|
print(f"\n✓ Example completed!")
|
|
print(f" Output saved to: {output_path}")
|
|
print(f" Image size: {combined_image.size[0]}x{combined_image.size[1]} pixels")
|
|
print(f" Created {len(examples)} table examples")
|
|
|
|
return combined_image
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|