KmpPdfViewer

fun KmpPdfViewer(source: PdfSource, modifier: Modifier = Modifier, title: String = "Document", fileName: String = "document.pdf", onBack: () -> Unit? = null, backLabel: String? = null, titleOverflow: PdfTopBarTitleOverflow = PdfTopBarTitleOverflow.Ellipsis, showTopBar: Boolean = true, showBack: Boolean = onBack != null, showSearch: Boolean = true, showShare: Boolean = true, showPrint: Boolean = false, showDownload: Boolean = true, invertColors: Boolean = false, showPageIndicator: Boolean = true, zoomEnabled: Boolean = true, doubleTapToZoom: Boolean = true, showZoomControls: Boolean = true, textSelectable: Boolean = true, hyperlinksEnabled: Boolean = true, showAnnotationTools: Boolean = false, initialAnnotations: List<PdfViewerAnnotation> = emptyList(), onAnnotationsChanged: (List<PdfViewerAnnotation>) -> Unit? = null, pageLayout: PdfPageLayout = PdfPageLayout.Single, password: String? = null, onDocumentError: (PdfViewerError) -> Unit? = null, backgroundColor: Color = MaterialTheme.colorScheme.surfaceContainerLow, pageBackgroundColor: Color = Color.White, contentPadding: PaddingValues = PaddingValues(0.dp), pageSpacing: Dp = 4.dp, renderDensity: Float = pdfViewerDefaultRenderDensity, maxZoom: Float = 5.0f, cacheStrategy: PdfPageCacheStrategy = PdfPageCacheStrategy.Auto)

All-in-one PDF viewer screen — recommended composable entry.

KmpPdfViewer(...) drops a complete PDF screen into your Compose hierarchy: topbar (Minimal Mono on Android, Classic iOS Native on iOS), inline search-bar morph, share & save actions, hyperlink launcher, page indicator chip, gesture-driven zoom + pan. Every piece of state — searchOpen, searchQuery, activeMatchIndex, share / save bindings — is owned by the library, not the host.

The host configures what the viewer can do (via the visibility toggles below) and what happens on back (via onBack). Nothing else is required:

KmpPdfViewer(
source = PdfSource.Remote("https://example.com/invoice.pdf"),
title = "Invoice",
onBack = { navController.popBackStack() },
)

Three overloads cover every realistic input: a PdfSource (recommended — covers remote URLs, local files, content URIs, bundled assets, raw bytes and PdfKmp documents through one sealed type), a PdfDocument built through the PdfKmp DSL, or a raw ByteArray. Async sources render an inline progress indicator while loading and an error message on failure — back navigation still works in both states.

Need to launch a viewer from outside a @Composable scope — say from a click handler or LaunchedEffect? Use the imperative counterpart KmpPdfLauncher.open instead. It hosts this same composable inside a platform-native shell (Activity on Android, UIViewController on iOS).

Need finer control (custom topbar, custom share, FAB placement, multi-action overlays)? Drop down to the lower-level public composables: PdfViewer, PdfViewerTopBar, PdfSearchBar, rememberPdfShareAction, rememberPdfSaveAction, rememberPdfUrlLauncher, and searchPdfText. KmpPdfViewer is the opinionated default; the building blocks remain available for advanced layouts.

Parameters

source

PDF payload. PdfSource.of(document) keeps text selection + hyperlinks alive; async variants stream bytes from the platform's native loader.

modifier

applied to the outer Column.

title

shown in the topbar's centered title (Classic iOS) / bold first line (Minimal Mono).

fileName

user-visible filename surfaced to the share sheet and the "Saved to Downloads" entry. Must include .pdf.

onBack

callback wired to the back chip / chevron. null removes the back affordance entirely.

backLabel

iOS-only previous-screen label rendered next to the chevron (e.g. "Files"). Ignored on Android.

titleOverflow

how the topbar title behaves when it is too long to fit. PdfTopBarTitleOverflow.Ellipsis (default) truncates it with ; PdfTopBarTitleOverflow.Marquee scrolls it horizontally. The back affordance and the share / search / download icons keep their size either way — only the title yields.

showTopBar

master switch for the entire chrome (topbar + search bar). false hides both unconditionally — yields a "poor viewer" surface that is just pages, indicator, and gestures. When false, showBack, showSearch, showShare, showDownload all become no-ops because there is no bar to surface them on. Host apps that hide the topbar typically wire their own back navigation and share / download affordances via the platform's system UI.

showBack

hide / show the back affordance independently of onBack. Defaults to true when onBack is provided.

showSearch

hide / show the search affordance. Auto- suppressed when the source carries no text runs.

showShare

hide / show the share affordance.

showPrint

hide / show the print affordance. false by default — printing is opt-in. When true the topbar surfaces a print button that hands the bytes to the platform print pipeline (PrintManager on Android, UIPrintInteractionController on iOS, java.awt.print.PrinterJob on Desktop).

showDownload

hide / show the download affordance.

invertColors

render every page bitmap colour-inverted (white page → near-black, black text → white) for a dark-mode reading surface. false by default. The inversion happens on the rasterised preview only — the encoded PDF and the bytes handed to share / save / print are untouched. Pair it with a dark pageBackgroundColor / backgroundColor for a seamless look.

showPageIndicator

hide / show the bottom-centre page chip.

zoomEnabled

master switch for pinch + double-tap zoom.

doubleTapToZoom

independent toggle for the double-tap shortcut.

textSelectable

toggles the invisible selectable text overlay. Only effective on documents loaded from a PdfDocument.

hyperlinksEnabled

toggles the invisible clickable layer that opens hyperlink annotations in the system browser. Same PdfDocument-only caveat as textSelectable.

backgroundColor

colour painted behind the page bitmaps. Defaults to the active Material 3 surface tone.

pageBackgroundColor

colour painted behind each individual page (visible while it's still rasterising).

contentPadding

padding around the androidx.compose.foundation.lazy.LazyColumn content. Defaults to zero for a fully edge-to-edge layout.

pageSpacing

vertical gap between page previews.

renderDensity

baseline scaling factor applied during rasterisation.

maxZoom

upper bound for the pinch gesture.

cacheStrategy

controls how far ahead and behind the visible page the renderer keeps rasterised bitmaps. See PdfPageCacheStrategy for the trade-offs; defaults to PdfPageCacheStrategy.Auto which picks a window based on available RAM and never crashes.

showAnnotationTools

surfaces a highlight-annotation toggle in the topbar. false by default — annotation tools are opt-in. When the toggle is on, dragging on a page paints a translucent yellow highlight and tapping an existing highlight deletes it. Overlay only: highlights live in viewer state and are never written into the PDF bytes — share / save / print get the original document (see PdfViewerAnnotation).

initialAnnotations

highlights to restore into the viewer on first composition (e.g. a list previously persisted by your app via onAnnotationsChanged). The viewer owns the mutable copy from then on.

onAnnotationsChanged

invoked with the full annotation list every time the user adds or removes a highlight, so the host can persist them. null (the default) drops the changes — annotations then live only for the composition's lifetime.

pageLayout

how pages are arranged: PdfPageLayout.Single (default — continuous one-per-row scroll) or PdfPageLayout.TwoPageBook (side-by-side book spreads, cover alone, best on Desktop / tablet). There is no topbar toggle — this is a parameter-only choice; the host flips it.

password

unlocks an encrypted PDF. null (default) for an unencrypted document. With a missing / wrong password the viewer shows an inline "password protected" message instead of crashing and fires onDocumentError. Desktop opens it with the right password; iOS unlocks via PDFKit (pending macOS verification); Android cannot open encrypted PDFs at all (PdfRenderer has no password API) so the error is terminal there.

onDocumentError

invoked when the document can't be opened — see PdfViewerError. The inline message is shown regardless; this lets the host react (e.g. re-prompt for a password). null by default.

Annotation export on save: when showAnnotationTools is on, there is at least one highlight, and the platform can write annotations (Desktop via PdfBox — see pdfViewerSupportsAnnotationExport), the existing download / save action writes the highlights INTO the saved PDF (real Highlight annotations, visible in any reader) instead of the originals. Share / print still export the untouched original, and on Android / iOS (no writable PDF API) save also keeps exporting the original — highlights stay overlay-only there.


fun KmpPdfViewer(document: <Error class: unknown class>, modifier: Modifier = Modifier, title: String = "Document", fileName: String = "document.pdf", onBack: () -> Unit? = null, backLabel: String? = null, titleOverflow: PdfTopBarTitleOverflow = PdfTopBarTitleOverflow.Ellipsis, showTopBar: Boolean = true, showBack: Boolean = onBack != null, showSearch: Boolean = true, showShare: Boolean = true, showPrint: Boolean = false, showDownload: Boolean = true, invertColors: Boolean = false, showPageIndicator: Boolean = true, zoomEnabled: Boolean = true, doubleTapToZoom: Boolean = true, showZoomControls: Boolean = true, textSelectable: Boolean = true, hyperlinksEnabled: Boolean = true, showAnnotationTools: Boolean = false, initialAnnotations: List<PdfViewerAnnotation> = emptyList(), onAnnotationsChanged: (List<PdfViewerAnnotation>) -> Unit? = null, pageLayout: PdfPageLayout = PdfPageLayout.Single, password: String? = null, onDocumentError: (PdfViewerError) -> Unit? = null, backgroundColor: Color = MaterialTheme.colorScheme.surfaceContainerLow, pageBackgroundColor: Color = Color.White, contentPadding: PaddingValues = PaddingValues(0.dp), pageSpacing: Dp = 4.dp, renderDensity: Float = pdfViewerDefaultRenderDensity, maxZoom: Float = 5.0f, cacheStrategy: PdfPageCacheStrategy = PdfPageCacheStrategy.Auto)

PdfDocument-flavoured overload — wraps the document in a PdfSource.Document and forwards. Recommended entry point when the PDF was authored through the PdfKmp DSL because text selection and hyperlinks light up automatically.


fun KmpPdfViewer(bytes: ByteArray, modifier: Modifier = Modifier, title: String = "Document", fileName: String = "document.pdf", onBack: () -> Unit? = null, backLabel: String? = null, titleOverflow: PdfTopBarTitleOverflow = PdfTopBarTitleOverflow.Ellipsis, showTopBar: Boolean = true, showBack: Boolean = onBack != null, showSearch: Boolean = true, showShare: Boolean = true, showPrint: Boolean = false, showDownload: Boolean = true, invertColors: Boolean = false, showPageIndicator: Boolean = true, zoomEnabled: Boolean = true, doubleTapToZoom: Boolean = true, showZoomControls: Boolean = true, textSelectable: Boolean = true, hyperlinksEnabled: Boolean = true, showAnnotationTools: Boolean = false, initialAnnotations: List<PdfViewerAnnotation> = emptyList(), onAnnotationsChanged: (List<PdfViewerAnnotation>) -> Unit? = null, pageLayout: PdfPageLayout = PdfPageLayout.Single, password: String? = null, onDocumentError: (PdfViewerError) -> Unit? = null, backgroundColor: Color = MaterialTheme.colorScheme.surfaceContainerLow, pageBackgroundColor: Color = Color.White, contentPadding: PaddingValues = PaddingValues(0.dp), pageSpacing: Dp = 4.dp, renderDensity: Float = pdfViewerDefaultRenderDensity, maxZoom: Float = 5.0f, cacheStrategy: PdfPageCacheStrategy = PdfPageCacheStrategy.Auto)

Raw-bytes overload — for payloads that came from disk, the network, an ACTION_OPEN_DOCUMENT picker, or any other source. Text selection + hyperlink layers are inert because the bytes carry no position metadata; everything else (zoom, share, save, page indicator) works exactly the same.


fun KmpPdfViewer(uri: String, modifier: Modifier = Modifier, title: String = "Document", fileName: String = "document.pdf", onBack: () -> Unit? = null, backLabel: String? = null, titleOverflow: PdfTopBarTitleOverflow = PdfTopBarTitleOverflow.Ellipsis, showTopBar: Boolean = true, showBack: Boolean = onBack != null, showSearch: Boolean = true, showShare: Boolean = true, showPrint: Boolean = false, showDownload: Boolean = true, invertColors: Boolean = false, showPageIndicator: Boolean = true, zoomEnabled: Boolean = true, doubleTapToZoom: Boolean = true, showZoomControls: Boolean = true, textSelectable: Boolean = true, hyperlinksEnabled: Boolean = true, showAnnotationTools: Boolean = false, initialAnnotations: List<PdfViewerAnnotation> = emptyList(), onAnnotationsChanged: (List<PdfViewerAnnotation>) -> Unit? = null, pageLayout: PdfPageLayout = PdfPageLayout.Single, password: String? = null, onDocumentError: (PdfViewerError) -> Unit? = null, backgroundColor: Color = MaterialTheme.colorScheme.surfaceContainerLow, pageBackgroundColor: Color = Color.White, contentPadding: PaddingValues = PaddingValues(0.dp), pageSpacing: Dp = 4.dp, renderDensity: Float = pdfViewerDefaultRenderDensity, maxZoom: Float = 5.0f, cacheStrategy: PdfPageCacheStrategy = PdfPageCacheStrategy.Auto)

Deprecated

Use KmpPdfViewer(source = PdfSource.auto(uri), …) — strings hide what transport is being used and can't carry headers / timeouts.

Replace with

import com.conamobile.pdfkmp.viewer.PdfSource
KmpPdfViewer(source = PdfSource.auto(uri), modifier = modifier, title = title, fileName = fileName, onBack = onBack, backLabel = backLabel, titleOverflow = titleOverflow, showTopBar = showTopBar, showBack = showBack, showSearch = showSearch, showShare = showShare, showPrint = showPrint, showDownload = showDownload, invertColors = invertColors, showPageIndicator = showPageIndicator, zoomEnabled = zoomEnabled, doubleTapToZoom = doubleTapToZoom, showZoomControls = showZoomControls, textSelectable = textSelectable, hyperlinksEnabled = hyperlinksEnabled, backgroundColor = backgroundColor, pageBackgroundColor = pageBackgroundColor, contentPadding = contentPadding, pageSpacing = pageSpacing, renderDensity = renderDensity, maxZoom = maxZoom, cacheStrategy = cacheStrategy)

String-URI overload, deprecated in favour of PdfSource.auto.

The string form hid which transport was being used and offered no place to attach per-shape configuration (HTTP headers, timeouts, etc.). Migrate to an explicit PdfSource variant — call PdfSource.auto if you genuinely don't know the scheme at call site, or pick a constructor directly when you do.