Android document viewer/editor in the spirit of Okular. Views and edits PDF, Markdown (.md), and Word (.docx) documents, and can import from / export to any of those formats.
- View PDFs (rendered pages; pinch zoom, double-tap zoom/reset, zoom buttons, page indicator, go-to-page), Markdown, and DOCX (rendered rich text).
- Search in any document — highlighted matches with prev/next in md/docx, page-jump matching in PDFs (pdfbox text extraction).
- OCR scanned PDFs on-device (ML Kit, offline) — result opens in the editor.
- Print and share any document (md/docx print via PDF conversion).
- Settings: theme (system/light/dark), keep-screen-on, editor text size.
- Edit Markdown and DOCX in a markdown editor with a formatting toolbar (bold, italic, heading, bullet list) and live preview. PDFs open in the editor via text extraction (layout/images are not preserved).
- Export any open document to PDF, DOCX, or Markdown.
- Plain text —
.txtfiles open and are shown verbatim, without markdown parsing. Internally they are handled as Markdown. - Recent-documents list on the home screen (long-press to remove).
- Registers as a handler for pdf/md/docx
VIEWintents from other apps. - Material 3, light/dark (DayNight), same palette as Sift.
Markdown is the canonical editable representation. There is no direct
PDF → DOCX or DOCX → PDF path; every conversion is source → markdown → target.
- MD ↔ text file directly.
- DOCX → markdown via a built-in OOXML parser (headings, bold/italic, bullets); markdown → DOCX via a built-in minimal OOXML writer.
- Markdown → PDF via Markwon + the platform
PdfDocument(A4 pagination). - PDF → text via pdfbox-android text extraction.
Because markdown is the hub, anything a format can express that markdown
cannot is dropped at load time, before an export target is even chosen. In
particular DOCX images, tables, numbering, hyperlink targets, headers/footers
and any style beyond Heading1–Heading6/Title do not survive a round trip.
Your original files are never overwritten with a lossy conversion. Saving a
.docx that the app did not itself write prompts for a new file and explains
what the conversion drops; the original is left untouched. Once you have saved
to a file the app wrote, its contents are exactly the markdown in the editor, so
later saves write to it in place. PDFs opened in the editor are likewise forced
through Save As.
Fenced code blocks survive a markdown → DOCX → markdown round trip: they are
written as monospace Code-styled paragraphs and read back as fences, with no
markdown parsing applied to their contents. The round trip is idempotent — it
converges after one pass rather than drifting.
Requirements (same as Sift):
- JDK 17
- Android SDK platform 35, build-tools 34.0.0 (create
local.propertieswithsdk.dir=/path/to/android-sdk) - Gradle wrapper 8.11.1 / AGP 8.7.3 (included)
export JAVA_HOME=/path/to/jdk-17
./gradlew :app:assembleRelease
# output: app/build/outputs/apk/release/app-release.apkSigning uses keystore.properties + a .jks keystore in the project root
(V2+V3 signing, V1 off). Both are gitignored — create your own keystore and a
keystore.properties with storeFile, storePassword, keyAlias,
keyPassword. If they are absent the build still succeeds and produces an
unsigned APK, so check the output rather than assuming.
Note that the release signing config is applied to both build types, so debug APKs are signed with the production key and install over release builds. That is deliberate, but it means a debug APK is not safe to distribute.
Testing needs an ARM device or system image. ML Kit ships native OCR
libraries and abiFilters is restricted to arm64-v8a and armeabi-v7a to
keep the APK small, so the app will not run on an x86/x86_64 emulator.
Version numbering lives in app/build.gradle. versionCode and versionName
are not in step — the first release was versionCode 2 / versionName "1.1".
Bump both when releasing, and record the change in CHANGELOG.md.
- Java 17, Android Views/XML (no Kotlin/Compose), single
:appmodule com.pdfviewedit.app, minSdk 26, targetSdk 35- Material 1.11.0, AppCompat 1.6.1, RecyclerView 1.3.2, Preference 1.2.1
- Markwon 4.6.2 (markdown rendering), pdfbox-android 2.0.27.0 (PDF text extraction)
- ML Kit text-recognition 16.0.1 (bundled on-device OCR; ARM ABIs only)
app/src/main/java/com/pdfviewedit/app/
├── PveApp.java Application — applies the saved theme at startup
├── format/ DocType, Converter, Markdowns (shared Markwon factory),
│ DocxReader, DocxWriter, PdfExporter, PdfImporter
├── ui/ MainActivity (recents/open/new), ViewerActivity, EditorActivity,
│ PdfPageAdapter, SettingsActivity
└── util/ UriUtils, RecentStore, Prefs, PdfPrintAdapter
ViewerActivity is the largest file by a wide margin — it carries PDF
rendering, zoom, both search implementations, OCR, print and share. It declares
configChanges="orientation|screenSize|keyboardHidden", so it is never
recreated on rotation and holds no saved instance state.
There are currently no automated tests — app/src/ contains only main.
See CHANGELOG.md.