Migration of application to own repo

This commit is contained in:
Duncan Tourolle 2025-11-07 18:47:10 +01:00
commit 2e926ab87a
12 changed files with 3056 additions and 0 deletions

58
.gitignore vendored Normal file
View File

@ -0,0 +1,58 @@
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
# Virtual environments
venv/
env/
ENV/
.venv
# IDEs
.vscode/
.idea/
*.swp
*.swo
*~
.DS_Store
# Testing
.pytest_cache/
.coverage
htmlcov/
.tox/
# Ereader data
ereader_bookmarks/
highlights/
*.png
*.gif
*.jpg
*.jpeg
# Examples output
examples/ereader_bookmarks/
examples/highlights/
examples/*.png
examples/*.gif
# Keep test data
!tests/data/**

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 pyWebLayout Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

210
README.md Normal file
View File

@ -0,0 +1,210 @@
# pyWebLayout-ereader
A complete ebook reader application built with [pyWebLayout](https://github.com/yourusername/pyWebLayout).
This project demonstrates how to build a full-featured ebook reader using pyWebLayout's layout engine. It serves as both a reference implementation and a ready-to-use ereader library.
## Features
- 📖 **EPUB Support** - Load and read EPUB files
- 📄 **Page Navigation** - Forward/backward navigation with smooth rendering
- 🔖 **Bookmarks** - Save and restore reading positions
- 🎨 **Text Highlighting** - Highlight words and selections with notes
- 🔍 **Text Selection** - Select and query text via touch/click
- ⚙️ **Customization** - Font size, line spacing, colors
- 📑 **Chapter Navigation** - Jump to chapters via table of contents
- 👆 **Gesture Support** - Tap, swipe, pinch, long-press handling
- 💾 **Position Persistence** - Stable positions across style changes
## Installation
### From Source
```bash
# Install pyWebLayout first if not already installed
cd /path/to/pyWebLayout
pip install -e .
# Install pyWebLayout-ereader
cd /path/to/pyWebLayout-ereader
pip install -e .
```
### As a Dependency
```bash
pip install pyweblayout-ereader
```
## Quick Start
```python
from pyweblayout_ereader import EbookReader
# Create reader
reader = EbookReader(page_size=(800, 1000))
# Load an EPUB
reader.load_epub("mybook.epub")
# Get current page as image
page = reader.get_current_page()
page.save("current_page.png")
# Navigate
reader.next_page()
reader.previous_page()
# Save position
reader.save_position("bookmark1")
# Later, restore position
reader.load_position("bookmark1")
```
## Examples
See the `examples/` directory for complete examples:
- **simple_ereader_example.py** - Basic ereader usage
- **ereader_demo.py** - Full-featured demo with all capabilities
- **word_selection_highlighting.py** - Text selection and highlighting
- **generate_ereader_gifs.py** - Generate animated demos
## Architecture
This project is a **high-level application layer** that combines pyWebLayout components:
```
pyweblayout_ereader.EbookReader
├── pyWebLayout.layout.EreaderLayoutManager # Layout & pagination
├── pyWebLayout.core.HighlightManager # Highlighting system
├── pyWebLayout.io.gesture # Touch/gesture handling
└── pyWebLayout.io.readers # EPUB parsing
```
## API Overview
### Loading Content
```python
reader.load_epub("book.epub")
reader.is_loaded() # Check if book loaded
reader.get_book_info() # Get metadata
```
### Navigation
```python
reader.next_page()
reader.previous_page()
reader.jump_to_chapter("Chapter 1")
reader.get_reading_progress() # 0.0 to 1.0
```
### Styling
```python
reader.increase_font_size()
reader.decrease_font_size()
reader.set_font_size(1.5) # 150% scale
reader.set_line_spacing(8)
reader.set_inter_block_spacing(20)
```
### Bookmarks
```python
reader.save_position("my_bookmark")
reader.load_position("my_bookmark")
reader.list_saved_positions()
reader.delete_position("my_bookmark")
```
### Highlighting
```python
# Highlight a word at pixel coordinates
highlight_id = reader.highlight_word(x=100, y=200, note="Important!")
# Highlight a selection
highlight_id = reader.highlight_selection(
start=(100, 200),
end=(300, 250),
color=(255, 255, 0, 128) # Yellow
)
# Manage highlights
reader.list_highlights()
reader.remove_highlight(highlight_id)
reader.clear_highlights()
```
### Gesture Handling
```python
from pyWebLayout.io.gesture import TouchEvent, GestureType
# 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']}")
```
## Use Cases
- **Desktop Ereader Applications** - Build native ereader apps
- **Web-based Readers** - Serve rendered pages via Flask/FastAPI
- **E-ink Device Firmware** - Optimized for e-ink displays
- **Reading Analytics** - Track reading patterns and highlights
- **Educational Tools** - Annotated reading with highlights and notes
## Relationship to pyWebLayout
**pyWebLayout** is a layout engine library providing low-level primitives for:
- Text rendering and layout
- Document structure and pagination
- Query systems for interactive content
**pyWebLayout-ereader** 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, etc.)
- Handles business logic for gestures and interactions
Think of it like this:
- pyWebLayout = React (library)
- pyWebLayout-ereader = Next.js (framework)
## Development
```bash
# Install in development mode with dev dependencies
pip install -e ".[dev]"
# Run tests
pytest
# Format code
black pyweblayout_ereader/
# Type checking
mypy pyweblayout_ereader/
```
## 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!
## License
MIT License - see LICENSE file for details
## Related Projects
- [pyWebLayout](https://github.com/yourusername/pyWebLayout) - The underlying layout engine
- Add your projects here!

363
examples/README_EREADER.md Normal file
View File

@ -0,0 +1,363 @@
# EbookReader - Simple EPUB Reader Application
The `EbookReader` class provides a complete, user-friendly interface for building ebook reader applications with pyWebLayout. It wraps all the complex ereader infrastructure into a simple API.
## Features
- 📖 **EPUB Loading** - Load EPUB files with automatic content extraction
- ⬅️➡️ **Page Navigation** - Forward and backward page navigation
- 🔖 **Position Management** - Save/load reading positions (stable across font changes)
- 📑 **Chapter Navigation** - Jump to chapters by title or index
- 🔤 **Font Size Control** - Increase/decrease font size with live re-rendering
- 📏 **Spacing Control** - Adjust line and block spacing
- 📊 **Progress Tracking** - Get reading progress and position information
- 💾 **Context Manager Support** - Automatic cleanup with `with` statement
## Quick Start
```python
from pyWebLayout.layout.ereader_application import EbookReader
# Create reader
reader = EbookReader(page_size=(800, 1000))
# Load an EPUB
reader.load_epub("mybook.epub")
# Get current page as PIL Image
page_image = reader.get_current_page()
page_image.save("current_page.png")
# Navigate
reader.next_page()
reader.previous_page()
# Close reader
reader.close()
```
## API Reference
### Initialization
```python
reader = EbookReader(
page_size=(800, 1000), # Page dimensions (width, height) in pixels
margin=40, # Page margin in pixels
background_color=(255, 255, 255), # RGB background color
line_spacing=5, # Line spacing in pixels
inter_block_spacing=15, # Space between blocks in pixels
bookmarks_dir="ereader_bookmarks", # Directory for bookmarks
buffer_size=5 # Number of pages to cache
)
```
### Loading EPUB
```python
# Load EPUB file
success = reader.load_epub("path/to/book.epub")
# Check if book is loaded
if reader.is_loaded():
print("Book loaded successfully")
# Get book information
book_info = reader.get_book_info()
# Returns: {
# 'title': 'Book Title',
# 'author': 'Author Name',
# 'document_id': 'book',
# 'total_blocks': 5000,
# 'total_chapters': 20,
# 'page_size': (800, 1000),
# 'font_scale': 1.0
# }
```
### Page Navigation
```python
# Get current page as PIL Image
page = reader.get_current_page()
# Navigate to next page
page = reader.next_page() # Returns None at end of book
# Navigate to previous page
page = reader.previous_page() # Returns None at beginning
# Save current page to file
reader.render_to_file("page.png")
```
### Position Management
Positions are saved based on abstract document structure (chapter/block/word indices), making them stable across font size and styling changes.
```python
# Save current position
reader.save_position("my_bookmark")
# Load saved position
page = reader.load_position("my_bookmark")
# List all saved positions
positions = reader.list_saved_positions()
# Returns: ['my_bookmark', 'chapter_2', ...]
# Delete a position
reader.delete_position("my_bookmark")
# Get detailed position info
info = reader.get_position_info()
# Returns: {
# 'position': {'chapter_index': 0, 'block_index': 42, 'word_index': 15, ...},
# 'chapter': {'title': 'Chapter 1', 'level': 'H1', ...},
# 'progress': 0.15, # 15% through the book
# 'font_scale': 1.0,
# 'book_title': 'Book Title',
# 'book_author': 'Author Name'
# }
# Get reading progress (0.0 to 1.0)
progress = reader.get_reading_progress()
print(f"You're {progress*100:.1f}% through the book")
```
### Chapter Navigation
```python
# Get all chapters
chapters = reader.get_chapters()
# Returns: [('Chapter 1', 0), ('Chapter 2', 1), ...]
# Get chapters with positions
chapter_positions = reader.get_chapter_positions()
# Returns: [('Chapter 1', RenderingPosition(...)), ...]
# Jump to chapter by index
page = reader.jump_to_chapter(1) # Jump to second chapter
# Jump to chapter by title
page = reader.jump_to_chapter("Chapter 1")
# Get current chapter info
chapter_info = reader.get_current_chapter_info()
# Returns: {'title': 'Chapter 1', 'level': HeadingLevel.H1, 'block_index': 0}
```
### Font Size Control
```python
# Get current font size scale
scale = reader.get_font_size() # Default: 1.0
# Set specific font size scale
page = reader.set_font_size(1.5) # 150% of normal size
# Increase font size by 10%
page = reader.increase_font_size()
# Decrease font size by 10%
page = reader.decrease_font_size()
```
### Spacing Control
```python
# Set line spacing (spacing between lines within a paragraph)
page = reader.set_line_spacing(10) # 10 pixels
# Set inter-block spacing (spacing between paragraphs, headings, etc.)
page = reader.set_inter_block_spacing(20) # 20 pixels
```
### Context Manager
The reader supports Python's context manager protocol for automatic cleanup:
```python
with EbookReader(page_size=(800, 1000)) as reader:
reader.load_epub("book.epub")
page = reader.get_current_page()
# ... do stuff
# Automatically saves position and cleans up resources
```
## Complete Example
```python
from pyWebLayout.layout.ereader_application import EbookReader
# Create reader with custom settings
with EbookReader(
page_size=(800, 1000),
margin=50,
line_spacing=8,
inter_block_spacing=20
) as reader:
# Load EPUB
if not reader.load_epub("my_novel.epub"):
print("Failed to load EPUB")
exit(1)
# Get book info
info = reader.get_book_info()
print(f"Reading: {info['title']} by {info['author']}")
print(f"Total chapters: {info['total_chapters']}")
# Navigate through first few pages
for i in range(5):
page = reader.get_current_page()
page.save(f"page_{i+1:03d}.png")
reader.next_page()
# Save current position
reader.save_position("page_5")
# Jump to a chapter
chapters = reader.get_chapters()
if len(chapters) > 2:
print(f"Jumping to: {chapters[2][0]}")
reader.jump_to_chapter(2)
reader.render_to_file("chapter_3_start.png")
# Return to saved position
reader.load_position("page_5")
# Adjust font size
reader.increase_font_size()
reader.render_to_file("page_5_larger_font.png")
# Get progress
progress = reader.get_reading_progress()
print(f"Reading progress: {progress*100:.1f}%")
```
## Demo Script
Run the comprehensive demo to see all features in action:
```bash
python examples/ereader_demo.py path/to/book.epub
```
This will demonstrate:
- Basic page navigation
- Position save/load
- Chapter navigation
- Font size adjustments
- Spacing adjustments
- Book information retrieval
The demo generates multiple PNG files showing different pages and settings.
## Position Storage Format
Positions are stored as JSON files in the `bookmarks_dir` (default: `ereader_bookmarks/`):
```json
{
"chapter_index": 0,
"block_index": 42,
"word_index": 15,
"table_row": 0,
"table_col": 0,
"list_item_index": 0,
"remaining_pretext": null,
"page_y_offset": 0
}
```
This format is tied to the abstract document structure, making positions stable across:
- Font size changes
- Line spacing changes
- Inter-block spacing changes
- Page size changes
## Integration Example: Simple GUI
Here's a minimal example of integrating with Tkinter:
```python
import tkinter as tk
from tkinter import filedialog
from PIL import ImageTk
from pyWebLayout.layout.ereader_application import EbookReader
class SimpleEreaderGUI:
def __init__(self, root):
self.root = root
self.reader = EbookReader(page_size=(600, 800))
# Create UI
self.image_label = tk.Label(root)
self.image_label.pack()
btn_frame = tk.Frame(root)
btn_frame.pack()
tk.Button(btn_frame, text="Open EPUB", command=self.open_epub).pack(side=tk.LEFT)
tk.Button(btn_frame, text="Previous", command=self.prev_page).pack(side=tk.LEFT)
tk.Button(btn_frame, text="Next", command=self.next_page).pack(side=tk.LEFT)
tk.Button(btn_frame, text="Font+", command=self.increase_font).pack(side=tk.LEFT)
tk.Button(btn_frame, text="Font-", command=self.decrease_font).pack(side=tk.LEFT)
def open_epub(self):
filepath = filedialog.askopenfilename(filetypes=[("EPUB files", "*.epub")])
if filepath:
self.reader.load_epub(filepath)
self.display_page()
def display_page(self):
page = self.reader.get_current_page()
if page:
photo = ImageTk.PhotoImage(page)
self.image_label.config(image=photo)
self.image_label.image = photo
def next_page(self):
if self.reader.next_page():
self.display_page()
def prev_page(self):
if self.reader.previous_page():
self.display_page()
def increase_font(self):
self.reader.increase_font_size()
self.display_page()
def decrease_font(self):
self.reader.decrease_font_size()
self.display_page()
root = tk.Tk()
root.title("Simple Ereader")
app = SimpleEreaderGUI(root)
root.mainloop()
```
## Performance Notes
- The reader uses intelligent page caching for fast navigation
- First page load may take ~1 second, subsequent pages are typically < 0.1 seconds
- Background rendering attempts to pre-cache upcoming pages (you may see pickle warnings, which can be ignored)
- Font size changes invalidate the cache and require re-rendering from the current position
- Position save/load is nearly instantaneous
## Limitations
- Currently supports EPUB files only (no PDF, MOBI, etc.)
- Images in EPUBs may not render in some cases
- Tables are skipped in rendering
- Complex HTML layouts may not render perfectly
- No text selection or search functionality (these would need to be added separately)
## See Also
- `examples/ereader_demo.py` - Comprehensive feature demonstration
- `pyWebLayout/layout/ereader_manager.py` - Underlying manager class
- `pyWebLayout/layout/ereader_layout.py` - Core layout engine
- `examples/README_EPUB_RENDERERS.md` - Lower-level EPUB rendering

326
examples/ereader_demo.py Normal file
View File

@ -0,0 +1,326 @@
#!/usr/bin/env python3
"""
Comprehensive demo of the EbookReader functionality.
This script demonstrates all features of the pyWebLayout EbookReader:
- Loading EPUB files
- Page navigation (forward/backward)
- Position saving/loading
- Chapter navigation
- Font size and spacing adjustments
- Getting book and position information
Usage:
python ereader_demo.py path/to/book.epub
"""
import sys
import os
from pathlib import Path
from pyweblayout_ereader import EbookReader
def print_separator():
"""Print a visual separator."""
print("\n" + "="*70 + "\n")
def demo_basic_navigation(reader: EbookReader):
"""Demonstrate basic page navigation."""
print("DEMO: Basic Navigation")
print_separator()
# Get current page
print("Getting first page...")
page = reader.get_current_page()
if page:
print(f"✓ Current page rendered: {page.size}")
reader.render_to_file("demo_page_001.png")
print(" Saved to: demo_page_001.png")
# Navigate forward
print("\nNavigating to next page...")
page = reader.next_page()
if page:
print(f"✓ Next page rendered: {page.size}")
reader.render_to_file("demo_page_002.png")
print(" Saved to: demo_page_002.png")
# Navigate backward
print("\nNavigating to previous page...")
page = reader.previous_page()
if page:
print(f"✓ Previous page rendered: {page.size}")
print_separator()
def demo_position_management(reader: EbookReader):
"""Demonstrate position save/load functionality."""
print("DEMO: Position Management")
print_separator()
# Navigate a few pages forward
print("Navigating forward 3 pages...")
for i in range(3):
reader.next_page()
# Save position
print("Saving current position as 'demo_bookmark'...")
success = reader.save_position("demo_bookmark")
if success:
print("✓ Position saved successfully")
# Get position info
pos_info = reader.get_position_info()
print(f"\nCurrent position info:")
print(f" Chapter: {pos_info.get('chapter', {}).get('title', 'N/A')}")
print(f" Block index: {pos_info['position']['block_index']}")
print(f" Word index: {pos_info['position']['word_index']}")
print(f" Progress: {pos_info['progress']*100:.1f}%")
# Navigate away
print("\nNavigating forward 5 more pages...")
for i in range(5):
reader.next_page()
# Load saved position
print("Loading saved position 'demo_bookmark'...")
page = reader.load_position("demo_bookmark")
if page:
print("✓ Position restored successfully")
reader.render_to_file("demo_restored_position.png")
print(" Saved to: demo_restored_position.png")
# List all saved positions
positions = reader.list_saved_positions()
print(f"\nAll saved positions: {positions}")
print_separator()
def demo_chapter_navigation(reader: EbookReader):
"""Demonstrate chapter navigation."""
print("DEMO: Chapter Navigation")
print_separator()
# Get all chapters
chapters = reader.get_chapters()
print(f"Found {len(chapters)} chapters:")
for title, idx in chapters[:5]: # Show first 5
print(f" [{idx}] {title}")
if len(chapters) > 5:
print(f" ... and {len(chapters) - 5} more")
# Jump to a chapter by index
if len(chapters) > 1:
print(f"\nJumping to chapter 1...")
page = reader.jump_to_chapter(1)
if page:
print("✓ Jumped to chapter successfully")
reader.render_to_file("demo_chapter_1.png")
print(" Saved to: demo_chapter_1.png")
# Get current chapter info
chapter_info = reader.get_current_chapter_info()
if chapter_info:
print(f" Current chapter: {chapter_info['title']}")
# Jump to a chapter by title (if we have chapters)
if len(chapters) > 0:
first_chapter_title = chapters[0][0]
print(f"\nJumping to chapter by title: '{first_chapter_title}'...")
page = reader.jump_to_chapter(first_chapter_title)
if page:
print("✓ Jumped to chapter by title successfully")
print_separator()
def demo_font_size_adjustment(reader: EbookReader):
"""Demonstrate font size adjustments."""
print("DEMO: Font Size Adjustment")
print_separator()
# Save current page for comparison
print("Rendering page at normal font size (1.0x)...")
page = reader.get_current_page()
if page:
reader.render_to_file("demo_font_normal.png")
print("✓ Saved to: demo_font_normal.png")
# Increase font size
print("\nIncreasing font size...")
page = reader.increase_font_size()
if page:
print(f"✓ Font size increased to {reader.get_font_size():.1f}x")
reader.render_to_file("demo_font_larger.png")
print(" Saved to: demo_font_larger.png")
# Increase again
print("\nIncreasing font size again...")
page = reader.increase_font_size()
if page:
print(f"✓ Font size increased to {reader.get_font_size():.1f}x")
reader.render_to_file("demo_font_largest.png")
print(" Saved to: demo_font_largest.png")
# Decrease font size
print("\nDecreasing font size...")
page = reader.decrease_font_size()
if page:
print(f"✓ Font size decreased to {reader.get_font_size():.1f}x")
# Set specific font size
print("\nResetting to normal font size (1.0x)...")
page = reader.set_font_size(1.0)
if page:
print("✓ Font size reset to 1.0x")
print_separator()
def demo_spacing_adjustment(reader: EbookReader):
"""Demonstrate line and block spacing adjustments."""
print("DEMO: Spacing Adjustment")
print_separator()
# Save current page
print("Rendering page with default spacing...")
page = reader.get_current_page()
if page:
reader.render_to_file("demo_spacing_default.png")
print("✓ Saved to: demo_spacing_default.png")
# Increase line spacing
print("\nIncreasing line spacing to 10px...")
page = reader.set_line_spacing(10)
if page:
print("✓ Line spacing increased")
reader.render_to_file("demo_spacing_lines_10.png")
print(" Saved to: demo_spacing_lines_10.png")
# Increase inter-block spacing
print("\nIncreasing inter-block spacing to 25px...")
page = reader.set_inter_block_spacing(25)
if page:
print("✓ Inter-block spacing increased")
reader.render_to_file("demo_spacing_blocks_25.png")
print(" Saved to: demo_spacing_blocks_25.png")
# Reset to defaults
print("\nResetting spacing to defaults (line: 5px, block: 15px)...")
reader.set_line_spacing(5)
page = reader.set_inter_block_spacing(15)
if page:
print("✓ Spacing reset to defaults")
print_separator()
def demo_book_information(reader: EbookReader):
"""Demonstrate getting book information."""
print("DEMO: Book Information")
print_separator()
# Get book info
book_info = reader.get_book_info()
print("Book Information:")
print(f" Title: {book_info['title']}")
print(f" Author: {book_info['author']}")
print(f" Document ID: {book_info['document_id']}")
print(f" Total blocks: {book_info['total_blocks']}")
print(f" Total chapters: {book_info['total_chapters']}")
print(f" Page size: {book_info['page_size']}")
print(f" Font scale: {book_info['font_scale']}")
# Get reading progress
progress = reader.get_reading_progress()
print(f"\nReading Progress: {progress*100:.1f}%")
# Get detailed position info
pos_info = reader.get_position_info()
print("\nDetailed Position:")
print(f" Chapter index: {pos_info['position']['chapter_index']}")
print(f" Block index: {pos_info['position']['block_index']}")
print(f" Word index: {pos_info['position']['word_index']}")
chapter = pos_info.get('chapter', {})
if chapter.get('title'):
print(f" Current chapter: {chapter['title']}")
print_separator()
def main():
"""Main function to run all demos."""
if len(sys.argv) < 2:
print("Usage: python ereader_demo.py path/to/book.epub")
print("\nExample EPUBs to try:")
print(" - tests/data/test.epub")
print(" - tests/data/test2.epub")
sys.exit(1)
epub_path = sys.argv[1]
if not os.path.exists(epub_path):
print(f"Error: File not found: {epub_path}")
sys.exit(1)
print("="*70)
print(" EbookReader Demo - pyWebLayout")
print("="*70)
print(f"\nLoading EPUB: {epub_path}")
# Create reader with context manager
with EbookReader(page_size=(800, 1000)) as reader:
# Load the EPUB
if not reader.load_epub(epub_path):
print("Error: Failed to load EPUB file")
sys.exit(1)
print("✓ EPUB loaded successfully")
# Run all demos
try:
demo_basic_navigation(reader)
demo_position_management(reader)
demo_chapter_navigation(reader)
demo_font_size_adjustment(reader)
demo_spacing_adjustment(reader)
demo_book_information(reader)
print("\n" + "="*70)
print(" Demo Complete!")
print("="*70)
print("\nGenerated demo images:")
demo_files = [
"demo_page_001.png",
"demo_page_002.png",
"demo_restored_position.png",
"demo_chapter_1.png",
"demo_font_normal.png",
"demo_font_larger.png",
"demo_font_largest.png",
"demo_spacing_default.png",
"demo_spacing_lines_10.png",
"demo_spacing_blocks_25.png"
]
for filename in demo_files:
if os.path.exists(filename):
print(f"{filename}")
print("\nAll features demonstrated successfully!")
except Exception as e:
print(f"\nError during demo: {e}")
import traceback
traceback.print_exc()
sys.exit(1)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,419 @@
#!/usr/bin/env python3
"""
Generate animated GIFs demonstrating EbookReader functionality.
This script creates animated GIFs showcasing:
1. Page navigation (next/previous)
2. Font size adjustment
3. Chapter navigation
4. Bookmark/position management
5. Word highlighting
The GIFs are saved to the examples/ directory and can be included in documentation.
Usage:
python generate_ereader_gifs.py path/to/book.epub [output_dir]
Example:
python generate_ereader_gifs.py ../tests/data/test.epub ../docs/images
"""
import sys
import os
from pathlib import Path
from typing import List
from pyweblayout_ereader import EbookReader
from pyWebLayout.core.highlight import HighlightColor
from PIL import Image
def create_gif(images: List[Image.Image], output_path: str, duration: int = 800, loop: int = 0):
"""
Create an animated GIF from a list of PIL Images.
Args:
images: List of PIL Images to animate
output_path: Path where to save the GIF
duration: Duration of each frame in milliseconds
loop: Number of loops (0 = infinite)
"""
if not images:
print(f"Warning: No images provided for {output_path}")
return False
try:
# Save as animated GIF
images[0].save(
output_path,
save_all=True,
append_images=images[1:],
duration=duration,
loop=loop,
optimize=False
)
print(f"✓ Created: {output_path} ({len(images)} frames)")
return True
except Exception as e:
print(f"✗ Error creating {output_path}: {e}")
return False
def generate_page_navigation_gif(reader: EbookReader, output_path: str):
"""Generate GIF showing page navigation (forward and backward)."""
print("\n[1/4] Generating page navigation GIF...")
frames = []
# Go to beginning
reader.set_font_size(1.0)
# Capture 5 pages going forward
for i in range(5):
page = reader.get_current_page()
if page:
frames.append(page.copy())
reader.next_page()
# Go back to start
for _ in range(4):
reader.previous_page()
# Capture 5 pages going forward again (smoother loop)
for i in range(5):
page = reader.get_current_page()
if page:
frames.append(page.copy())
reader.next_page()
create_gif(frames, output_path, duration=600)
def generate_font_size_gif(reader: EbookReader, output_path: str):
"""Generate GIF showing font size adjustment."""
print("\n[2/4] Generating font size adjustment GIF...")
frames = []
# Reset to beginning and normal font
for _ in range(10):
reader.previous_page()
reader.set_font_size(1.0)
# Font sizes to demonstrate
font_scales = [0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.3, 1.2, 1.1, 1.0, 0.9, 0.8]
for scale in font_scales:
page = reader.set_font_size(scale)
if page:
frames.append(page.copy())
# Reset to normal
reader.set_font_size(1.0)
create_gif(frames, output_path, duration=500)
def generate_chapter_navigation_gif(reader: EbookReader, output_path: str):
"""Generate GIF showing chapter navigation."""
print("\n[3/4] Generating chapter navigation GIF...")
frames = []
# Reset font
reader.set_font_size(1.0)
# Get chapters
chapters = reader.get_chapters()
if len(chapters) == 0:
print(" Warning: No chapters found, skipping chapter navigation GIF")
return
# Visit first few chapters (or loop through available chapters)
chapter_indices = list(range(min(5, len(chapters))))
# Add some chapters twice for smoother animation
for idx in chapter_indices:
page = reader.jump_to_chapter(idx)
if page:
frames.append(page.copy())
# Add a second frame at each chapter for pause effect
frames.append(page.copy())
# Go back to first chapter
page = reader.jump_to_chapter(0)
if page:
frames.append(page.copy())
if frames:
create_gif(frames, output_path, duration=1000)
else:
print(" Warning: No frames captured for chapter navigation")
def generate_bookmark_gif(reader: EbookReader, output_path: str):
"""Generate GIF showing bookmark save/load functionality."""
print("\n[4/5] Generating bookmark/position GIF...")
frames = []
# Reset font
reader.set_font_size(1.0)
# Go to beginning
for _ in range(20):
reader.previous_page()
# Capture initial position
page = reader.get_current_page()
if page:
frames.append(page.copy())
frames.append(page.copy()) # Hold frame
# Navigate forward a bit
for i in range(3):
reader.next_page()
page = reader.get_current_page()
if page:
frames.append(page.copy())
# Save this position
reader.save_position("demo_bookmark")
page = reader.get_current_page()
if page:
frames.append(page.copy())
frames.append(page.copy()) # Hold frame to show saved position
# Navigate away
for i in range(5):
reader.next_page()
page = reader.get_current_page()
if page:
frames.append(page.copy())
# Hold at distant position
page = reader.get_current_page()
if page:
frames.append(page.copy())
frames.append(page.copy())
# Jump back to bookmark
page = reader.load_position("demo_bookmark")
if page:
frames.append(page.copy())
frames.append(page.copy())
frames.append(page.copy()) # Hold longer to show we're back
create_gif(frames, output_path, duration=600)
def generate_highlighting_gif(reader: EbookReader, output_path: str):
"""Generate GIF showing word highlighting functionality."""
print("\n[5/5] Generating word highlighting GIF...")
frames = []
# Reset font
reader.set_font_size(1.0)
# Find a page with actual text content (skip title/cover pages)
for _ in range(5):
reader.next_page()
# Collect text objects from the page with their actual positions
from pyWebLayout.concrete.text import Line
text_positions = []
# Try to find a page with text
max_attempts = 10
for attempt in range(max_attempts):
page = reader.manager.get_current_page()
text_positions = []
for child in page._children:
if isinstance(child, Line):
for text_obj in child._text_objects:
# Skip empty text
if not hasattr(text_obj, '_text') or not text_obj._text or not text_obj._text.strip():
continue
# Calculate center of text object, but clamp Y to Line bounds
origin = text_obj._origin
size = text_obj.size
center_x = int(origin[0] + size[0] / 2)
center_y = int(origin[1] + size[1] / 2)
# Clamp Y to be within Line bounds (avoids the baseline extension issue)
line_y_min = int(child._origin[1])
line_y_max = int(child._origin[1] + child._size[1])
clamped_y = max(line_y_min, min(line_y_max - 1, center_y))
text_positions.append((center_x, clamped_y, text_obj._text))
# If we found enough text, use this page
if len(text_positions) > 10:
print(f" Found page with {len(text_positions)} words")
break
# Otherwise try next page
reader.next_page()
if len(text_positions) == 0:
print(" Warning: Could not find a page with text after searching")
# Capture initial page without highlights
page_img = reader.get_current_page(include_highlights=False)
if page_img:
frames.append(page_img.copy())
frames.append(page_img.copy()) # Hold frame
# Use different colors for highlighting
colors = [
HighlightColor.YELLOW.value,
HighlightColor.GREEN.value,
HighlightColor.BLUE.value,
HighlightColor.PINK.value,
HighlightColor.ORANGE.value,
]
# Select a subset of words to highlight (spread across the page)
# Take every Nth word to get a good distribution
if len(text_positions) > 10:
step = len(text_positions) // 5
selected_positions = [text_positions[i * step] for i in range(5) if i * step < len(text_positions)]
else:
selected_positions = text_positions[:5]
highlighted_words = 0
color_names = ['YELLOW', 'GREEN', 'BLUE', 'PINK', 'ORANGE']
print(f"\n Highlighting words:")
for i, (x, y, text) in enumerate(selected_positions):
color = colors[i % len(colors)]
color_name = color_names[i % len(color_names)]
# Highlight the word at this position
highlight_id = reader.highlight_word(x, y, color=color)
if highlight_id:
highlighted_words += 1
print(f" [{color_name:6s}] {text}")
# Capture page with new highlight
page_img = reader.get_current_page(include_highlights=True)
if page_img:
frames.append(page_img.copy())
# Hold frame briefly to show the new highlight
frames.append(page_img.copy())
# If we managed to highlight any words, show the final result
if highlighted_words > 0:
page_img = reader.get_current_page(include_highlights=True)
if page_img:
# Hold final frame longer
for _ in range(3):
frames.append(page_img.copy())
# Clear highlights one by one
for highlight in reader.list_highlights():
reader.remove_highlight(highlight.id)
page_img = reader.get_current_page(include_highlights=True)
if page_img:
frames.append(page_img.copy())
# Show final cleared page
page_img = reader.get_current_page(include_highlights=False)
if page_img:
frames.append(page_img.copy())
frames.append(page_img.copy())
print(f" Successfully highlighted {highlighted_words} words")
else:
print(" Warning: No words found to highlight on current page")
if frames:
create_gif(frames, output_path, duration=700)
else:
print(" Warning: No frames captured for highlighting")
def main():
"""Main function to generate all GIFs."""
if len(sys.argv) < 2:
print("Usage: python generate_ereader_gifs.py path/to/book.epub [output_dir]")
print("\nExample:")
print(" python generate_ereader_gifs.py ../tests/data/test.epub ../docs/images")
sys.exit(1)
epub_path = sys.argv[1]
output_dir = sys.argv[2] if len(sys.argv) > 2 else "."
# Validate EPUB path
if not os.path.exists(epub_path):
print(f"Error: EPUB file not found: {epub_path}")
sys.exit(1)
# Create output directory
os.makedirs(output_dir, exist_ok=True)
print("="*70)
print(" EbookReader Animated GIF Generator")
print("="*70)
print(f"\nInput EPUB: {epub_path}")
print(f"Output directory: {output_dir}")
# Create paths for output GIFs
nav_gif = os.path.join(output_dir, "ereader_page_navigation.gif")
font_gif = os.path.join(output_dir, "ereader_font_size.gif")
chapter_gif = os.path.join(output_dir, "ereader_chapter_navigation.gif")
bookmark_gif = os.path.join(output_dir, "ereader_bookmarks.gif")
highlight_gif = os.path.join(output_dir, "ereader_highlighting.gif")
try:
# Create reader
with EbookReader(page_size=(600, 800), margin=30) as reader:
# Load EPUB
print("\nLoading EPUB...")
if not reader.load_epub(epub_path):
print("Error: Failed to load EPUB file")
sys.exit(1)
print("✓ EPUB loaded successfully")
# Get book info
book_info = reader.get_book_info()
print(f"\nBook: {book_info['title']}")
print(f"Author: {book_info['author']}")
print(f"Chapters: {book_info['total_chapters']}")
print(f"Blocks: {book_info['total_blocks']}")
print("\nGenerating GIFs...")
print("-" * 70)
# Generate all GIFs
generate_page_navigation_gif(reader, nav_gif)
generate_font_size_gif(reader, font_gif)
generate_chapter_navigation_gif(reader, chapter_gif)
generate_bookmark_gif(reader, bookmark_gif)
generate_highlighting_gif(reader, highlight_gif)
print("\n" + "="*70)
print(" Generation Complete!")
print("="*70)
print("\nGenerated files:")
for gif_path in [nav_gif, font_gif, chapter_gif, bookmark_gif, highlight_gif]:
if os.path.exists(gif_path):
size = os.path.getsize(gif_path)
print(f"{gif_path} ({size/1024:.1f} KB)")
print("\nYou can now add these GIFs to your README.md!")
except Exception as e:
print(f"\nError: {e}")
import traceback
traceback.print_exc()
sys.exit(1)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,93 @@
#!/usr/bin/env python3
"""
Simple example showing the most common EbookReader usage.
This script loads an EPUB and allows you to navigate through it,
saving each page as an image.
Usage:
python simple_ereader_example.py book.epub
"""
import sys
from pathlib import Path
from pyweblayout_ereader import EbookReader
def main():
if len(sys.argv) < 2:
print("Usage: python simple_ereader_example.py book.epub")
sys.exit(1)
epub_path = sys.argv[1]
# Create reader and load EPUB
print(f"Loading: {epub_path}")
reader = EbookReader(page_size=(800, 1000))
if not reader.load_epub(epub_path):
print("Failed to load EPUB")
sys.exit(1)
# Get book information
info = reader.get_book_info()
print(f"\nBook: {info['title']}")
print(f"Author: {info['author']}")
print(f"Total blocks: {info['total_blocks']}")
# Get chapters
chapters = reader.get_chapters()
print(f"Chapters: {len(chapters)}")
if chapters:
print("\nChapter list:")
for title, idx in chapters[:10]: # Show first 10
print(f" {idx}: {title}")
if len(chapters) > 10:
print(f" ... and {len(chapters) - 10} more")
# Navigate through first 10 pages
print("\nRendering first 10 pages...")
for i in range(10):
page = reader.get_current_page()
if page:
filename = f"page_{i+1:03d}.png"
reader.render_to_file(filename)
# Show progress
progress = reader.get_reading_progress()
chapter_info = reader.get_current_chapter_info()
chapter_name = chapter_info['title'] if chapter_info else "N/A"
print(f" Page {i+1}: {filename} (Progress: {progress*100:.1f}%, Chapter: {chapter_name})")
# Move to next page
if not reader.next_page():
print(" Reached end of book")
break
# Save current position
reader.save_position("stopped_at_page_10")
print("\nSaved position as 'stopped_at_page_10'")
# Example: Jump to a chapter (if available)
if len(chapters) >= 2:
print(f"\nJumping to chapter: {chapters[1][0]}")
reader.jump_to_chapter(1)
reader.render_to_file("chapter_2_start.png")
print(" Saved to: chapter_2_start.png")
# Example: Increase font size
print("\nIncreasing font size...")
reader.increase_font_size()
reader.render_to_file("larger_font.png")
print(f" Font size now: {reader.get_font_size():.1f}x")
print(" Saved to: larger_font.png")
# Close reader (saves current position automatically)
reader.close()
print("\nDone! Current position saved automatically.")
if __name__ == "__main__":
main()

View File

@ -0,0 +1,55 @@
#!/usr/bin/env python3
"""
Simple Example: Highlight a Word on Tap
This is the minimal example showing how to:
1. Load an ebook
2. Simulate a tap
3. Find the word at that location
4. Highlight it
Perfect for understanding the basic query system.
"""
from PIL import Image, ImageDraw
from pyweblayout_ereader import EbookReader
def main():
# 1. Create reader and load book
reader = EbookReader(page_size=(800, 1000))
reader.load_epub("tests/data/test.epub")
# 2. Get current page as image
page_img = reader.get_current_page()
# 3. Simulate a tap at pixel coordinates
tap_x, tap_y = 200, 300
result = reader.query_pixel(tap_x, tap_y)
# 4. If we found a word, highlight it
if result and result.text:
print(f"Tapped on word: '{result.text}'")
print(f"Bounds: {result.bounds}")
# Draw yellow highlight
x, y, w, h = result.bounds
overlay = Image.new('RGBA', page_img.size, (255, 255, 255, 0))
draw = ImageDraw.Draw(overlay)
draw.rectangle([x, y, x + w, y + h], fill=(255, 255, 0, 100))
# Combine and save
highlighted = Image.alpha_composite(
page_img.convert('RGBA'),
overlay
)
highlighted.save("highlighted_word.png")
print("Saved: highlighted_word.png")
else:
print("No word found at that location")
reader.close()
if __name__ == "__main__":
main()

View File

@ -0,0 +1,361 @@
#!/usr/bin/env python3
"""
Example: Word Selection and Highlighting
This example demonstrates how to:
1. Query a pixel location to find a word
2. Select a range of words between two points
3. Highlight selected words by drawing overlays
4. Handle tap gestures to select words
This is useful for:
- Word definition lookup
- Text highlighting/annotation
- Copy/paste functionality
- Interactive reading features
"""
from PIL import Image, ImageDraw
import numpy as np
from pyweblayout_ereader import EbookReader
from pyWebLayout.io.gesture import TouchEvent, GestureType
from pyWebLayout.core.query import QueryResult
def draw_highlight(image: Image.Image, bounds: tuple, color: tuple = (255, 255, 0, 100)):
"""
Draw a highlight overlay on an image at the given bounds.
Args:
image: PIL Image to draw on
bounds: (x, y, width, height) tuple
color: RGBA color tuple (with alpha for transparency)
"""
# Create a semi-transparent overlay
overlay = Image.new('RGBA', image.size, (255, 255, 255, 0))
draw = ImageDraw.Draw(overlay)
x, y, w, h = bounds
# Draw rectangle with rounded corners for nicer appearance
draw.rectangle([x, y, x + w, y + h], fill=color)
# Composite the overlay onto the original image
image = Image.alpha_composite(image.convert('RGBA'), overlay)
return image
def example_1_single_word_selection():
"""Example 1: Select and highlight a single word by tapping"""
print("=" * 60)
print("Example 1: Single Word Selection")
print("=" * 60)
# Create reader and load a book
reader = EbookReader(page_size=(800, 1000))
success = reader.load_epub("tests/data/test.epub")
if not success:
print("Failed to load EPUB")
return
print(f"Loaded: {reader.book_title} by {reader.book_author}")
# Get current page as image
page_img = reader.get_current_page()
if not page_img:
print("No page rendered")
return
# Simulate a tap at coordinates (200, 300)
tap_x, tap_y = 200, 300
print(f"\nSimulating tap at ({tap_x}, {tap_y})")
# Query what's at that location
result = reader.query_pixel(tap_x, tap_y)
if result and result.text:
print(f"Found word: '{result.text}'")
print(f"Type: {result.object_type}")
print(f"Bounds: {result.bounds}")
print(f"Is interactive: {result.is_interactive}")
# Highlight the word
highlighted_img = draw_highlight(page_img, result.bounds, color=(255, 255, 0, 80))
highlighted_img.save("output_single_word_highlight.png")
print(f"\nSaved highlighted image to: output_single_word_highlight.png")
else:
print("No word found at that location")
reader.close()
def example_2_range_selection():
"""Example 2: Select and highlight a range of words (text selection)"""
print("\n" + "=" * 60)
print("Example 2: Range Selection (Multi-word)")
print("=" * 60)
# Create reader and load a book
reader = EbookReader(page_size=(800, 1000))
success = reader.load_epub("tests/data/test.epub")
if not success:
print("Failed to load EPUB")
return
# Get current page
page_img = reader.get_current_page()
if not page_img:
return
# Simulate dragging from (100, 200) to (400, 250)
start_x, start_y = 100, 200
end_x, end_y = 400, 250
print(f"Simulating selection from ({start_x}, {start_y}) to ({end_x}, {end_y})")
# Create drag gesture events
drag_start = TouchEvent(GestureType.DRAG_START, start_x, start_y)
drag_move = TouchEvent(GestureType.DRAG_MOVE, end_x, end_y)
drag_end = TouchEvent(GestureType.DRAG_END, end_x, end_y)
# Handle the gesture (business logic)
reader.handle_touch(drag_start)
reader.handle_touch(drag_move)
response = reader.handle_touch(drag_end)
if response.action == "selection_complete":
selected_text = response.data.get('text', '')
bounds_list = response.data.get('bounds', [])
word_count = response.data.get('word_count', 0)
print(f"\nSelected {word_count} words:")
print(f"Text: \"{selected_text}\"")
# Highlight all selected words
highlighted_img = page_img
for bounds in bounds_list:
highlighted_img = draw_highlight(
highlighted_img,
bounds,
color=(100, 200, 255, 80) # Light blue highlight
)
highlighted_img.save("output_range_highlight.png")
print(f"\nSaved highlighted image to: output_range_highlight.png")
else:
print(f"Selection action: {response.action}")
reader.close()
def example_3_interactive_word_lookup():
"""Example 3: Interactive word lookup with gesture handling"""
print("\n" + "=" * 60)
print("Example 3: Interactive Word Lookup (with Gestures)")
print("=" * 60)
# Create reader
reader = EbookReader(page_size=(800, 1000))
success = reader.load_epub("tests/data/test.epub")
if not success:
print("Failed to load EPUB")
return
# Get page
page_img = reader.get_current_page()
if not page_img:
return
# Define some simulated touch events
test_gestures = [
("Tap at (250, 300)", TouchEvent(GestureType.TAP, 250, 300)),
("Long press at (250, 300)", TouchEvent(GestureType.LONG_PRESS, 250, 300)),
("Swipe left", TouchEvent(GestureType.SWIPE_LEFT, 600, 500)),
]
for description, event in test_gestures:
print(f"\n{description}:")
response = reader.handle_touch(event)
print(f" Action: {response.action}")
if response.action == "word_selected":
word = response.data.get('word', '')
bounds = response.data.get('bounds', (0, 0, 0, 0))
print(f" Selected word: '{word}'")
print(f" Bounds: {bounds}")
# Highlight the word
highlighted_img = draw_highlight(page_img, bounds, color=(255, 200, 0, 100))
filename = f"output_word_lookup_{word}.png"
highlighted_img.save(filename)
print(f" Saved: {filename}")
elif response.action == "define":
word = response.data.get('word', '')
print(f" Show definition for: '{word}'")
# In real app, you'd call a dictionary API here
elif response.action == "page_turn":
direction = response.data.get('direction', '')
progress = response.data.get('progress', 0)
print(f" Page turn {direction}, progress: {progress:.1%}")
reader.close()
def example_4_multi_word_annotation():
"""Example 4: Annotate multiple words with different colors"""
print("\n" + "=" * 60)
print("Example 4: Multi-word Annotation")
print("=" * 60)
# Create reader
reader = EbookReader(page_size=(800, 1000))
success = reader.load_epub("tests/data/test.epub")
if not success:
print("Failed to load EPUB")
return
# Get page
page_img = reader.get_current_page()
if not page_img:
return
# Simulate multiple taps at different locations
tap_locations = [
(150, 200, "Important word", (255, 100, 100, 80)), # Red
(300, 200, "Key concept", (100, 255, 100, 80)), # Green
(450, 200, "Notable term", (100, 100, 255, 80)), # Blue
]
annotated_img = page_img
annotations = []
for x, y, label, color in tap_locations:
result = reader.query_pixel(x, y)
if result and result.text:
print(f"\nFound word at ({x}, {y}): '{result.text}'")
print(f" Annotation: {label}")
# Highlight with specific color
annotated_img = draw_highlight(annotated_img, result.bounds, color)
annotations.append({
'word': result.text,
'label': label,
'bounds': result.bounds
})
# Save annotated image
annotated_img.save("output_multi_annotation.png")
print(f"\nSaved annotated image with {len(annotations)} highlights")
print("File: output_multi_annotation.png")
# Print annotation summary
print("\nAnnotation Summary:")
for i, ann in enumerate(annotations, 1):
print(f" {i}. '{ann['word']}' - {ann['label']}")
reader.close()
def example_5_link_highlighting():
"""Example 5: Find and highlight all links on a page"""
print("\n" + "=" * 60)
print("Example 5: Find and Highlight All Links")
print("=" * 60)
# Create reader
reader = EbookReader(page_size=(800, 1000))
success = reader.load_epub("tests/data/test.epub")
if not success:
print("Failed to load EPUB")
return
# Get page
page_img = reader.get_current_page()
if not page_img:
return
# Get the page object to scan for links
page = reader.manager.get_current_page()
# Scan through all rendered content to find links
links_found = []
from pyWebLayout.concrete.text import Line
from pyWebLayout.concrete.functional import LinkText
for child in page._children:
if isinstance(child, Line):
for text_obj in child._text_objects:
if isinstance(text_obj, LinkText):
origin = text_obj._origin
size = text_obj.size
bounds = (
int(origin[0]),
int(origin[1]),
int(size[0]),
int(size[1])
)
links_found.append({
'text': text_obj._text,
'target': text_obj._link.location,
'bounds': bounds
})
print(f"Found {len(links_found)} links on page")
# Highlight all links
highlighted_img = page_img
for link in links_found:
print(f"\nLink: '{link['text']}'{link['target']}")
highlighted_img = draw_highlight(
highlighted_img,
link['bounds'],
color=(0, 150, 255, 100) # Blue for links
)
if links_found:
highlighted_img.save("output_links_highlighted.png")
print(f"\nSaved image with {len(links_found)} highlighted links")
print("File: output_links_highlighted.png")
else:
print("\nNo links found on this page")
reader.close()
if __name__ == "__main__":
print("Word Selection and Highlighting Examples")
print("=" * 60)
print()
print("These examples demonstrate the query system for:")
print("- Single word selection")
print("- Range selection (multiple words)")
print("- Interactive gesture handling")
print("- Multi-word annotation")
print("- Link detection and highlighting")
print()
try:
# Run all examples
example_1_single_word_selection()
example_2_range_selection()
example_3_interactive_word_lookup()
example_4_multi_word_annotation()
example_5_link_highlighting()
print("\n" + "=" * 60)
print("All examples completed successfully!")
print("=" * 60)
except Exception as e:
print(f"\nError running examples: {e}")
import traceback
traceback.print_exc()

65
pyproject.toml Normal file
View File

@ -0,0 +1,65 @@
[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "pyweblayout-ereader"
version = "0.1.0"
description = "A complete ebook reader application built with pyWebLayout"
readme = "README.md"
requires-python = ">=3.8"
license = {text = "MIT"}
authors = [
{name = "Your Name", email = "your.email@example.com"}
]
keywords = ["ebook", "reader", "epub", "ereader", "layout"]
classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Topic :: Software Development :: Libraries",
"Topic :: Text Processing :: Markup",
]
dependencies = [
"pyweblayout>=0.1.0",
"Pillow>=9.0.0",
]
[project.optional-dependencies]
dev = [
"pytest>=7.0.0",
"pytest-cov>=3.0.0",
"black>=22.0.0",
"flake8>=4.0.0",
"mypy>=0.950",
]
[project.urls]
Homepage = "https://github.com/yourusername/pyWebLayout-ereader"
Documentation = "https://github.com/yourusername/pyWebLayout-ereader#readme"
Repository = "https://github.com/yourusername/pyWebLayout-ereader"
Issues = "https://github.com/yourusername/pyWebLayout-ereader/issues"
[tool.setuptools.packages.find]
where = ["."]
include = ["pyweblayout_ereader*"]
[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = "test_*.py"
[tool.black]
line-length = 100
target-version = ['py38']
[tool.mypy]
python_version = "3.8"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = false

View File

@ -0,0 +1,11 @@
"""
pyWebLayout-ereader: A complete ebook reader application built with pyWebLayout.
This package provides a high-level, user-friendly ebook reader implementation
with all essential features for building ereader applications.
"""
from pyweblayout_ereader.application import EbookReader, create_ebook_reader
__version__ = "0.1.0"
__all__ = ["EbookReader", "create_ebook_reader"]

File diff suppressed because it is too large Load Diff