This commit is contained in:
parent
1bd9fdb551
commit
bde846bc3e
44
README.md
44
README.md
@ -50,29 +50,60 @@ from pyWebLayout.layout.ereader_application import EbookReader
|
|||||||
with EbookReader(page_size=(800, 1000)) as reader:
|
with EbookReader(page_size=(800, 1000)) as reader:
|
||||||
# Load an EPUB file
|
# Load an EPUB file
|
||||||
reader.load_epub("mybook.epub")
|
reader.load_epub("mybook.epub")
|
||||||
|
|
||||||
# Get current page as PIL Image
|
# Get current page as PIL Image
|
||||||
page = reader.get_current_page()
|
page = reader.get_current_page()
|
||||||
page.save("page_001.png")
|
page.save("page_001.png")
|
||||||
|
|
||||||
# Navigate through pages
|
# Navigate through pages
|
||||||
reader.next_page()
|
reader.next_page()
|
||||||
reader.previous_page()
|
reader.previous_page()
|
||||||
|
|
||||||
# Save reading position
|
# Save reading position
|
||||||
reader.save_position("chapter_3")
|
reader.save_position("chapter_3")
|
||||||
|
|
||||||
# Jump to a chapter
|
# Jump to a chapter
|
||||||
reader.jump_to_chapter("Chapter 5")
|
reader.jump_to_chapter("Chapter 5")
|
||||||
|
|
||||||
# Adjust font size
|
# Adjust font size
|
||||||
reader.increase_font_size()
|
reader.increase_font_size()
|
||||||
|
|
||||||
# Get progress
|
# Get progress
|
||||||
progress = reader.get_reading_progress()
|
progress = reader.get_reading_progress()
|
||||||
print(f"Progress: {progress*100:.1f}%")
|
print(f"Progress: {progress*100:.1f}%")
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### EbookReader in Action
|
||||||
|
|
||||||
|
Here are animated demonstrations of the EbookReader's key features:
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td align="center">
|
||||||
|
<b>Page Navigation</b><br>
|
||||||
|
<img src="examples/gifs/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="examples/gifs/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="examples/gifs/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="examples/gifs/ereader_bookmarks.gif" width="300" alt="Bookmarks"><br>
|
||||||
|
<em>Save and restore reading positions anywhere in the book</em>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
### HTML Multi-Page Rendering
|
### HTML Multi-Page Rendering
|
||||||
|
|
||||||
```python
|
```python
|
||||||
@ -100,6 +131,7 @@ Check out the `examples/` directory for complete working examples:
|
|||||||
|
|
||||||
- **`simple_ereader_example.py`** - Quick start with EbookReader
|
- **`simple_ereader_example.py`** - Quick start with EbookReader
|
||||||
- **`ereader_demo.py`** - Comprehensive EbookReader feature demo
|
- **`ereader_demo.py`** - Comprehensive EbookReader feature demo
|
||||||
|
- **`generate_ereader_gifs.py`** - Generate animated GIF demonstrations
|
||||||
- **`html_multipage_demo.py`** - HTML to multi-page rendering
|
- **`html_multipage_demo.py`** - HTML to multi-page rendering
|
||||||
- See `examples/README.md` for full list
|
- See `examples/README.md` for full list
|
||||||
|
|
||||||
|
|||||||
12
ereader_bookmarks/test_bookmarks.json
Normal file
12
ereader_bookmarks/test_bookmarks.json
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"demo_bookmark": {
|
||||||
|
"chapter_index": 0,
|
||||||
|
"block_index": 27,
|
||||||
|
"word_index": 0,
|
||||||
|
"table_row": 0,
|
||||||
|
"table_col": 0,
|
||||||
|
"list_item_index": 0,
|
||||||
|
"remaining_pretext": null,
|
||||||
|
"page_y_offset": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
10
ereader_bookmarks/test_position.json
Normal file
10
ereader_bookmarks/test_position.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"chapter_index": 0,
|
||||||
|
"block_index": 27,
|
||||||
|
"word_index": 0,
|
||||||
|
"table_row": 0,
|
||||||
|
"table_col": 0,
|
||||||
|
"list_item_index": 0,
|
||||||
|
"remaining_pretext": null,
|
||||||
|
"page_y_offset": 0
|
||||||
|
}
|
||||||
286
examples/generate_ereader_gifs.py
Normal file
286
examples/generate_ereader_gifs.py
Normal file
@ -0,0 +1,286 @@
|
|||||||
|
#!/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
|
||||||
|
|
||||||
|
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]
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
# Add parent directory to path to import pyWebLayout
|
||||||
|
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||||
|
|
||||||
|
from pyWebLayout.layout.ereader_application import EbookReader
|
||||||
|
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/4] 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 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 ./gifs")
|
||||||
|
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")
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
print("\n" + "="*70)
|
||||||
|
print(" Generation Complete!")
|
||||||
|
print("="*70)
|
||||||
|
print("\nGenerated files:")
|
||||||
|
for gif_path in [nav_gif, font_gif, chapter_gif, bookmark_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()
|
||||||
100
examples/gifs/README.md
Normal file
100
examples/gifs/README.md
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
# EbookReader Animated Demonstrations
|
||||||
|
|
||||||
|
This directory contains animated GIF demonstrations of the pyWebLayout EbookReader functionality.
|
||||||
|
|
||||||
|
## Generated GIFs
|
||||||
|
|
||||||
|
### 1. Page Navigation (`ereader_page_navigation.gif`)
|
||||||
|
Demonstrates forward and backward page navigation through an EPUB book. Shows smooth transitions between pages using `next_page()` and `previous_page()` methods.
|
||||||
|
|
||||||
|
**Features shown:**
|
||||||
|
- Sequential page advancement
|
||||||
|
- Page-by-page content rendering
|
||||||
|
- Natural reading flow
|
||||||
|
|
||||||
|
### 2. Font Size Adjustment (`ereader_font_size.gif`)
|
||||||
|
Shows dynamic font size scaling from 0.8x to 1.4x and back. The reader maintains the current reading position even as the layout changes with different font sizes.
|
||||||
|
|
||||||
|
**Features shown:**
|
||||||
|
- `increase_font_size()` / `decrease_font_size()`
|
||||||
|
- `set_font_size(scale)` with specific values
|
||||||
|
- Position preservation across layout changes
|
||||||
|
- Text reflow with different sizes
|
||||||
|
|
||||||
|
### 3. Chapter Navigation (`ereader_chapter_navigation.gif`)
|
||||||
|
Demonstrates jumping between chapters in a book. Each chapter's first page is displayed, showing the ability to navigate non-linearly through the content.
|
||||||
|
|
||||||
|
**Features shown:**
|
||||||
|
- `jump_to_chapter(index)` for index-based navigation
|
||||||
|
- `jump_to_chapter(title)` for title-based navigation
|
||||||
|
- `get_chapters()` to list available chapters
|
||||||
|
- Quick access to any part of the book
|
||||||
|
|
||||||
|
### 4. Bookmarks & Positions (`ereader_bookmarks.gif`)
|
||||||
|
Illustrates the bookmark system: navigating to a position, saving it, navigating away, and then returning to the saved position.
|
||||||
|
|
||||||
|
**Features shown:**
|
||||||
|
- `save_position(name)` to bookmark current location
|
||||||
|
- `load_position(name)` to return to saved bookmark
|
||||||
|
- Position stability across navigation
|
||||||
|
- Multiple bookmark support
|
||||||
|
|
||||||
|
## Generating Your Own GIFs
|
||||||
|
|
||||||
|
To generate these animations with your own EPUB file:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd examples
|
||||||
|
python generate_ereader_gifs.py path/to/your/book.epub gifs/
|
||||||
|
```
|
||||||
|
|
||||||
|
This will create all four GIF animations in the specified output directory.
|
||||||
|
|
||||||
|
### Script Options
|
||||||
|
|
||||||
|
```python
|
||||||
|
python generate_ereader_gifs.py <epub_path> [output_dir]
|
||||||
|
```
|
||||||
|
|
||||||
|
- `epub_path`: Path to your EPUB file (required)
|
||||||
|
- `output_dir`: Directory to save GIFs (default: current directory)
|
||||||
|
|
||||||
|
### Customization
|
||||||
|
|
||||||
|
You can modify `generate_ereader_gifs.py` to adjust:
|
||||||
|
- Frame duration (`duration` parameter in `create_gif()`)
|
||||||
|
- Page dimensions (change `page_size` in `EbookReader`)
|
||||||
|
- Number of frames for each animation
|
||||||
|
- Font scale ranges
|
||||||
|
- Animation sequences
|
||||||
|
|
||||||
|
## Technical Details
|
||||||
|
|
||||||
|
- **Format**: Animated GIF
|
||||||
|
- **Page Size**: 600x800 pixels
|
||||||
|
- **Frame Rate**: Variable (500-1000ms per frame)
|
||||||
|
- **Loop**: Infinite
|
||||||
|
- **Book Used**: Alice's Adventures in Wonderland (test.epub)
|
||||||
|
|
||||||
|
## File Sizes
|
||||||
|
|
||||||
|
| GIF | Size | Frames | Duration per Frame |
|
||||||
|
|-----|------|--------|-------------------|
|
||||||
|
| `ereader_page_navigation.gif` | ~500 KB | 10 | 600ms |
|
||||||
|
| `ereader_font_size.gif` | ~680 KB | 13 | 500ms |
|
||||||
|
| `ereader_chapter_navigation.gif` | ~290 KB | 11 | 1000ms |
|
||||||
|
| `ereader_bookmarks.gif` | ~500 KB | 17 | 600ms |
|
||||||
|
|
||||||
|
## Usage in Documentation
|
||||||
|
|
||||||
|
These GIFs are embedded in the main [README.md](../../README.md) to showcase the EbookReader's capabilities to potential users.
|
||||||
|
|
||||||
|
To embed in Markdown:
|
||||||
|
```markdown
|
||||||
|

|
||||||
|
```
|
||||||
|
|
||||||
|
To embed in HTML with size control:
|
||||||
|
```html
|
||||||
|
<img src="examples/gifs/ereader_page_navigation.gif" width="300" alt="Page Navigation">
|
||||||
|
```
|
||||||
Loading…
x
Reference in New Issue
Block a user