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.
I'm not pretending I came up with the architecture. Framework is a deliberate synthesis: the cache + threading idioms of SumatraPDF, the zero-copy MuPDF→cairo pipeline and GThreadPool priority dispatch from zathura-pdf-mupdf / zathura, the magnifying loupe and double-spread detection from YACReader, and (when it lands) the native-GTK reflow architecture from Fractal, all glued into a minimalist libadwaita UI for GNOME. Patterns are also borrowed from Sioyek, Plato, MComix, and Komikku where they had something specific to teach. Per-pattern attribution lives in the Influences section below — every borrow is named with its upstream file:line and the Framework version it shipped in.
| 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. |
| Comic Layouts | Manga mode (RTL nav), Webtoon mode (zero-gap continuous strip), and Facing Pages (two-up with cover standalone) — composable, layout-anchor-preserving, and live-toggleable from the menu or F4/F5/F10. |
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 |
|---|---|
| Manga mode (RTL) | F4 |
| Webtoon mode | F5 |
| Facing pages | F10 |
| 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. Every concrete pattern below names the upstream project, the file:line we studied, the Framework version it shipped in, and the source file where the borrow lives — full chain of attribution so anyone can audit the lineage. Per-technique attribution lives here; per-source-file SPDX headers stay GPL-3.0-or-later regardless of source.
The four projects in the opening "what this is" line — SumatraPDF, zathura-pdf-mupdf / Zathura, YACReader, and Fractal — are the explicit foundations: I picked the parts I liked most and glued them into one viewer. Sioyek, Plato, MComix, Komikku, and Foliate contributed targeted patterns or design references on top of that.
SumatraPDF — spiritual foundation, cache & threading
Copyright © 2006–2024 the SumatraPDF project authors. Licensed GPL-3.0 (compatible).
Sumatra's "just a viewer" philosophy — uncompromising performance, no editor features, no library manager, every action visible — is Framework's design north star. Specific borrows:
- Engine abstraction layer (
src/EngineBase.h,src/EngineMupdf.cpp) — the shape of ourFwDocumentinterface follows the same pattern. Conceptual reference, not a code copy. - 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. - Bytes-aware cache cap (v0.16;
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). - In-flight render cancellation via
fz_cookie(v0.17;src/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. - Auto-reload on file change (v0.21;
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 (v0.19;
src/fw-document-pdf.c:pdf_select_at+pdf_get_selection_quads) — double-click word, triple-click line, per-line drag highlights. Pattern adapted fromTextSelection.cppFindClosestGlyph/SelectWordAt, implemented via MuPDF'sfz_snap_selection+fz_highlight_selection(which do the same thing the canonical way).
zathura-pdf-mupdf and Zathura — render pipeline & sandboxing
Copyright © 2009–2024 pwmt.org. Licensed Zlib (compatible).
zathura-pdf-mupdf's zero-copy MuPDF→cairo pipeline is the textbook minimalist implementation; the parent zathura project's render scheduler is the GLib-native blueprint Framework's cache mirrors.
- Zero-copy MuPDF render (v1.6;
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. Pattern fromzathura-pdf-mupdf/render.c. - Cached
fz_stext_pageper page (v0.18;src/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 (v0.14;src/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 (v0.22;
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. - Landlock LSM hardening (v0.38;
src/fw-sandbox.c) — drops filesystemEXECUTE+MAKE_*rights at startup so a malicious document exploiting MuPDF / DjVuLibre / libarchive into RCE can't escalate to spawning a shell or planting a backdoor. Pattern fromzathura/landlock.c.
YACReader — comic-reading polish
Copyright © Luis Ángel San Martín. Licensed GPL-3.0 (compatible).
YACReader is the comic reader I keep going back to on Linux; its loupe and double-spread detection are the parts I wanted in Framework.
- Magnifying loupe (v0.24;
src/fw-view.csnapshot path) — circular zoom-around-cursor viewport, pure GPU work viagtk_snapshot_push_rounded_clip+ scale transform + texture re-append. F7 toggle. Pattern fromYACReader/magnifying_glass.cpp. - Aspect-ratio double-spread detection (v0.27.1;
src/fw-view.c::view_page_is_spread) — pages withw/h > 1.0are treated as pre-rendered spreads and stand alone in facing-pages mode, with the page that would have been their natural partner orphaning so alternation resumes after. - Filename-based double-spread detection (v0.37;
src/fw-document-cbr.c::cbr_is_spread_filename) — complements the aspect-ratio test by catching scanlation rips where the spread image has portrait dimensions but the filename concatenates two consecutive page numbers (chapter01_034035.jpg). Direct port of YACReader's algorithm atcommon/comic.cpp:925-1028.
Fractal — native GTK reflow architecture (Phase 13.1)
Copyright © 2017–2024 Fractal contributors. Licensed GPL-3.0 (compatible).
Fractal is a Matrix chat client — totally unrelated domain — but its GListModel + GtkSignalListItemFactory + per-row widget pattern is exactly what reflowed EPUB / MOBI / AZW3 / FB2 / TXT chapters want. The Phase 13.1 rewrite (docs/fractal-rewrite.md) is named for it.
- Dynamic-height list-model pattern (planned, Phase 13.1) — structurally-typed items (paragraph, heading, image, blockquote, list, code) rendered into native widgets that wrap text natively, instead of pre-rendered to pixmaps. Pattern from
.fractal/src/utils/grouping_list_model/. - Per-row widget factory idioms (planned, Phase 13.1) — map block type → widget choice (
GtkLabelfor text,GtkPicturefor images, etc.) viaGtkSignalListItemFactory. Pattern from.fractal/src/components/rows/.
Sioyek — zoom transitions, async search, threading hybrid
Copyright © Ali Mostafavi. Licensed GPL-3.0 (compatible).
Sioyek's pdf_renderer.cpp is the most carefully tuned single-document Linux MuPDF reader I found.
try_closest_rendered_pagezoom transition (v0.28;src/fw-cache.c::fw_cache_get_texture) — eachCacheEntryretains up to 3 prior-zoom snapshots; nearest-zoom slot returned for GTK to auto-scale, so continuous Ctrl+scroll zoom shows a sharp preview instead of a blurry one-zoom-back fallback. Pattern frompdf_renderer.cpp:236.- TTL+LRU hybrid eviction (v0.26;
src/fw-cache.c) —last_access_usperCacheEntrybumped on every hit; bytes-cap eviction sorts outside-priority candidates oldest-first. Pattern frompdf_renderer.cpp:269delete_old_pages. - Async, progressive search worker (v0.7;
src/fw-search.c) — dedicated worker thread scans page-by-page and posts hits back to the main loop via signals as they're found, with generation-counter cancellation. Pattern frompdf_renderer.cpp::run_search. - Hybrid threading model (validated as not currently needed in v0.36; tracked as fallback) — one parent
fz_context, per-threadfz_clone_context, per-(thread, path)fz_document. Sits between Sumatra's full-clone and Framework's 8-independent-document model on the memory/parallelism curve. Stays as a roadmap option if a memory-constrained deployment target ever shows up.
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.
- Per-instance MuPDF store size scaling (v0.26;
src/fw-document-pdf.c:pdf_pick_store_size) — Plato uses a 32 MB MuPDF store cap (crates/core/src/document/mupdf_sys.rs:37), half of what Sumatra'sFZ_STORE_DEFAULTand Framework's biggest tier pick. The discrepancy seeded Framework's file-size-aware ladder (16/32/64/128 MB) so novels don't pay textbook overhead and textbooks aren't pinched. Sioyek (lets MuPDF pick) and Sumatra (uses default) were the other two reference points.
MComix — manga RTL navigation
Copyright © Pontus Ekberg et al. Licensed GPL-2.0-or-later (compatible; combined work distributable under GPL-3-or-later).
- Manga mode RTL navigation (v0.27;
src/fw-window.c::on_key_pressed) — F4 toggle that swaps Left/Right arrow page-nav semantics: Left advances, Right goes back, following the reading order of Japanese manga. Conceptual pattern from MComix'sevent.py. Combined with facing-pages, also flips left/right within each pair so the lower-numbered page sits on the right.
Komikku — manga / webtoon reader UX
Copyright © Valéry Febvre et al. Licensed GPL-3.0-or-later (compatible).
Top-tier native GNOME manga / webtoon reader. Reference for the comic-mode UX work.
- Webtoon mode (zero-gap continuous strip) (v0.27;
src/fw-view.c::recompute_layout) — F5 toggle dropsPAGE_GAPto zero so vertically-laid-out long-strip comics stitch into a seamless single canvas. Conceptual pattern from Komikku's reader pager. - Facing pages with cover-standalone (v0.27;
src/fw-view.c::view_page_is_paired) — F10 toggle pairs pages 1+2, 3+4, etc., with page 0 as the standalone cover. Mirrors how a physical book opens. - Phase 13.1 reader-pager pattern reference (planned) — secondary reference for the Fractal-style EPUB reflow rewrite, alongside Fractal itself. See
.komikku/komikku/reader/pager/.
Foliate — feature-complete ebook UX (no code borrows)
Copyright © John Factotum. Licensed GPL-3.0-or-later (compatible, but unused).
Foliate is the serious GNOME ebook reader — annotation, sync, DRM-aware conversion, full publisher CSS. Their stack is WebKitGTK + JavaScript (ebook.js), so the implementation patterns don't transfer to native GTK and no code is borrowed. Mentioned here because Foliate is the explicit "we are not trying to replace this" reference in docs/fractal-rewrite.md: Framework's reflow mode is "open this EPUB cleanly without launching another app," not "be the best ebook reader on Linux."
- MuPDF — PDF / XPS / EPUB / MOBI / FB2 / CBZ rendering, structured-text extraction, font/image stores. Copyright © Artifex Software. Licensed AGPL-3.0. The shipping binary is effectively AGPL because of this link — see the License section.
- DjVuLibre — DjVu rendering. Copyright © Léon Bottou et al. Licensed GPL-2.0-or-later.
- libarchive — RAR / 7-Zip / tar / ZIP enumeration for the CBR backend (so we can ship CBR support without the libunrar licensing trap). Copyright © Tim Kientzle et al. Licensed BSD-2-Clause.
- GTK (4.16+) and libadwaita (1.7+) — UI toolkit. LGPL-2.1-or-later.
- Cairo (1.18+) — image surface management, the buffer MuPDF and DjVuLibre render directly into. LGPL-2.1-or-later / MPL-1.1.
- GLib (2.82+) — data structures, threading (
GThreadPool), GObject, async I/O via GIO (file monitoring, archives, portals). LGPL-2.1-or-later. - JSON-GLib (1.10+) — per-document state persistence at
$XDG_DATA_HOME/framework/state.json. LGPL-2.1-or-later. - Pango (via GTK) — text layout for the search bar and dialogs (and the planned Phase 13.1 reflow mode). LGPL-2.1-or-later.
- Linux kernel Landlock LSM (kernel 5.13+, ABI 1+) — process-scoped filesystem sandboxing applied at startup. Optional at runtime; the binary degrades to a no-op on older kernels.
- Meson (1.4+) and Ninja — build system.
- gettext — i18n scaffolding (active translations are TBD; the infrastructure is wired).
- gcc / clang + AddressSanitizer / UndefinedBehaviorSanitizer — used during development; not shipping deps.
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, YACReader, Fractal, Komikku) are incorporated, the resulting combined work is distributable under GPL-3 (the common denominator). Zlib techniques (Zathura, zathura-pdf-mupdf) and GPL-2-or-later techniques (MComix) are also cleanly compatible. Plato (AGPL-3.0) is technique-reference only — no code is copied. The original authors are credited in the Influences section above and our SPDX headers stay GPL-3.0-or-later regardless of source.
If Framework's useful to you and you'd like to chip in:
bc1qkge6zr45tzqfwfmvma2ylumt6mg7wlwmhr05yv
