PDF form filling in Kotlin for Android
Fill out PDF forms in code. Get additional resources by visiting our guide on filling PDF form fields programmatically in Android.
/* * Copyright © 2020-2025 PSPDFKit GmbH. All rights reserved. * * The PSPDFKit Sample applications are licensed with a modified BSD license. * Please see License for details. This notice may not be removed from this file. */
package com.pspdfkit.catalog.examples.kotlin
import android.content.Contextimport android.net.Uriimport android.view.Menuimport android.view.MenuItemimport androidx.annotation.UiThreadimport com.pspdfkit.catalog.Rimport com.pspdfkit.catalog.SdkExampleimport com.pspdfkit.catalog.tasks.ExtractAssetTaskimport com.pspdfkit.configuration.activity.PdfActivityConfigurationimport com.pspdfkit.document.PdfDocumentimport com.pspdfkit.forms.CheckBoxFormElementimport com.pspdfkit.forms.FormTypeimport com.pspdfkit.forms.RadioButtonFormElementimport com.pspdfkit.forms.RadioButtonFormFieldimport com.pspdfkit.forms.TextFormElementimport com.pspdfkit.forms.TextInputFormatimport com.pspdfkit.ui.PdfActivityimport com.pspdfkit.ui.PdfActivityIntentBuilderimport io.reactivex.rxjava3.disposables.CompositeDisposableimport io.reactivex.rxjava3.disposables.Disposable
/** * Showcases how to fill forms programmatically. */class FormFillingExample(context: Context) : SdkExample(context, R.string.formFillingExampleTitle, R.string.formFillingExampleDescription) { override fun launchExample(context: Context, configuration: PdfActivityConfiguration.Builder) { // Turn off saving, so we have the clean original document every time the example is launched. configuration.autosaveEnabled(false)
// Enable form editing UI. configuration.formEditingEnabled(true)
// Extract the example document from the assets. ExtractAssetTask.extract("Form_example.pdf", title, context) { documentFile -> val intent = PdfActivityIntentBuilder.fromUri(context, Uri.fromFile(documentFile)) .configuration(configuration.build()) .activityClass(FormFillingActivity::class) .build() context.startActivity(intent) } }}
class FormFillingActivity : PdfActivity() {
/** Holds disposables for all async operations done in this example. We dispose this when destroying the activity. */ private val disposables = CompositeDisposable()
@UiThread override fun onDocumentLoaded(document: PdfDocument) { super.onDocumentLoaded(document) fillAllFormFields() }
/** * Fills form fields in the document with text "Example <field name>". */ private fun fillAllFormFields() { val document = document ?: return document.formProvider.formElementsAsync.subscribe { formElements -> for (formElement in formElements) { when (formElement.type) { FormType.TEXT -> { val textFormElement = formElement as TextFormElement when (textFormElement.inputFormat) { TextInputFormat.DATE -> textFormElement.setText("03/14/1994") else -> textFormElement.setText("Example " + formElement.getName()) } } FormType.CHECKBOX -> (formElement as CheckBoxFormElement).toggleSelection() FormType.RADIOBUTTON -> (formElement as RadioButtonFormElement).toggleSelection() else -> Unit } } }.addToDisposables() }
/** * Resets all form fields in the document to their default values. */ private fun resetForm() { val document = document ?: return
document.formProvider.formFieldsAsync.subscribe { formFields -> for (formField in formFields) { formField.reset() } }.addToDisposables() }
/** * Shows how to query form fields/elements by their name. */ private fun fillByName() { val document = document ?: return
// Form fields can be queried by their fully qualified name. // Each form field can have multiple child form elements that are // the widget annotations that are visually representing actionable // controls inside the form field. document.formProvider.getFormFieldWithFullyQualifiedNameAsync("Sex").subscribe { formField -> val formElement = formField as RadioButtonFormField // Sex radio button field has 2 child form elements. These represent 2 radio buttons in the radio group. // First radio element has the name "Sex.0" and represents the MALE option. // formElement.getFormElements().get(0) // Second radio element has name "Sex.1" and represents the FEMALE option. // formElement.getFormElements().get(1) // Select the MALE radio option. formElement.formElements[0].select() }.addToDisposables()
// Form elements (visible portion of the form field) can be queried by their name and filled that way. document.formProvider.getFormElementWithNameAsync("First Name").subscribe { formElement -> (formElement as TextFormElement).setText("John") }.addToDisposables() document.formProvider.getFormElementWithNameAsync("Last Name").subscribe { formElement -> (formElement as TextFormElement).setText("Appleseed") }.addToDisposables()
// Querying form elements by name can be slow. If you need to fill many form elements // at once, retrieve list of all form fields/elements first and iterate through it. document.formProvider.formElementsAsync.subscribe { formElements -> // For the sake of example we'll fill only address fields here. val formFillMap = mapOf( "Address_1" to "7440-7498 S Hanna St.", "Address_2" to "", "City" to "Fort Wayne", "STATE" to "IN", "ZIP" to "46774" )
formElements.asSequence() .filterIsInstance(TextFormElement::class.java) .forEach { textElement -> formFillMap[textElement.name]?.let { textElement.setText(it) } } }.addToDisposables() }
override fun onCreateOptionsMenu(menu: Menu): Boolean { super.onCreateOptionsMenu(menu) menu.add(0, RESET_FORM_MENU_ITEM_ID, 0, "Reset form") menu.add(0, FILL_BY_FIELD_NAME, 0, "Fill by field name") return true }
override fun onOptionsItemSelected(item: MenuItem): Boolean { return when (item.itemId) { RESET_FORM_MENU_ITEM_ID -> { resetForm() true } FILL_BY_FIELD_NAME -> { fillByName() true } else -> super.onOptionsItemSelected(item) } }
override fun onDestroy() { super.onDestroy() disposables.clear() }
private fun Disposable.addToDisposables() = disposables.add(this)
companion object { private const val RESET_FORM_MENU_ITEM_ID = 1 private const val FILL_BY_FIELD_NAME = 2 }}
This code sample is an example that illustrates how to use our SDK. Please adapt it to your specific use case.