This is very much a work-in-progress and is not feature-complete. I'm a computer science student playing with AI on this one, so it's not a big priority, but it currently renders perfectly fine.
A fast, native GNOME document viewer built on MuPDF, DjVuLibre, and libarchive. Framework opens PDF, DjVu, CBZ, CBR, XPS, EPUB, FB2, and MOBI documents, and is engineered for performance — utilizing aggressive pre-caching and a modern libadwaita UI to provide a "SumatraPDF-like" experience for Linux.
Linux document viewers often fall into two categories: feature-heavy clients (like Okular) that bring extensive dependencies to GNOME, or minimal MuPDF wrappers that lack a functional UI. Framework fills the gap by providing a native, high-performance GNOME solution that prioritizes rendering speed and kinetic scrolling without the bloat of an editor.
| Feature | Description |
|---|---|
| Velocity Engine | Render queue dispatch driven by scroll velocity, with mid-render fz_cookie abort on PDFs so workers can bail in milliseconds when the user has already moved on. |
| Three-Tier Cache | Persistent thumbnails, parsed page handles, and rendered surfaces with bytes-aware eviction (default 512 MB cap, tunable via FW_CACHE_BYTES_CAP_MB). |
| Sort-Function Priority Dispatch | g_thread_pool_set_sort_function reorders the render queue by last_view_time so the most recently prioritized page runs next. The viewport always wins. |
| Parallel Rendering | Eight independent MuPDF instances render pages across multiple CPU cores with zero shared state. |
| Zero-Copy Render | MuPDF and DjVuLibre both write rendered pixels straight into the cairo surface buffer — no intermediate pixmap, no channel shuffle. |
| HiDPI Scaling | Native device pixel ratio rendering for sharp text on Wayland fractional scaling. |
| Async Search with Cached Stext | Page-by-page scan on a worker thread, surface hits as found. The 5-figure-page-textbook case warms once (~330 ms) and serves every subsequent search from cached structured text in tens of ms. |
| Smart Text Selection | Double-click selects a word, triple-click selects a line. Drag selection follows reading order across line wraps with per-line highlight rectangles. |
| Auto-Reload | GFileMonitor watches the open document — recompile your LaTeX or Typst doc and Framework refreshes automatically, restoring exact scroll position. |
| Document Properties | Per-document metadata dialog (title, author, dates, format, page count, file size) backed by a get_metadata interface method. |
gtk4(4.16+),libadwaita(1.7+)mupdf(1.24+),djvulibre(3.5.28+)meson(1.4+)
meson setup builddir
meson compile -C builddirFramework is strictly a viewer. It is not an editor (no annotations), not a library manager, and not an image viewer. It focuses on doing one thing exceptionally well: opening and displaying documents.
| Action | Shortcut |
|---|---|
| Next page | Page Down |
| Previous page | Page Up |
| First page | Home, Ctrl+Home |
| Last page | End, Ctrl+End |
| Go to page | Ctrl+G |
| Back (history) | Alt+Left |
| Forward (history) | Alt+Right |
| Action | Shortcut |
|---|---|
| Zoom in | Ctrl+Plus, Ctrl+=, Ctrl+Scroll Up |
| Zoom out | Ctrl+Minus, Ctrl+Scroll Down |
| Fit width | Ctrl+1 |
| Fit page | Ctrl+2 |
| Actual size (100%) | Ctrl+0 |
| Action | Shortcut |
|---|---|
| Toggle sidebar | F9 |
| Fullscreen | F11 |
| Invert colors | Ctrl+I |
| Reading ruler | F8 |
| Magnifying loupe | F7 |
| Crop margins | F6 |
| Action | Shortcut |
|---|---|
| Find | Ctrl+F |
| Next match | F3 |
| Previous match | Shift+F3 |
| Close search | Escape |
| Action | Shortcut |
|---|---|
| Open file | Ctrl+O |
| Ctrl+P | |
| Copy selected text | Ctrl+C |
| Quit | Ctrl+Q, Ctrl+W |
You can also drop a file directly onto the window to open it.
Build dependencies:
| Dependency | Purpose |
|---|---|
| gtk4 (4.16+) | UI toolkit |
| libadwaita (1.7+) | GNOME design patterns |
| mupdf (1.24+) | PDF / CBZ / XPS / EPUB / FB2 / MOBI rendering |
| djvulibre (3.5.28+) | DjVu rendering |
| libarchive (3.6+) | CBR (RAR) decompression |
| cairo (1.18+) | Surface management |
| glib (2.82+) | Data structures, threading |
| json-glib (1.10+) | State persistence |
| meson (1.4+) | Build system |
On Fedora:
sudo dnf install gtk4-devel libadwaita-devel mupdf-devel djvulibre-devel \
libarchive-devel cairo-devel glib2-devel json-glib-devel \
meson gccWe standardize on builddir as the output directory. Do not use build to avoid confusion.
meson setup builddir
meson compile -C builddirA Flatpak manifest at the project root builds Framework against org.gnome.Platform//50 with bundled MuPDF and DjVuLibre. Install locally with:
flatpak install flathub org.gnome.Platform//50 org.gnome.Sdk//50
flatpak-builder --user --install --force-clean build-flatpak io.github.virinvictus.framework.yml
flatpak run io.github.virinvictus.frameworkThe sandbox has no network access, no broad filesystem access, and uses the Document portal for arbitrary file picks — xdg-documents, xdg-download, and xdg-desktop are reachable directly for command-line invocations.
# Open a PDF
framework document.pdf
# Open a DjVu file
framework book.djvu
# Open a CBZ comic-book archive
framework volume.cbzCBR archives are handled via libarchive (BSD-licensed), so RAR-compressed comics open natively without the libunrar licensing trap. EPUB pagination is whatever MuPDF's default layout produces — Framework is good for fixed-layout EPUBs and as an "open everything" reader; serious EPUB readers like Foliate handle reflow and font customization better.
One document per window. Multiple files open multiple windows.
- Not an editor. No annotations, no form filling, no signatures
- Not a converter. No export, no save-as, no format conversion
- Not a file manager. No recent files, no library, no collections
- Not a browser. No tabs, no multi-document within a single window
- Not an image viewer. No JPEG, PNG, TIFF, SVG support
Framework is a study in standing on shoulders. The following projects were studied closely and specific techniques are borrowed from each — landed today, scheduled in the roadmap, or under evaluation. Per-technique attribution lives here; per-source-file attributions stay in our SPDX headers.
SumatraPDF — spiritual foundation
Copyright © 2006–2024 the SumatraPDF project authors. Licensed GPL-3.0.
Sumatra's "just a viewer" philosophy — uncompromising performance, no editor features, no library manager, every action visible — is the design north star for Framework. Specific techniques studied:
- Engine abstraction layer (
src/EngineBase.h,EngineMupdf.cpp) — the shape of ourFwDocumentinterface follows the same pattern. - Render-cache state machine (
src/RenderCache.cpp) — per-threadcurReqs[]with abort cookies, semaphore-driven worker dispatch, and the "promote duplicate request to head of queue" trick informedfw-cache.c. - In-flight render cancellation via
fz_cookie(shipped in v0.17.0; seesrc/fw-document-pdf.c:pdf_cancel_render) — per-renderfz_cookiepublished under a separatecookies_lockmutex so cancel never blocks on the worker;fz_run_pageseescookie->abort = 1and bails at its next checkpoint. Pattern fromEngineMupdf.cpp:3178. - Bytes-aware cache cap (shipped in v0.16.0; see
src/fw-cache.c) —total_cached_bytestracking + 512 MB defaultbyte_capwithFW_CACHE_BYTES_CAP_MBoverride, replacing the page-count window. Pattern fromRenderCache.cpp:178(FreeIfFull). - Auto-reload on file change (shipped in v0.21.0; see
src/fw-window.c) —GFileMonitor-driven swap with state-restore, the LaTeX/Typst killer feature. Conceptual pattern from Sumatra'sFileWatcher.cppreimagined in GIO idioms. - Smart text selection (shipped in v0.19/v0.20; see
src/fw-document-pdf.c:pdf_select_atandpdf_get_selection_quads) — double-click word, triple-click line, per-line drag highlights viafz_snap_selection+fz_highlight_selection. Pattern adapted fromTextSelection.cppFindClosestGlyph/SelectWordAt. - Scheduled borrows (roadmap Phase 11+): tile-rendering as a high-zoom fallback; opportunistic per-page text extraction during render (the v0.18 stext cache populates lazily, not during render).
Zathura and zathura-pdf-mupdf — render pipeline
Copyright © 2009–2024 pwmt.org. Licensed Zlib.
zathura's zero-copy MuPDF→cairo pipeline is the textbook minimalist implementation. Specific techniques in Framework today and planned:
- Zero-copy MuPDF render (shipped in v1.6.0 as the v1.6 Zero-Copy MuPDF Render patch note; see
src/fw-document-pdf.c:render_page_direct) — constructs the MuPDF pixmap around the cairo surface buffer viafz_new_pixmap_with_bbox_and_data+fz_device_bgr, eliminating the channel-shuffle loop entirely. Borrowed verbatim in pattern fromzathura-pdf-mupdf/render.c. - Cached
fz_stext_pageper page (shipped in v0.18.0; seesrc/fw-document-pdf.c:pdf_get_or_extract_stext) — lazy per-document cache, populated on first text-related call. Search across 901 pages: 332 ms cold → 48 ms warm (6.85× speedup). Pattern fromzathura-pdf-mupdf/page.c. g_thread_pool_set_sort_functionpriority dispatch (shipped in v0.14.0; seesrc/fw-cache.c:render_job_compare) — the pool reorders queued jobs bylast_view_timeso the most recently prioritized page runs next. Pattern fromzathura/render.c:94.- Hue-preserving recolor (shipped in v0.22.0; see
src/fw-view.csnapshot path) — luminance-aware affine that flips the lightness axis while preserving each pixel's chromatic offset. Red diagrams stay red on a dark background. Conceptually similar tozathura/render.c'scolorumaxHSL recolor, reduced to a 4×4 GSK matrix.
Sioyek — zoom transitions and async search
Copyright © Ali Mostafavi. Licensed GPL-3.0.
Sioyek's PDF renderer is the most carefully tuned single-document Linux MuPDF reader we found.
- Closest-zoom fallback during transitions (planned, roadmap Phase 11) — when the requested zoom level isn't ready, return the nearest-zoom rendered surface and scale it for display while the exact render proceeds. Pattern from
pdf_renderer.cpp:try_closest_rendered_page. - Async, progressive search worker (shipped in v0.7.0; see
src/fw-search.c) — dedicated worker thread that scans page-by-page and posts hits back to the main loop via signals as they're found, with generation-counter cancellation when the query changes. Pattern frompdf_renderer.cpp:run_search(which emitssearch_advanceevery 16 pages). - Slice-based rendering for huge pages (planned, fallback only) — render in N×M slices when a single surface would exceed a memory threshold. Pattern from Sioyek's
(num_h_slices, num_v_slices)per request. - Hybrid threading model (under evaluation) — one parent
fz_context, per-threadfz_clone_context, per-(thread, path)fz_document— sits between Sumatra's full-clone and Framework's 8-instance model on the memory/parallelism curve.
Plato — memory-pressure reference
Copyright © 2017 Bastien Dejean. Licensed AGPL-3.0.
Plato runs MuPDF on Kobo e-readers (single-core ARM, ~256 MB RAM). Technique reference only — AGPL-3.0 is not source-compatible with our GPL-3-or-later, so no code is copied. Useful as a sanity check on memory-pressure decisions: Plato uses a 32 MB MuPDF store cap, half of Framework's per-instance budget. The discrepancy seeded roadmap Phase 11's "Per-instance MuPDF store size scaling" item.
- MuPDF — PDF and structured-text rendering. Copyright © Artifex Software. Licensed AGPL-3.0.
- DjVuLibre — DjVu rendering. Copyright © Léon Bottou et al. Licensed GPL-2.0-or-later.
We link these as system libraries; we do not vendor or copy their source.
- GTK and libadwaita — UI toolkit. LGPL-2.1-or-later.
- Cairo — surface management. LGPL-2.1-or-later / MPL-1.1.
- GLib and JSON-GLib — data structures, threading, JSON state. LGPL-2.1-or-later.
Thanks to the GNOME project for the platform that makes this kind of single-purpose viewer possible at all.
Framework's source code is licensed under the GNU General Public License, version 3 or later.
Because Framework links against MuPDF (AGPL-3.0), the shipping binary is effectively AGPL-3.0 — redistributors must make corresponding source available. Framework's source remains GPL-3-or-later: when distributing source, recipients may choose any GPL version 3 or later.
When techniques from GPL-3 sources (SumatraPDF, Sioyek) are incorporated, the resulting combined work is distributable under GPL-3 (the common denominator); the original authors are credited in this README and our SPDX headers remain GPL-3.0-or-later.
Support me by donating bitcoin (even a coffee would help):
bc1qkge6zr45tzqfwfmvma2ylumt6mg7wlwmhr05yv
