
Generate PDFs from Compose UI with multi-page and dynamic page support, configurable page sizes and filenames, customizable output directory, automatic initialization, and simple sharing integration.
Generate PDF documents from Compose UI on Android, iOS, Desktop, and Web.
| Platform | Status | Notes |
|---|---|---|
| Android | ✅ Supported | API 26+ (Android 8.0+) |
| iOS | ✅ Supported | iOS 14.0+ (iosArm64, iosX64, iosSimulatorArm64) |
| Desktop (JVM) | ✅ Supported | JVM 17+ (macOS, Windows, Linux) |
| Web (WASM) | ✅ Supported | Browsers with WebAssembly GC (Chrome 119+, Firefox 120+, Safari 18.2+) |
commonMain {
dependencies {
implementation("io.github.big-jared:kmpdf:1.2.0")
}
}val generator = createKmPdfGenerator()
val result = generator.generatePdf(
config = PdfConfig(
pageSize = PageSize.Letter,
fileName = "my-document.pdf"
)
) {
page {
Text("Hello, PDF!")
}
page {
Text("Page 2 content")
}
}
when (result) {
is PdfResult.Success -> sharePdf(result.uri)
is PdfResult.Error -> println(result.message)
}generator.generatePdf(
config = PdfConfig(
pageSize = PageSize.A4,
fileName = "document.pdf"
)
) {
page {
Column(Modifier.fillMaxSize().padding(24.dp)) {
Text("My Document", style = MaterialTheme.typography.headlineLarge)
Spacer(Modifier.height(16.dp))
Text("Content goes here")
}
}
}generator.generatePdf(
config = PdfConfig(fileName = "multi-page.pdf")
) {
page {
Text("Page 1")
}
page {
Text("Page 2")
}
page {
Text("Page 3")
}
}Use loops or any Kotlin logic to create pages from data:
generator.generatePdf(
config = PdfConfig(fileName = "report.pdf")
) {
items.forEach { item ->
page {
ItemContent(item)
}
}
}PdfConfig(
pageSize = PageSize.A4, // A4, Letter, Legal, A3, A5, Tabloid
fileName = "report.pdf", // Output filename
outputDirectory = "/custom/path" // Optional, Desktop only (defaults to ~/Documents/pdfs/)
)PageSize.A4 - 210mm × 297mm (default)PageSize.Letter - 8.5" × 11"PageSize.Legal - 8.5" × 14"PageSize.A3 - 297mm × 420mmPageSize.A5 - 148mm × 210mmPageSize.Tabloid - 11" × 17"Since 1.1.0, KmPDF initializes automatically at app startup through a ContentProvider, so no setup code is needed.
On 1.0.0, or if you've removed the KmPdfInitializer provider from your manifest, initialize KmPDF in your Activity's onCreate():
import io.github.bigboyapps.kmpdf.initKmPdfGenerator
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
initKmPdfGenerator(this)
// ...
}Add FileProvider to your app's AndroidManifest.xml for sharing PDFs:
<application>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
</application>Create res/xml/file_paths.xml:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<cache-path name="pdfs" path="pdfs/" />
</paths>No additional setup required.
PDFs are saved to ~/Documents/pdfs/ by default. You can specify a custom output directory using the outputDirectory parameter in PdfConfig.
No additional setup required. Browsers have no file system, so the PDF is kept in memory:
result.uri is a blob: URL and result.filePath is the file name from PdfConfig
sharePdf(result.uri) downloads the file, then frees it from memory about a minute laterTo keep the PDF around instead, the web target also provides:
downloadPdf(result.uri, fileName = "custom-name.pdf") // Download without freeing it
val bytes: ByteArray = readPdfBytes(result.uri) // Read the PDF, e.g. to upload it
releasePdf(result.uri) // Free the memory when you're doneEach generated PDF stays in memory until sharePdf or releasePdf is called for it.
when (result) {
is PdfResult.Success -> {
println("PDF: ${result.filePath}")
println("${result.pageCount} pages, ${result.fileSize} bytes")
}
is PdfResult.Error -> {
println("Error: ${result.message}")
}
}MIT License - Copyright (c) 2025 Jared Guttromson
See LICENSE for full details.
Generate PDF documents from Compose UI on Android, iOS, Desktop, and Web.
| Platform | Status | Notes |
|---|---|---|
| Android | ✅ Supported | API 26+ (Android 8.0+) |
| iOS | ✅ Supported | iOS 14.0+ (iosArm64, iosX64, iosSimulatorArm64) |
| Desktop (JVM) | ✅ Supported | JVM 17+ (macOS, Windows, Linux) |
| Web (WASM) | ✅ Supported | Browsers with WebAssembly GC (Chrome 119+, Firefox 120+, Safari 18.2+) |
commonMain {
dependencies {
implementation("io.github.big-jared:kmpdf:1.2.0")
}
}val generator = createKmPdfGenerator()
val result = generator.generatePdf(
config = PdfConfig(
pageSize = PageSize.Letter,
fileName = "my-document.pdf"
)
) {
page {
Text("Hello, PDF!")
}
page {
Text("Page 2 content")
}
}
when (result) {
is PdfResult.Success -> sharePdf(result.uri)
is PdfResult.Error -> println(result.message)
}generator.generatePdf(
config = PdfConfig(
pageSize = PageSize.A4,
fileName = "document.pdf"
)
) {
page {
Column(Modifier.fillMaxSize().padding(24.dp)) {
Text("My Document", style = MaterialTheme.typography.headlineLarge)
Spacer(Modifier.height(16.dp))
Text("Content goes here")
}
}
}generator.generatePdf(
config = PdfConfig(fileName = "multi-page.pdf")
) {
page {
Text("Page 1")
}
page {
Text("Page 2")
}
page {
Text("Page 3")
}
}Use loops or any Kotlin logic to create pages from data:
generator.generatePdf(
config = PdfConfig(fileName = "report.pdf")
) {
items.forEach { item ->
page {
ItemContent(item)
}
}
}PdfConfig(
pageSize = PageSize.A4, // A4, Letter, Legal, A3, A5, Tabloid
fileName = "report.pdf", // Output filename
outputDirectory = "/custom/path" // Optional, Desktop only (defaults to ~/Documents/pdfs/)
)PageSize.A4 - 210mm × 297mm (default)PageSize.Letter - 8.5" × 11"PageSize.Legal - 8.5" × 14"PageSize.A3 - 297mm × 420mmPageSize.A5 - 148mm × 210mmPageSize.Tabloid - 11" × 17"Since 1.1.0, KmPDF initializes automatically at app startup through a ContentProvider, so no setup code is needed.
On 1.0.0, or if you've removed the KmPdfInitializer provider from your manifest, initialize KmPDF in your Activity's onCreate():
import io.github.bigboyapps.kmpdf.initKmPdfGenerator
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
initKmPdfGenerator(this)
// ...
}Add FileProvider to your app's AndroidManifest.xml for sharing PDFs:
<application>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
</application>Create res/xml/file_paths.xml:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<cache-path name="pdfs" path="pdfs/" />
</paths>No additional setup required.
PDFs are saved to ~/Documents/pdfs/ by default. You can specify a custom output directory using the outputDirectory parameter in PdfConfig.
No additional setup required. Browsers have no file system, so the PDF is kept in memory:
result.uri is a blob: URL and result.filePath is the file name from PdfConfig
sharePdf(result.uri) downloads the file, then frees it from memory about a minute laterTo keep the PDF around instead, the web target also provides:
downloadPdf(result.uri, fileName = "custom-name.pdf") // Download without freeing it
val bytes: ByteArray = readPdfBytes(result.uri) // Read the PDF, e.g. to upload it
releasePdf(result.uri) // Free the memory when you're doneEach generated PDF stays in memory until sharePdf or releasePdf is called for it.
when (result) {
is PdfResult.Success -> {
println("PDF: ${result.filePath}")
println("${result.pageCount} pages, ${result.fileSize} bytes")
}
is PdfResult.Error -> {
println("Error: ${result.message}")
}
}MIT License - Copyright (c) 2025 Jared Guttromson
See LICENSE for full details.