Getting Started¶
Learn how to open a PDF, display it in the UI, and extract its content with KitePDF: a pure-Kotlin multiplatform PDF engine.
Step 1: Add the dependency¶
KitePDF is published to Maven Central as seven artifacts: the kitepdf umbrella, the kitepdf-pdf and kitepdf-epub handlers, the shared kitepdf-core substrate (transitive only, never add it directly), and three alternative renderers. Start with the headless engine, and add one renderer only when you need to draw pages.
The core kitepdf artifact depends on kotlin-stdlib and the pure-Kotlin KiteImage codec engine. It works on every Kotlin target (JVM, Android, Native, JS, wasmJs).
Step 2: Open your first PDF¶
Get PDF bytes from a file, network, or resources; the method depends on your platform.
JVM / Desktop:
import java.io.File
import io.github.yuroyami.kitepdf.KitePDF
val bytes = File("sample.pdf").readBytes()
val doc = KitePDF.open(bytes)
println("${doc.pageCount} pages")
println("PDF version ${doc.version}")
Android:
import io.github.yuroyami.kitepdf.KitePDF
// From resources or assets
val bytes = context.resources.openRawResource(R.raw.sample).readBytes()
val doc = KitePDF.open(bytes)
// Or from a file
val bytes = File(context.cacheDir, "sample.pdf").readBytes()
val doc = KitePDF.open(bytes)
Password-protected PDFs:
import io.github.yuroyami.kitepdf.PdfDocument
val doc = PdfDocument.open(bytes, "secret".encodeToByteArray())
EPUB books
The same dependency graph reads EPUBs: add io.github.yuroyami:kitepdf-epub:0.6.0 and call EpubDocument.open(bytes). See the EPUB guide.
Step 3: Show it on screen in Compose¶
The kitepdf-compose-viewer artifact provides PdfView: a Compose-Multiplatform viewer supporting pinch zoom, paging, continuous scroll, and more.
Add the dependency:
dependencies {
commonMain.dependencies {
implementation("io.github.yuroyami:kitepdf-compose-viewer:0.6.0")
}
}
Display the whole document (vertical continuous scroll):
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.ui.Modifier
import io.github.yuroyami.kitepdf.PdfDocument
import io.github.yuroyami.kitepdf.compose.PdfView
@Composable
fun PdfViewer(doc: PdfDocument) {
PdfView(document = doc, modifier = Modifier.fillMaxSize())
}
Show a single page with fit-to-viewport letterboxing:
@Composable
fun SinglePageView(doc: PdfDocument) {
PdfView(
document = doc,
page = 0, // zero-based index
modifier = Modifier.fillMaxSize()
)
}
Full control over layout, zoom, and rendering:
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.ui.Alignment
import androidx.compose.ui.graphics.Color
import io.github.yuroyami.kitepdf.PdfDocument
import io.github.yuroyami.kitepdf.compose.*
@Composable
fun AdvancedViewer(doc: PdfDocument) {
val state = rememberPdfViewState(doc)
PdfView(
state = state,
modifier = Modifier.fillMaxSize(),
layout = PdfLayout.Paged(Orientation.Horizontal),
zoomSpec = PdfZoomSpec(minZoom = 0.5f, maxZoom = 6f),
renderSpec = PdfRenderSpec.Rasterized(
quality = 1f,
maxBitmapLongSide = 4096,
rerasterizeOnZoom = true,
preserveHairlines = true
),
colors = PdfViewColors(
pageBackground = Color.White,
viewportBackground = Color(0xFF1E1E1E)
),
overlay = { state ->
PdfNavigationControls(state, Modifier.align(Alignment.BottomCenter))
}
)
}
Tip
Use PdfRenderSpec.Rasterized() (the default) for scrolling performance and memory efficiency. Switch to PdfRenderSpec.Vectorized() for resolution-independent, bitmap-free rendering: best for simple pages or when memory is tight.
Step 4: Read text out of it¶
Extract text from any page without displaying it:
For geometry-aware extraction (to build selection rectangles or search highlights), use structured text:
val structured = page.structuredText
// structured.blocks → lines → spans, each with bounds
for (block in structured.blocks) {
println("Block: ${block.bounds}")
for (line in block.lines) {
println(" Line: ${line.text}")
for (span in line.spans) {
println(" Span: ${span.text} at ${span.bounds}")
}
}
}
Step 5: Render a page to PNG¶
Export a page as a PNG (or JPEG) without showing the UI. The kitepdf-native-renderer artifact provides headless rasterization using the JDK's built-in AWT + ImageIO.
Add the dependency:
Render and encode:
import io.github.yuroyami.kitepdf.nativerenderer.AwtPdfRasterizer
import java.io.File
val page = doc.pages[0]
// Render at 2× scale (e.g. for print or high-DPI screenshots)
val pngBytes = AwtPdfRasterizer.encodeToPng(page, scale = 2.0)
File("page-0.png").writeBytes(pngBytes)
// Or JPEG
val jpegBytes = AwtPdfRasterizer.encodeToJpeg(page, scale = 1.0)
File("page-0.jpg").writeBytes(jpegBytes)
You can also grab the BufferedImage directly:
Note
The native-renderer artifact works on JVM, Android, and macOS. For other platforms (JS, wasm, iOS), use the Skia renderer (kitepdf-skia-renderer) or render through Compose (kitepdf-compose-viewer).
Where to next?¶
- Compose Viewer: zoom gestures, paging modes, navigation overlays, and thumbnails.
- Editing & Writing: modify PDFs programmatically (form fields, metadata, incremental updates).
- Recipes: common patterns: search text, extract images, parse annotations, batch processing.