Acrobat wants a subscription. Online PDF sites want your files. Qyra wants neither.
It's a desktop app that does the PDF work you actually do: view, mark up, merge, split, convert, OCR, protect. Everything runs locally. Nothing phones home. Open source under GPL-3.0.
- Tabbed multi-document interface with persistent session
- Virtualized page list, thumbnails, zoom, rotate, search
- Outline (bookmarks), document metadata, recent-files library
- Freehand ink with pressure (
perfect-freehand), highlighter, sticky notes - Shapes, stamps, text boxes, comments
- Form filling, redaction (true content removal), watermarks, page numbers
- Flatten annotations into the page
- Merge multiple PDFs into one
- Split by range or page count
- Reorder via drag-and-drop
- Remove, rotate, crop pages
- PDF → images (PNG/JPEG, per page)
- Images → PDF
- PDF → plain text or layout-preserving text
- PDF → Word (
.docx) - OCR scanned pages via Tesseract.js, fully offline
- Password protect (AES-128/256)
- Unlock (with password)
- Compress and optimize
Grab a build from Releases, or nightly artifacts from the latest Actions run.
| Platform | Formats |
|---|---|
| Windows | .msi, .exe (NSIS) |
| Linux | .deb, .rpm, .AppImage |
| macOS | .dmg, .app.tar.gz |
| Android | .apk |
The AppImage is patched to use the system libwayland-client.so, and WebKitGTK's DMA-buf renderer is disabled at startup. If you still hit issues:
LD_PRELOAD=/usr/lib/libwayland-client.so ./Qyra.AppImagePrerequisites
- Node.js 20+
- Rust toolchain (stable)
- Tauri platform deps: https://tauri.app/start/prerequisites/
- Windows: run
cargofrom a Visual Studio Developer PowerShell so MSVC env vars are set for the native C deps (MuPDF).
npm install
npm run tauri devLint:
npm run lintnpm run tauri buildBundles land in src-tauri/target/release/bundle/.
CI builds an APK via .github/workflows/build-android.yml. Locally you need the Android NDK and the aarch64-linux-android Rust target.
src/ React 19 + TypeScript frontend
├─ tools/ One page per feature (Merge, Split, OCR, …)
├─ viewer/ PDF viewer + annotation surface
├─ components/ Shared UI
├─ store/ Zustand stores
└─ hooks/ React hooks
src-tauri/
├─ src/commands/ Rust Tauri commands, one file per feature
├─ src/pdf/ MuPDF bindings and PDF helpers
└─ src/utils/ Shared Rust utilities
| Layer | Choice |
|---|---|
| Shell | Tauri 2 |
| Frontend | React 19, TypeScript, Vite, Tailwind 4, Radix UI |
| State | Zustand, TanStack Query |
| PDF engine | MuPDF via Rust FFI |
| OCR | Tesseract.js |
| Ink | perfect-freehand |
Issues and PRs welcome. Conventions:
- One logical change per commit
- Many small commits per PR
- Merge commit only (no squash, no rebase)
- Reference an issue in the PR body when one exists
GPL-3.0. Use it, modify it, ship it, just keep it open.