PdfViewer
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
1×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
1×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 2× 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
encoded PDF wrapped in a PdfSource. Use PdfSource.of to convert from a PdfDocument or raw bytes.
applied to the outer Box.
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).
user-visible filename presented to the share sheet (must include the .pdf extension).
colour painted behind the page bitmaps. The default tracks the active Material 3 theme.
colour painted behind each individual page (visible while the page is still rasterising or through transparent margins inside the PDF itself).
padding applied around the LazyColumn content. Defaults to zero for a fully edge-to-edge layout.
vertical gap between page previews. Defaults to a tight 4.dp so adjacent pages read as a continuous document.
baseline rasterisation density at 1× 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.
upper bound for the pinch gesture. Defaults to 5f to match iOS PDFKit.
master switch for both pinch-to-zoom and double-tap-to-zoom. false keeps the document permanently at 1× and disables the auxiliary horizontal pan logic — useful for read-only one-page receipts where zoom would only get in the way.
toggles the double-tap shortcut between 1× 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.
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.
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.
toggles the invisible clickable overlay that opens hyperlink annotations in the system browser. Same "needs PdfKmp-built document" caveat as textSelectable.
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.
toggles the bottom-centre n / total chip. true by default.
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.
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.
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.
positions the built-in share FAB inside the outer Box. Defaults to Alignment.BottomEnd to match Material 3 guidance.
padding between the built-in share FAB and the nearest Box edge.
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.
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).
in-viewer highlight annotations painted over the pages, scaled with zoom exactly like searchHighlights. Overlay only — never written into the PDF bytes (see PdfViewerAnnotation).
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.
invoked with a freshly drawn highlight when the user finishes a drag in annotationMode.
invoked with the index (into annotations) of the highlight a tap deleted in annotationMode.
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 { }.
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.