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 += ' \n' - html += ' \n' - html += ' \n' - html += ' \n' - html += ' \n' - html += ' \n' - html += ' \n' - - for cover_file, title, author, price in books: - html += ' \n' - - # Cover cell - html += ' \n' - - # Title cell - html += f' \n' - - # Author cell - html += f' \n' - - # Price cell - html += f' \n' - - html += ' \n' - - html += ' \n' - html += '
CoverTitleAuthorPrice
\n' - if cover_file in cover_images: - html += f' {title}\n' - html += ' {title}{author}{price}
\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 += ' \n' - html += ' \n' - html += ' \n' - html += ' \n' - html += ' \n' - - for cover_file, description in products: - html += ' \n' - - # Product cell with image - html += ' \n' - - # Description cell - html += f' \n' - - html += ' \n' - - html += ' \n' - html += '
ProductDescription
\n' - if cover_file in cover_images: - html += f' Product cover\n' - html += ' {description}
\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()