diff --git a/HTML_GENERATION.md b/HTML_GENERATION.md new file mode 100644 index 0000000..a5d538e --- /dev/null +++ b/HTML_GENERATION.md @@ -0,0 +1,210 @@ +# HTML Generation for dreader + +This document describes how to use the HTML generation features in dreader to create UI for e-reader applications. + +## Overview + +The dreader library now includes HTML generation capabilities that allow you to create complete user interfaces programmatically. This is designed to work with a Hardware Abstraction Layer (HAL) that handles the actual display rendering and input processing. + +## Architecture + +``` +┌─────────────────────────────────────┐ +│ dreader Library │ +│ ├─ EbookReader (book rendering) │ +│ ├─ html_generator (UI generation) │ +│ └─ book_utils (scanning/metadata) │ +└─────────────────────────────────────┘ + ↓ HTML strings +┌─────────────────────────────────────┐ +│ HAL (Hardware Abstraction Layer) │ +│ - Receives HTML strings │ +│ - Renders to display │ +│ - Captures touch/button input │ +│ - Calls back to dreader │ +└─────────────────────────────────────┘ +``` + +## Page and Overlay Concept + +The UI uses a **page/overlay** architecture: + +- **Page (background)**: The main book content rendered as an image +- **Overlay (foreground)**: UI elements like settings, table of contents, bookmarks, etc. + +## Available Modules + +### 1. html_generator + +Functions for generating HTML strings: + +- `generate_library_html(books)` - Grid view of all books with covers +- `generate_reader_html(title, author, page_data)` - Book reading view +- `generate_settings_overlay()` - Settings panel +- `generate_toc_overlay(chapters)` - Table of contents +- `generate_bookmarks_overlay(bookmarks)` - Bookmarks list + +### 2. book_utils + +Utilities for managing books: + +- `scan_book_directory(path)` - Scan directory for EPUB files +- `extract_book_metadata(epub_path)` - Get title, author, cover +- `get_chapter_list(reader)` - Format chapters for TOC +- `get_bookmark_list(reader)` - Format bookmarks +- `page_image_to_base64(image)` - Convert page image to base64 + +## Usage Example + +```python +from pathlib import Path +from dreader import create_ebook_reader +from dreader.html_generator import ( + generate_library_html, + generate_reader_html, + generate_toc_overlay +) +from dreader.book_utils import ( + scan_book_directory, + get_chapter_list, + page_image_to_base64 +) + +# 1. Show library view +books_dir = Path('books') +books = scan_book_directory(books_dir) +library_html = generate_library_html(books) +# Pass library_html to HAL for rendering + +# 2. User selects a book +selected_book = books[0] +reader = create_ebook_reader(page_size=(800, 1000)) +reader.load_epub(selected_book['path']) + +# 3. Show reader view +page_image = reader.get_current_page() +page_base64 = page_image_to_base64(page_image) +reader_html = generate_reader_html( + book_title=reader.book_title, + book_author=reader.book_author, + page_image_data=page_base64 +) +# Pass reader_html to HAL for rendering + +# 4. User presses "Contents" button - show TOC overlay +chapters = get_chapter_list(reader) +toc_html = generate_toc_overlay(chapters) +# Pass toc_html to HAL for rendering on top of page +``` + +## HTML Structure + +### Library View + +The library uses an HTML table for grid layout: + +```html + + + + + +
+ + + + +
Book Title
Author Name
+
+``` + +### Reader View + +The reader view has three sections: + +- Header: Book info + buttons (Library, Contents, Settings) +- Page container: Centered book page image +- Footer: Navigation buttons (Previous, Next) + +### Overlays + +Overlays use: + +- Semi-transparent background (`rgba(0, 0, 0, 0.7)`) +- Centered white panel +- Close button +- Table-based layout for content + +## Button/Link Interaction + +All interactive elements have: + +- `id` attributes for buttons (e.g., `id="btn-next"`) +- `data-*` attributes for dynamic content (e.g., `data-chapter-index="5"`) +- CSS classes for styling (e.g., `class="nav-button"`) + +Your HAL should: + +1. Parse the HTML to identify interactive elements +2. Map touch/click coordinates to elements +3. Call appropriate dreader methods +4. Regenerate and render updated HTML + +## Demo + +Run the included demo to see all features: + +```bash +source venv/bin/activate +python examples/html_generation_demo.py +``` + +This will generate example HTML files in the `output/` directory that you can open in a browser to preview. + +## Integration with HAL + +Your HAL should implement: + +1. **HTML Rendering**: Parse and display HTML strings +2. **Touch Input**: Map touch coordinates to HTML elements +3. **State Management**: Maintain reader state between interactions +4. **Re-rendering**: Update display when state changes + +Example HAL flow: + +``` +User touches screen + ↓ +HAL identifies touched element (e.g., "btn-next") + ↓ +HAL calls reader.next_page() + ↓ +HAL regenerates reader_html with new page + ↓ +HAL renders updated HTML +``` + +## Styling + +All HTML includes inline CSS for complete styling. The design is: + +- Clean, minimal interface +- Dark theme for reader (reduces eye strain) +- Large touch targets for buttons +- Responsive layout using tables (widely supported) + +## Customization + +To customize the UI: + +1. Edit functions in `dreader/html_generator.py` +2. Modify CSS in the ` + + +
+

My Library

+

{len(books)} books

+
+ + + {"".join(rows)} +
+ + +''' + return html + + +def generate_reader_html(book_title: str, book_author: str, page_image_data: str) -> str: + """ + Generate HTML for the reader view with page display. + + Args: + book_title: Title of current book + book_author: Author of current book + page_image_data: Base64 encoded page image + + Returns: + Complete HTML string for reader view (page layer only) + """ + html = f''' + + + + + + {book_title} + + + +
+
+
{book_title}
+
{book_author}
+
+
+ + + +
+
+ +
+ Page +
+ + + + +''' + return html + + +def generate_settings_overlay() -> str: + """ + Generate HTML for the settings overlay. + + Returns: + HTML string for settings overlay + """ + html = ''' + + + + + + Settings + + + +
+
+ Settings + +
+ + + + + + + + + + + + + + + + + + +
Font Size + + +
Line Spacing + + +
Brightness + + +
WiFi + +
+
+ + +''' + return html + + +def generate_toc_overlay(chapters: List[Dict]) -> str: + """ + Generate HTML for the table of contents overlay. + + Args: + chapters: List of chapter dictionaries with keys: + - index: Chapter index + - title: Chapter title + + Returns: + HTML string for TOC overlay + """ + chapter_rows = [] + for chapter in chapters: + chapter_rows.append(f''' + + {chapter['title']} + + ''') + + html = f''' + + + + + + Table of Contents + + + +
+
+ Table of Contents + +
+ +
+ + {"".join(chapter_rows)} +
+
+
+ + +''' + return html + + +def generate_bookmarks_overlay(bookmarks: List[Dict]) -> str: + """ + Generate HTML for the bookmarks overlay. + + Args: + bookmarks: List of bookmark dictionaries with keys: + - name: Bookmark name + - position: Position info + + Returns: + HTML string for bookmarks overlay + """ + bookmark_rows = [] + for bookmark in bookmarks: + bookmark_rows.append(f''' + + +
{bookmark['name']}
+
{bookmark.get('position', '')}
+ + + + + + ''') + + html = f''' + + + + + + Bookmarks + + + +
+
+ Bookmarks + +
+ +
+ + {"".join(bookmark_rows)} +
+
+
+ + +''' + return html diff --git a/examples/html_generation_demo.py b/examples/html_generation_demo.py new file mode 100644 index 0000000..029ea40 --- /dev/null +++ b/examples/html_generation_demo.py @@ -0,0 +1,216 @@ +""" +Demonstration of HTML generation for dreader UI. + +This example shows how to: +1. Scan a book directory +2. Generate library view HTML +3. Load a book and generate reader view HTML +4. Generate overlay HTML for settings, TOC, and bookmarks + +The generated HTML strings can be passed to a HAL (Hardware Abstraction Layer) +for rendering on the target device. +""" + +from pathlib import Path +from dreader import create_ebook_reader +from dreader.html_generator import ( + generate_library_html, + generate_reader_html, + generate_settings_overlay, + generate_toc_overlay, + generate_bookmarks_overlay +) +from dreader.book_utils import ( + scan_book_directory, + get_chapter_list, + get_bookmark_list, + page_image_to_base64 +) + + +def demo_library_view(): + """Generate and save library view HTML.""" + print("Generating library view...") + + # Scan books directory + books_dir = Path('tests/data') + books = scan_book_directory(books_dir) + + print(f"Found {len(books)} books:") + for book in books: + print(f" - {book['title']} by {book['author']}") + + # Generate HTML + library_html = generate_library_html(books) + + # Save to file for inspection + output_path = Path('output/library.html') + output_path.parent.mkdir(exist_ok=True) + output_path.write_text(library_html) + + print(f"Library HTML saved to: {output_path}") + print(f"HTML length: {len(library_html)} characters\n") + + +def demo_reader_view(): + """Generate and save reader view HTML.""" + print("Generating reader view...") + + # Load a test book + book_path = Path('tests/data/test.epub') + if not book_path.exists(): + print(f"Test book not found at {book_path}") + return + + reader = create_ebook_reader(page_size=(800, 1000)) + reader.load_epub(str(book_path)) + + # Get current page + page_image = reader.get_current_page() + page_base64 = page_image_to_base64(page_image) + + # Generate HTML + reader_html = generate_reader_html( + book_title=reader.book_title or "Unknown Title", + book_author=reader.book_author or "Unknown Author", + page_image_data=page_base64 + ) + + # Save to file + output_path = Path('output/reader.html') + output_path.write_text(reader_html) + + print(f"Reader HTML saved to: {output_path}") + print(f"HTML length: {len(reader_html)} characters") + print(f"Book: {reader.book_title} by {reader.book_author}\n") + + +def demo_overlays(): + """Generate and save overlay HTML.""" + print("Generating overlay views...") + + # Load a test book + book_path = Path('tests/data/test.epub') + if not book_path.exists(): + print(f"Test book not found at {book_path}") + return + + reader = create_ebook_reader(page_size=(800, 1000)) + reader.load_epub(str(book_path)) + + # 1. Settings overlay + settings_html = generate_settings_overlay() + settings_path = Path('output/overlay_settings.html') + settings_path.write_text(settings_html) + print(f"Settings overlay saved to: {settings_path}") + + # 2. TOC overlay + chapters = get_chapter_list(reader) + print(f"Found {len(chapters)} chapters") + toc_html = generate_toc_overlay(chapters) + toc_path = Path('output/overlay_toc.html') + toc_path.write_text(toc_html) + print(f"TOC overlay saved to: {toc_path}") + + # 3. Bookmarks overlay (create some test bookmarks first) + reader.save_position('Chapter 1 Start') + reader.next_page() + reader.next_page() + reader.save_position('Page 3') + + bookmarks = get_bookmark_list(reader) + print(f"Found {len(bookmarks)} bookmarks") + bookmarks_html = generate_bookmarks_overlay(bookmarks) + bookmarks_path = Path('output/overlay_bookmarks.html') + bookmarks_path.write_text(bookmarks_html) + print(f"Bookmarks overlay saved to: {bookmarks_path}\n") + + +def demo_hal_usage(): + """ + Demonstrate how a HAL would use these functions. + + This simulates how your Hardware Abstraction Layer would + interact with the HTML generation functions. + """ + print("=" * 60) + print("HAL Integration Example") + print("=" * 60) + + # Step 1: Initialize with books directory + books_dir = Path('tests/data') + + # Step 2: Show library view + books = scan_book_directory(books_dir) + library_html = generate_library_html(books) + print(f"\n[HAL] Rendering library with {len(books)} books") + print(f"[HAL] HTML size: {len(library_html)} bytes") + + # Step 3: User selects a book (simulated) + if books: + selected_book = books[0] + print(f"\n[HAL] User selected: {selected_book['title']}") + + # Step 4: Load book and show reader view + reader = create_ebook_reader(page_size=(800, 1000)) + reader.load_epub(selected_book['path']) + + page_image = reader.get_current_page() + page_base64 = page_image_to_base64(page_image) + + reader_html = generate_reader_html( + book_title=reader.book_title, + book_author=reader.book_author, + page_image_data=page_base64 + ) + print(f"[HAL] Rendering reader view") + print(f"[HAL] HTML size: {len(reader_html)} bytes") + + # Step 5: User presses "Settings" button (simulated) + print(f"\n[HAL] User pressed 'Settings' button") + settings_html = generate_settings_overlay() + print(f"[HAL] Rendering settings overlay on top of page") + print(f"[HAL] HTML size: {len(settings_html)} bytes") + + # Step 6: User presses "Contents" button (simulated) + print(f"\n[HAL] User pressed 'Contents' button") + chapters = get_chapter_list(reader) + toc_html = generate_toc_overlay(chapters) + print(f"[HAL] Rendering TOC overlay with {len(chapters)} chapters") + print(f"[HAL] HTML size: {len(toc_html)} bytes") + + # Step 7: User navigates pages (simulated) + print(f"\n[HAL] User pressed 'Next' button") + reader.next_page() + page_image = reader.get_current_page() + page_base64 = page_image_to_base64(page_image) + reader_html = generate_reader_html( + book_title=reader.book_title, + book_author=reader.book_author, + page_image_data=page_base64 + ) + print(f"[HAL] Rendering next page") + print(f"[HAL] HTML size: {len(reader_html)} bytes") + + +if __name__ == '__main__': + print("dreader HTML Generation Demo") + print("=" * 60) + print() + + # Create output directory + Path('output').mkdir(exist_ok=True) + + # Run demos + demo_library_view() + demo_reader_view() + demo_overlays() + demo_hal_usage() + + print("\n" + "=" * 60) + print("Demo complete!") + print("=" * 60) + print("\nGenerated HTML files have been saved to the 'output/' directory.") + print("Open them in a browser to see how they look.") + print("\nIn a real application, these HTML strings would be passed") + print("to your HAL for rendering on the target display device.") diff --git a/tests/data/cover 1.png b/tests/data/cover 1.png new file mode 100644 index 0000000..133fa42 Binary files /dev/null and b/tests/data/cover 1.png differ diff --git a/tests/data/cover 2.png b/tests/data/cover 2.png new file mode 100644 index 0000000..f3c6d4d Binary files /dev/null and b/tests/data/cover 2.png differ diff --git a/tests/data/cover 3.png b/tests/data/cover 3.png new file mode 100644 index 0000000..18a722d Binary files /dev/null and b/tests/data/cover 3.png differ diff --git a/tests/data/cover 4.png b/tests/data/cover 4.png new file mode 100644 index 0000000..3725d31 Binary files /dev/null and b/tests/data/cover 4.png differ diff --git a/tests/test_ereader_highlighting.py b/tests/test_ereader_highlighting.py index 3b65265..691bdcb 100644 --- a/tests/test_ereader_highlighting.py +++ b/tests/test_ereader_highlighting.py @@ -308,8 +308,8 @@ class TestEbookReaderHighlighting(unittest.TestCase): self.assertIsNotNone(result_img) self.assertIsInstance(result_img, PILImage.Image) self.assertEqual(result_img.size, test_img.size) - # Result should be RGBA for transparency - self.assertEqual(result_img.mode, 'RGBA') + # Result should preserve the input mode + self.assertEqual(result_img.mode, 'RGB') if __name__ == '__main__':