Skip to content

yuroyami/KitePDF

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

20 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

KitePDF

Maven Central Docs status kotlin license

One pure-Kotlin PDF engine for Kotlin Multiplatform. Read, create, edit and render PDFs from commonMain, with the exact same code on every target.

Getting started, guides, recipes, and the full API reference. If you read one thing, read this.

KitePDF is a complete PDF engine written from scratch in Kotlin: parser, renderer, writer, editor, encryption, fonts, the whole stack. You call it from common code and it runs unchanged on Android, iOS, desktop (JVM), the web (JS / Wasm) and Kotlin/Native. There is no platform PDF engine underneath, no expect/actual, no JNI, no native binary.

// commonMain. Nothing platform-specific. This runs everywhere Kotlin runs.
val doc = PdfDocument.open(bytes)

val pages = doc.pageCount                 // inspect
val text  = doc.pages[0].extractText()    // read

// edit in place, then save
val edited = doc.edit().apply {
    redactRegion(doc.pages[0], Rectangle(72.0, 700.0, 320.0, 720.0))
}.saveRewritten()

// or build a brand-new document
val fresh = PdfBuilder()
    .page { text(StandardFont.Helvetica, 24.0, x = 72.0, y = 720.0, "Hello, world!") }
    .build()

Why KitePDF

Almost every other Kotlin / KMP "PDF library" is not really a PDF library. It is a thin expect/actual (or JNI) wrapper around the platform's own engine: PdfRenderer on Android, PDFKit on iOS, PDF.js in the browser, PDFBox on the JVM. You inherit four different engines, four sets of bugs, and four feature sets that never quite line up.

KitePDF is the opposite. It is a single standalone engine, 100% common Kotlin, with zero expect/actual in the core. Write your PDF code once in commonMain and it behaves identically on every target, because it is literally the same code. When something renders wrong, it is one bug in one place, and it is ours to fix.

Rendering to a screen is the only thing that needs a platform. KitePDF keeps that cleanly separate (see Putting pixels on screen), so the engine stays pure and portable.

Install

The engine is one dependency. Add it to commonMain and you have everything except drawing to a screen:

kotlin {
    sourceSets {
        commonMain.dependencies {
            implementation("io.github.yuroyami:kitepdf:0.1.0")
        }
    }
}

That is it for reading, writing, editing, redacting, encryption, text extraction and building PDFs, on every Kotlin target. Its only dependency is kotlin-stdlib.

Not using Kotlin Multiplatform? The same artifact works in a plain Android or JVM project. Add io.github.yuroyami:kitepdf:0.1.0 to your normal dependencies { } block.

Rendering bindings are opt-in and covered under Putting pixels on screen.

What it does

Everything below is pure common code from the kitepdf artifact. Each item links to its guide.

Read

Create and edit

  • Build from scratch: a content DSL for text, vector graphics, images and clipping, with the standard 14 fonts.
  • Fill forms, stamp and watermark pages.
  • True redaction: the underlying text and images are removed from the file, not hidden behind a black box.
  • Save incrementally (append-only, signature-friendly) or rewrite the document fresh.

Render

A few quick tastes:

// Read text
val text = doc.pages[0].extractText()

// Open a password-protected PDF
val doc = PdfDocument.open(bytes, password = "secret".encodeToByteArray())
require(doc.isAuthenticated)

// Fill a form field and save (append-only)
val out = doc.edit()
    .apply { setTextFieldValue(doc.formField("ApplicantName")!!, "Jane Doe") }
    .saveIncremental()

// Watermark every page
val stamped = doc.edit().apply {
    doc.pages.forEach { page ->
        stampPage(page) {
            setFillRgb(0.8, 0.1, 0.1)
            text(StandardFont.HelveticaBold, 48.0, x = 120.0, y = 400.0, "DRAFT")
        }
    }
}.saveIncremental()

See the full documentation for the complete API and worked examples.

Putting pixels on screen

The engine is headless. Showing a PDF is the one job that needs a platform, so it lives in separate, optional artifacts. Pick the one that matches how you draw.

Compose Multiplatform: kitepdf-compose

A PDF page is just another composable. PdfView draws into a Compose DrawScope, so it scrolls, zooms and composes with your UI like anything else on a Canvas. No AndroidView, no UIKitView, no embedded web view.

// commonMain of a Compose Multiplatform app
val state = rememberPdfViewState(doc)

PdfView(
    state = state,
    layout = PdfLayout.Paged(Orientation.Horizontal),     // or Continuous / SinglePage
    zoomSpec = PdfZoomSpec(maxZoom = 6f),                  // pinch, double-tap, pan
    renderSpec = PdfRenderSpec.Rasterized(),              // or Vectorized()
    overlay = { PdfNavigationControls(it, Modifier.align(Alignment.BottomCenter)) },
)

// The same state drives widgets anywhere in your UI
PdfPageIndicator(state)        // "3 / 12"
PdfThumbnailStrip(state)       // tappable thumbnails

See the viewer guide for layouts, zoom, render modes, navigation and export.

Headless rasterizers: kitepdf-native-renderer and kitepdf-skia

For servers, CI, thumbnails, or non-Compose UIs, render a page straight to image bytes.

// kitepdf-native-renderer, JVM (AWT). Also CoreGraphics (Apple), android.graphics, Canvas2D (JS).
val png = AwtPdfRasterizer.encodeToPng(doc.pages[0], scale = 2.0)

// kitepdf-skia, one common API via Skiko (JVM, Android, Apple, Linux, web).
val png = PdfPageRasterizer.encodeToPng(doc.pages[0], scale = 2.0)

See the rendering guide to choose between them.

Platform support

The engine runs on every target Kotlin supports, with no per-platform code. Only the rendering bindings are limited by what their underlying toolkit ships.

Target Engine (kitepdf) Compose (-compose) Native renderer (-native-renderer) Skia (-skia)
Android βœ“ βœ“ βœ“ βœ“
iOS (arm64 / simulator) βœ“ βœ“ βœ“ βœ“
JVM (Desktop / Server) βœ“ βœ“ βœ“ βœ“
macOS (Apple Silicon) βœ“ βœ“ βœ“ βœ“
JS (Browser / Node) βœ“ βœ“ βœ“ βœ“
wasmJs βœ“ βœ“ – βœ“
tvOS βœ“ – βœ“ βœ“
watchOS βœ“ – – –
Linux (x64 / arm64) βœ“ – – βœ“
Windows (mingwX64) βœ“ – – –
Android Native, WASI βœ“ – – –

Full details and the reasons behind each gap are in the platform support guide.

Status

KitePDF is pre-1.0 and actively developed.

Working today: reading and text extraction, metadata, outlines, annotations, form fields, encrypted documents, the Compose viewer (continuous / paged scroll, pinch zoom), headless rendering, editing and saving, true redaction, and building PDFs from scratch.

On the way: digital signatures, the JBIG2 and JPEG 2000 image codecs, less common form widgets, and advanced colour management.

If a PDF renders incorrectly, please open an issue with the file attached. The project ships a pixel-diff harness against MuPDF, and every fix lands as a regression test.

Sample app

The sample/ module is a runnable Compose Multiplatform app that opens a PDF and exercises the API across Android, iOS and Desktop. Use it as a copy-paste starting point.

License

Apache License 2.0. A few encoding tables and font data are derived from MuPDF and keep their AGPL-3.0 headers in those specific source files; see the comments there.

Acknowledgements

Architectural reference: MuPDF by Artifex Software. Standard 14 font metrics derive from URW++ AFM files. Thanks to everyone who published the PDF specification and the tools that made this possible.

About

PDF library written in pure Kotlin (reading, viewing, rendering, editing and creating PDFs) for Android, iOS, Web and JVM Desktop. No JNI or expect/actuals, only 100% Kotlin. Supports Compose multiplatform.

Topics

Resources

License

Stars

23 stars

Watchers

1 watching

Forks

Contributors

Languages