Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

VerseApp

A high-performance, multi-format document reader written in Rust using the Iced 0.13 framework (Elm Architecture) and the MuPDF Fitz C engine (mupdf crate) alongside native DjVu and DOCX decoders.


Supported Formats

PDF · EPUB · DOCX · DjVu · XPS · MOBI · CBZ · FB2


Directory Layout

verseapp/
├── Cargo.toml                # Dependencies & features
└── src/
    ├── main.rs               # Binary entry point & Iced app launcher
    ├── app/                  # Elm Architecture Core
    │   ├── mod.rs            # Facade re-exporting ReaderApp & Message
    │   ├── state.rs          # ReaderApp struct, new(), theme(), save_session()
    │   ├── messages.rs       # Exhaustive Message enum
    │   ├── actions.rs        # Key normalization & Action dispatcher
    │   ├── tasks.rs          # Tokio worker tasks & pre-fetch priority queue
    │   ├── update.rs         # Reducer loop, debounced scroll/zoom, session sync
    │   └── view.rs           # Root UI composition & modal stack
    ├── engine/               # Document Decoders & Renderers
    │   ├── mod.rs            # Load dispatcher (load_document)
    │   ├── traits.rs         # DocumentBackend trait, RenderQuality, PageRenderRequest
    │   ├── mupdf.rs          # Thread-safe MuPDF engine (PDF, EPUB, MOBI, CBZ, XPS, FB2)
    │   ├── djvu.rs           # Native DjVu engine
    │   └── docx.rs           # Native DOCX ZIP XML extractor & layout renderer
    ├── models/               # Domain Models & State
    │   ├── mod.rs
    │   ├── session.rs        # SessionData, AppSettings, FileHistoryRecord, KeyBinding
    │   └── workspace.rs      # RuntimeTab, byte-budget LRU texture cache
    └── ui/                   # GUI Components & Styling
        ├── mod.rs
        ├── theme.rs          # Theme definitions & transparent scrollable styles
        └── components/
            ├── mod.rs
            ├── tab_bar.rs    # Scrollable browser tab bar
            ├── viewer.rs     # Canvas viewport, control tray, virtualized scrollable
            ├── side_panel.rs # TOC Outline tree & thumbnail preview grid
            └── settings.rs   # Glassmorphism keybinding modal

Prerequisites & Building

System Dependencies (Linux)

MuPDF C bindings require C toolchain dependencies:

# Debian / Ubuntu
sudo apt install build-essential pkg-config libclang-dev

Build Commands

# Debug build
cargo build

# Optimized Release build
cargo build --release

# Run application
cargo run --release

Architecture & Developer Overview

1. State Management (Model-View-Update)

  • Model (ReaderApp): Holds global application settings, open RuntimeTab instances, active tab pointers, keybinding mappings, and modal states.
  • Message (messages.rs): Exhaustive enum handling UI events, keyboard shortcuts, Tokio async results, debounced scroll/zoom settling, and rendering outputs.
  • Update (update.rs): Reducer modifying state, persisting session state to session.json, and dispatching async tasks.
  • View (view.rs / ui/components/): Pure, stateless UI functions assembling layout components.

2. Thread Safety & Lock-Free Rendering

  • MuPDF Thread Safety: Document is wrapped in ThreadSafeDocument(Mutex<Document>) to allow safe cross-thread usage across background Tokio blocking threads.
  • Lock-Free UI Dimension Access: MuPdfBackend pre-caches all page dimensions (Vec<(f32, f32)>) during open(). The main UI thread reads page dimensions in $O(1)$ time without acquiring Mutex locks, preventing 60 FPS GUI freezes during rasterization.
  • Offloaded Handle Construction: iced::widget::image::Handle::from_rgba runs inside tokio::task::spawn_blocking. The UI thread receives pre-built, display-ready image handles.

3. Rendering Pipeline & Memory Management

  • Progressive Quality Tiers:
    • Fuzzy ($0.20\times$ scale) — Immediate low-res preview ($&lt;1\text{ ms}$).
    • Draft ($0.55\times$ scale) — Fast scrolling ($60\text{ FPS}$).
    • High ($1.25\times$ scale) — Sharp text focus.
  • Resolution Clamping: Raster scale is bounded to max $3840 \times 3840\text{ px}$. High zoom factors on 8K scanned pages will never allocate gigabytes of uncompressed RGBA RAM.
  • SIMD Pixel Pipeline: Fast slice-copy copy_from_slice buffer construction vectorizes RGBA conversion, reducing per-frame processing from $250\text{ ms}$ to $2\text{ ms}$.
  • Viewport Virtualization (viewer.rs): Continuous mode only passes image handles to the UI for visible pages (current_page - 4 to current_page + 8). Distant pages render as zero-texture blank boxes with exact pixel dimensions, keeping RAM usage under $35\text{ MB}$ per tab.
  • Byte-Budget LRU Cache: RuntimeTab::enforce_memory_budget enforces a strict $45\text{ MB}$ RAM budget per tab by evicting distant page textures when exceeded.

Extending the App: Adding a New Format Backend

To implement support for a new document format (e.g., .txt or .cbr):

  1. Implement DocumentBackend in src/engine/your_format.rs:

    use crate::engine::traits::{DocumentBackend, PageRenderRequest, TocItem};
    use image::RgbaImage;
    
    #[derive(Debug)]
    pub struct CustomBackend { ... }
    
    impl DocumentBackend for CustomBackend {
        fn page_count(&self) -> usize { ... }
        fn page_dimensions(&self, page_index: usize) -> (f32, f32) { ... }
        fn render_page(&self, request: &PageRenderRequest) -> Result<RgbaImage, String> { ... }
        fn table_of_contents(&self) -> Vec<TocItem> { ... }
    }
  2. Register Extension in src/engine/mod.rs:

    match ext.as_str() {
        "custom" => Ok(Arc::new(CustomBackend::open(path)?)),
        _ => Ok(Arc::new(MuPdfBackend::open(path)?)),
    }
  3. Update File Dialog Filter in src/app/update.rs: Add extension to AsyncFileDialog::new().add_filter(...).

About

Cross-platform reader application

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages