# DReader Application Architecture ## Overview DReader is a full-featured ebook reader application built on top of [pyWebLayout](https://gitea.tourolle.paris/dtourolle/pyWebLayout). It provides a complete reading experience with navigation, bookmarks, highlights, and customizable display settings. ## System Architecture ### High-Level Component Structure ``` dreader/ ├── application.py # Main EbookReader class (coordinator) ├── managers/ # Specialized management modules │ ├── document.py # Document loading (EPUB/HTML) │ ├── settings.py # Font and spacing controls │ └── highlight_coordinator.py # Text highlighting ├── handlers/ │ └── gestures.py # Touch event routing ├── overlays/ # UI overlay system │ ├── base.py # Base overlay functionality │ ├── navigation.py # TOC and bookmarks overlay │ └── settings.py # Settings overlay ├── library.py # Library browsing and book selection ├── state.py # Application state persistence ├── html_generator.py # HTML generation for overlays └── gesture.py # Gesture definitions and responses ``` ### Relationship to pyWebLayout **pyWebLayout** provides low-level rendering primitives: - Text layout and rendering algorithms - Document structure and pagination - Query systems for interactive content - Core rendering infrastructure **DReader** is an application framework that: - Combines pyWebLayout components into a complete reader - Provides high-level APIs for common ereader tasks - Manages application state (bookmarks, highlights, positions) - Handles business logic for gestures and interactions Think of it as: - **pyWebLayout** = React (library) - **DReader** = Next.js (framework) ## Core Components ### 1. EbookReader (Main Coordinator) **Location**: [application.py](dreader/application.py) The central orchestrator that coordinates all subsystems: ```python class EbookReader: """Main ebook reader application""" # Core dependencies manager: EreaderLayoutManager # pyWebLayout layout engine doc_manager: DocumentManager # Document loading settings_manager: SettingsManager # Display settings highlight_coordinator: HighlightCoordinator # Text highlighting gesture_router: GestureRouter # Gesture handling overlay_manager: OverlayManager # Overlay rendering ``` **Key Responsibilities**: - Document lifecycle (load, close) - Page navigation (next, previous, chapters) - Bookmark management - Position persistence - Settings coordination - Gesture event routing ### 2. Document Manager **Location**: [managers/document.py](dreader/managers/document.py) Handles document loading and metadata extraction. **Responsibilities**: - Load EPUB files via pyWebLayout - Extract book metadata (title, author, etc.) - Provide document info to other components ### 3. Settings Manager **Location**: [managers/settings.py](dreader/managers/settings.py) Manages all display settings with persistence. **Settings**: - Font scale (adjustable font size) - Line spacing - Inter-block spacing (paragraph spacing) - Word spacing **Features**: - Real-time preview in settings overlay - Persistent across sessions - Position preservation when settings change ### 4. Gesture Router **Location**: [handlers/gestures.py](dreader/handlers/gestures.py) Routes touch events to appropriate handlers based on application state. **Gesture Types**: - `TAP` - Word selection, link following, overlay interaction - `SWIPE_LEFT` - Next page - `SWIPE_RIGHT` - Previous page - `SWIPE_UP` - Open navigation overlay (from bottom 20%) - `SWIPE_DOWN` - Open settings overlay (from top) or close overlay - `PINCH_IN/OUT` - Font size adjustment - `DRAG` - Text selection (start, move, end) **Routing Logic**: ``` Touch Event → GestureRouter ├─ Is overlay open? │ ├─ Yes → Route to overlay handler │ └─ No → Route to reading mode handler └─ Return GestureResponse ``` ### 5. Overlay System **Location**: [overlays/](dreader/overlays/) The overlay system provides modal UI panels over the reading content. #### Overlay Manager **Location**: [overlays/base.py](dreader/overlays/base.py) Core overlay rendering and compositing infrastructure. **Responsibilities**: - Render overlay HTML to images - Composite overlays over base page - Darken background for modal effect - Handle coordinate translation for interaction - Cache for performance #### Navigation Overlay **Location**: [overlays/navigation.py](dreader/overlays/navigation.py) Unified overlay with tabbed interface for: - **Contents Tab**: Chapter navigation (TOC) - **Bookmarks Tab**: Saved position management **Features**: - Tab switching without closing overlay - Chapter selection with jump - Bookmark selection with jump - Add/delete bookmarks #### Settings Overlay **Location**: [overlays/settings.py](dreader/overlays/settings.py) Interactive settings panel with real-time preview. **Controls**: - Font size: A- / A+ buttons - Line spacing: +/- buttons - Block spacing: +/- buttons - Word spacing: +/- buttons **Interaction**: Changes apply immediately, overlay refreshes to show updated values. ### 6. Library Manager **Location**: [library.py](dreader/library.py) Manages the library browsing experience. **Features**: - Scan directory for EPUB files - Extract and cache metadata - Render library grid view - Handle book selection via tap - Cache cover images for performance **Display**: Renders books in a grid with cover thumbnails and metadata. ### 7. State Manager **Location**: [state.py](dreader/state.py) Persistent application state across sessions. **State Structure**: ```python class AppState: mode: EreaderMode # LIBRARY or READING overlay: OverlayState # Current overlay type current_book: BookState # Currently open book library: LibraryState # Library scan cache settings: SettingsState # Display settings ``` **Persistence**: - Location: `~/.config/dreader/state.json` - Auto-save every 60 seconds - Immediate save on mode change, settings change, shutdown - Atomic writes for safety **Boot Behavior**: - Resume last book at last position - Restore all settings - Fall back to library if book missing ## Data Flow Diagrams ### Opening an Overlay ``` User Action ↓ EbookReader.open_navigation_overlay() ├─ Get current page (base layer) ├─ Get chapters and bookmarks ↓ OverlayManager.open_navigation_overlay() ├─ Generate HTML ├─ Render to image (using temp reader) ├─ Composite over base page │ ├─ Darken background │ ├─ Add border │ └─ Paste panel at center └─ Cache base page, overlay, offset ↓ Return composited image ``` ### Overlay Interaction ``` User Touch (x, y) ↓ GestureRouter.handle_touch() ├─ Overlay open? YES ↓ EbookReader._handle_overlay_tap(x, y) ↓ OverlayManager.query_overlay_pixel(x, y) ├─ Translate screen coords to overlay coords ├─ Query pyWebLayout for link at position └─ Return link_target (e.g., "chapter:5") ↓ Parse link_target and execute action: ├─ "chapter:N" → jump_to_chapter(N), close overlay ├─ "bookmark:name" → load_position(name), close overlay ├─ "setting:action" → apply setting, refresh overlay └─ "tab:name" → switch tab, keep overlay open ↓ Return GestureResponse ``` ### State Persistence ``` Application Running ↓ StateManager auto-save timer (every 60s) ├─ Gather current state ├─ Serialize to JSON └─ Atomic write to disk OR User performs action (page turn, setting change) ├─ StateManager.save_state() └─ Immediate write Application Shutdown ├─ Save position: reader.save_position("__auto_resume__") ├─ Stop auto-save └─ Final state.json write ``` ### Boot Sequence ``` Application Start ↓ StateManager.load_state() ├─ Read state.json ├─ Validate and parse └─ Create AppState object ↓ Check previous mode: ├─ READING mode? │ ├─ Load last book │ ├─ Apply saved settings │ └─ Restore position ("__auto_resume__") │ └─ LIBRARY mode? └─ Show library grid ``` ## File Organization ### Application State Files ``` ~/.config/dreader/ ├── state.json # Application state ├── covers/ # Cached book covers │ └── {book_id}.png ├── bookmarks/ # Per-book bookmarks │ └── {document_id}_{bookmark_name}.json └── highlights/ # Per-book highlights └── {document_id}_highlights.json ``` ### Bookmark Format Each book's position is stored separately using document ID: ```json { "document_id": "book123", "bookmark_name": "__auto_resume__", "position": { "offset": 1234, "chapter": 5 }, "timestamp": "2025-11-09T10:30:00Z" } ``` ## Gesture Handling ### Gesture Priority and Routing ``` Touch Event ↓ Is overlay open? ├─ YES → Overlay Mode │ ├─ TAP → Handle overlay interaction │ ├─ SWIPE_DOWN → Close overlay │ └─ Other → Ignore (modal behavior) │ └─ NO → Reading Mode ├─ TAP │ ├─ On link → Follow link │ ├─ On word → Select word │ ├─ Left edge → Previous page │ └─ Right edge → Next page │ ├─ SWIPE │ ├─ LEFT → Next page │ ├─ RIGHT → Previous page │ ├─ UP (from bottom 20%) → Open navigation │ └─ DOWN (from top 20%) → Open settings │ ├─ PINCH │ ├─ IN → Decrease font size │ └─ OUT → Increase font size │ └─ DRAG ├─ START → Begin text selection ├─ MOVE → Extend selection └─ END → Complete selection ``` ### Response Types ```python class ActionType(Enum): NONE = "none" PAGE_TURN = "page_turn" WORD_SELECTED = "word_selected" LINK_FOLLOWED = "link_followed" CHAPTER_SELECTED = "chapter_selected" BOOKMARK_SELECTED = "bookmark_selected" SETTING_CHANGED = "setting_changed" OVERLAY_OPENED = "overlay_opened" OVERLAY_CLOSED = "overlay_closed" TAB_SWITCHED = "tab_switched" ``` ## Performance Characteristics ### Rendering Performance - **Page Turn**: ~50-100ms (depends on page complexity) - **Overlay Open**: ~200-250ms (includes HTML generation and rendering) - **Tab Switch**: ~125ms (uses cached base page) - **Setting Change**: ~150ms (re-render with new settings) - **Tap Interaction**: ~5-10ms (coordinate query) ### Memory Usage - **Base Application**: ~20-30MB - **Per Book**: ~10-50MB (depends on images) - **Overlay Cache**: ~5-10MB ### Optimization Strategies 1. **Caching**: - Base page cached during overlay display - Overlay panel cached for tab switching - Cover images cached to disk - Metadata cached between sessions 2. **Lazy Loading**: - Library covers loaded on-demand - Book content loaded only when opened - Overlays rendered only when needed 3. **Efficient Updates**: - Tab switching reuses base page - Setting changes use incremental rendering - Position saves are debounced ## Extension Points ### Adding New Overlays To add a new overlay type: 1. Define new `OverlayState` enum value in [state.py](dreader/state.py#L27-L33) 2. Create HTML generator in [html_generator.py](dreader/html_generator.py) 3. Add overlay class in `overlays/` directory 4. Implement open/close methods in [overlay manager](dreader/overlays/base.py) 5. Add gesture handling in [application.py](dreader/application.py) ### Custom Gesture Handlers To add custom gestures: 1. Define gesture type in [gesture.py](dreader/gesture.py) 2. Add handler in [gestures.py](dreader/handlers/gestures.py) 3. Define action type for response 4. Update gesture router logic ### HAL Integration To integrate with hardware: Create a display abstraction layer implementing: ```python class DisplayHAL(ABC): @abstractmethod def show_image(self, image: Image.Image): """Display image on hardware""" @abstractmethod def get_touch_events(self) -> Iterator[TouchEvent]: """Get touch input from hardware""" @abstractmethod def set_brightness(self, level: int): """Control display brightness""" ``` Examples: - **E-ink**: IT8951, Remarkable device SDK - **Desktop**: pygame, tkinter - **Web**: Flask + HTML canvas - **Qt**: QPixmap + QTouchEvent ## Testing Strategy ### Unit Tests - State serialization and persistence - Gesture routing logic - Coordinate translation - HTML generation ### Integration Tests - Mode transitions (LIBRARY ↔ READING) - Overlay lifecycle (open → interact → close) - Boot recovery and resume - Settings persistence ### Example-Based Testing Working examples demonstrate full integration: - [simple_ereader_example.py](examples/simple_ereader_example.py) - [library_reading_integration.py](examples/library_reading_integration.py) - [navigation_overlay_example.py](examples/navigation_overlay_example.py) - [demo_settings_overlay.py](examples/demo_settings_overlay.py) ## Design Patterns ### Component-Based Architecture - **Managers**: Single-responsibility modules for specific tasks - **Handlers**: Event routing and processing - **Overlays**: Self-contained UI components ### Delegation Over Inheritance - EbookReader delegates to specialized managers - No deep inheritance hierarchies - Composition for flexibility ### State Machine Pattern - Clear state transitions (modes, overlays) - State persistence for resume - Predictable behavior ### Event-Driven Architecture - Touch events drive all interactions - Response objects communicate results - Decoupled components ## Future Architecture Considerations ### Sub-Application Pattern Current overlay handling uses a monolithic approach. Future refactoring could extract overlays into sub-applications: ```python class OverlaySubApplication(ABC): def open(self, context: OverlayContext) -> Image.Image: ... def handle_tap(self, x: int, y: int) -> GestureResponse: ... def close(self) -> Image.Image: ... ``` Benefits: - Self-contained overlay logic - Easier testing - Plugin support - Composable overlays ### Plugin System Enable third-party extensions: - Custom overlay types - Additional gestures - Export formats - Cloud sync providers ## References - [pyWebLayout Documentation](https://gitea.tourolle.paris/dtourolle/pyWebLayout) - [REQUIREMENTS.md](REQUIREMENTS.md) - Detailed feature specifications - [README.md](README.md) - User-facing documentation - [examples/](examples/) - Working code examples