Files
epub-read/docs/superpowers/specs/2026-05-13-epub-reader-design.md

5.5 KiB

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

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

  1. Font size adjustment (A⁺/A⁻)
  2. Light/Dark theme toggle
  3. Chinese font support (bundled Noto Sans SC)

Bookmarks

  1. Add/remove bookmarks at current page
  2. Bookmark list view

Persistence

  1. 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>
    • 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