Docs

Documentation

PDFCora is PDFium, pinned to a known revision, wrapped so that Swift, Kotlin, TypeScript and Java each get the API their developers expect — with the same types, the same method names and the same core error cases, on a phone, in a browser and on a server.

Modules

Take the core. Add the rest when you need it.

The core never depends on an AI runtime — a build check enforces it on iOS, Android and the web — so an app that only renders PDFs ships no model code. On iOS and Android the UI is its own package; on the web the viewer component is part of the core package. On a server there is no UI to take.

Modules by SDK
ModuleiOSAndroidWebNodeJavaWhat it gives you
CorePDFCoracom.pdfcora:pdfcora@pdfcora/web@pdfcora/nodecom.pdfcora:pdfcora-javaRender, text, search, forms, annotations, signatures, redaction, tags, save; on Java, also opens DOCX by converting it to PDF
AIPDFCoraAIcom.pdfcora:pdfcora-ai@pdfcora/web-ai@pdfcora/web-ai—Answers, summaries, redaction and form proposals, alt text, remediation, diff
UIPDFCoraUIcom.pdfcora:pdfcora-uiPdfViewerElement—PdfViewer state onlyA viewer component with a configurable toolbar, theme and review bar
Agents——registerWebMcpTools in @pdfcora/web-aipdfcora-mcp—WebMCP registration for a browser’s agent; an MCP server over stdio for agents such as Claude Desktop

Requirements: iOS 15+ with Swift 5.9 · Android minSdk 23 with all four ABIs · Web and Node as ESM with the .wasm inside the package, Node 18+ · Java 17+ on macOS and Linux, x86-64 and arm64, with the native engine in the jar.

Node is the web package with file and PNG helpers on top, not a second implementation, so anything written against the web SDK runs there unchanged. Java is its own implementation over the native engine: it carries the document API, annotations, form filling, redaction, tagging, signing, DOCX opening and the viewer state layer — there is no component to draw, and no AI package yet.

Threading

Thread-safe, on every platform.

iOS

One engine thread, shared

Each document is an actor, and every document’s actor runs on the same engine thread, because PDFium’s state is global. Calls on any document serialise. async/await throughout.

Android

One engine thread, shared

Every document’s suspending functions run on one engine thread for the whole process, so calls on any document serialise. Your coroutine scope stays yours.

Web

A promise chain, or a Worker

In-process calls run on the JavaScript thread, and each document queues them behind one chain. PdfWorker runs the whole SDK in a Worker behind the same API, so a stalled render can be terminated without taking the page down.

Node & Java

Promise chain, or one engine thread

Node inherits the web SDK’s promise chain. On Java every call on every document runs on one engine thread for the whole process, so a request pool can share documents safely: the calls serialise, they do not run in parallel. DOCX conversion runs on the calling thread.

iOS & Android

A watchdog for the calls that never return

A malformed page can stall inside the engine where no cancellation reaches. One engine thread serves every document, so a watchdog makes the calls queued behind a stuck call fail fast with engineUnresponsive — on every document, not only the stuck one — rather than piling up behind it. They succeed again if the stuck call ever returns. Java has no watchdog yet. On the web, PdfWorker isolates a hang in its Worker.

Errors

A closed set of errors, the same core everywhere.

No native crashes, no untyped throws. The first seven cases below exist in Swift, Kotlin, TypeScript and Java, each in its language’s spelling (wrongPassword, WrongPassword, WRONG_PASSWORD), so error handling ports between platforms. Java has only those seven. iOS, Android and the web add a few more, such as unsupportedSecurity, cancelled, engine and engineUnresponsive.

Error cases
CaseWhen it happens
passwordRequiredThe document is encrypted and no password was given
wrongPasswordA password was given and the document refused it
corruptThe file, or one page of it, cannot be parsed
pageOutOfRangeA page index outside the document
closedThe document was used after close() — raised instead of touching freed memory
invalidOperationThe call cannot apply: tagging a tagged document, a field that is read-only, alt text on a non-figure
saveFailedWriting the document failed
engineUnresponsiveNot on Java. The engine is stuck on an earlier call, so this call fails instead of waiting: through the watchdog on iOS and Android, or when PdfWorker terminates a hung Worker on the web

Nothing is silently dropped

Every sealed hierarchy — annotation kinds, form field kinds, structure types — has an unknown case carrying the raw value. An annotation PDFCora does not model is surfaced, not hidden.

Coordinates

PDF space in, view space out — explicitly.

PdfRect is always PDF space, with the origin bottom-left, normalised so left <= right and bottom <= top. The viewer’s transform() converts in both directions with toView, toViewRect and toPdf. There is no hidden Y-flip anywhere in the API.

TypeScript
Mapping a search hit onto the screen
const hits = await page.text.search('invoice')
const box  = await page.text.boundingBox(hits[0].start)
const view = viewer.transform().toViewRect(box)  // pixels, origin top-left

Viewer

A viewer you can drop in, or drive yourself.

The state layer — page, zoom, scroll, tool, selection — is a plain value you own, so it transfers between platforms. The UI package renders it; you can also render it yourself and keep only the logic.

Cross-platform state

Zoom, scroll and page travel as data: a session started on a phone resumes in the browser at the same place.

Configurable toolbar

Pick the items and their order, or start from the default and viewing presets. Custom buttons take an icon and an action.

Themeable

Tokens with the same meaning on all three platforms — accent, backgrounds, corner radius, icon size, toolbar height.

Review bar

Marks over the page with accept, reject and apply, used by every AI proposal flow. Nothing is written until someone accepts.

Reference

API documentation, per SDK.

Generated from the source by each language’s own tool. All SDKs on one page →

Each package has its own reference. On the web the viewer component is part of the core package and AI is a separate one, which Node uses unchanged; the Java SDK has no AI package yet. The iOS references load their content at runtime and need the site served over http.

Limits

What it does not do yet.

  • The raw PDFium escape hatch exists on iOS only.
  • The Java SDK does not yet read outlines, links or attachments, report page rotation, return search rectangles, or save incrementally except when appending a signature or a structure tree.
  • Only the Java SDK opens Word files, and only DOCX — not .doc, XLSX or PPTX. The PDF matches Word’s only as far as the server has the document’s fonts; the Java README lists the packages to install.
  • Java signs as PAdES B-B. A timestamp, which would make it B-T, needs a call to an authority and is not wired up there yet; iOS, Android and the Web take a timestampUrl.
  • Cancelling a render mid-call is not possible on any platform; iOS cancels between progressive steps.
  • Recovering from an engine hang fully is Web-only, through PdfWorker. On iOS and Android the watchdog makes every document’s calls fail fast with engineUnresponsive until the stuck call returns; Java has no watchdog, so a hang blocks every document. PdfWorker is re-exported on Node but needs a browser Worker, which Node does not have.
  • Remediation writes tags from rules, not a model: tables become paragraphs, vector charts become artifacts, reading order is content order, and annotations and links are not tagged.
  • There is no OCR, so a scanned page has nothing to tag or search.
  • On an encrypted document, the features that append to the file — signing, tagging, alternate text, creating form fields and incremental saves — are refused, because appended strings would have to be encrypted with the document’s own key. Reading, rendering, filling existing fields, annotating, redacting and saving all work.