253 lines
8.1 KiB
Python
253 lines
8.1 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Table with Images Example
|
|
|
|
This example demonstrates rendering tables with images:
|
|
- Creating tables programmatically
|
|
- Adding images to table cells
|
|
- Book catalog / product showcase tables
|
|
- Mixed content (images and text) in cells
|
|
|
|
Uses the cover images from tests/data directory.
|
|
"""
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
from PIL import Image, ImageDraw, ImageFont
|
|
|
|
# Add pyWebLayout to path
|
|
sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
|
|
from pyWebLayout.concrete.page import Page
|
|
from pyWebLayout.style.page_style import PageStyle
|
|
from pyWebLayout.concrete.table import TableRenderer, TableStyle
|
|
from pyWebLayout.abstract.block import Table, Image as AbstractImage
|
|
from pyWebLayout.abstract.inline import Word
|
|
from pyWebLayout.style import Font
|
|
|
|
|
|
def create_book_catalog_table(cover_images: dict):
|
|
"""Create a book catalog table with cover images and details."""
|
|
print(" - Creating book catalog table...")
|
|
|
|
# Get base path for images
|
|
data_path = Path(__file__).parent.parent / "tests" / "data"
|
|
|
|
# Create table
|
|
table = Table(caption="Book Catalog", style=Font(font_size=14))
|
|
|
|
# Header row
|
|
header = table.create_row("header")
|
|
header.create_cell(is_header=True).create_paragraph().add_word(Word("Cover", Font(font_size=12)))
|
|
header.create_cell(is_header=True).create_paragraph().add_word(Word("Title", Font(font_size=12)))
|
|
header.create_cell(is_header=True).create_paragraph().add_word(Word("Author", Font(font_size=12)))
|
|
header.create_cell(is_header=True).create_paragraph().add_word(Word("Price", Font(font_size=12)))
|
|
|
|
# Book entries
|
|
books = [
|
|
("cover 1.png", "The Great Adventure", "John Smith", "$19.99"),
|
|
("cover 2.png", "Mystery of the Ages", "Jane Doe", "$24.99"),
|
|
("cover 3.png", "Science Today", "Dr. Brown", "$29.99"),
|
|
("cover 4.png", "Art & Design", "M. Artist", "$34.99"),
|
|
]
|
|
|
|
for cover_file, title, author, price in books:
|
|
row = table.create_row("body")
|
|
|
|
# Cover cell with actual Image block
|
|
cover_cell = row.create_cell()
|
|
if cover_file in cover_images:
|
|
cover_path = str(data_path / cover_file)
|
|
AbstractImage.create_and_add_to(cover_cell, source=cover_path, alt_text=title)
|
|
|
|
# Title cell
|
|
title_cell = row.create_cell()
|
|
title_para = title_cell.create_paragraph()
|
|
for word in title.split():
|
|
title_para.add_word(Word(word, Font(font_size=11)))
|
|
|
|
# Author cell
|
|
author_cell = row.create_cell()
|
|
author_para = author_cell.create_paragraph()
|
|
for word in author.split():
|
|
author_para.add_word(Word(word, Font(font_size=11)))
|
|
|
|
# Price cell
|
|
price_cell = row.create_cell()
|
|
price_para = price_cell.create_paragraph()
|
|
price_para.add_word(Word(price, Font(font_size=11, weight="bold")))
|
|
|
|
return table
|
|
|
|
|
|
def create_product_showcase_table(cover_images: dict):
|
|
"""Create a product showcase table."""
|
|
print(" - Creating product showcase table...")
|
|
|
|
# Get base path for images
|
|
data_path = Path(__file__).parent.parent / "tests" / "data"
|
|
|
|
table = Table(caption="Product Showcase", style=Font(font_size=14))
|
|
|
|
# Header row
|
|
header = table.create_row("header")
|
|
header.create_cell(is_header=True).create_paragraph().add_word(Word("Product", Font(font_size=12)))
|
|
header.create_cell(is_header=True).create_paragraph().add_word(Word("Description", Font(font_size=12)))
|
|
|
|
# Products with covers
|
|
products = [
|
|
("cover 1.png", "Premium Edition - Hardcover with gold embossing"),
|
|
("cover 2.png", "Collector's Item - Limited print run"),
|
|
]
|
|
|
|
for cover_file, description in products:
|
|
row = table.create_row("body")
|
|
|
|
# Product cell with actual Image block
|
|
product_cell = row.create_cell()
|
|
if cover_file in cover_images:
|
|
cover_path = str(data_path / cover_file)
|
|
AbstractImage.create_and_add_to(product_cell, source=cover_path, alt_text="Product cover")
|
|
|
|
# Description cell
|
|
desc_cell = row.create_cell()
|
|
desc_para = desc_cell.create_paragraph()
|
|
for word in description.split():
|
|
desc_para.add_word(Word(word, Font(font_size=10)))
|
|
|
|
return table
|
|
|
|
|
|
def render_table_with_images(table: Table, title: str, style_variant: int = 0,
|
|
page_size=(600, 500)):
|
|
"""Render a table with images using the library API."""
|
|
# Create page
|
|
page_style = PageStyle(
|
|
border_width=2,
|
|
border_color=(180, 180, 180),
|
|
padding=(20, 20, 20, 20),
|
|
background_color=(255, 255, 255)
|
|
)
|
|
|
|
page = Page(size=page_size, style=page_style)
|
|
canvas = page.render()
|
|
draw = ImageDraw.Draw(canvas)
|
|
|
|
# Add title
|
|
try:
|
|
title_font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 16)
|
|
except:
|
|
title_font = ImageFont.load_default()
|
|
|
|
draw.text((page.border_size + 10, page.border_size + 10), title, fill=(50, 50, 150), font=title_font)
|
|
|
|
# Table styles
|
|
table_styles = [
|
|
TableStyle(
|
|
border_width=1,
|
|
border_color=(100, 100, 100),
|
|
cell_padding=(8, 10, 8, 10),
|
|
header_bg_color=(70, 130, 180),
|
|
cell_bg_color=(255, 255, 255),
|
|
alternate_row_color=(245, 248, 250)
|
|
),
|
|
TableStyle(
|
|
border_width=2,
|
|
border_color=(60, 120, 60),
|
|
cell_padding=(10, 12, 10, 12),
|
|
header_bg_color=(144, 238, 144),
|
|
cell_bg_color=(255, 255, 255),
|
|
alternate_row_color=(240, 255, 240)
|
|
),
|
|
]
|
|
|
|
table_style = table_styles[style_variant % len(table_styles)]
|
|
|
|
# Position table
|
|
table_origin = (page.border_size + 10, page.border_size + 45)
|
|
table_width = page.content_size[0] - 20
|
|
|
|
# Render table with canvas support for images
|
|
renderer = TableRenderer(
|
|
table,
|
|
table_origin,
|
|
table_width,
|
|
draw,
|
|
table_style,
|
|
canvas # Pass canvas to enable image rendering
|
|
)
|
|
renderer.render()
|
|
|
|
return canvas
|
|
|
|
|
|
def main():
|
|
"""Demonstrate tables with images."""
|
|
print("Table with Images Example")
|
|
print("=" * 50)
|
|
|
|
# Load cover images
|
|
print("\n Loading cover images...")
|
|
cover_images = {}
|
|
data_path = Path(__file__).parent.parent / "tests" / "data"
|
|
|
|
for i in range(1, 5):
|
|
cover_path = data_path / f"cover {i}.png"
|
|
if cover_path.exists():
|
|
cover_images[f"cover {i}.png"] = Image.open(cover_path)
|
|
print(f" ✓ Loaded cover {i}.png")
|
|
|
|
if not cover_images:
|
|
print(" ✗ No cover images found!")
|
|
return
|
|
|
|
# Create tables
|
|
print("\n Creating tables...")
|
|
book_table = create_book_catalog_table(cover_images)
|
|
product_table = create_product_showcase_table(cover_images)
|
|
|
|
# Render tables
|
|
print("\n Rendering tables with images...")
|
|
print(" - Rendering book catalog...")
|
|
book_image = render_table_with_images(
|
|
book_table,
|
|
"Book Catalog with Covers",
|
|
style_variant=0,
|
|
page_size=(700, 600)
|
|
)
|
|
|
|
print(" - Rendering product showcase...")
|
|
product_image = render_table_with_images(
|
|
product_table,
|
|
"Product Showcase",
|
|
style_variant=1,
|
|
page_size=(600, 350)
|
|
)
|
|
|
|
# Combine images side by side
|
|
print("\n Combining images...")
|
|
padding = 20
|
|
total_width = book_image.size[0] + product_image.size[0] + padding * 3
|
|
total_height = max(book_image.size[1], product_image.size[1]) + padding * 2
|
|
|
|
combined = Image.new('RGB', (total_width, total_height), (240, 240, 240))
|
|
combined.paste(book_image, (padding, padding))
|
|
combined.paste(product_image, (book_image.size[0] + padding * 2, padding))
|
|
|
|
# Save output
|
|
output_dir = Path("docs/images")
|
|
output_dir.mkdir(parents=True, exist_ok=True)
|
|
output_path = output_dir / "example_05_table_with_images.png"
|
|
combined.save(output_path)
|
|
|
|
print(f"\n✓ Example completed!")
|
|
print(f" Output saved to: {output_path}")
|
|
print(f" Image size: {combined.size[0]}x{combined.size[1]} pixels")
|
|
print(f" Used {len(cover_images)} cover images")
|
|
|
|
return combined
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|