Skip to content

About

Declarative PDF and document viewer for Compose Multiplatform with native zoom, smooth scrolling, and caching support across Android and iOS.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

Β 

History

1 Commit

Folders and files

Repository files navigation

DocuCompose

DocuCompose Banner

Kotlin Version Compose Multiplatform Version Platforms License

DocuCompose is a modern, declarative PDF & Document Viewer library built for Compose Multiplatform (CMP) targeting Android and iOS (iosX64, iosArm64, iosSimulatorArm64).

It provides an intuitive @Composable interface that wraps native document rendering engines on both platformsβ€”delivering high performance, smooth scrolling, interactive pinch-to-zoom, and state observation with zero boilerplate.


πŸ’» Features

  • πŸ“± Native Performance:
    • iOS: Uses Apple's native PDFKit (PDFView & PDFDocument) via UIKitView interop for smooth 60/120fps scrolling and pinch-to-zoom.
    • Android: Uses Android's native android.graphics.pdf.PdfRenderer with LazyColumn virtualized rendering and transformable gestures.
  • 🌐 Flexible Document Sources: Support for local file paths (FilePath), raw byte arrays (ByteArraySource), and remote URLs (RemoteUrl).
  • πŸŽ›οΈ State Management: Reactive DocumentViewerState providing real-time tracking of currentPage, pageCount, zoomLevel, isLoading, and error.
  • 🎨 Customizable Placeholders & Callbacks: Easily supply custom Jetpack/Compose loading indicators and handle document opening errors gracefully.
  • πŸ“¦ Modular Architecture: Clean separation into :core (Models & Utilities) and :viewer (Composable UI & Native Interop).
  • πŸš€ Maven Central Ready: Configured with Vanniktech's com.vanniktech.maven.publish plugin for seamless open-source distribution.

πŸ›οΈ Module Architecture

DocuCompose is structured into three clean modules:

graph TD
    App[":sample (CMP App)"] --> Viewer[":viewer (Expect/Actual Composable)"]
    Viewer --> Core[":core (State, Model & Utilities)"]
Loading
Module Description Package
:core Defines DocumentSource, DocumentViewerState, rememberDocumentViewerState(), and cache utilities. io.github.hariagusw.docucompose.core
:viewer Contains expect/actual @Composable fun DocumentViewer interop implementations for Android & iOS. io.github.hariagusw.docucompose.viewer
:sample Cross-platform Compose Multiplatform demo app demonstrating library capabilities. io.github.hariagusw.docucompose.sample

πŸ“¦ Installation

Add DocuCompose to your Compose Multiplatform project.

1. Version Catalog (gradle/libs.versions.toml)

[versions]
docucompose = "1.0.0-SNAPSHOT"

[libraries]
docucompose-core = { module = "io.github.hariagusw.docucompose:docucompose-core", version.ref = "docucompose" }
docucompose-viewer = { module = "io.github.hariagusw.docucompose:docucompose-viewer", version.ref = "docucompose" }

2. Module build.gradle.kts

kotlin {
    sourceSets {
        commonMain.dependencies {
            implementation(libs.docucompose.core)
            implementation(libs.docucompose.viewer)
        }
    }
}

πŸš€ Quick Start

1. Basic Usage with Remote PDF URL

import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import io.github.hariagusw.docucompose.core.DocumentSource
import io.github.hariagusw.docucompose.viewer.DocumentViewer

@Composable
fun SimplePdfScreen() {
    DocumentViewer(
        source = DocumentSource.RemoteUrl("https://example.com/sample.pdf"),
        modifier = Modifier.fillMaxSize()
    )
}

2. Observing State and Controlling Navigation

Use rememberDocumentViewerState() to inspect progress, jump between pages, or read the current zoom level:

import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import io.github.hariagusw.docucompose.core.DocumentSource
import io.github.hariagusw.docucompose.core.rememberDocumentViewerState
import io.github.hariagusw.docucompose.viewer.DocumentViewer

@Composable
fun ControlledDocumentScreen() {
    val viewerState = rememberDocumentViewerState()

    Column(modifier = Modifier.fillMaxSize()) {
        // Document Viewer Area
        Box(modifier = Modifier.weight(1f)) {
            DocumentViewer(
                source = DocumentSource.RemoteUrl("https://pdfobject.com/pdf/sample.pdf"),
                state = viewerState,
                enableZoom = true,
                onError = { throwable ->
                    println("Failed to load PDF: ${throwable.message}")
                },
                loadingPlaceholder = {
                    Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
                        CircularProgressIndicator()
                    }
                }
            )
        }

        // Control Bar
        Surface(tonalElevation = 4.dp, modifier = Modifier.fillMaxWidth()) {
            Row(
                modifier = Modifier.padding(16.dp).fillMaxWidth(),
                horizontalArrangement = Arrangement.SpaceBetween,
                verticalAlignment = Alignment.CenterVertically
            ) {
                Button(
                    onClick = { viewerState.jumpToPage(viewerState.currentPage - 1) },
                    enabled = viewerState.currentPage > 1
                ) {
                    Text("Previous")
                }

                Text(
                    text = "Page ${viewerState.currentPage} of ${viewerState.pageCount}",
                    style = MaterialTheme.typography.bodyMedium
                )

                Button(
                    onClick = { viewerState.jumpToPage(viewerState.currentPage + 1) },
                    enabled = viewerState.currentPage < viewerState.pageCount
                ) {
                    Text("Next")
                }
            }
        }
    }
}

πŸ“„ Document Sources

DocuCompose supports three primary document source types:

// 1. Remote HTTP/HTTPS URL
val remoteSource = DocumentSource.RemoteUrl("https://example.com/document.pdf")

// 2. Local File System Path
val fileSource = DocumentSource.FilePath("/path/to/local/file.pdf")

// 3. Raw Byte Array
val byteArray = fetchPdfBytes()
val byteSource = DocumentSource.ByteArraySource(bytes = byteArray, name = "report.pdf")

πŸ› οΈ API Reference

DocumentViewer

@Composable
expect fun DocumentViewer(
    source: DocumentSource,
    modifier: Modifier = Modifier,
    state: DocumentViewerState = rememberDocumentViewerState(),
    enableZoom: Boolean = true,
    onError: (Throwable) -> Unit = {},
    loadingPlaceholder: @Composable (() -> Unit)? = null
)
Parameter Type Default Description
source DocumentSource Required Document content provider (RemoteUrl, FilePath, or ByteArraySource).
modifier Modifier Modifier Composable modifier for sizing and styling layout.
state DocumentViewerState rememberDocumentViewerState() State object for tracking document details and controlling page navigation.
enableZoom Boolean true Toggles interactive pinch-to-zoom capability.
onError (Throwable) -> Unit {} Callback invoked when an error occurs during PDF parsing or downloading.
loadingPlaceholder @Composable (() -> Unit)? null Optional custom UI component shown while the document is loading.

DocumentViewerState

Property / Method Description
currentPage: Int Current 1-based active page index.
pageCount: Int Total count of pages in the open document.
zoomLevel: Float Current zoom scale level (e.g. 1.0f = 100%).
isLoading: Boolean true while document data is being fetched or prepared.
error: Throwable? Holds any error thrown during loading.
jumpToPage(page: Int) Navigates directly to the specified page index.
updateZoom(zoom: Float) Adjusts zoom level within bounds [0.5f, 5.0f].

πŸ› οΈ Building & Running locally

Prerequisites

  • JDK 17+
  • Android SDK (installed at ~/Library/Android/sdk or specified via local.properties)
  • Xcode 16+ (for iOS builds on macOS)

Build All Modules

./gradlew assemble

Run Android Sample App

./gradlew :sample:installDebug

πŸ“„ Publishing to Maven Central

DocuCompose uses Vanniktech's com.vanniktech.maven.publish plugin. To publish to Maven Central, configure your credentials in ~/.gradle/gradle.properties:

mavenCentralUsername=YOUR_SONATYPE_USERNAME
mavenCentralPassword=YOUR_SONATYPE_PASSWORD
signing.keyId=YOUR_GPG_KEY_ID
signing.password=YOUR_GPG_PASSWORD
signing.secretKeyRingFile=/path/to/secring.gpg

Run publish command:

./gradlew publishAndReleaseToMavenCentral

πŸ“œ License

Copyright 2026 Hari Agus W

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

About

Declarative PDF and document viewer for Compose Multiplatform with native zoom, smooth scrolling, and caching support across Android and iOS.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages