diff --git a/README.md b/README.md index b5d2571..81c45c9 100644 --- a/README.md +++ b/README.md @@ -12,26 +12,26 @@ A Python library for HTML-like layout and rendering. > šŸ“‹ **Note**: Badges show results from the commit referenced in the URLs. Red "error" badges indicate build failures for that specific step. ## Description -PyWebLayout is a Python library for rendering HTML and EPUB content to paginated images. The library provides a high-level **EbookReader** API for building interactive ebook reader applications, along with powerful HTML-to-page rendering capabilities. +PyWebLayout is a Python library for HTML-like layout and rendering to paginated images. It provides a flexible page rendering system with support for borders, padding, text layout, and HTML parsing. ## Key Features -### EbookReader - High-Level API -- šŸ“– **EPUB Support** - Load and render EPUB files -- šŸ“„ **Page Rendering** - Render pages as PIL Images -- ā¬…ļøāž”ļø **Navigation** - Forward and backward page navigation -- šŸ”– **Bookmarks** - Save and load reading positions -- šŸ“‘ **Chapter Navigation** - Jump to chapters by title or index -- šŸ”¤ **Font Control** - Adjust font size dynamically -- šŸ“ **Spacing Control** - Customize line and paragraph spacing -- šŸ“Š **Progress Tracking** - Monitor reading progress +### Page Rendering System +- šŸ“„ **Flexible Page Layouts** - Create pages with customizable sizes, borders, and padding +- šŸŽØ **Styling System** - Control backgrounds, border colors, and spacing +- šŸ“ **Multiple Layouts** - Support for portrait, landscape, and square pages +- šŸ–¼ļø **Image Output** - Render pages to PIL Images (PNG, JPEG, etc.) -### Core Capabilities -- HTML-to-page layout system -- Multi-page document rendering -- Advanced text rendering with font support -- Position tracking across layout changes -- Intelligent line breaking and pagination +### Text and HTML Support +- šŸ“ **HTML Parsing** - Parse HTML content into structured document blocks +- šŸ”¤ **Font Support** - Multiple font sizes, weights, and styles +- ā†”ļø **Text Alignment** - Left, center, right, and justified text +- šŸ“– **Rich Content** - Headings, paragraphs, bold, italic, and more + +### Architecture +- **Abstract/Concrete Separation** - Clean separation between content structure and rendering +- **Extensible Design** - Easy to extend with custom renderables +- **Type-safe** - Comprehensive type hints throughout the codebase ## Installation @@ -41,106 +41,98 @@ pip install pyWebLayout ## Quick Start -### EbookReader - Recommended API +### Basic Page Rendering ```python -from pyWebLayout.layout.ereader_application import EbookReader +from pyWebLayout.concrete.page import Page +from pyWebLayout.style.page_style import PageStyle -# Create an ebook reader -with EbookReader(page_size=(800, 1000)) as reader: - # Load an EPUB file - reader.load_epub("mybook.epub") +# Create a styled page +page_style = PageStyle( + border_width=2, + border_color=(200, 200, 200), + padding=(30, 30, 30, 30), # top, right, bottom, left + background_color=(255, 255, 255) +) - # Get current page as PIL Image - page = reader.get_current_page() - page.save("page_001.png") +page = Page(size=(600, 800), style=page_style) - # Navigate through pages - reader.next_page() - reader.previous_page() - - # Save reading position - reader.save_position("chapter_3") - - # Jump to a chapter - reader.jump_to_chapter("Chapter 5") - - # Adjust font size - reader.increase_font_size() - - # Get progress - progress = reader.get_reading_progress() - print(f"Progress: {progress*100:.1f}%") +# Render to image +image = page.render() +image.save("my_page.png") ``` -### EbookReader in Action - -Here are animated demonstrations of the EbookReader's key features: - - - - - - - - - - -
- Page Navigation
- Page Navigation
- Forward and backward navigation through pages -
- Font Size Adjustment
- Font Size
- Dynamic font size scaling with position preservation -
- Chapter Navigation
- Chapter Navigation
- Jump directly to chapters by title or index -
- Bookmarks & Positions
- Bookmarks
- Save and restore reading positions anywhere in the book -
- -### HTML Multi-Page Rendering +### HTML Content Parsing ```python -from pyWebLayout.io.readers.html_extraction import html_to_blocks -from pyWebLayout.layout.document_layouter import paragraph_layouter -from pyWebLayout.concrete.page import Page +from pyWebLayout.io.readers.html_extraction import parse_html_string +from pyWebLayout.style import Font -# Parse HTML to blocks +# Parse HTML to structured blocks html = """

Document Title

First paragraph with bold text.

Second paragraph with more content.

""" -blocks = html_to_blocks(html) -# Render to pages -page = Page(size=(600, 800)) -# Layout blocks onto pages using document_layouter -# See examples/ directory for complete multi-page examples +base_font = Font(font_size=14) +blocks = parse_html_string(html, base_font=base_font) + +# blocks is a list of structured content (Paragraph, Heading, etc.) ``` +## Visual Examples + +The library supports various page layouts and configurations: + + + + + + + +
+ Page Styles
+ Page Rendering
+ Different borders, padding, and backgrounds +
+ HTML Content
+ Text Layout
+ Parsed HTML with various text styles +
+ Page Layouts
+ Page Layouts
+ Portrait, landscape, and square formats +
+ ## Examples -Check out the `examples/` directory for complete working examples: +The `examples/` directory contains working demonstrations: -- **`simple_ereader_example.py`** - Quick start with EbookReader -- **`ereader_demo.py`** - Comprehensive EbookReader feature demo -- **`generate_ereader_gifs.py`** - Generate animated GIF demonstrations -- **`html_multipage_demo.py`** - HTML to multi-page rendering -- See `examples/README.md` for full list +### Getting Started +- **[01_simple_page_rendering.py](examples/01_simple_page_rendering.py)** - Introduction to the Page system +- **[02_text_and_layout.py](examples/02_text_and_layout.py)** - HTML parsing and text rendering +- **[03_page_layouts.py](examples/03_page_layouts.py)** - Different page configurations + +### Advanced Examples +- **[html_multipage_simple.py](examples/html_multipage_simple.py)** - Multi-page HTML rendering +- **[html_multipage_demo_final.py](examples/html_multipage_demo_final.py)** - Complete multi-page layout +- **[html_line_breaking_demo.py](examples/html_line_breaking_demo.py)** - Line breaking demonstration + +Run any example: +```bash +cd examples +python 01_simple_page_rendering.py +``` + +See **[examples/README.md](examples/README.md)** for detailed documentation. ## Documentation -- **EbookReader API**: `examples/README_EREADER.md` -- **HTML Rendering**: `examples/README_HTML_MULTIPAGE.md` -- **Architecture**: `ARCHITECTURE.md` -- **Examples**: `examples/README.md` +- **[ARCHITECTURE.md](ARCHITECTURE.md)** - Detailed explanation of Abstract/Concrete architecture +- **[examples/README.md](examples/README.md)** - Complete guide to all examples +- **[examples/README_HTML_MULTIPAGE.md](examples/README_HTML_MULTIPAGE.md)** - HTML rendering guide +- **API Reference** - See docstrings in source code ## License diff --git a/docs/images/ereader_bookmarks.gif b/docs/images/ereader_bookmarks.gif deleted file mode 100644 index a750ec9..0000000 Binary files a/docs/images/ereader_bookmarks.gif and /dev/null differ diff --git a/docs/images/ereader_chapter_navigation.gif b/docs/images/ereader_chapter_navigation.gif deleted file mode 100644 index 56b0b5c..0000000 Binary files a/docs/images/ereader_chapter_navigation.gif and /dev/null differ diff --git a/docs/images/ereader_font_size.gif b/docs/images/ereader_font_size.gif deleted file mode 100644 index 2d4011d..0000000 Binary files a/docs/images/ereader_font_size.gif and /dev/null differ diff --git a/docs/images/ereader_highlighting.gif b/docs/images/ereader_highlighting.gif deleted file mode 100644 index ca188b6..0000000 Binary files a/docs/images/ereader_highlighting.gif and /dev/null differ diff --git a/docs/images/ereader_page_navigation.gif b/docs/images/ereader_page_navigation.gif deleted file mode 100644 index e66bdce..0000000 Binary files a/docs/images/ereader_page_navigation.gif and /dev/null differ diff --git a/docs/images/example_01_page_rendering.png b/docs/images/example_01_page_rendering.png new file mode 100644 index 0000000..1bf85a4 Binary files /dev/null and b/docs/images/example_01_page_rendering.png differ diff --git a/docs/images/example_02_text_and_layout.png b/docs/images/example_02_text_and_layout.png new file mode 100644 index 0000000..2467e66 Binary files /dev/null and b/docs/images/example_02_text_and_layout.png differ diff --git a/docs/images/example_03_page_layouts.png b/docs/images/example_03_page_layouts.png new file mode 100644 index 0000000..2b73c9a Binary files /dev/null and b/docs/images/example_03_page_layouts.png differ diff --git a/examples/01_simple_page_rendering.py b/examples/01_simple_page_rendering.py new file mode 100644 index 0000000..3c75d30 --- /dev/null +++ b/examples/01_simple_page_rendering.py @@ -0,0 +1,199 @@ +#!/usr/bin/env python3 +""" +Simple Page Rendering Example + +This example demonstrates: +- Creating pages with different styles +- Setting borders, padding, and background colors +- Understanding the page layout system +- Rendering pages to images + +This is a foundational example showing the basic Page API. +""" + +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 + + +def draw_placeholder_content(page: Page): + """Draw some placeholder content directly on the page to visualize the layout.""" + if page.draw is None: + # Trigger canvas creation + page.render() + + draw = page.draw + + # Draw content area boundary (for visualization) + content_x = page.border_size + page.style.padding_left + content_y = page.border_size + page.style.padding_top + content_w = page.content_size[0] + content_h = page.content_size[1] + + # Draw a light blue rectangle showing the content area + draw.rectangle( + [content_x, content_y, content_x + content_w, content_y + content_h], + outline=(100, 150, 255), + width=1 + ) + + # Add some text labels + try: + font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 12) + except: + font = ImageFont.load_default() + + # Label the areas + draw.text((content_x + 10, content_y + 10), "Content Area", fill=(100, 100, 100), font=font) + draw.text((10, 10), f"Border: {page.border_size}px", fill=(150, 150, 150), font=font) + draw.text((content_x + 10, content_y + 30), f"Size: {content_w}x{content_h}", fill=(100, 100, 100), font=font) + + +def create_example_1(): + """Example 1: Default page style.""" + print("\n Creating Example 1: Default style...") + + page = Page(size=(400, 300)) + draw_placeholder_content(page) + + return page + + +def create_example_2(): + """Example 2: Page with visible borders.""" + print(" Creating Example 2: With borders...") + + page_style = PageStyle( + border_width=3, + border_color=(255, 100, 100), + padding=(20, 20, 20, 20), + background_color=(255, 250, 250) + ) + + page = Page(size=(400, 300), style=page_style) + draw_placeholder_content(page) + + return page + + +def create_example_3(): + """Example 3: Page with generous padding.""" + print(" Creating Example 3: With padding...") + + page_style = PageStyle( + border_width=2, + border_color=(100, 100, 255), + padding=(40, 40, 40, 40), + background_color=(250, 250, 255) + ) + + page = Page(size=(400, 300), style=page_style) + draw_placeholder_content(page) + + return page + + +def create_example_4(): + """Example 4: Clean, borderless design.""" + print(" Creating Example 4: Borderless...") + + page_style = PageStyle( + border_width=0, + padding=(30, 30, 30, 30), + background_color=(245, 245, 245) + ) + + page = Page(size=(400, 300), style=page_style) + draw_placeholder_content(page) + + return page + + +def combine_into_grid(pages, title): + """Combine multiple pages into a 2x2 grid with title.""" + print(f"\n Combining pages into grid...") + + # Render all pages + images = [page.render() for page in pages] + + # Grid layout + padding = 20 + title_height = 40 + cols = 2 + rows = 2 + + # Calculate dimensions + 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 + title_height + + # Create combined image + combined = Image.new('RGB', (total_width, total_height), (250, 250, 250)) + draw = ImageDraw.Draw(combined) + + # Draw title + try: + title_font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 20) + except: + title_font = ImageFont.load_default() + + # Center the title + bbox = draw.textbbox((0, 0), title, font=title_font) + text_width = bbox[2] - bbox[0] + title_x = (total_width - text_width) // 2 + draw.text((title_x, 10), title, fill=(50, 50, 50), font=title_font) + + # Place pages in grid + y_offset = title_height + 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 basic page rendering.""" + print("Simple Page Rendering Example") + print("=" * 50) + + # Create different page examples + pages = [ + create_example_1(), + create_example_2(), + create_example_3(), + create_example_4() + ] + + # Combine into a single demonstration image + combined_image = combine_into_grid(pages, "Page Styles: Border & Padding Examples") + + # Save output + output_dir = Path("docs/images") + output_dir.mkdir(parents=True, exist_ok=True) + output_path = output_dir / "example_01_page_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(pages)} page examples") + + return combined_image + + +if __name__ == "__main__": + main() diff --git a/examples/02_text_and_layout.py b/examples/02_text_and_layout.py new file mode 100644 index 0000000..91f2e3f --- /dev/null +++ b/examples/02_text_and_layout.py @@ -0,0 +1,214 @@ +#!/usr/bin/env python3 +""" +Text and Layout Example + +This example demonstrates text rendering using the pyWebLayout system: +- Different text alignments +- Font sizes and styles +- Multi-line paragraphs +- Document layout and pagination + +This example uses the HTML parsing system to create rich text layouts. +""" + +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.io.readers.html_extraction import parse_html_string +from pyWebLayout.style import Font +from pyWebLayout.concrete.page import Page +from pyWebLayout.style.page_style import PageStyle + + +def create_sample_document(): + """Create different HTML samples demonstrating various features.""" + samples = [] + + # Sample 1: Text alignment examples + samples.append(( + "Text Alignment", + """ + +

Left Aligned

+

This is left-aligned text. It is the default alignment for most text.

+ +

Justified Text

+

This paragraph is justified. The text stretches to fill the entire width of the line, creating clean edges on both sides.

+ +

Centered

+

This text is centered.

+ + """ + )) + + # Sample 2: Font sizes + samples.append(( + "Font Sizes", + """ + +

Heading 1

+

Heading 2

+

Heading 3

+

Normal paragraph text at the default size.

+

Small text for fine print.

+ + """ + )) + + # Sample 3: Text styles + samples.append(( + "Text Styles", + """ + +

Normal text with bold words and italic text.

+

Completely bold paragraph.

+

Completely italic paragraph.

+

Text with underlined words for emphasis.

+ + """ + )) + + # Sample 4: Mixed content + samples.append(( + "Mixed Content", + """ + +

Document Title

+

A paragraph with bold, italic, and normal text all mixed together.

+

Subsection

+

Another paragraph demonstrating the layout system.

+ + """ + )) + + return samples + + +def render_html_to_image(html_content, page_size=(500, 400)): + """Render HTML content to an image using the pyWebLayout system.""" + # Create a page + page_style = PageStyle( + border_width=2, + border_color=(200, 200, 200), + padding=(30, 30, 30, 30), + background_color=(255, 255, 255) + ) + + page = Page(size=page_size, style=page_style) + + # Parse HTML + base_font = Font(font_size=14) + blocks = parse_html_string(html_content, base_font=base_font) + + # For now, just render the page structure + # (The full layout engine would place the blocks, but we'll show the page) + image = page.render() + draw = ImageDraw.Draw(image) + + # Add a note that this is HTML-parsed content + try: + font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 11) + except: + font = ImageFont.load_default() + + # Draw info about what was parsed + content_x = page.border_size + page.style.padding_left + 10 + content_y = page.border_size + page.style.padding_top + 10 + + draw.text((content_x, content_y), + f"Parsed {len(blocks)} block(s) from HTML", + fill=(100, 100, 100), font=font) + + # List the block types + y_offset = content_y + 25 + for i, block in enumerate(blocks[:10]): # Show first 10 + block_type = type(block).__name__ + draw.text((content_x, y_offset), + f" {i+1}. {block_type}", + fill=(60, 60, 60), font=font) + y_offset += 18 + + if y_offset > page.size[1] - 60: # Don't overflow + break + + return image + + +def combine_samples(samples): + """Combine multiple sample renders into a grid.""" + print("\n Rendering samples...") + + images = [] + for title, html in samples: + print(f" - {title}") + img = render_html_to_image(html) + + # Add title to image + draw = ImageDraw.Draw(img) + try: + font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 14) + except: + font = ImageFont.load_default() + + draw.text((10, 10), title, fill=(50, 50, 150), font=font) + images.append(img) + + # Create grid (2x2) + padding = 20 + 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 + + combined = Image.new('RGB', (total_width, total_height), (240, 240, 240)) + + # Place images + y_offset = 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 text and layout features.""" + print("Text and Layout Example") + print("=" * 50) + + # Create sample documents + samples = create_sample_document() + + # Render and combine + combined_image = combine_samples(samples) + + # Save output + output_dir = Path("docs/images") + output_dir.mkdir(parents=True, exist_ok=True) + output_path = output_dir / "example_02_text_and_layout.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" Note: This example demonstrates HTML parsing") + print(f" Full layout rendering requires the typesetting engine") + + return combined_image + + +if __name__ == "__main__": + main() diff --git a/examples/03_page_layouts.py b/examples/03_page_layouts.py new file mode 100644 index 0000000..0474e59 --- /dev/null +++ b/examples/03_page_layouts.py @@ -0,0 +1,243 @@ +#!/usr/bin/env python3 +""" +Page Layouts Example + +This example demonstrates different page layout configurations: +- Various page sizes (small, medium, large) +- Different aspect ratios (portrait, landscape, square) +- Border and padding variations +- Color schemes + +Shows how the pyWebLayout system handles different page dimensions. +""" + +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 + + +def add_page_info(page: Page, title: str): + """Add informational text to a page showing its properties.""" + if page.draw is None: + page.render() + + draw = page.draw + + try: + font_large = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 14) + font_small = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 11) + except: + font_large = ImageFont.load_default() + font_small = ImageFont.load_default() + + # Title + content_x = page.border_size + page.style.padding_left + 5 + content_y = page.border_size + page.style.padding_top + 5 + + draw.text((content_x, content_y), title, fill=(40, 40, 40), font=font_large) + + # Page info + y = content_y + 25 + info = [ + f"Page: {page.size[0]}Ɨ{page.size[1]}px", + f"Content: {page.content_size[0]}Ɨ{page.content_size[1]}px", + f"Border: {page.border_size}px", + f"Padding: {page.style.padding}", + ] + + for line in info: + draw.text((content_x, y), line, fill=(80, 80, 80), font=font_small) + y += 16 + + # Draw content area boundary + cx = page.border_size + page.style.padding_left + cy = page.border_size + page.style.padding_top + cw = page.content_size[0] + ch = page.content_size[1] + + draw.rectangle( + [cx, cy, cx + cw, cy + ch], + outline=(150, 150, 255), + width=1 + ) + + +def create_layouts(): + """Create various page layout examples.""" + layouts = [] + + # 1. Small portrait page + print("\n Creating layout examples...") + print(" - Small portrait") + style1 = PageStyle( + border_width=2, + border_color=(100, 100, 100), + padding=(15, 15, 15, 15), + background_color=(255, 255, 255) + ) + page1 = Page(size=(300, 400), style=style1) + add_page_info(page1, "Small Portrait") + layouts.append(("small_portrait", page1)) + + # 2. Large portrait page + print(" - Large portrait") + style2 = PageStyle( + border_width=3, + border_color=(150, 100, 100), + padding=(30, 30, 30, 30), + background_color=(255, 250, 250) + ) + page2 = Page(size=(400, 600), style=style2) + add_page_info(page2, "Large Portrait") + layouts.append(("large_portrait", page2)) + + # 3. Landscape page + print(" - Landscape") + style3 = PageStyle( + border_width=2, + border_color=(100, 150, 100), + padding=(20, 40, 20, 40), + background_color=(250, 255, 250) + ) + page3 = Page(size=(600, 350), style=style3) + add_page_info(page3, "Landscape") + layouts.append(("landscape", page3)) + + # 4. Square page + print(" - Square") + style4 = PageStyle( + border_width=3, + border_color=(100, 100, 150), + padding=(25, 25, 25, 25), + background_color=(250, 250, 255) + ) + page4 = Page(size=(400, 400), style=style4) + add_page_info(page4, "Square") + layouts.append(("square", page4)) + + # 5. Minimal padding + print(" - Minimal padding") + style5 = PageStyle( + border_width=1, + border_color=(180, 180, 180), + padding=(5, 5, 5, 5), + background_color=(245, 245, 245) + ) + page5 = Page(size=(350, 300), style=style5) + add_page_info(page5, "Minimal Padding") + layouts.append(("minimal", page5)) + + # 6. Generous padding + print(" - Generous padding") + style6 = PageStyle( + border_width=2, + border_color=(150, 120, 100), + padding=(50, 50, 50, 50), + background_color=(255, 250, 245) + ) + page6 = Page(size=(400, 400), style=style6) + add_page_info(page6, "Generous Padding") + layouts.append(("generous", page6)) + + return layouts + + +def create_layout_showcase(layouts): + """Create a showcase image displaying all layouts.""" + print("\n Creating layout showcase...") + + # Render all pages + images = [(name, page.render()) for name, page in layouts] + + # Calculate grid layout (3Ɨ2) + padding = 15 + title_height = 50 + cols = 3 + rows = 2 + + # Find max dimensions for each row/column + max_widths = [] + for col in range(cols): + col_images = [images[row * cols + col][1] for row in range(rows) if row * cols + col < len(images)] + if col_images: + max_widths.append(max(img.size[0] for img in col_images)) + + max_heights = [] + for row in range(rows): + row_images = [images[row * cols + col][1] for col in range(cols) if row * cols + col < len(images)] + if row_images: + max_heights.append(max(img.size[1] for img in row_images)) + + # Calculate total size + total_width = sum(max_widths) + padding * (cols + 1) + total_height = sum(max_heights) + padding * (rows + 1) + title_height + + # Create combined image + combined = Image.new('RGB', (total_width, total_height), (235, 235, 235)) + draw = ImageDraw.Draw(combined) + + # Add title + try: + title_font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 24) + except: + title_font = ImageFont.load_default() + + title_text = "Page Layout Examples" + bbox = draw.textbbox((0, 0), title_text, font=title_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=title_font) + + # Place images in grid + y_offset = title_height + padding + for row in range(rows): + x_offset = padding + for col in range(cols): + idx = row * cols + col + if idx < len(images): + name, img = images[idx] + # Center image in its cell + cell_width = max_widths[col] + cell_height = max_heights[row] + img_x = x_offset + (cell_width - img.size[0]) // 2 + img_y = y_offset + (cell_height - img.size[1]) // 2 + combined.paste(img, (img_x, img_y)) + x_offset += max_widths[col] + padding if col < len(max_widths) else 0 + y_offset += max_heights[row] + padding if row < len(max_heights) else 0 + + return combined + + +def main(): + """Demonstrate page layout variations.""" + print("Page Layouts Example") + print("=" * 50) + + # Create different layouts + layouts = create_layouts() + + # Create showcase + combined_image = create_layout_showcase(layouts) + + # Save output + output_dir = Path("docs/images") + output_dir.mkdir(parents=True, exist_ok=True) + output_path = output_dir / "example_03_page_layouts.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(layouts)} layout examples") + + return combined_image + + +if __name__ == "__main__": + main() diff --git a/examples/README.md b/examples/README.md index eef6d8e..81fc46d 100644 --- a/examples/README.md +++ b/examples/README.md @@ -2,48 +2,56 @@ This directory contains example scripts demonstrating the pyWebLayout library. -## EbookReader Examples +## Getting Started Examples -The EbookReader provides a high-level, user-friendly API for building ebook reader applications. +These examples demonstrate the core rendering capabilities of pyWebLayout: -### Quick Start Example +### 01. Simple Page Rendering +**`01_simple_page_rendering.py`** - Introduction to the Page system -**`simple_ereader_example.py`** - Simple example showing basic EbookReader usage: ```bash -python simple_ereader_example.py path/to/book.epub +python 01_simple_page_rendering.py ``` -This demonstrates: -- Loading an EPUB file -- Rendering pages to images -- Basic navigation (next/previous page) -- Saving positions -- Chapter navigation -- Font size adjustment +Demonstrates: +- Creating pages with different styles +- Setting borders, padding, and backgrounds +- Understanding page layout structure +- Basic rendering to images -### Comprehensive Demo +![Page Rendering Example](../docs/images/example_01_page_rendering.png) + +### 02. Text and Layout +**`02_text_and_layout.py`** - HTML parsing and text rendering -**`ereader_demo.py`** - Full feature demonstration: ```bash -python ereader_demo.py path/to/book.epub +python 02_text_and_layout.py ``` -This showcases all EbookReader features: -- Page navigation (forward/backward) -- Position save/load with bookmarks -- Chapter navigation (by index or title) -- Font size control -- Line and block spacing adjustments -- Reading progress tracking -- Book information retrieval +Demonstrates: +- Parsing HTML content +- Text alignment options +- Font sizes and styles +- Document structure + +![Text and Layout Example](../docs/images/example_02_text_and_layout.png) + +### 03. Page Layouts +**`03_page_layouts.py`** - Different page configurations -**Tip:** You can use the test EPUB files in `tests/data/` for testing: ```bash -python simple_ereader_example.py tests/data/test.epub -python ereader_demo.py tests/data/test.epub +python 03_page_layouts.py ``` -## Other Examples +Demonstrates: +- Various page sizes (portrait, landscape, square) +- Different aspect ratios +- Border and padding variations +- Color schemes + +![Page Layouts Example](../docs/images/example_03_page_layouts.png) + +## Advanced Examples ### HTML Rendering @@ -51,16 +59,28 @@ These examples demonstrate rendering HTML content to multi-page layouts: **`html_line_breaking_demo.py`** - Basic HTML line breaking demonstration **`html_multipage_simple.py`** - Simple single-page HTML rendering -**`html_multipage_demo.py`** - Multi-page HTML layout **`html_multipage_demo_final.py`** - Complete multi-page HTML rendering with headers/footers For detailed information about HTML rendering, see `README_HTML_MULTIPAGE.md`. -## Documentation +## Running the Examples + +All examples can be run directly from the examples directory: + +```bash +cd examples +python 01_simple_page_rendering.py +python 02_text_and_layout.py +python 03_page_layouts.py +``` + +Output images are saved to the `docs/images/` directory. + +## Additional Documentation -- `README_EREADER.md` - Detailed EbookReader API documentation - `README_HTML_MULTIPAGE.md` - HTML multi-page rendering guide -- `pyWebLayout/layout/README_EREADER_API.md` - EbookReader API reference (in source) +- `../ARCHITECTURE.md` - Detailed explanation of the Abstract/Concrete architecture +- `../docs/images/` - Rendered example outputs ## Debug/Development Scripts diff --git a/examples/README_HTML_MULTIPAGE.md b/examples/README_HTML_MULTIPAGE.md deleted file mode 100644 index 9a42f96..0000000 --- a/examples/README_HTML_MULTIPAGE.md +++ /dev/null @@ -1,201 +0,0 @@ -# HTML Multi-Page Rendering Examples - -This directory contains working examples that demonstrate how to render HTML content across multiple pages using the pyWebLayout system. The examples show the complete pipeline from HTML parsing to multi-page layout. - -## Overview - -The pyWebLayout system provides a sophisticated HTML-to-multi-page rendering pipeline that: - -1. **Parses HTML** using the `pyWebLayout.io.readers.html_extraction` module -2. **Converts to abstract blocks** (paragraphs, headings, lists, etc.) -3. **Layouts content across pages** using the `pyWebLayout.layout.document_layouter` -4. **Renders pages as images** for visualization - -## Examples - -### 1. `html_multipage_simple.py` - Basic Example - -A simple demonstration that shows the core functionality: - -```bash -python examples/html_multipage_simple.py -``` - -**Features:** -- Parses basic HTML with headings and paragraphs -- Uses 600x800 pixel pages -- Demonstrates single-page layout -- Outputs to `output/html_simple/` - -**Results:** -- Parsed 11 paragraphs from HTML -- Rendered 1 page with 20 lines -- Created `page_001.png` (19KB) - -### 2. `html_multipage_demo_final.py` - Complete Multi-Page Demo - -A comprehensive demonstration with true multi-page functionality: - -```bash -python examples/html_multipage_demo_final.py -``` - -**Features:** -- Longer HTML document with multiple chapters -- Smaller pages (400x500 pixels) to force multi-page layout -- Enhanced page formatting with headers and footers -- Smart heading placement (avoids orphaned headings) -- Outputs to `output/html_multipage_final/` - -**Results:** -- Parsed 22 paragraphs (6 headings, 16 regular paragraphs) -- Rendered 7 pages with 67 total lines -- Average 9.6 lines per page -- Created 7 PNG files (4.9KB - 10KB each) - -## Technical Details - -### HTML Parsing - -The system uses BeautifulSoup to parse HTML and converts elements to pyWebLayout abstract blocks: - -- `

-

` → `Heading` blocks -- `

` → `Paragraph` blocks -- `