#!/usr/bin/env python3 """ HTML Table with Images Example - End-to-End Rendering This example demonstrates the complete pipeline: 1. HTML table source with tags in cells 2. parse_html_string() converts HTML → Abstract document structure 3. DocumentLayouter handles all layout and rendering No custom rendering code needed - DocumentLayouter handles everything! """ import sys from pathlib import Path from PIL import Image # Add pyWebLayout to path sys.path.insert(0, str(Path(__file__).parent.parent)) from pyWebLayout.io.readers.html_extraction import parse_html_string from pyWebLayout.concrete.page import Page from pyWebLayout.style.page_style import PageStyle from pyWebLayout.layout.document_layouter import DocumentLayouter from pyWebLayout.concrete.table import TableStyle from pyWebLayout.style import Font def create_book_catalog_html(): """Create HTML for a book catalog table with actual tags.""" # Get base path for images - use absolute paths for the img src data_path = Path(__file__).parent.parent / "tests" / "data" html = f"""
Cover Title Author Price
The Great Adventure The Great Adventure John Smith $19.99
Mystery of the Ages Mystery of the Ages Jane Doe $24.99
Science Today Science Today Dr. Brown $29.99
Art & Design Art & Design M. Artist $34.99
""" return html def create_product_showcase_html(): """Create HTML for a product showcase table with images.""" data_path = Path(__file__).parent.parent / "tests" / "data" html = f"""
Product Description
Premium Edition Premium Edition - Hardcover with gold embossing
Collector's Item Collector's Item - Limited print run
""" return html def render_html_with_layouter(html_string: str, title: str, table_style: TableStyle, page_size=(600, 500)): """ Render HTML using DocumentLayouter - the proper way! This function demonstrates the correct usage: 1. Parse HTML → Abstract blocks 2. Create Page 3. Create DocumentLayouter 4. Layout all blocks using layouter Args: html_string: HTML source containing table with tags title: Title for the output (for logging) table_style: Table styling configuration page_size: Page dimensions Returns: PIL Image with rendered content """ print(f"\n Processing '{title}'...") # Step 1: Parse HTML to abstract blocks print(" 1. Parsing HTML → Abstract blocks...") base_font = Font(font_size=11) blocks = parse_html_string(html_string, base_font=base_font) print(f" → Parsed {len(blocks)} blocks") # Step 2: Create page print(" 2. Creating 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) # Step 3: Create DocumentLayouter print(" 3. Creating DocumentLayouter...") layouter = DocumentLayouter(page) # Step 4: Layout all blocks using the layouter print(" 4. Laying out all blocks...") for block in blocks: # For tables, we can pass a custom style from pyWebLayout.abstract.block import Table if isinstance(block, Table): success = layouter.layout_table(block, style=table_style) else: # For other blocks (paragraphs, headings, images), use layout_document success = layouter.layout_document([block]) if not success: print(f" ⚠ Warning: Block {type(block).__name__} didn't fit on page") print(f" ✓ Layout complete!") # Step 5: Get the rendered canvas # Note: Tables render directly onto page._canvas # We access page.draw to ensure canvas is initialized print(" 5. Getting rendered canvas...") _ = page.draw # Ensure canvas exists return page._canvas def main(): """Demonstrate end-to-end HTML table with images rendering using DocumentLayouter.""" print("HTML Table with Images Example - DocumentLayouter") print("=" * 60) print("\nThis example demonstrates:") print(" 1. HTML with tags inside cells") print(" 2. parse_html_string() automatically handles images") print(" 3. DocumentLayouter handles all layout and rendering") print(" 4. NO manual TableRenderer or custom rendering code!") # Verify images exist print("\n Checking for cover images...") data_path = Path(__file__).parent.parent / "tests" / "data" cover_count = 0 for i in range(1, 5): cover_path = data_path / f"cover {i}.png" if cover_path.exists(): cover_count += 1 print(f" ✓ Found cover {i}.png") if cover_count == 0: print(" ✗ No cover images found! This example requires cover images.") return # Create HTML sources with tags print("\n Creating HTML sources with tags...") print(" - Book catalog HTML") book_html = create_book_catalog_html() print(" - Product showcase HTML") product_html = create_product_showcase_html() # Define table styles book_style = 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) ) product_style = 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) ) # Render using DocumentLayouter - the proper way! print("\n Rendering with DocumentLayouter (HTML → Abstract → Layout → PNG)...") book_image = render_html_with_layouter( book_html, "Book Catalog", book_style, page_size=(700, 600) ) product_image = render_html_with_layouter( product_html, "Product Showcase", product_style, page_size=(600, 350) ) # Combine images side by side print("\n Combining output 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_html_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"\nThe complete pipeline:") print(f" 1. HTML with tags → parse_html_string() → Abstract blocks") print(f" 2. Abstract blocks → DocumentLayouter → Concrete objects") print(f" 3. Page.render() → PNG output") print(f"\n ✓ Using DocumentLayouter - NO custom rendering code!") return combined if __name__ == "__main__": main()