572 lines
18 KiB
Markdown
572 lines
18 KiB
Markdown
# DReader Application
|
||
|
||
## Project Status
|
||
|
||
| Badge | Description |
|
||
|-------|-------------|
|
||
|  | **Test Coverage** - Percentage of code covered by unit tests |
|
||
|  | **Documentation Coverage** - Percentage of code with docstrings |
|
||
|  | **License** - Project licensing information |
|
||
|
||
## Description
|
||
|
||
DReader Application is a complete, production-ready ebook reader built on [pyWebLayout](https://gitea.tourolle.paris/dtourolle/pyWebLayout). It demonstrates how to build a full-featured ebook reader with library browsing, text highlighting, bookmarks, gesture support, overlays, and position persistence.
|
||
|
||
This project serves as both a reference implementation and a ready-to-use ereader library for building desktop, web-based, or embedded reading applications.
|
||
|
||
## Key Features
|
||
|
||
### Core Reading Features
|
||
- 📖 **EPUB Support** - Load and render EPUB files with full text extraction
|
||
- 📚 **Library Management** - Browse and select books from your collection
|
||
- 📄 **Page Rendering** - Render pages as PIL Images optimized for any display
|
||
- ⬅️➡️ **Navigation** - Smooth forward and backward page navigation
|
||
- 🔖 **Bookmarks** - Save and restore reading positions with persistence
|
||
- 📑 **Chapter Navigation** - Jump to chapters by title or index via TOC
|
||
- 📋 **Unified Overlays** - Navigation (TOC + Bookmarks) and Settings overlays
|
||
- 📊 **Progress Tracking** - Real-time reading progress percentage
|
||
|
||
### Text Interaction
|
||
- 🎨 **Text Highlighting** - Highlight words and text selections with custom colors
|
||
- 💡 **Highlight Notes** - Attach notes and annotations to highlights
|
||
- 🔍 **Text Selection** - Select words or ranges via pixel coordinates
|
||
- 👆 **Gesture Support** - Handle tap, swipe, pinch, long-press events
|
||
- 🎯 **Pixel Queries** - Query text content at any screen position
|
||
|
||
### Customization & Display
|
||
- 🔤 **Font Control** - Dynamically adjust font size with live preview
|
||
- 📏 **Spacing Control** - Customize line and paragraph spacing
|
||
- 💾 **Position Persistence** - Stable positions across style changes
|
||
- ⚡ **Smart Reflow** - Automatic text reflow on font/spacing changes
|
||
- 🎨 **Custom Styling** - Full control over colors, fonts, and layout
|
||
- 💾 **Settings Persistence** - Save and restore preferences across sessions
|
||
|
||
## Installation
|
||
|
||
```bash
|
||
# Clone the repository
|
||
git clone https://gitea.tourolle.paris/dtourolle/dreader-application.git
|
||
cd dreader-application
|
||
|
||
# Install in development mode
|
||
pip install -e .
|
||
|
||
# Or install with dev dependencies
|
||
pip install -e ".[dev]"
|
||
```
|
||
|
||
The pyWebLayout dependency will be automatically installed from the git repository.
|
||
|
||
## DReader in Action
|
||
|
||
Here are animated demonstrations of the key features:
|
||
|
||
<table>
|
||
<tr>
|
||
<td align="center">
|
||
<b>Page Navigation</b><br>
|
||
<img src="docs/images/ereader_page_navigation.gif" width="300" alt="Page Navigation"><br>
|
||
<em>Forward and backward navigation through pages</em>
|
||
</td>
|
||
<td align="center">
|
||
<b>Font Size Adjustment</b><br>
|
||
<img src="docs/images/ereader_font_size.gif" width="300" alt="Font Size"><br>
|
||
<em>Dynamic font size scaling with position preservation</em>
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<td align="center">
|
||
<b>Chapter Navigation</b><br>
|
||
<img src="docs/images/ereader_chapter_navigation.gif" width="300" alt="Chapter Navigation"><br>
|
||
<em>Jump directly to chapters by title or index</em>
|
||
</td>
|
||
<td align="center">
|
||
<b>Bookmarks & Positions</b><br>
|
||
<img src="docs/images/ereader_bookmarks.gif" width="300" alt="Bookmarks"><br>
|
||
<em>Save and restore reading positions anywhere in the book</em>
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<td align="center">
|
||
<b>Text Highlighting</b><br>
|
||
<img src="docs/images/ereader_highlighting.gif" width="300" alt="Highlighting"><br>
|
||
<em>Highlight words and selections with custom colors and notes</em>
|
||
</td>
|
||
<td align="center">
|
||
<b>TOC Overlay</b><br>
|
||
<img src="docs/images/toc_overlay_demo.gif" width="300" alt="TOC Overlay"><br>
|
||
<em>Interactive table of contents with gesture-based navigation</em>
|
||
</td>
|
||
</tr>
|
||
</table>
|
||
|
||
## Quick Start
|
||
|
||
```python
|
||
from dreader.application import EbookReader
|
||
|
||
# Create an ebook reader
|
||
with EbookReader(page_size=(800, 1000)) as reader:
|
||
# Load an EPUB file
|
||
reader.load_epub("mybook.epub")
|
||
|
||
# Get current page as PIL Image
|
||
page = reader.get_current_page()
|
||
page.save("page_001.png")
|
||
|
||
# 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()
|
||
|
||
# Highlight text at coordinates
|
||
highlight_id = reader.highlight_word(x=200, y=300, note="Important!")
|
||
|
||
# Get progress
|
||
progress = reader.get_reading_progress()
|
||
print(f"Progress: {progress*100:.1f}%")
|
||
```
|
||
|
||
## API Overview
|
||
|
||
### Loading Content
|
||
|
||
```python
|
||
reader.load_epub("book.epub")
|
||
reader.is_loaded() # Check if book loaded
|
||
reader.get_book_info() # Get metadata (title, author, etc.)
|
||
```
|
||
|
||
### Navigation
|
||
|
||
```python
|
||
reader.next_page()
|
||
reader.previous_page()
|
||
reader.jump_to_chapter("Chapter 1") # By title
|
||
reader.jump_to_chapter(0) # By index
|
||
reader.get_chapters() # List all chapters
|
||
reader.get_current_chapter_info()
|
||
reader.get_reading_progress() # Returns 0.0 to 1.0
|
||
|
||
# Navigation Overlay (unified TOC + Bookmarks)
|
||
overlay_image = reader.open_navigation_overlay() # Opens with tabs
|
||
reader.close_overlay()
|
||
reader.is_overlay_open()
|
||
```
|
||
|
||
### Styling & Display
|
||
|
||
```python
|
||
reader.increase_font_size()
|
||
reader.decrease_font_size()
|
||
reader.set_font_size(1.5) # 150% scale
|
||
reader.get_font_size()
|
||
|
||
reader.set_line_spacing(8)
|
||
reader.set_inter_block_spacing(20)
|
||
```
|
||
|
||
### Bookmarks & Position Management
|
||
|
||
```python
|
||
reader.save_position("my_bookmark")
|
||
reader.load_position("my_bookmark")
|
||
reader.list_saved_positions()
|
||
reader.delete_position("my_bookmark")
|
||
```
|
||
|
||
### Text Highlighting
|
||
|
||
```python
|
||
from pyWebLayout.core.highlight import HighlightColor
|
||
|
||
# Highlight a word at pixel coordinates
|
||
highlight_id = reader.highlight_word(
|
||
x=100,
|
||
y=200,
|
||
color=HighlightColor.YELLOW,
|
||
note="Important concept!"
|
||
)
|
||
|
||
# Highlight a text selection
|
||
highlight_id = reader.highlight_selection(
|
||
start=(100, 200),
|
||
end=(300, 250),
|
||
color=(255, 255, 0, 128) # RGBA
|
||
)
|
||
|
||
# Query word at position
|
||
result = reader.query_pixel(x=200, y=300)
|
||
if result:
|
||
print(f"Word: {result.word.text}")
|
||
|
||
# Manage highlights
|
||
highlights = reader.list_highlights()
|
||
reader.remove_highlight(highlight_id)
|
||
reader.clear_highlights()
|
||
reader.get_highlights_for_current_page()
|
||
```
|
||
|
||
### Gesture Handling
|
||
|
||
```python
|
||
from dreader.gesture import TouchEvent, GestureType, ActionType
|
||
|
||
# Handle touch input
|
||
event = TouchEvent(GestureType.TAP, x=400, y=300)
|
||
response = reader.handle_touch(event)
|
||
|
||
# Response contains action type and data
|
||
if response.action == ActionType.PAGE_TURN:
|
||
print(f"Page turned: {response.data['direction']}")
|
||
elif response.action == ActionType.WORD_SELECTED:
|
||
print(f"Word selected: {response.data['word']}")
|
||
elif response.action == ActionType.CHAPTER_SELECTED:
|
||
print(f"Chapter selected: {response.data['chapter_title']}")
|
||
|
||
# Supported gestures:
|
||
# - TAP: Select words, activate links, navigate TOC
|
||
# - LONG_PRESS: Show definitions or context menu
|
||
# - SWIPE_LEFT/RIGHT: Page navigation
|
||
# - SWIPE_UP: Open navigation overlay (from bottom 20% of screen)
|
||
# - SWIPE_DOWN: Close overlay or open settings (from top 20%)
|
||
# - PINCH_IN/OUT: Font size adjustment
|
||
# - DRAG: Text selection
|
||
```
|
||
|
||
### Settings Persistence
|
||
|
||
```python
|
||
from dreader.state import StateManager
|
||
from pathlib import Path
|
||
|
||
# Initialize state manager
|
||
state_file = Path.home() / ".config" / "dreader" / "state.json"
|
||
state_manager = StateManager(state_file=state_file)
|
||
|
||
# Load saved state
|
||
state = state_manager.load_state()
|
||
|
||
# Create reader and apply saved settings
|
||
reader = EbookReader(page_size=(800, 1000))
|
||
reader.load_epub("mybook.epub")
|
||
reader.apply_settings(state.settings.to_dict())
|
||
|
||
# Settings are automatically saved
|
||
reader.increase_font_size()
|
||
state_manager.update_settings(reader.get_current_settings())
|
||
state_manager.save_state()
|
||
```
|
||
|
||
### Library Management
|
||
|
||
```python
|
||
from dreader.library import LibraryManager
|
||
|
||
# Initialize library
|
||
library = LibraryManager(
|
||
library_path="/path/to/books",
|
||
page_size=(800, 1200)
|
||
)
|
||
|
||
# Scan for EPUB files
|
||
library.scan_library()
|
||
|
||
# Render library view
|
||
library_image = library.render_library()
|
||
|
||
# Handle book selection
|
||
book_path = library.handle_library_tap(x=400, y=300)
|
||
if book_path:
|
||
reader.load_epub(book_path)
|
||
```
|
||
|
||
## Examples
|
||
|
||
Check out the [examples/](examples/) directory for complete working examples:
|
||
|
||
### Basic Examples
|
||
- **[simple_ereader_example.py](examples/simple_ereader_example.py)** - Basic ereader usage with EPUB loading and navigation
|
||
- **[ereader_demo.py](examples/ereader_demo.py)** - Comprehensive demo showcasing all features
|
||
- **[simple_word_highlight.py](examples/simple_word_highlight.py)** - Minimal highlighting example
|
||
|
||
### Text Highlighting
|
||
- **[word_selection_highlighting.py](examples/word_selection_highlighting.py)** - Text selection and highlighting
|
||
|
||
### Overlays
|
||
- **[demo_toc_overlay.py](examples/demo_toc_overlay.py)** - Interactive table of contents overlay
|
||
- **[navigation_overlay_example.py](examples/navigation_overlay_example.py)** - Unified navigation overlay (TOC + Bookmarks)
|
||
- **[demo_settings_overlay.py](examples/demo_settings_overlay.py)** - Settings panel with font/spacing controls
|
||
|
||
### Library & State
|
||
- **[library_reading_integration.py](examples/library_reading_integration.py)** - Complete library → reading → resume workflow
|
||
- **[persistent_settings_example.py](examples/persistent_settings_example.py)** - Save/restore settings across sessions
|
||
|
||
### Advanced
|
||
- **[demo_pagination.py](examples/demo_pagination.py)** - Pagination system demonstration
|
||
- **[generate_ereader_gifs.py](examples/generate_ereader_gifs.py)** - Generate animated GIF demonstrations
|
||
- **[generate_library_demo_gif.py](examples/generate_library_demo_gif.py)** - Generate library demo animations
|
||
|
||
## Architecture
|
||
|
||
DReader Application is a **high-level application layer** that combines pyWebLayout's low-level components:
|
||
|
||
```
|
||
dreader.application.EbookReader (High-Level API)
|
||
↓
|
||
├── pyWebLayout.layout.EreaderLayoutManager # Layout engine & pagination
|
||
├── pyWebLayout.layout.EreaderLayout # Bidirectional layout
|
||
├── pyWebLayout.core.HighlightManager # Highlighting system
|
||
├── pyWebLayout.io.gesture # Touch/gesture handling
|
||
└── pyWebLayout.io.readers.epub_reader # EPUB parsing
|
||
```
|
||
|
||
### 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** is a layout engine library providing low-level primitives:
|
||
- Text rendering and layout algorithms
|
||
- Document structure and pagination
|
||
- Query systems for interactive content
|
||
- Core rendering infrastructure
|
||
|
||
**DReader Application** is an application framework that:
|
||
- Combines pyWebLayout components into a complete reader
|
||
- Provides user-friendly APIs for common ereader tasks
|
||
- Manages application state (bookmarks, highlights, positions)
|
||
- Handles business logic for gestures and interactions
|
||
|
||
Think of it like this:
|
||
- **pyWebLayout** = React (library)
|
||
- **DReader Application** = Next.js (framework)
|
||
|
||
## State Management
|
||
|
||
### File Structure
|
||
```
|
||
~/.config/dreader/
|
||
├── state.json # Application state
|
||
├── covers/ # Cached book covers
|
||
├── bookmarks/ # Per-book bookmarks
|
||
├── highlights/ # Per-book highlights
|
||
└── xray/ # X-Ray data (future)
|
||
```
|
||
|
||
### State Persistence
|
||
- **Auto-save**: Every 60 seconds
|
||
- **Immediate save**: On mode change, settings change, shutdown
|
||
- **Boot behavior**: Resume last book at last position or show library
|
||
- **Error handling**: Fall back to library if book missing or state corrupt
|
||
|
||
### Position Stability
|
||
- Positions stored by abstract document structure (chapter/block/word indices)
|
||
- Stable across font size changes, spacing changes, page size changes
|
||
- Per-book storage using document IDs
|
||
- Special `__auto_resume__` bookmark for last reading position
|
||
|
||
## Use Cases
|
||
|
||
- 📱 **Desktop Ereader Applications** - Build native ereader apps with Python
|
||
- 🌐 **Web-based Readers** - Serve rendered pages via Flask/FastAPI
|
||
- 📟 **E-ink Device Firmware** - Optimized rendering for e-ink displays
|
||
- 📊 **Reading Analytics** - Track reading patterns, highlights, and engagement
|
||
- 🎓 **Educational Tools** - Create annotated reading experiences with highlights and notes
|
||
- 🔬 **Research Applications** - Build specialized reading tools for academic work
|
||
|
||
## Development
|
||
|
||
```bash
|
||
# Install in development mode with dev dependencies
|
||
pip install -e ".[dev]"
|
||
|
||
# Run tests
|
||
pytest
|
||
|
||
# Run tests with coverage
|
||
pytest --cov=dreader --cov-report=html
|
||
|
||
# Format code
|
||
black dreader/ tests/
|
||
|
||
# Type checking
|
||
mypy dreader/
|
||
|
||
# Linting
|
||
flake8 dreader/ tests/
|
||
```
|
||
|
||
### Running Examples
|
||
|
||
```bash
|
||
# Ensure you have an EPUB file for testing
|
||
cd examples
|
||
|
||
# Run simple example
|
||
python simple_ereader_example.py /path/to/book.epub
|
||
|
||
# Run comprehensive demo
|
||
python ereader_demo.py /path/to/book.epub
|
||
|
||
# Run library integration demo
|
||
python library_reading_integration.py /path/to/library/
|
||
|
||
# Generate animated GIFs
|
||
python generate_ereader_gifs.py /path/to/book.epub
|
||
```
|
||
|
||
## Testing
|
||
|
||
The project includes comprehensive tests covering:
|
||
|
||
- **Application API** - All EbookReader methods and workflows
|
||
- **System Integration** - Layout manager, bookmarks, and state management
|
||
- **Highlighting** - Word and selection highlighting with persistence
|
||
- **Overlays** - Navigation and settings overlay interactions
|
||
- **Gestures** - Touch event handling and routing
|
||
- **Boot Recovery** - State persistence and position restoration
|
||
- **Library** - Book scanning, selection, and metadata
|
||
- **Edge Cases** - Error handling, boundary conditions, and recovery
|
||
|
||
```bash
|
||
# Run all tests
|
||
pytest
|
||
|
||
# Run specific test file
|
||
pytest tests/test_ereader_application.py
|
||
|
||
# Run with verbose output
|
||
pytest -v
|
||
|
||
# Run with coverage report
|
||
pytest --cov=dreader --cov-report=term-missing
|
||
```
|
||
|
||
## Hardware Integration
|
||
|
||
DReader includes complete hardware support for e-ink displays via the **dreader-hal** library.
|
||
|
||
### Supported Hardware
|
||
|
||
- **Display**: IT8951 e-ink controller (1872×1404)
|
||
- **Touch**: FT5316 capacitive touch panel
|
||
- **Buttons**: GPIO buttons (configurable)
|
||
- **Sensors**: BMA400 accelerometer, PCF8523 RTC, INA219 power monitor
|
||
|
||
### Quick Setup on Raspberry Pi
|
||
|
||
```bash
|
||
# 1. Clone and install
|
||
git clone https://gitea.tourolle.paris/dtourolle/dreader-application.git
|
||
cd dreader-application
|
||
python3 -m venv venv
|
||
source venv/bin/activate
|
||
pip install -e .
|
||
./install_hardware_drivers.sh
|
||
|
||
# 2. Interactive hardware setup
|
||
sudo python3 setup_rpi.py
|
||
|
||
# 3. Run on hardware
|
||
python examples/run_on_hardware_config.py
|
||
```
|
||
|
||
### Hardware Configuration
|
||
|
||
The repository includes a pre-configured **[hardware_config.json](hardware_config.json)** for the reference hardware:
|
||
|
||
- **Buttons**: GPIO 22 (prev), GPIO 27 (next), GPIO 21 (power)
|
||
- **Display**: 1872×1404 IT8951 e-ink
|
||
- **I2C Bus**: GPIO 2/3 (touch, sensors, RTC, power)
|
||
|
||
See [HARDWARE_SETUP.md](HARDWARE_SETUP.md) for complete wiring diagrams and setup instructions.
|
||
|
||
### HAL Architecture
|
||
|
||
```python
|
||
from dreader.hal_hardware import HardwareDisplayHAL
|
||
from dreader.main import DReaderApplication, AppConfig
|
||
|
||
# Hardware HAL with GPIO buttons
|
||
hal = HardwareDisplayHAL(width=1872, height=1404, vcom=-2.0)
|
||
config = AppConfig(display_hal=hal, library_path="~/Books")
|
||
app = DReaderApplication(config)
|
||
```
|
||
|
||
**Available HAL Implementations:**
|
||
- **HardwareDisplayHAL** - Real e-ink hardware (IT8951 + dreader-hal)
|
||
- **PygameDisplayHAL** - Desktop testing with pygame window
|
||
|
||
See [HARDWARE_PINOUT.md](HARDWARE_PINOUT.md) for pin assignments and [GPIO_BUTTONS.md](GPIO_BUTTONS.md) for button configuration.
|
||
|
||
## Documentation
|
||
|
||
- [README.md](README.md) - This file, main project documentation
|
||
- [REQUIREMENTS.md](REQUIREMENTS.md) - Application requirements specification
|
||
- [ARCHITECTURE.md](ARCHITECTURE.md) - System architecture and design details
|
||
- [HAL_IMPLEMENTATION_SPEC.md](HAL_IMPLEMENTATION_SPEC.md) - Hardware integration guide
|
||
|
||
## Performance
|
||
|
||
- **Boot Time**: ~2-3 seconds to resume reading
|
||
- **Page Turn**: ~50-100ms (depends on page complexity)
|
||
- **Overlay Open**: ~200-250ms (includes HTML generation and rendering)
|
||
- **Memory Usage**: ~20-30MB base + 10-50MB per book
|
||
- **Cache**: Automatic cover image and metadata caching for fast library loading
|
||
|
||
## Contributing
|
||
|
||
Contributions welcome! This project demonstrates what's possible with pyWebLayout. If you build something cool or find ways to improve the reader, please share!
|
||
|
||
### How to Contribute
|
||
|
||
1. Fork the repository
|
||
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
|
||
3. Make your changes with tests
|
||
4. Run tests and ensure coverage stays high
|
||
5. Format code with black
|
||
6. Submit a pull request
|
||
|
||
## License
|
||
|
||
MIT License - see [LICENSE](LICENSE) file for details
|
||
|
||
## Author
|
||
|
||
Duncan Tourolle - duncan@tourolle.paris
|
||
|
||
## Related Projects
|
||
|
||
- [pyWebLayout](https://gitea.tourolle.paris/dtourolle/pyWebLayout) - The underlying layout engine library
|
||
|
||
## Acknowledgments
|
||
|
||
Built with [pyWebLayout](https://gitea.tourolle.paris/dtourolle/pyWebLayout) - A powerful Python library for HTML-like layout and rendering.
|