Article Image

Building MOC Office: An Offline Android PDF Suite

March 12, 2026

Re-Engineering PDF Operations for Mobile

Most PDF editors on Android are heavy wrappers around web services that upload your personal files to distant servers to perform basic actions like merging, splitting, or watermarking.

For MOC Office, we built an offline-first architecture where every action is processed locally, keeping your files safe from data profiling.

Document manipulation is a prime target for privacy leaks. Users should never have to upload a bank statement or a passport scan to merge two pages.
MOC Office Development Lead

Under the Hood: Hybrid Rendering

Rather than shipping a heavy native C++ PDF engine that bloated our app size by 40MB, MOC Office utilizes a custom WebKit-based implementation of Mozilla's PDF.js. This allowed us to offer seamless pinch-to-zoom, horizontal page scrubbers, and full-text document searches in a lightweight container.

However, rendering large PDFs (>100MB) inside a mobile WebView introduces memory limits. WebKit processes run in isolated memory spaces, and loading extensive arrays of high-resolution page canvas elements can trigger Out-Of-Memory (OOM) app crashes.

To solve this:


Safe Scoped Storage File Editing

On Android 11+ (API 30+), apps must respect scoped storage constraints. They cannot write files directly using absolute paths. Instead, they must obtain a ParcelFileDescriptor to write to shared directories.

MOC Office uses iText Core to parse and modify files locally via stream descriptors. Below is the Kotlin method we use to unlock password-protected PDF files, decrypting the payload using Bouncy Castle cryptographic providers and writing the results back to the user's selected location:

package com.rebelroot.mocoffice.pdf
import android.content.Context
import android.net.Uri
import com.itextpdf.kernel.pdf.PdfDocument
import com.itextpdf.kernel.pdf.PdfReader
import com.itextpdf.kernel.pdf.PdfWriter
import com.itextpdf.kernel.pdf.ReaderProperties
import java.io.InputStream
import java.io.OutputStream
class SecurePdfEngine(private val context: Context) {
fun decryptPdfFile(inputUri: Uri, outputUri: Uri, passwordBytes: ByteArray) {
var inputStream: InputStream? = null
var outputStream: OutputStream? = null
try {
inputStream = context.contentResolver.openInputStream(inputUri)
outputStream = context.contentResolver.openOutputStream(outputUri)
// Setup iText properties with user's decryption password
val properties = ReaderProperties().setReaderPassword(passwordBytes)
val reader = PdfReader(inputStream, properties)
// Open the document and write it decrypted to the output stream
val pdfDoc = PdfDocument(reader, PdfWriter(outputStream))
// Closing commits modifications and writes file descriptor headers
pdfDoc.close()
} finally {
inputStream?.close()
outputStream?.close()
}
}
}

Dynamic Theming with Jetpack Compose & Material You

MOC Office integrates a Material Design 3 UI layer. We map system colors to the reader theme, enabling support for dynamic background adaptation (adapt-to-wallpaper) on Android 12+ devices.

Furthermore, we configure custom CSS style rules on our WebView layout to match the app theme, providing a unified appearance (light, dark, or AMOLED true-black) across both native pages and webview canvas components.

Join the RebelRoot Community. Let's Build Better Software Together.