Releases: ConaMobileDev/PdfKmp
v1.2.0
PdfKmp 1.2.0 is the largest release to date: a new Web (Kotlin/Wasm) target, a new pdfkmp-markdown module, and a wave of ~40 features across the text engine, pagination, graphics, navigation, document security, and the viewer. The public API (pdf { … }, pdfAsync { … }, save(...), toByteArray()) is unchanged — everything below is additive.
✨ Highlights
🌐 New: Web (Kotlin/Wasm) target
pdfkmp,pdfkmp-compose-resources, andpdfkmp-markdownnow ship awasmJstarget — PdfKmp runs in the browser. Browsers expose no PDF engine to Wasm, so a new pure-Kotlin PDF 1.7 writer backs the target: vector text, shapes, gradients, QR/barcodes/charts, freeDraw, rotation/opacity, JPEG + PNG embedding, links, outline, and the info dictionary.- TrueType subsetting + embedding in pure Kotlin —
PdfFont.Customworks on the web, and non-WinAnsi text (e.g. Cyrillic) falls back to a bundled-Inter subset out of the box. Content streams are Flate-compressed by a pure-Kotlin DEFLATE. PdfDocument.openInNewTab()hands the bytes to the browser's own viewer;save(...)becomes a download.
📦 New module: pdfkmp-markdown
io.github.conamobiledev:pdfkmp-markdown— renders a CommonMark-lite subset (headings, emphasis, code, lists, pipe tables, blockquotes, links, rules) straight into the PdfKmp DSL viamarkdown(text, theme). Android + iOS + JVM + Wasm, Compose-free. First release on Maven Central.
✍️ Text engine
TextAlign.Justifynow actually justifies (inter-word slack, bothtextandrichText), with kashida elongation for Arabic (kashidaJustify = true).- Right-to-left support —
TextDirection { Auto, Ltr, Rtl }with automatic detection; the JVM backend runs its own bidi reorder + Arabic contextual shaping. maxLines+TextOverflow { Clip, Ellipsis }, soft hyphens (U+00AD), automatic hyphenation (hyphenation = Hyphenators.EnUs, Liang's algorithm), and mid-word breaking for over-wide words.- Superscript / subscript rich-text spans; orphan/widow control (
minLinesBeforeBreak/minLinesAfterBreak).
📄 Layout & pagination
- Table row slicing with repeating headers (
repeatHeader = trueby default) andcolSpan/rowSpancell merging. - Recursive column slicing,
keepTogether { }, newspaper-stylecolumns(count, gap), andgrid(columns). - Over-wide tables shrink to fit; oversized images scale down instead of overflowing;
PageSize.landscape/.portrait+PageContext.isFirst/isLast/isEven/isOddfor book-style chrome.
🎨 Graphics & content
- QR codes (full ISO 18004, versions 1–40), Code 128, EAN-13 / UPC-A, and Data Matrix (ECC 200) — all pure-Kotlin, pure vector.
- Charts —
barChart,lineChart(multi-series),pieChart,donutChart,stackedBarChart, with legends, value captions, and grid lines. freeDraw { path { … } }, drop shadows, dashed/dotted borders, rotation & opacity on containers.- Full SVG file support —
<rect>,<circle>,<ellipse>,<line>,<polyline>,<polygon>, nested<g>transforms, inline styles, named colors, viewBox offsets.
🧭 Navigation & document features
- Bookmarks/outline, internal links (
anchor+linkToAnchor), and an automatic table of contents with resolved page numbers — on all three native platforms; Android gains metadata, clickable links, and the outline via a pure-Kotlin incremental-update post-processor. - Encryption (AES-256 on JVM), file attachments, AcroForm fields (
textField/checkBox), PDF/A best-effort + tagged-PDF basics, and digital signing (PdfSigner, JVM). PdfTools(JVM) —merge,split,extractPages, watermarks, pageoverlay, plus a Factur-X/ZUGFeRD MINIMUM-profile invoice helper.
🖥️ Viewer (pdfkmp-viewer)
- Print, dark mode (
invertColors), search in external PDFs (iOS + Desktop), and clipboard support. - Highlight annotations with drag-to-highlight — and on Desktop they can be written into the PDF as real
Highlightannotations. - Password-protected PDFs open via
password = ...with a typedPdfViewerError(Desktop + iOS). - Two-page book layout (
pageLayout = PdfPageLayout.TwoPageBook) and an adaptive cache for 200+-page documents.
📚 Documentation
- New documentation site — 20 hand-written guides + the aggregated Dokka API reference: https://conamobiledev.github.io/PdfKmp/
🐛 Fixed
- iOS outline entries now reference page numbers —
CGPDFContextSetOutlinecrashed on named destinations / emptyChildrenwheneverbookmark()was used. TextAlign.Justifyno longer silently falls back to start alignment.- An adversarial-review pass over the wave fixed 11 edge cases (TOC inside
columns { }, sliced-table headers, rich-text justification precision, Android post-processor xref offsets, and more — see the CHANGELOG).
All four modules ship together at 1.2.0: pdfkmp, pdfkmp-compose-resources, pdfkmp-viewer, and pdfkmp-markdown.
Full details in the CHANGELOG.
1.1.1
pdfkmp 1.1.1
Patch release for pdfkmp-viewer.
Fixed
- iOS topbar action icons no longer shrink or disappear on long titles. The Classic iOS topbar centered the filename in an unweighted slot, so a long title consumed the whole bar and collapsed the side columns to zero — the share icon vanished and the rest shrank. The bar now uses a custom three-slot layout that measures the trailing icons (and back affordance) at their natural size first and gives the title only the symmetric gutter that remains, so the icons are inviolable and the title stays optically centered.
Added
PdfTopBarTitleOverflow { Ellipsis, Marquee }— choose how a long topbar title behaves.Ellipsis(default) truncates with…like Android;Marqueescrolls it horizontally when it overflows. Exposed as atitleOverflowparameter onKmpPdfViewer,PdfViewerTopBar,PdfViewerTopBarClassicIos, andPdfViewerTopBarMinimalMono; applies on Android / Desktop too.
All three modules — pdfkmp, pdfkmp-compose-resources, pdfkmp-viewer — ship together at 1.1.1.
v1.1.0
PdfKmp 1.1.0 adds Desktop (JVM) support — macOS, Windows, and Linux — alongside the existing Android and iOS targets. The public API (pdf { … }, pdfAsync { … }, save(...), toByteArray()) is unchanged and produces identical vector output on every platform. Desktop is purely additive.
✨ Highlights
- New
jvmtarget on all three modules —pdfkmp-jvm,pdfkmp-viewer-jvm,pdfkmp-compose-resources-jvm. KMP consumers with ajvm()target resolve the right variant automatically. - Desktop backend on Apache PDFBox — pure-Java, no native libraries. Renders the full DSL as real vector output: subset-embedded TrueType text, shapes, rounded rects, dashed/dotted lines, clipping, axial & radial gradients, and images. Scoped to the
jvmsource set only; Android and iOS keep their native backends. - Desktop beats Android in two places — the document info dictionary (title/author/subject/keywords) is written, and hyperlinks become real clickable
PDAnnotationLinkannotations. pdfkmp-vieweron Desktop —KmpPdfViewer+KmpPdfLauncherrender through PDFBox'sPDFRendererin a Compose-for-Desktop window; links open in the default browser.- Desktop-native zoom & actions — macOS trackpad pinch (no launch flag), Ctrl/⌘ + mouse-wheel anchored under the cursor, double-click toggle, optional +/− pill, native Save As dialog, and share via the OS default handler.
- Sharper text on Desktop — baseline
renderDensitydefaults to 3× (vs 2× on mobile), and zoom re-render tracks the full zoom factor (≤ ~720 DPI). :sample-desktop— a Compose-for-Desktop sample app. Run with./gradlew :sample-desktop:run.
⚠️ Compatibility
Every existing Android / iOS API, signature, and behaviour is preserved. The one non-additive change: the iosX64 (Intel-Mac simulator) artifact is dropped — Compose Multiplatform 1.11.0 removed it and Intel Macs are EOL. Build for the Apple-Silicon simulator (iosSimulatorArm64) instead; no source changes needed.
Also bumped: Compose Multiplatform 1.10.3 → 1.11.0, kotlinx.coroutines 1.10.2 → 1.11.0.
📦 Install
implementation("io.github.conamobiledev:pdfkmp:1.1.0")
implementation("io.github.conamobiledev:pdfkmp-viewer:1.1.0") // optional
implementation("io.github.conamobiledev:pdfkmp-compose-resources:1.1.0") // optionalFull changelog: v1.0.2...v1.1.0 (v1.0.2...v1.1.0)
PdfKmp 1.0.2
Full Changelog: v1.0.1...v1.0.2
PdfKmp 1.0.2 ships a hardware-level iOS crash fix plus a meaningful upgrade
to the optional pdfkmp-viewer module — bigger source surface, an opt-in
in-memory page cache, and a master switch to hide the topbar entirely.
Highlights
- iOS rounded-rect crash on real devices is fixed. Containers with a
cornerRadiuslarger than half of the measured rect (e.g. pill badges
withcornerRadius = 100.dpon a 40 pt-wide box) now render correctly
on physical iPhones / iPads. Previous builds passed the radius straight
toCGPathAddRoundedRect, which asserts hard on hardware. pdfkmp-viewerlearns four new source shapes. Remote URLs (with
headers and timeout), local file paths, Android content URIs, and
bundled assets are first-classPdfSourcevariants — no more
stringly-typed URIs.- Memory-budgeted page bitmap cache. Pages rendered while scrolling
stay warm in a per-document LRU so scroll-back is an instant memory
hit instead of a fresh rasterisation. Hard RAM cap on both platforms. showTopBar = falseturns the viewer into a "poor viewer" surface
— just pages, indicator, and gestures — for hosts that wire their own
chrome.
Fixed — pdfkmp
- iOS
CGPathAddRoundedRectassertion crash on real devices. The
iOS canvas now clamps the radius tomin(width, height) / 2across
drawRoundedRect/strokeRoundedRect/clipRoundedRect, matching
Android'sCanvas.drawRoundRectand the existing per-corner
buildRoundedRectPathclamping. Cross-platform output stays
bit-identical; "pill" radii collapse to a fully-rounded ellipse
instead of crashing.
Added — pdfkmp-viewer
showTopBar: Booleanmaster switch onKmpPdfViewerand
KmpPdfLauncher.open.falsehides both the topbar and the morphed
search bar entirely. The individualshowSearch/showShare/
showDownloadtoggles become no-ops in that mode — host apps that
hide the chrome are expected to wire their own navigation and share
affordances.PdfPageCacheStrategy+ memory-budgeted bitmap LRU. Pages
rendered while scrolling are retained in a per-document bitmap cache.
Three strategies:Auto(default) — modest prefetch window, RAM-bounded.Window(pagesBefore, pagesAfter)— explicit warm window.All— try to keep every page warm, still RAM-bounded.
The cache is always capped to a per-platform memory budget —
Android uses 25 % of Runtime.maxMemory(), iOS caps at 200 MB — and
evicts the oldest entries first, so over-eager windows can never
crash the process. Wider prefetch is best-effort: the cache keeps as
much as fits.
- Unified
PdfSourceshape. In addition to the existingBytes/
Documentvariants, the sealed type now carries: FilePath(path)— local filesystem (bare absolute paths or
file://URLs).Remote(url, headers, timeoutMillis)— HTTP(S) fetch. Headers and
timeout honoured on Android; iOS falls back to
NSData(contentsOfURL:)and ignores both (documented).ContentUri(uri)— Android-onlycontent://resolution through
ContentResolver. Throws on iOS.Asset(path)— packaged with the app, resolved through
Context.assetson Android andNSBundleon iOS.PdfSource.auto(uri)— best-effort string-to-shape detector for
callers that only hold an opaque URI (notification payload, deep
link, picker result).
Deprecated — pdfkmp-viewer
KmpPdfViewer(uri: String, …)— replaced by
KmpPdfViewer(source = PdfSource.auto(uri), …)or the matching
explicit variant. String inputs hide which transport is in use and
can't carry per-shape configuration like HTTP headers. The
through the same async loader.
v1.0.1
Fixes a cross-axis alignment regression where text inside an aligned parent could overflow past the parent's right edge.
v1.0.0
First stable release. Public API committed under semver.
Maven coordinates
io.github.conamobiledev:pdfkmp:1.0.0io.github.conamobiledev:pdfkmp-compose-resources:1.0.0io.github.conamobiledev:pdfkmp-viewer:1.0.0
Requirements
- Kotlin 2.3.21+
- AGP 9.2.0+
- JVM 17+
- Android minSdk 26+
- Compose Multiplatform 1.10.3+ (only for
pdfkmp-viewer)
Since 0.2.0-rc01
- Fix: Android share-sheet crash in
pdfkmp-viewercaused by
FileProvider authority collision with the host app's manifest. - API: Full configuration parity between
KmpPdfViewerand
KmpPdfLauncher.open(showSearch,showShare,showDownload,
showPageIndicator,zoomEnabled,doubleTapToZoom,
textSelectable,hyperlinksEnabled,backLabel,renderDensity,
maxZoom).
v0.2.0-rc03
KmpPdfLauncher improvements
v0.2.0-rc02
- Android: fix
pdfkmp-viewershare sheet crash withIllegalArgumentException: Couldn't find meta-data for provider with authority …pdfkmp.viewer.fileprovider. Manifest merger
was collapsing the library's FileProvider into the host app's declaration; we now use a dedicatedPdfKmpViewerFileProvidersubclass so the merge key is unique.
0.2.0-rc01
🎉 PdfKmp 0.2.0-rc01 — pdfkmp-viewer ships
The headline of this release: a brand-new Compose Multiplatform PDF viewer screen. Drop one composable into your nav graph (or fire one imperative call from any scope) and you
get a complete reader with topbar, search, share, save-to-Downloads, hyperlinks, gestures, and a page indicator — Android and iOS, same code.
RC status — public API for :pdfkmp-viewer is settled. We're cutting an RC instead of a final to give the community one round of feedback before 0.2.0. Please file issues if
anything feels off.
✨ Highlights
One-call viewer screen
KmpPdfViewer(
uri = "https://example.com/invoice.pdf",
title = "Invoice 2026 Q1",
onBack = { navController.popBackStack() },
)
Four input shapes (PdfSource / PdfDocument / ByteArray / URI string) and 17 parameters covering visibility toggles, behaviour flags, and theming. Lower-level building blocks
(PdfViewer, PdfViewerTopBar, PdfSearchBar, …) stay public for advanced layouts.
Imperative launcher
For when you can't reach a @composable scope — click handlers, LaunchedEffect, suspend functions, notification taps:
KmpPdfLauncher.open(
document = pdfAsync { /* … */ },
title = "Invoice",
)
Hosts the same KmpPdfViewer inside a library-shipped Activity (Android) / ComposeUIViewController (iOS). Document payload survives the hop with text-selection / hyperlink
metadata intact.
Topbar that follows the platform
- Android — Minimal Mono variant: 38×38 chips, primary black download chip, white background, hairline divider.
v0.2.0-alpha01
Highlights
- image(bytes = ...) now subsamples the source bitmap to roughly the rendered size at ~200 DPI by default — smartphone photos no longer blow up the heap on dense documents. Pass
allowDownScale = false for archival or print workflows. - PdfKmp.VERSION is now generated from gradle.properties at build time, ending the drift between the runtime constant and the published version.
Install
implementation("io.github.conamobiledev:pdfkmp:0.2.0-alpha01")
implementation("io.github.conamobiledev:pdfkmp-compose-resources:0.2.0-alpha01") // optional
Both modules ship together — never mix versions.
Full Changelog: v0.1.0-alpha03...v0.2.0-alpha01