Skip to content

Releases: ConaMobileDev/PdfKmp

v1.2.0

07 Jun 16:41

Choose a tag to compare

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, and pdfkmp-markdown now ship a wasmJs target — 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 KotlinPdfFont.Custom works 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 via markdown(text, theme). Android + iOS + JVM + Wasm, Compose-free. First release on Maven Central.

✍️ Text engine

  • TextAlign.Justify now actually justifies (inter-word slack, both text and richText), with kashida elongation for Arabic (kashidaJustify = true).
  • Right-to-left supportTextDirection { 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 = true by default) and colSpan / rowSpan cell merging.
  • Recursive column slicing, keepTogether { }, newspaper-style columns(count, gap), and grid(columns).
  • Over-wide tables shrink to fit; oversized images scale down instead of overflowing; PageSize.landscape/.portrait + PageContext.isFirst/isLast/isEven/isOdd for 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.
  • ChartsbarChart, 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, page overlay, 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 Highlight annotations.
  • Password-protected PDFs open via password = ... with a typed PdfViewerError (Desktop + iOS).
  • Two-page book layout (pageLayout = PdfPageLayout.TwoPageBook) and an adaptive cache for 200+-page documents.

📚 Documentation

🐛 Fixed

  • iOS outline entries now reference page numbers — CGPDFContextSetOutline crashed on named destinations / empty Children whenever bookmark() was used.
  • TextAlign.Justify no 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

30 May 06:29

Choose a tag to compare

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; Marquee scrolls it horizontally when it overflows. Exposed as a titleOverflow parameter on KmpPdfViewer, PdfViewerTopBar, PdfViewerTopBarClassicIos, and PdfViewerTopBarMinimalMono; applies on Android / Desktop too.

All three modules — pdfkmp, pdfkmp-compose-resources, pdfkmp-viewer — ship together at 1.1.1.

v1.1.0

29 May 20:26

Choose a tag to compare

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 jvm target on all three modulespdfkmp-jvm, pdfkmp-viewer-jvm, pdfkmp-compose-resources-jvm. KMP consumers with a jvm() 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 jvm source 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 PDAnnotationLink annotations.
  • pdfkmp-viewer on DesktopKmpPdfViewer + KmpPdfLauncher render through PDFBox's PDFRenderer in 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 renderDensity defaults to (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") // optional

Full changelog: v1.0.2...v1.1.0 (v1.0.2...v1.1.0)

PdfKmp 1.0.2

25 May 10:44

Choose a tag to compare

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
    cornerRadius larger than half of the measured rect (e.g. pill badges
    with cornerRadius = 100.dp on a 40 pt-wide box) now render correctly
    on physical iPhones / iPads. Previous builds passed the radius straight
    to CGPathAddRoundedRect, which asserts hard on hardware.
  • pdfkmp-viewer learns four new source shapes. Remote URLs (with
    headers and timeout), local file paths, Android content URIs, and
    bundled assets are first-class PdfSource variants — 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 = false turns the viewer into a "poor viewer" surface
    — just pages, indicator, and gestures — for hosts that wire their own
    chrome.

Fixed — pdfkmp

  • iOS CGPathAddRoundedRect assertion crash on real devices. The
    iOS canvas now clamps the radius to min(width, height) / 2 across
    drawRoundedRect / strokeRoundedRect / clipRoundedRect, matching
    Android's Canvas.drawRoundRect and the existing per-corner
    buildRoundedRectPath clamping. Cross-platform output stays
    bit-identical; "pill" radii collapse to a fully-rounded ellipse
    instead of crashing.

Added — pdfkmp-viewer

  • showTopBar: Boolean master switch on KmpPdfViewer and
    KmpPdfLauncher.open. false hides both the topbar and the morphed
    search bar entirely. The individual showSearch / showShare /
    showDownload toggles 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 PdfSource shape. In addition to the existing Bytes /
    Document variants, 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-only content:// resolution through
    ContentResolver. Throws on iOS.
  • Asset(path) — packaged with the app, resolved through
    Context.assets on Android and NSBundle on 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

05 May 15:49

Choose a tag to compare

Fixes a cross-axis alignment regression where text inside an aligned parent could overflow past the parent's right edge.

v1.0.0

03 May 21:08

Choose a tag to compare

First stable release. Public API committed under semver.

Maven coordinates

  • io.github.conamobiledev:pdfkmp:1.0.0
  • io.github.conamobiledev:pdfkmp-compose-resources:1.0.0
  • io.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-viewer caused by
    FileProvider authority collision with the host app's manifest.
  • API: Full configuration parity between KmpPdfViewer and
    KmpPdfLauncher.open (showSearch, showShare, showDownload,
    showPageIndicator, zoomEnabled, doubleTapToZoom,
    textSelectable, hyperlinksEnabled, backLabel, renderDensity,
    maxZoom).

v0.2.0-rc03

03 May 19:44

Choose a tag to compare

KmpPdfLauncher improvements

v0.2.0-rc02

03 May 18:30

Choose a tag to compare

  • Android: fix pdfkmp-viewer share sheet crash with IllegalArgumentException: 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 dedicated PdfKmpViewerFileProvider subclass so the merge key is unique.

0.2.0-rc01

02 May 11:27

Choose a tag to compare

🎉 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

01 May 12:15

Choose a tag to compare

v0.2.0-alpha01 Pre-release
Pre-release

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