Skip to content

PDF-SDK 2.4.1 is out.See what’s new

PDF-SDK
Skip to Content
Docs

Android

A complete editor as a Jetpack Compose screen, PdfEditorScreen, or as a classic View, PdfEditorView. Hand it a file path, get the edited bytes back.

Every type and option: Android API reference.

1. Add the dependency

The SDK is a Maven artifact, com.pdfsdk:pdfeditor. It comes with your license as a Maven repository — either a hosted registry or a zip you unpack into your project. Add the repository and one dependency:

// settings.gradle.kts
dependencyResolutionManagement {
    repositories {
        google(); mavenCentral()
        maven { url = uri("${rootDir}/vendor/pdfsdk/maven") }   // the unzipped bundle, or your registry URL
    }
}
// app/build.gradle.kts
dependencies {
    implementation("com.pdfsdk:pdfeditor:2.4.1")
    implementation("androidx.activity:activity-compose:1.9.3")
}

Compose comes across transitively. The native engine is included for arm64-v8a and is aligned for 16 KB memory pages (required by Google Play for apps that target Android 15 or later). Minimum SDK 26.

Prefer the repository over a loose .aar in libs/: a raw AAR has no POM, so its dependencies do not come with it.

2. Set your license key

In Application.onCreate — it runs before any activity, in every process of your app. The key must list your app's package name (application ID).

import android.app.Application
import com.pdfsdk.editor.PdfEditorSdk

class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()
        PdfEditorSdk.setLicenseKey("PE1.eyJ2Ijox…")
    }
}
<!-- AndroidManifest.xml -->
<application android:name=".MyApp" … >

The SDK reads the package name from where Android installed the app (the process name only for unusual installs, such as a system image), so it isn't taken from anything the app can change. A build variant with an applicationIdSuffix (such as .debug) is a different package — ask for it to be added, or for com.acme.app.* alongside com.acme.app (the wildcard doesn't cover the base package itself). See License keys.

3. Show the editor — Compose

import com.pdfsdk.editor.PdfEditorScreen

setContent {
    PdfEditorScreen(
        pdfPath = file.absolutePath,          // a WRITABLE path
        onSave = { bytes -> file.writeBytes(bytes) },
        onClose = { finish() },
    )
}

Done hands the document to onSave and the editor stays open; onClose runs when the user leaves (back, or the close button). If Done should also exit, close the editor from onSave.

Declare android:configChanges="orientation|screenSize|screenLayout|smallestScreenSize|density|uiMode|keyboardHidden|keyboard|navigation" on the activity that hosts the editor. Without it, rotating the device or switching dark mode recreates the activity, and unsaved edits are lost.

Or as a View (Views, Fragments, Java)

class EditorActivity : ComponentActivity() {     // not android.app.Activity
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(PdfEditorView(this).apply {
            pdfPath = file.absolutePath
            onSave = PdfBytesListener { bytes -> file.writeBytes(bytes) }
            onClose = PdfEditorCloseListener { finish() }
        })
    }
}

PdfEditorView hosts Compose internally, so its window needs the lifecycle and saved-state owners that ComponentActivity, AppCompatActivity and Fragment provide. In a Fragment, set ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed.

Options

ParameterTypeWhat it does
pdfPathStringthe PDF to open — a writable file path
passwordString?for an encrypted PDF
initialModePdfEditorMode?VIEWER, EDIT, FORMS, ANNOTATE, FILL_AND_SIGN, INSERT, DRAW
initialToolPdfEditorTool?a tool to pre-arm on open
configurationPdfEditorConfigurationmodes, tools, menu items, colour scheme, tool styles, zoom — see Customize
onSave(ByteArray) -> Unitthe edited PDF
onClose() -> Unitthe user left the editor (back, or the close button) — not called on Done
onSharePdf, onPrint, onFlattenAndShare((ByteArray) -> Unit)?intercept the export actions
onDigitallySigned((ByteArray) -> Unit)?the signed PDF after an e-signature
onUnsavedChangesChanged((Boolean) -> Unit)?tracks unsaved edits

Customize the editor

Pass a PdfEditorConfiguration as configuration (or set PdfEditorView.configuration, before pdfPath) to choose which modes and tools the editor offers, hide ⋮-menu items, pin light or dark, and set the tools' first-use styles. Leave it out and you get the editor as it ships. These options narrow or restyle; the sections below also rename things and add your own buttons and fonts. Out-of-range values are clamped. It's read once, when the editor opens.

val config = PdfEditorConfiguration().apply {
    modes = listOf(PdfEditorMode.VIEWER, PdfEditorMode.ANNOTATE, PdfEditorMode.FILL_AND_SIGN)
    hiddenTools = setOf(PdfEditorTool.STAMP, PdfEditorTool.LINK)
    hiddenMenuItems = setOf(PdfEditorMenuItem.PRINT, PdfEditorMenuItem.RESET_TO_ORIGINAL)
    colorScheme = PdfEditorColorScheme.DARK
    toolDefaults.highlight = PdfEditorMarkupStyle(0xFF34C759.toInt(), opacity = 0.4f)
    minZoom = 1f; maxZoom = 2f
}
PdfEditorScreen(pdfPath = path, configuration = config, onSave = ::save, onClose = ::finish)
OptionDefaultNotes
modesevery modeYour order; a mode whose tools are all hidden is dropped (not VIEWER). With one mode the header shows its name, no picker. initialMode falls back to the first listed.
hiddenToolsnoneHidden in every mode that carries the tool. A hidden initialTool is not armed.
hiddenMenuItemsnoneSEARCH, MANAGE_PAGES, SHARE_PDF, PRINT, FLATTEN_AND_SHARE, FLATTEN_DOCUMENT, RESET_FORM, RESET_TO_ORIGINAL. With all hidden, the ⋮ button goes.
colorSchemeSYSTEMLIGHT or DARK: chrome, icons and page surround.
toolDefaultswarm yellow, black penFirst-use styles (ARGB colours) for highlight, underline, strikeout, squiggly and ink. A user's own later choice is remembered and wins.
minZoom / maxZoom0.5f / 3fClamped to 0.5–1 and 1–3. 1f/1f disables pinch-zoom.
haptics, showsUsageHintstrueTurn off haptic feedback, or the one-time "drag across text…" hints.
signaturePlacementBOTTOM_RIGHTWhere the e-Sign tab's Placement picker starts: BOTTOM_LEFT, TOP_RIGHT, TOP_LEFT or INVISIBLE. The user can still pick another before signing.

Names, icons, order and your own buttons

config.modeTitles = mapOf(PdfEditorMode.FILL_AND_SIGN to "Sign")
config.toolTitles = mapOf(PdfEditorTool.SIGNATURE to "Sign here")
config.toolIcons = mapOf(PdfEditorTool.HIGHLIGHT to R.drawable.ic_marker)
config.customTools = mapOf(PdfEditorMode.FILL_AND_SIGN to listOf(
    PdfEditorCustomAction("approve", "Approve", R.drawable.ic_approve) { ctx ->
        ctx.currentPdf { pdf -> pdf?.let { approve(it, page = ctx.pageIndex) } }
    }))
config.toolOrder = mapOf(PdfEditorMode.FILL_AND_SIGN to listOf(
    PdfEditorToolbarItem.Custom("approve"), PdfEditorToolbarItem.Tool(PdfEditorTool.ADD_TEXT)))
config.customMenuItems = listOf(PdfEditorCustomAction("crm", "Send to CRM") { ctx ->
    ctx.currentPdf { pdf -> pdf?.let(::upload) }
})
config.menuOrder = listOf(PdfEditorMenuEntry.Custom("crm"))

Names: toolTitles, modeTitles, menuItemTitles. Icons (drawable resources, drawn as tinted templates): toolIcons, modeIcons, menuItemIcons, headerIcons. Your own buttons: customTools[mode] and customMenuItems, whose handler can read the document with ctx.currentPdf { … } (null if the export failed). Order: toolOrder[mode] and menuOrder put the listed items first, and everything else follows in its usual order.

Fonts

config.interfaceFontFamily = FontFamily(Font(R.font.brand_regular), Font(R.font.brand_medium, FontWeight.Medium))
config.fonts = listOf(PdfEditorFont.fromResource(R.font.brand_regular))
config.fontsTitle = "Brand"
config.allowsFontImport = false

interfaceFontFamily sets the editor's own text (toolbars, header, sheets, dialogs, menus) in a Compose FontFamily; the document's text is never set in it. fonts lists your fonts first in the text font picker, under fontsTitle: PdfEditorFont.fromResource, fromAsset, fromFile or fromBytes. Each must be a TrueType/OpenType file, and one that isn't is skipped. showsStandardFonts, showsDocumentFonts, showsDeviceFonts and allowsFontImport hide the picker's other sections. newTextFont sets the font of every text box the user places; its size and colour still follow the text around it.

Selection

config.selectionColor = 0xFFE0245E.toInt()
config.handleShape = PdfEditorHandleShape.CIRCLE
config.handleScale = 1.25f
config.selectionBorderWidth = 2f
config.rotateIcon = R.drawable.ic_rotate

selectionColor (and selectionColorDark) colours the selection box, its handles, the rotation knob, the text caret, the text-selection band and the page-alignment guide. handleFillColor and handleShape (ROUNDED_SQUARE, CIRCLE, SQUARE) style the handles; handleScale (0.75–1.5) sizes them, and a mouse's reach follows while a finger's stays the same. selectionBorderWidth is 1–3 dp. rotateIcon, a drawable resource, is drawn in the knob as a template, in the selection colour. searchHighlightColor and searchCurrentHighlightColor colour the search highlights; their translucency stays the editor's, so the text under them reads.

Edit documents from code

Everything a user can do in the editor can be done from code, with no UI: read every layer of a page and its properties, replace text in any script (the layer keeps its font, size, colour and position, and the system's fonts fill in letters its font lacks), restyle, move, resize, rotate and restack layers, add text, images and shapes, mark text up, fill and author forms, edit metadata and pages, search, render a page, and save.

A layer is addressed by its page and an id that keeps naming it while other layers change. Geometry is in page points from the page's top-left corner. Each change is one undo step.

PdfDocumentEditor(file).use { doc ->
    for (block in doc.textBlocks(0)) {
        doc.setText(block, translate(block.text.orEmpty()), shrinkToFit = true)
    }
    file.writeBytes(doc.save())
}

Pass onDocumentReady to PdfEditorScreen (or set it on PdfEditorView) to get the document the editor is showing: changes appear on screen at once and the user can undo them. Failures throw PdfDocumentException; NotFound means the layer is gone, so read the layers again.

Localize the editor

Every word the editor shows has a stable key, the same on every platform, with English as its built-in text. Give text for the keys you want translated; the rest stay English. The editor checks a dictionary first, then a provider function, then your app's own pe_<key> string resources in its current language (the per-app language included), then English. Right-to-left languages mirror the editor; the page navigator stays left to right, like the pages it turns.

configuration.localization = PdfEditorLocalization(
    language = "hi",
    strings = mapOf("cancel" to "रद्द करें", "done" to "हो गया"),
)

Placeholders are positional (Page {1} of {2}), so a translation can reorder them. A count has two keys: <key>_one and <key>. Names you set per tool, mode or menu item win over translations.

Signing without the editor

PdfEditorSigning signs and verifies PDFs headlessly — bytes in, bytes out — with a device identity kept in the Android Keystore, or with your own signer (PdfCmsSigner) for a remote HSM or signing service.

if (!PdfEditorSigning.hasDeviceIdentity) {
    PdfEditorSigning.createDeviceIdentity(name = "Ada Lovelace", organization = "Acme")
}
val reports = PdfEditorSigning.verify(pdfBytes)   // List<PdfSignatureReport>

Public API

TypeWhat it is
PdfEditorScreenthe editor, as a @Composable
PdfEditorViewthe editor, as a View
PdfEditorSdkVERSION, setLicenseKey, license, isSupportedDevice, chromeScale, setCjkFallbackFont
PdfEditorLicensethe license verdict: status, isLicensed, reason, dates
PdfEditorMode · PdfEditorToolwhat to open in, and what to pre-arm
PdfEditorConfiguration · PdfEditorMenuItem · PdfEditorColorScheme · PdfEditorToolDefaults · PdfEditorMarkupStyle · PdfEditorInkStyle · PdfEditorHeaderButtoncustomizing the editor
PdfEditorCustomAction · PdfEditorActionHandler · PdfEditorActionContext · PdfEditorPdfCallback · PdfEditorToolbarItem · PdfEditorMenuEntryyour own buttons, and ordering
PdfEditorFontyour fonts for the document's text
PdfEditorHandleShapethe selection handles' shape
PdfSignaturesScreena read-only report on a document's signatures
PdfEditorSigning · PdfCmsSigner · PdfSignaturePlacement · PdfSignatureReportheadless signing
PdfBytesListener · PdfEditorCloseListener · UnsavedChangesListenerthe PdfEditorView callbacks

Everything else in the library is internal. The consumer ProGuard/R8 rules the SDK needs travel inside the AAR, so a minified app needs no extra configuration.

Troubleshooting

SymptomFix
"PDF-SDK is not licensed for this app"Read the reason and package shown. Check applicationIdSuffix, and that setLicenseKey runs in Application.onCreate.
"The editor can't run on this device"No arm64 engine on this device or ABI split — the SDK ships arm64-v8a only. PdfEditorSdk.isSupportedDevice tells you before you open it.
ViewTreeLifecycleOwner not foundHost PdfEditorView in a ComponentActivity, AppCompatActivity or Fragment.