""" HTML generation functions for dreader UI. Generates HTML strings programmatically for library view, reader view, and various overlays (settings, TOC, etc.) that can be passed to a HAL for rendering. """ from pathlib import Path from typing import List, Dict, Optional import base64 from io import BytesIO def generate_library_html(books: List[Dict[str, str]], save_covers_to_disk: bool = False) -> str: """ Generate HTML for the library view showing all books in a simple table. Args: books: List of book dictionaries with keys: - title: Book title - author: Book author - filename: EPUB filename - cover_data: Optional base64 encoded cover image - cover_path: Optional path to saved cover image (if save_covers_to_disk=True) save_covers_to_disk: If True, expect cover_path instead of cover_data Returns: Complete HTML string for library view """ # Build table rows rows = [] for book in books: # Add cover image cell if available if save_covers_to_disk and book.get('cover_path'): cover_cell = f'' elif book.get('cover_data'): cover_cell = f'' else: cover_cell = '[No cover]' # Add book info cell info_cell = f'{book["title"]}
{book["author"]}' rows.append(f'{cover_cell}{info_cell}') table_html = '\n'.join(rows) return f''' Library

My Library

{len(books)} books

{table_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( font_scale: float = 1.0, line_spacing: int = 5, inter_block_spacing: int = 15, word_spacing: int = 0, page_size: tuple = (800, 1200) ) -> str: """ Generate HTML for the settings overlay with current values. Uses simple paragraphs with links, similar to TOC overlay, since pyWebLayout doesn't support HTML tables. Args: font_scale: Current font scale (e.g., 1.0 = 100%, 1.2 = 120%) line_spacing: Current line spacing in pixels inter_block_spacing: Current inter-block spacing in pixels word_spacing: Current word spacing in pixels page_size: Page dimensions (width, height) for sizing the overlay Returns: HTML string for settings overlay with clickable controls """ # Format current values for display font_percent = int(font_scale * 100) html = f''' Settings

Settings

Adjust reading preferences

Font Size: {font_percent}%

Decrease [ - ]

Increase [ + ]

Line Spacing: {line_spacing}px

Decrease [ - ]

Increase [ + ]

Paragraph Spacing: {inter_block_spacing}px

Decrease [ - ]

Increase [ + ]

Word Spacing: {word_spacing}px

Decrease [ - ]

Increase [ + ]

◄ Back to Library

Changes apply in real-time • Tap outside to close

''' return html def generate_toc_overlay( chapters: List[Dict], page_size: tuple = (800, 1200), toc_page: int = 0, toc_items_per_page: int = 10 ) -> str: """ Generate HTML for the table of contents overlay. Args: chapters: List of chapter dictionaries with keys: - index: Chapter index - title: Chapter title page_size: Page dimensions (width, height) for sizing the overlay toc_page: Current page number (0-indexed) toc_items_per_page: Number of items to show per page Returns: HTML string for TOC overlay (60% popup with transparent background) """ # Calculate pagination toc_total_pages = (len(chapters) + toc_items_per_page - 1) // toc_items_per_page if chapters else 1 toc_start = toc_page * toc_items_per_page toc_end = min(toc_start + toc_items_per_page, len(chapters)) toc_paginated = chapters[toc_start:toc_end] # Build chapter list items with clickable links for pyWebLayout query chapter_items = [] for i, chapter in enumerate(toc_paginated): title = chapter["title"] # Use original chapter number (not the paginated index) chapter_num = toc_start + i + 1 # Wrap each row in a paragraph with an inline link # For very short titles (I, II), pad the link text to ensure it's clickable link_text = f'{chapter_num}. {title}' if len(title) <= 2: # Add extra padding spaces inside the link to make it easier to click link_text = f'{chapter_num}. {title} ' # Extra spaces for padding chapter_items.append( f'

' f'' f'{link_text}

' ) # Generate pagination controls toc_pagination = "" if toc_total_pages > 1: prev_disabled = 'opacity: 0.3; pointer-events: none;' if toc_page == 0 else '' next_disabled = 'opacity: 0.3; pointer-events: none;' if toc_page >= toc_total_pages - 1 else '' toc_pagination = f'''
← Prev Page {toc_page + 1} of {toc_total_pages} Next →
''' # Render simple white panel - compositing will be done by OverlayManager html = f''' Table of Contents

Table of Contents

{len(chapters)} chapters

{"".join(chapter_items)}
{toc_pagination}

Tap a chapter to navigate • Tap outside to close

''' 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 def generate_navigation_overlay( chapters: List[Dict], bookmarks: List[Dict], active_tab: str = "contents", page_size: tuple = (800, 1200), toc_page: int = 0, toc_items_per_page: int = 10, bookmarks_page: int = 0 ) -> str: """ Generate HTML for the unified navigation overlay with Contents and Bookmarks tabs. This combines TOC and Bookmarks into a single overlay with tab switching and pagination. Tabs are clickable links that switch between contents (tab:contents) and bookmarks (tab:bookmarks). Pagination buttons (page:next, page:prev) allow navigating through large lists. Args: chapters: List of chapter dictionaries with keys: - index: Chapter index - title: Chapter title bookmarks: List of bookmark dictionaries with keys: - name: Bookmark name - position: Position info (optional) active_tab: Which tab to show ("contents" or "bookmarks") page_size: Page dimensions (width, height) for sizing the overlay toc_page: Current page number for TOC (0-indexed) toc_items_per_page: Number of items to show per page bookmarks_page: Current page number for bookmarks (0-indexed) Returns: HTML string for navigation overlay with tab switching and pagination """ # Calculate pagination for chapters toc_total_pages = (len(chapters) + toc_items_per_page - 1) // toc_items_per_page if chapters else 1 toc_start = toc_page * toc_items_per_page toc_end = min(toc_start + toc_items_per_page, len(chapters)) toc_paginated = chapters[toc_start:toc_end] # Build chapter list items with clickable links chapter_items = [] for i, chapter in enumerate(toc_paginated): title = chapter["title"] # Use original chapter number (not the paginated index) chapter_num = toc_start + i + 1 link_text = f'{chapter_num}. {title}' if len(title) <= 2: link_text = f'{chapter_num}. {title} ' # Extra spaces for padding chapter_items.append( f'

' f'' f'{link_text}

' ) # Calculate pagination for bookmarks bookmarks_total_pages = (len(bookmarks) + toc_items_per_page - 1) // toc_items_per_page if bookmarks else 1 bookmarks_start = bookmarks_page * toc_items_per_page bookmarks_end = min(bookmarks_start + toc_items_per_page, len(bookmarks)) bookmarks_paginated = bookmarks[bookmarks_start:bookmarks_end] # Build bookmark list items with clickable links bookmark_items = [] for bookmark in bookmarks_paginated: name = bookmark['name'] position_text = bookmark.get('position', 'Saved position') bookmark_items.append( f'

' f'' f'{name}' f'{position_text}' f'

' ) # Determine which content to show contents_display = "block" if active_tab == "contents" else "none" bookmarks_display = "block" if active_tab == "bookmarks" else "none" # Style active tab contents_tab_style = "background-color: #000; color: #fff;" if active_tab == "contents" else "background-color: #f0f0f0; color: #000;" bookmarks_tab_style = "background-color: #000; color: #fff;" if active_tab == "bookmarks" else "background-color: #f0f0f0; color: #000;" chapters_html = ''.join(chapter_items) if chapter_items else '

No chapters available

' bookmarks_html = ''.join(bookmark_items) if bookmark_items else '

No bookmarks yet

' # Generate pagination controls for TOC toc_pagination = "" if toc_total_pages > 1: prev_disabled = 'opacity: 0.3; pointer-events: none;' if toc_page == 0 else '' next_disabled = 'opacity: 0.3; pointer-events: none;' if toc_page >= toc_total_pages - 1 else '' toc_pagination = f'''
← Prev Page {toc_page + 1} of {toc_total_pages} Next →
''' # Generate pagination controls for Bookmarks bookmarks_pagination = "" if bookmarks_total_pages > 1: prev_disabled = 'opacity: 0.3; pointer-events: none;' if bookmarks_page == 0 else '' next_disabled = 'opacity: 0.3; pointer-events: none;' if bookmarks_page >= bookmarks_total_pages - 1 else '' bookmarks_pagination = f'''
← Prev Page {bookmarks_page + 1} of {bookmarks_total_pages} Next →
''' html = f''' Navigation
Contents Bookmarks

Table of Contents

{len(chapters)} chapters

{chapters_html}
{toc_pagination}

Bookmarks

{len(bookmarks)} saved

{bookmarks_html}
{bookmarks_pagination}
Close
''' return html