PdfViewer

fun PdfViewer(source: PdfSource, modifier: Modifier = Modifier, showShareButton: Boolean = true, shareFileName: String = "document.pdf", backgroundColor: Color = MaterialTheme.colorScheme.surfaceContainerLow, pageBackgroundColor: Color = Color.White, contentPadding: PaddingValues = PaddingValues(0.dp), pageSpacing: Dp = 4.dp, renderDensity: Float = pdfViewerDefaultRenderDensity, maxZoom: Float = DEFAULT_MAX_ZOOM, zoomEnabled: Boolean = true, doubleTapToZoom: Boolean = true, showZoomControls: Boolean = true, textSelectable: Boolean = true, hyperlinksEnabled: Boolean = true, invertColors: Boolean = false, showPageIndicator: Boolean = true, pageLayout: PdfPageLayout = PdfPageLayout.Single, password: String? = null, onDocumentError: (PdfViewerError) -> Unit? = null, shareButtonAlignment: Alignment = Alignment.BottomEnd, shareButtonPadding: PaddingValues = PaddingValues(16.dp), searchHighlights: List<PdfSearchHighlight> = emptyList(), activeSearchHighlightIndex: Int = -1, annotations: List<PdfViewerAnnotation> = emptyList(), annotationMode: Boolean = false, onAnnotationCreated: (PdfViewerAnnotation) -> Unit? = null, onAnnotationDeleted: (Int) -> Unit? = null, cacheStrategy: PdfPageCacheStrategy = PdfPageCacheStrategy.Auto)

Compose Multiplatform viewer for PDFs produced by :pdfkmp (or any other source that hands you raw %PDF-… bytes).

Pages stream through the host platform's native PDF renderer (Android PdfRenderer, iOS PDFKit.PDFDocument) one at a time, driven by a LazyColumn — even hundred-page documents only keep the visible bitmaps in memory. The encoded PDF retains its vector geometry, so what users see on screen and what reaches the share sheet are identical.

Gestures (document-wide):

  • Pinch anywhere to zoom the whole document between and maxZoom. The pinch focal point stays under the user's fingers on the horizontal axis.

  • Single-finger drag scrolls vertically through the document. Once the user has zoomed in, drags also pan the horizontally scrolling viewport.

  • Double tap (double click on Desktop) toggles between and a comfortable reading zoom (2.5×).

  • Desktop: a macOS trackpad pinch zooms anchored under the cursor; Ctrl/⌘ + mouse wheel zooms with a mouse; a plain wheel scrolls through pages. Windows/Linux trackpads (which don't deliver a pinch gesture to the toolkit) use the on-screen + / − controls (showZoomControls).

Sharpness on zoom is handled automatically — once the gesture settles, the viewer re-rasterises each visible page at renderDensity × zoom, so the deeper you zoom the sharper the text gets. On Desktop this tracks the full zoom range (up to maxZoom); touch platforms cap the boost at to bound memory. The re-render is debounced, so the gesture itself stays smooth (the existing bitmap is scaled until the sharper one is ready).

Parameters

source

encoded PDF wrapped in a PdfSource. Use PdfSource.of to convert from a PdfDocument or raw bytes.

modifier

applied to the outer Box.

showShareButton

toggles the built-in share FAB. true by default; set false to suppress it (e.g. when the host screen already has its own share affordance).

shareFileName

user-visible filename presented to the share sheet (must include the .pdf extension).

backgroundColor

colour painted behind the page bitmaps. The default tracks the active Material 3 theme.

pageBackgroundColor

colour painted behind each individual page (visible while the page is still rasterising or through transparent margins inside the PDF itself).

contentPadding

padding applied around the LazyColumn content. Defaults to zero for a fully edge-to-edge layout.

pageSpacing

vertical gap between page previews. Defaults to a tight 4.dp so adjacent pages read as a continuous document.

renderDensity

baseline rasterisation density at zoom — the sharpness before the user zooms at all. Defaults per platform via pdfViewerDefaultRenderDensity: 3× on Desktop (large / hi-DPI screens, ample RAM), 2× on Android / iOS (retina-crisp, memory- bounded). Each unit is 72 DPI. Bump higher (4f+) for very fine print or large technical drawings, at proportionally more memory per page. Zoom multiplies on top of this — see the sharpness note above.

maxZoom

upper bound for the pinch gesture. Defaults to 5f to match iOS PDFKit.

zoomEnabled

master switch for both pinch-to-zoom and double-tap-to-zoom. false keeps the document permanently at and disables the auxiliary horizontal pan logic — useful for read-only one-page receipts where zoom would only get in the way.

doubleTapToZoom

toggles the double-tap shortcut between and a comfortable reading zoom. Independent of zoomEnabled; leaving pinch on while suppressing double-tap is a common accessibility preference. Has no effect when zoomEnabled is false.

showZoomControls

shows an on-screen + / − zoom pill on Desktop only (touch platforms have native pinch, so it never appears there). true by default; set false to hide it — e.g. when the host provides its own zoom UI, or to keep a clean page view and rely on double-click + Ctrl-scroll (mouse). No effect when zoomEnabled is false.

textSelectable

toggles the invisible selectable text overlay. true by default. Only effective on documents loaded via PdfSource.of from a PdfKmp PdfDocument — opaque external PDFs carry no text-position data so the overlay has nothing to render regardless of this flag.

hyperlinksEnabled

toggles the invisible clickable overlay that opens hyperlink annotations in the system browser. Same "needs PdfKmp-built document" caveat as textSelectable.

invertColors

renders each page bitmap colour-inverted (white page → near-black, black text → white) for a dark-mode reading surface. false by default. Only the rasterised preview is inverted — the encoded PDF is untouched. The bitmap cache keys on this flag, so flipping it re-renders rather than serving a stale bitmap.

showPageIndicator

toggles the bottom-centre n / total chip. true by default.

pageLayout

how pages are arranged in the scroll list: PdfPageLayout.Single (default — one page per row, byte-identical to the prior behaviour) or PdfPageLayout.TwoPageBook (side-by-side verso/recto spreads, cover alone, best on Desktop / tablet). Both share the same zoom / pan, search highlights, and annotations.

password

unlocks an encrypted PDF. null (default) is fine for an unencrypted document; an encrypted one with a missing / wrong password shows a "password protected" message instead of crashing (and fires onDocumentError with PdfViewerError.PasswordRequired). Android cannot open encrypted PDFs at all — its PdfRenderer has no password API — so an encrypted document always surfaces the error there regardless of password.

onDocumentError

invoked when the document can't be opened — encrypted with no/wrong password (PdfViewerError.PasswordRequired), corrupt (PdfViewerError.CannotOpen), or a byte-fetch failure (PdfViewerError.LoadFailed). The viewer still shows an inline message; this hook lets the host react (e.g. prompt for a password). null by default.

shareButtonAlignment

positions the built-in share FAB inside the outer Box. Defaults to Alignment.BottomEnd to match Material 3 guidance.

shareButtonPadding

padding between the built-in share FAB and the nearest Box edge.

searchHighlights

translucent yellow rectangles painted over page bitmaps to mark in-document search matches. Pass the result of searchPdfText (or any custom logic that produces PdfSearchHighlights) — empty list disables the layer.

activeSearchHighlightIndex

index into searchHighlights identifying the "current" match. Rendered with a stronger fill and auto-scrolled into view whenever it changes. -1 means no active match (all highlights painted with the resting fill).

annotations

in-viewer highlight annotations painted over the pages, scaled with zoom exactly like searchHighlights. Overlay only — never written into the PDF bytes (see PdfViewerAnnotation).

annotationMode

when true, a drag on a page draws a new highlight (onAnnotationCreated) and a tap on an existing one deletes it (onAnnotationDeleted); page panning is suppressed while the mode is on so the drag is unambiguously a highlight. false (the default) leaves the annotations as passive paint.

onAnnotationCreated

invoked with a freshly drawn highlight when the user finishes a drag in annotationMode.

onAnnotationDeleted

invoked with the index (into annotations) of the highlight a tap deleted in annotationMode.


fun PdfViewer(document: <Error class: unknown class>, modifier: Modifier = Modifier, showShareButton: Boolean = true, shareFileName: String = "document.pdf", backgroundColor: Color = MaterialTheme.colorScheme.surfaceContainerLow, pageBackgroundColor: Color = Color.White, contentPadding: PaddingValues = PaddingValues(0.dp), pageSpacing: Dp = 4.dp, renderDensity: Float = pdfViewerDefaultRenderDensity, maxZoom: Float = DEFAULT_MAX_ZOOM, zoomEnabled: Boolean = true, doubleTapToZoom: Boolean = true, showZoomControls: Boolean = true, textSelectable: Boolean = true, hyperlinksEnabled: Boolean = true, invertColors: Boolean = false, showPageIndicator: Boolean = true, pageLayout: PdfPageLayout = PdfPageLayout.Single, password: String? = null, onDocumentError: (PdfViewerError) -> Unit? = null, shareButtonAlignment: Alignment = Alignment.BottomEnd, shareButtonPadding: PaddingValues = PaddingValues(16.dp), searchHighlights: List<PdfSearchHighlight> = emptyList(), activeSearchHighlightIndex: Int = -1, annotations: List<PdfViewerAnnotation> = emptyList(), annotationMode: Boolean = false, onAnnotationCreated: (PdfViewerAnnotation) -> Unit? = null, onAnnotationDeleted: (Int) -> Unit? = null, cacheStrategy: PdfPageCacheStrategy = PdfPageCacheStrategy.Auto)

Convenience overload that accepts a PdfDocument directly.

Equivalent to PdfViewer(PdfSource.of(document), …) — saves the caller from building a PdfSource when the document is already in hand from pdf { } / pdfAsync { }.


fun PdfViewer(bytes: ByteArray, modifier: Modifier = Modifier, showShareButton: Boolean = true, shareFileName: String = "document.pdf", backgroundColor: Color = MaterialTheme.colorScheme.surfaceContainerLow, pageBackgroundColor: Color = Color.White, contentPadding: PaddingValues = PaddingValues(0.dp), pageSpacing: Dp = 4.dp, renderDensity: Float = pdfViewerDefaultRenderDensity, maxZoom: Float = DEFAULT_MAX_ZOOM, zoomEnabled: Boolean = true, doubleTapToZoom: Boolean = true, showZoomControls: Boolean = true, invertColors: Boolean = false, showPageIndicator: Boolean = true, pageLayout: PdfPageLayout = PdfPageLayout.Single, password: String? = null, onDocumentError: (PdfViewerError) -> Unit? = null, shareButtonAlignment: Alignment = Alignment.BottomEnd, shareButtonPadding: PaddingValues = PaddingValues(16.dp), searchHighlights: List<PdfSearchHighlight> = emptyList(), activeSearchHighlightIndex: Int = -1, annotations: List<PdfViewerAnnotation> = emptyList(), annotationMode: Boolean = false, onAnnotationCreated: (PdfViewerAnnotation) -> Unit? = null, onAnnotationDeleted: (Int) -> Unit? = null, cacheStrategy: PdfPageCacheStrategy = PdfPageCacheStrategy.Auto)

Convenience overload for callers that already have raw PDF bytes (downloaded from the network, picked from ACTION_OPEN_DOCUMENT, etc.) and don't need to construct a PdfSource explicitly.