163 lines
5.5 KiB
Markdown
163 lines
5.5 KiB
Markdown
|
|
# ePub Reader — Design Document
|
||
|
|
|
||
|
|
## Overview
|
||
|
|
|
||
|
|
A lightweight ePub reader desktop application built with Rust + egui, compiled as a single Windows executable (x86_64-pc-windows-gnu / MSYS2+MinGW). No console window. Built-in Chinese font support. Kindle-like reading experience with medium feature set.
|
||
|
|
|
||
|
|
## Tech Stack
|
||
|
|
|
||
|
|
| Layer | Choice |
|
||
|
|
|---|---|
|
||
|
|
| GUI Framework | `eframe` (egui official framework) |
|
||
|
|
| Rendering Backend | `wgpu` |
|
||
|
|
| ePub Parsing | `epub-rs` |
|
||
|
|
| Font | Bundled Noto Sans SC (or similar open-source Chinese font) |
|
||
|
|
| Build Target | `x86_64-pc-windows-gnu` |
|
||
|
|
| Binary | Single `.exe`, `#![windows_subsystem = "windows"]` |
|
||
|
|
|
||
|
|
## Core Data Model
|
||
|
|
|
||
|
|
```rust
|
||
|
|
struct AppState {
|
||
|
|
book: Option<Book>,
|
||
|
|
current_section: usize,
|
||
|
|
current_page: usize,
|
||
|
|
settings: Settings,
|
||
|
|
bookmarks: Vec<Bookmark>,
|
||
|
|
sidebar_open: bool,
|
||
|
|
}
|
||
|
|
|
||
|
|
struct Book {
|
||
|
|
title: String,
|
||
|
|
author: String,
|
||
|
|
cover: Option<Vec<u8>>,
|
||
|
|
sections: Vec<Section>,
|
||
|
|
toc: Vec<TocEntry>,
|
||
|
|
}
|
||
|
|
|
||
|
|
struct Section {
|
||
|
|
title: String,
|
||
|
|
content: String, // plain text (HTML tags stripped)
|
||
|
|
pages: Vec<usize>, // character offsets for page boundaries
|
||
|
|
}
|
||
|
|
|
||
|
|
struct Settings {
|
||
|
|
font_size: f32, // default 20.0
|
||
|
|
theme: Theme,
|
||
|
|
}
|
||
|
|
|
||
|
|
enum Theme { Light, Dark }
|
||
|
|
|
||
|
|
struct Bookmark {
|
||
|
|
section: usize,
|
||
|
|
page: usize,
|
||
|
|
label: String,
|
||
|
|
timestamp: i64,
|
||
|
|
}
|
||
|
|
|
||
|
|
struct TocEntry {
|
||
|
|
label: String,
|
||
|
|
section: usize,
|
||
|
|
children: Vec<TocEntry>,
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Architecture
|
||
|
|
|
||
|
|
```
|
||
|
|
┌──────────────────────────────────────────┐
|
||
|
|
│ eframe::App │
|
||
|
|
│ ┌──────────┐ ┌───────────────────────┐ │
|
||
|
|
│ │ WelcomePg │ │ ReadingView │ │
|
||
|
|
│ │ (no book) │ │ ├─ TopToolbar │ │
|
||
|
|
│ │ ──────── │ │ ├─ Sidebar (TOC) │ │
|
||
|
|
│ │ open btn │ │ ├─ TextArea │ │
|
||
|
|
│ │ recents │ │ └─ BottomProgressBar │ │
|
||
|
|
│ └──────────┘ └───────────────────────┘ │
|
||
|
|
│ ┌──────────────────────────────────────┐ │
|
||
|
|
│ │ AppState │ │
|
||
|
|
│ └──────────────────────────────────────┘ │
|
||
|
|
└──────────────────────────────────────────┘
|
||
|
|
```
|
||
|
|
|
||
|
|
## UI Layout
|
||
|
|
|
||
|
|
### Welcome Screen (no book open)
|
||
|
|
- Centered "Open ePub File" button
|
||
|
|
- Recently opened books list (read from settings file)
|
||
|
|
|
||
|
|
### Reading Screen (Kindle-like)
|
||
|
|
- **Top toolbar**: back button, book title, font size controls, menu, bookmark toggle, theme toggle
|
||
|
|
- **Left sidebar** (collapsible): table of contents tree
|
||
|
|
- **Center**: text content area with pagination
|
||
|
|
- Click left 30% → prev page, right 30% → next page, center 40% → toggle toolbar visibility
|
||
|
|
- Keyboard: ← → for prev/next page
|
||
|
|
- **Bottom bar**: current chapter name, progress slider, page number indicator
|
||
|
|
|
||
|
|
## Features (Medium Scope)
|
||
|
|
|
||
|
|
### Core
|
||
|
|
1. Open `.epub` file via native file dialog
|
||
|
|
2. Parse ePub (OPF manifest, NCX/TOC, Spine)
|
||
|
|
3. Render chapter content with pagination
|
||
|
|
4. Next/previous page navigation (click & keyboard)
|
||
|
|
5. Table of contents sidebar with chapter navigation
|
||
|
|
6. Remember last reading position (bookmark per file)
|
||
|
|
|
||
|
|
### Settings & Display
|
||
|
|
7. Font size adjustment (A⁺/A⁻)
|
||
|
|
8. Light/Dark theme toggle
|
||
|
|
9. Chinese font support (bundled Noto Sans SC)
|
||
|
|
|
||
|
|
### Bookmarks
|
||
|
|
10. Add/remove bookmarks at current page
|
||
|
|
11. Bookmark list view
|
||
|
|
|
||
|
|
### Persistence
|
||
|
|
12. `settings.json` saves: font_size, theme, recent files, reading positions, bookmarks
|
||
|
|
|
||
|
|
## Data Flow
|
||
|
|
|
||
|
|
1. User clicks "Open" → native file dialog → select `.epub`
|
||
|
|
2. `EpubLoader` reads zip, extracts OPF metadata, reads spine order, parses NCX for TOC
|
||
|
|
3. For each spine item: extract HTML content, strip tags → plain text stored in `Section.content`
|
||
|
|
4. On section load: calculate pagination based on current font_size + panel width → store page offsets
|
||
|
|
5. On page change: slice `Section.content[pages[current_page]..pages[current_page+1]]` → render to egui `Label`
|
||
|
|
6. On font_size change: recalculate all pagination for current section
|
||
|
|
7. On theme toggle: switch `egui::Style` between light/dark visuals
|
||
|
|
|
||
|
|
## Pagination Algorithm
|
||
|
|
|
||
|
|
```
|
||
|
|
fn paginate(text: &str, font_size: f32, panel_width: f32) -> Vec<usize>
|
||
|
|
chars_per_line = floor(panel_width / (font_size * 0.6))
|
||
|
|
lines_per_page = floor(panel_height / (font_size * 1.5))
|
||
|
|
chars_per_page = chars_per_line * lines_per_page
|
||
|
|
Iterate text, split at chars_per_page boundaries (prefer word/char boundaries)
|
||
|
|
```
|
||
|
|
|
||
|
|
## File Storage
|
||
|
|
|
||
|
|
- `settings.json` (next to exe OR in standard config dir)
|
||
|
|
- font_size, theme
|
||
|
|
- recent_files: Vec<{path, title}>
|
||
|
|
- reading_positions: HashMap<path, {section, page}>
|
||
|
|
- bookmarks: HashMap<path, Vec<Bookmark>>
|
||
|
|
- Last window size/position
|
||
|
|
|
||
|
|
## Error Handling
|
||
|
|
|
||
|
|
- ePub parsing failure → show error dialog with message
|
||
|
|
- File not found (recent list) → remove from list silently
|
||
|
|
- Font loading failure → fall back to system monospace
|
||
|
|
- Settings file corruption → reset to defaults, log warning
|
||
|
|
|
||
|
|
## Out of Scope (v1)
|
||
|
|
|
||
|
|
- Text search within book
|
||
|
|
- Highlighting / annotations
|
||
|
|
- Notes/export
|
||
|
|
- TTS
|
||
|
|
- Epub with complex CSS styling / reflowable layout math
|
||
|
|
- DRM-protected ePubs
|