Kotlin Multiplatform PDF generator for Android, iOS, Desktop (JVM) & Web (Wasm) β vector-first, type-safe, DSL-driven.
π Full documentation: conamobiledev.github.io/PdfKmp β 20 hand-written guides, every sample, and the Dokka API reference.
| Android | Desktop β macOS Β· Windows Β· Linux (JVM) |
![]() |
![]() |
| iOS | Web (Kotlin/Wasm) |
![]() |
![]() |
The same brochure document rendered on Android, iOS and Desktop β pixel-identical vector output β plus the browser sample generating it live on the Web.
PdfKmp lets you build PDF documents from a Compose-style DSL that runs identically on Android, iOS, Desktop (JVM β macOS, Windows, Linux), and the browser (Kotlin/Wasm). Text becomes glyph paths, shapes become path operators β every page stays sharp at any zoom level. The library ships the Inter font for cross-platform Latin parity and exposes opt-in references to system CJK / Arabic / Persian fonts so non-Latin scripts render natively on Android and iOS (on Desktop, supply a custom font β see i18n fonts).
What's in the box:
- Rich text engine β full justification, automatic hyphenation,
maxLines+ ellipsis, soft-hyphen + mid-word breaking, super/subscript spans, right-to-left support with bidi reorder + Arabic shaping (incl. kashida justification), and orphan/widow control. - Layout & pagination β column / row / box / card, weighted children, recursive column slicing, table row slicing with repeating headers,
keepTogether, multi-column (columns) and uniformgrid, mixed page orientations. - Tables, lists, dividers, circles & ellipses, plus a free-form vector DSL (
freeDraw). - Graphics β QR codes, Code 128 / EAN-13 / UPC-A barcodes, Data Matrix, and bar / stacked-bar / (multi-series) line / pie / donut charts β all pure vector, no external dependencies.
- Decorations β backgrounds, per-corner radii, per-side borders, dashed / dotted borders, gradients, drop shadows, rotation, and group opacity.
- Navigation & document features β bookmarks / outline, internal links + auto table of contents, hyperlinks, AcroForm fields, encryption, file attachments, best-effort PDF/A, and JVM digital signing.
- Images & vectors β PNG / JPEG / WebP / HEIF, Android
<vector>and W3C<svg>(kept vector inside the PDF). - Companion modules β a Compose Multiplatform PDF viewer, a Compose Resources bridge, and a Markdown renderer.
See the platform feature parity table for what each backend supports.
Under the hood β native / pure-Java PDF stacks per platform:
| Platform | Backend |
|---|---|
| Android | android.graphics.pdf.PdfDocument + android.graphics.Canvas |
| iOS | UIGraphicsBeginPDFContextToData + Core Graphics (CGContext) |
| Desktop (JVM) | Apache PDFBox β pure-Java, runs on macOS / Windows / Linux with no native libraries |
| Web (Wasm) | kmpwriter β PdfKmp's own pure-Kotlin PDF 1.7 writer (no browser PDF API exists), with TrueType subsetting + Flate compression |
Every DSL node funnels into these PDF backends, so the resulting bytes are real, vector PDFs β readable in Preview, Adobe Reader, Chrome, and any spec-compliant viewer. On Desktop the embedded TrueType fonts are subsetted to only the glyphs you draw, so text stays vector and the file stays small.
PdfKmp is published to Maven Central. The library exposes:
- an Android
aar(io.github.conamobiledev:pdfkmp-android), - an iOS framework named
PdfKmpfor arm64 (device) and simulator-arm64 (Apple-Silicon simulator), - a Desktop / JVM
jar(io.github.conamobiledev:pdfkmp-jvm) for macOS, Windows, and Linux, - a Web / Kotlin-Wasm klib (
io.github.conamobiledev:pdfkmp-wasm-js) β see Web (Kotlin/Wasm) for what the browser backend covers, - common Kotlin metadata (
io.github.conamobiledev:pdfkmp), - an optional Compose Multiplatform Resources companion (
io.github.conamobiledev:pdfkmp-compose-resources, with-android/-jvm/-wasm-jsplatform variants), - an optional Markdown renderer (
io.github.conamobiledev:pdfkmp-markdown, same platform variants), - an optional Compose Multiplatform PDF viewer screen (
io.github.conamobiledev:pdfkmp-viewer, with-android/-jvmplatform variants β not on web; see below).
The browser exposes no PDF engine to Wasm, so the web target renders through PdfKmp's own pure-Kotlin PDF writer β everything vector works exactly like the other platforms, custom TrueType fonts embed as subsets, and Cyrillic works out of the box via the bundled Inter. Current limits and font details: see the Web guide.
Viewing needs no library at all β browsers ship excellent PDF viewers, so hand them the bytes:
val doc = pdf { page { text("Hello, Wasm!") } }
doc.openInNewTab() // browser's own viewer
doc.save(StorageLocation.Downloads, "hello.pdf") // browser downloadMake sure Maven Central is in your repository list:
// settings.gradle.kts
dependencyResolutionManagement {
repositories {
mavenCentral()
}
}Pick the section that matches your project. The paths are mutually exclusive β KMP projects depend on pdfkmp only (Gradle resolves the right platform variant automatically, including for Android and Desktop targets), while plain Android projects depend on pdfkmp-android and plain JVM/Desktop projects on pdfkmp-jvm. Inside each path, use whichever dependency style your project already uses; the libs.versions.toml form is recommended for new projects.
Depend on pdfkmp from commonMain. Do not add pdfkmp-android separately β Gradle picks the Android variant of pdfkmp for your Android target on its own. (Version catalogs work as usual β map the same coordinates in libs.versions.toml.)
// build.gradle.kts
kotlin {
sourceSets {
commonMain.dependencies {
implementation("io.github.conamobiledev:pdfkmp:1.2.0")
implementation("io.github.conamobiledev:pdfkmp-compose-resources:1.2.0") // optional
implementation("io.github.conamobiledev:pdfkmp-viewer:1.2.0") // optional
}
}
}Both companions are opt-in:
pdfkmp-compose-resourcesβ add it only if your project uses Compose Multiplatform Resources (Res.drawable.*) and you want typed resource references inside the PdfKmp DSL. Full usage in the Compose Resources guide.pdfkmp-viewerβ Compose Multiplatform PDF viewer screen with topbar, search, share, save, hyperlinks, gesture-driven zoom & text selection. Skip it if you only need to generate PDFs and let users view them in their system default reader. Full usage in the viewer guide.
The core pdfkmp artifact itself stays Compose-free β non-Compose consumers don't pay for either companion.
For projects that don't use Kotlin Multiplatform (plain com.android.application or com.android.library), depend on pdfkmp-android. KMP consumers should use the section above instead β they should not add this artifact.
// app/build.gradle.kts
dependencies {
implementation("io.github.conamobiledev:pdfkmp-android:1.2.0")
implementation("io.github.conamobiledev:pdfkmp-viewer:1.2.0") // optional β Compose viewer screen
}pdfkmp-viewer brings in Compose Multiplatform β skip it if your Android project still uses the legacy View system. The viewer assumes Compose hosting (a ComponentActivity with setContent { β¦ } or an embedded ComposeView).
KMP projects with a jvm() target need no extra setup β Gradle resolves the Desktop variant of pdfkmp for the JVM target automatically, exactly like it does for Android. For a plain JVM project (a standalone Compose for Desktop app or any non-KMP Gradle JVM module), depend on pdfkmp-jvm directly:
// build.gradle.kts
dependencies {
implementation("io.github.conamobiledev:pdfkmp-jvm:1.2.0")
implementation("io.github.conamobiledev:pdfkmp-viewer-jvm:1.2.0") // optional β Compose for Desktop viewer
}The Desktop backend is built on Apache PDFBox (pulled in transitively) and is pure-Java, so the same artifact runs on macOS, Windows, and Linux with no native libraries to bundle. The generation API (pdf { β¦ }, document.save(...), toByteArray()) is identical across all platforms. pdfkmp-viewer on Desktop hosts the viewer in a Compose for Desktop window and rasterises pages with PDFBox's PDFRenderer.
- JDK 17+
- Android Gradle Plugin 8.x,
compileSdk34+ (Android targets) - Xcode 16+ when targeting iOS via Kotlin Multiplatform
- A desktop JRE 17+ when targeting Desktop / JVM (macOS, Windows, Linux)
R8 is fully supported β no additional keep rules required.
import com.conamobile.pdfkmp.pdf
import com.conamobile.pdfkmp.storage.StorageLocation
import com.conamobile.pdfkmp.storage.save
import com.conamobile.pdfkmp.style.PdfColor
import com.conamobile.pdfkmp.unit.sp
// 1. Build the document
// Use `pdf { }` for a synchronous build, or `pdfAsync { }` (suspend) when
// the tree contains typed `Res.drawable.*` references β see Compose
// Multiplatform Resources below.
val document = pdf {
metadata { title = "Hello, PdfKmp" }
page {
text("Hello, world!") {
fontSize = 24.sp
bold = true
color = PdfColor.Blue
}
}
}
// 2. Pick what to do with it:
val bytes: ByteArray = document.toByteArray() // raw bytes
// or
val saved = document.save(StorageLocation.Cache, "hello.pdf") // returns SavedPdf (synchronous)
println(saved.path) // absolute filesystem path you can hand to a viewer / share intentAfter save(...) you get a SavedPdf with a real filesystem path you can hand to a PDF viewer or a share sheet β the Getting started guide has ready-made view / share / open snippets for every platform.
This README covers installation and your first document only β everything else lives on the documentation site:
π conamobiledev.github.io/PdfKmp
- Getting started β every feature guide: text & typography, layout, tables, decorations, images & SVG, QR / barcodes / charts, navigation & TOC, pages / headers / watermarks, forms & security, fonts & i18n, the Compose viewer, Markdown-to-PDF, and the Web (Wasm) target.
- Samples β every bundled showcase document, with the code that builds it.
- Internals β how the layout engine and the per-platform renderers work.
- API Reference β generated KDoc for every public declaration.
For repo conventions and AI-agent guidance see CLAUDE.md (working on this codebase) and AGENTS.md / .claude/skills/pdfkmp/SKILL.md (using the library).
Apache License 2.0 β see LICENSE.
The bundled Inter font is licensed separately under the SIL Open Font License 1.1 β see pdfkmp/fonts/Inter-LICENSE.txt.
If PdfKmp saved you some time, don't forget to click β β it helps others find the project :)



