diff --git a/docs/images/example_04_table_rendering.png b/docs/images/example_04_table_rendering.png
index 97d4aed..b0f5879 100644
Binary files a/docs/images/example_04_table_rendering.png and b/docs/images/example_04_table_rendering.png differ
diff --git a/docs/images/example_05_table_with_images.png b/docs/images/example_05_table_with_images.png
index 2fd543e..a58d021 100644
Binary files a/docs/images/example_05_table_with_images.png and b/docs/images/example_05_table_with_images.png differ
diff --git a/docs/images/example_06_html_table_with_images.png b/docs/images/example_06_html_table_with_images.png
deleted file mode 100644
index 3384603..0000000
Binary files a/docs/images/example_06_html_table_with_images.png and /dev/null differ
diff --git a/examples/04_table_rendering.py b/examples/04_table_rendering.py
index 09cfa2c..cdbf830 100644
--- a/examples/04_table_rendering.py
+++ b/examples/04_table_rendering.py
@@ -7,9 +7,9 @@ This example demonstrates rendering HTML tables:
- Tables with multiple rows and columns
- Tables with colspan and borders
- Tables with formatted content
-- Tables parsed from HTML
+- Tables parsed from HTML using DocumentLayouter
-Shows the TableRenderer system in action.
+Shows the HTML-first rendering pipeline.
"""
import sys
@@ -20,8 +20,9 @@ from PIL import Image, ImageDraw
sys.path.insert(0, str(Path(__file__).parent.parent))
from pyWebLayout.concrete.page import Page
-from pyWebLayout.concrete.table import TableRenderer, TableStyle
+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
@@ -179,7 +180,7 @@ def create_data_table_example():
def render_table_example(html: str, title: str, style_variant: int = 0, page_size=(500, 400)):
- """Render a table from HTML to an image."""
+ """Render a table from HTML to an image using DocumentLayouter."""
# Create page with varying backgrounds
bg_colors = [
(255, 255, 255), # White
@@ -196,17 +197,6 @@ def render_table_example(html: str, title: str, style_variant: int = 0, page_siz
)
page = Page(size=page_size, style=page_style)
- image = page.render()
- draw = ImageDraw.Draw(image)
-
- # Add title
- from PIL import ImageFont
- try:
- title_font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 14)
- except:
- title_font = ImageFont.load_default()
-
- draw.text((page.border_size + 10, page.border_size + 10), title, fill=(50, 50, 150), font=title_font)
# Parse HTML to get table
base_font = Font(font_size=12)
@@ -219,67 +209,66 @@ def render_table_example(html: str, title: str, style_variant: int = 0, page_siz
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 table renderer with different styles
- 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
- ),
- ]
+ # Create DocumentLayouter
+ layouter = DocumentLayouter(page)
- table_style = table_styles[style_variant % len(table_styles)]
+ # Use DocumentLayouter to layout the table
+ layouter.layout_table(table, style=table_style)
- # Position table below title
- table_origin = (page.border_size + 10, page.border_size + 40)
- table_width = page.content_size[0] - 20
-
- renderer = TableRenderer(
- table,
- table_origin,
- table_width,
- draw,
- table_style
- )
-
- renderer.render()
+ # Get the rendered canvas
+ _ = page.draw # Ensure canvas exists
+ image = page._canvas
else:
- # Draw "No table found" message
+ # 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), font=title_font)
+ fill=(200, 0, 0))
+ image = page._canvas
return image
diff --git a/examples/06_html_table_with_images.py b/examples/05_html_table_with_images.py
similarity index 99%
rename from examples/06_html_table_with_images.py
rename to examples/05_html_table_with_images.py
index f8af442..23ec05f 100644
--- a/examples/06_html_table_with_images.py
+++ b/examples/05_html_table_with_images.py
@@ -254,7 +254,7 @@ def main():
# Save output
output_dir = Path("docs/images")
output_dir.mkdir(parents=True, exist_ok=True)
- output_path = output_dir / "example_06_html_table_with_images.png"
+ output_path = output_dir / "example_05_html_table_with_images.png"
combined.save(output_path)
print(f"\n✓ Example completed!")
diff --git a/examples/05_table_with_images.py b/examples/05_table_with_images.py
deleted file mode 100644
index eba263c..0000000
--- a/examples/05_table_with_images.py
+++ /dev/null
@@ -1,252 +0,0 @@
-#!/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()
diff --git a/examples/05_table_with_images_html.py b/examples/05_table_with_images_html.py
deleted file mode 100644
index 419742d..0000000
--- a/examples/05_table_with_images_html.py
+++ /dev/null
@@ -1,318 +0,0 @@
-#!/usr/bin/env python3
-"""
-Table with Images Example - HTML Output
-
-This example demonstrates creating HTML tables with images:
-- Creating HTML tables programmatically
-- Embedding images in table cells
-- Book catalog / product showcase tables
-- Styled tables with CSS
-- Mixed content (images and text) in cells
-
-Generates standalone HTML files with embedded images (base64).
-"""
-
-import sys
-from pathlib import Path
-import base64
-from typing import Dict, List, Tuple
-
-# Add pyWebLayout to path
-sys.path.insert(0, str(Path(__file__).parent.parent))
-
-
-def image_to_base64(image_path: Path) -> str:
- """Convert image file to base64 string for HTML embedding."""
- with open(image_path, 'rb') as img_file:
- img_data = img_file.read()
- return base64.b64encode(img_data).decode('utf-8')
-
-
-def create_html_header(title: str) -> str:
- """Create HTML document header with CSS styles."""
- return f"""
-
-
-
-
- {title}
-
-
-
-
-
{title}
-"""
-
-
-def create_html_footer() -> str:
- """Create HTML document footer."""
- return """
-
-
-
-
-"""
-
-
-def create_book_catalog_html(cover_images: Dict[str, str]) -> str:
- """Create HTML for book catalog table with cover images."""
- 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"),
- ]
-
- html = '\n'
- html += '
Book Catalog
\n'
- html += '
\n'
- html += ' \n'
- html += ' \n'
- html += ' | Cover | \n'
- html += ' Title | \n'
- html += ' Author | \n'
- html += ' Price | \n'
- html += '
\n'
- html += ' \n'
- html += ' \n'
-
- for cover_file, title, author, price in books:
- html += ' \n'
-
- # Cover cell
- html += ' \n'
- if cover_file in cover_images:
- html += f' \n'
- html += ' | \n'
-
- # Title cell
- html += f' {title} | \n'
-
- # Author cell
- html += f' {author} | \n'
-
- # Price cell
- html += f' {price} | \n'
-
- html += '
\n'
-
- html += ' \n'
- html += '
\n'
- html += '
\n'
-
- return html
-
-
-def create_product_showcase_html(cover_images: Dict[str, str]) -> str:
- """Create HTML for product showcase table."""
- products = [
- ("cover 1.png", "Premium Edition - Hardcover with gold embossing"),
- ("cover 2.png", "Collector's Item - Limited print run"),
- ]
-
- html = '\n'
- html += '
Product Showcase
\n'
- html += '
\n'
- html += ' \n'
- html += ' \n'
- html += ' | Product | \n'
- html += ' Description | \n'
- html += '
\n'
- html += ' \n'
- html += ' \n'
-
- for cover_file, description in products:
- html += ' \n'
-
- # Product cell with image
- html += ' \n'
- if cover_file in cover_images:
- html += f' \n'
- html += ' | \n'
-
- # Description cell
- html += f' {description} | \n'
-
- html += '
\n'
-
- html += ' \n'
- html += '
\n'
- html += '
\n'
-
- return html
-
-
-def main():
- """Generate HTML tables with images."""
- print("Table with Images Example - HTML Version")
- print("=" * 50)
-
- # Load cover images and convert to base64
- print("\n Loading and encoding 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():
- try:
- cover_images[f"cover {i}.png"] = image_to_base64(cover_path)
- print(f" ✓ Loaded and encoded cover {i}.png")
- except Exception as e:
- print(f" ✗ Failed to load cover {i}.png: {e}")
-
- if not cover_images:
- print(" ✗ No cover images found!")
- return
-
- # Generate HTML content
- print("\n Generating HTML tables...")
- html_content = create_html_header("Table with Images - HTML Example")
-
- print(" - Creating book catalog table...")
- html_content += create_book_catalog_html(cover_images)
-
- print(" - Creating product showcase table...")
- html_content += create_product_showcase_html(cover_images)
-
- html_content += create_html_footer()
-
- # Save HTML output
- output_dir = Path("docs/html")
- output_dir.mkdir(parents=True, exist_ok=True)
- output_path = output_dir / "example_05_table_with_images.html"
-
- with open(output_path, 'w', encoding='utf-8') as f:
- f.write(html_content)
-
- print(f"\n✓ Example completed!")
- print(f" Output saved to: {output_path}")
- print(f" Used {len(cover_images)} cover images (embedded as base64)")
- print(f" Open the file in a web browser to view the tables")
-
- return output_path
-
-
-if __name__ == "__main__":
- main()