Create PDF forms in Kotlin for Android
Add new PDF Form elements in code. Get additional resources by visiting our guide on creating fillable PDF forms in Android.
/* * Copyright © 2018-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.content.res.AssetManagerimport android.graphics.Bitmapimport android.graphics.BitmapFactoryimport android.graphics.RectFimport android.net.Uriimport android.util.Logimport androidx.activity.viewModelsimport com.pspdfkit.annotations.actions.UriActionimport com.pspdfkit.catalog.Rimport com.pspdfkit.catalog.SdkExampleimport com.pspdfkit.configuration.activity.PdfActivityConfigurationimport com.pspdfkit.document.PdfDocumentimport com.pspdfkit.document.processor.NewPageimport com.pspdfkit.document.processor.PdfProcessorimport com.pspdfkit.document.processor.PdfProcessorTaskimport com.pspdfkit.forms.CheckBoxFormConfigurationimport com.pspdfkit.forms.ComboBoxFormConfigurationimport com.pspdfkit.forms.FormOptionimport com.pspdfkit.forms.ListBoxFormConfigurationimport com.pspdfkit.forms.PushButtonFormConfigurationimport com.pspdfkit.forms.RadioButtonFormConfigurationimport com.pspdfkit.forms.SignatureFormConfigurationimport com.pspdfkit.forms.TextFormConfigurationimport com.pspdfkit.ui.PdfActivityimport com.pspdfkit.ui.PdfActivityIntentBuilderimport io.reactivex.rxjava3.android.schedulers.AndroidSchedulersimport io.reactivex.rxjava3.disposables.Disposableimport io.reactivex.rxjava3.schedulers.Schedulersimport java.io.Fileimport java.io.IOExceptionimport java.io.InputStream
/** * Kotlin example. Showcases how to create forms programmatically. */class FormCreationExample(context: Context) : SdkExample(context, R.string.formCreationExampleTitle, R.string.formCreationExampleDescription) {
private var documentProcessingDisposable: Disposable? = null
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)
// Create an A4 page document from scratch. val newPage = NewPage.emptyPage(NewPage.PAGE_SIZE_A4).build() val task = PdfProcessorTask.newPage(newPage)
val outputFile: File try { outputFile = File(getCatalogCacheDirectory(context), "Blank.pdf").canonicalFile } catch (exception: IOException) { throw IllegalStateException("Couldn't create Blank.pdf file.", exception) }
documentProcessingDisposable = PdfProcessor.processDocumentAsync(task, outputFile) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( { val intent = PdfActivityIntentBuilder.fromUri(context, Uri.fromFile(outputFile)) .configuration(configuration.build()) .activityClass(FormCreationActivity::class) .build()
context.startActivity(intent) }, { throwable -> Log.e(TAG, "Error while trying to create PDF document.", throwable) } ) }
override fun onDestroy() { super.onDestroy() documentProcessingDisposable?.dispose() documentProcessingDisposable = null }
@Throws(IOException::class) private fun getCatalogCacheDirectory(context: Context): File { val directory = File(context.cacheDir, PSPDFKIT_DIRECTORY_NAME) if (!directory.exists()) { if (!directory.mkdirs()) { throw IOException("Failed to create Catalog cache directory.") } } return directory }
companion object { private const val TAG = "FormCreationExample" private const val PSPDFKIT_DIRECTORY_NAME = "catalog-pspdfkit" }}
class FormCreationActivity : PdfActivity() {
private val viewModel: AnnotationCreationViewModel by viewModels() private var getFormElementsDisposable: Disposable? = null
override fun onDocumentLoaded(document: PdfDocument) { super.onDocumentLoaded(document)
viewModel.createObjects { // Retrieve existing form elements and create form fields only when there are no forms. getFormElementsDisposable = document.formProvider.formElementsAsync.subscribe { formElements -> if (formElements.isNotEmpty()) return@subscribe createForms() } } }
/** * Creates several forms and attach them to the document. */ private fun createForms() { createTextFormField() createCheckBoxFormField() createRadioButtonFormField() createPushButtonFormField() createSignatureFormField() createComboBoxFormField() createListBoxFormField() }
/** * Creates and attaches to the document a text form field. */ private fun createTextFormField() { val rectF = RectF(30f, 750f, 200f, 720f) val textFormConfiguration = TextFormConfiguration.Builder(0, rectF) .setText("Example text") .build() document?.formProvider?.addFormElementToPage("textfield-1", textFormConfiguration) }
/** * Creates and attaches to the document a checkbox form field with two checkbox form elements. */ private fun createCheckBoxFormField() { val rectFCheckBoxFormConfiguration1 = RectF(30f, 650f, 60f, 620f) val checkBoxFormConfiguration1 = CheckBoxFormConfiguration.Builder(0, rectFCheckBoxFormConfiguration1) .select() .build()
val rectFCheckBoxFormConfiguration2 = RectF(30f, 600f, 60f, 570f) val checkBoxFormConfiguration2 = CheckBoxFormConfiguration.Builder(0, rectFCheckBoxFormConfiguration2) .deselect() .build()
val checkBoxFormConfigurationList = listOf(checkBoxFormConfiguration1, checkBoxFormConfiguration2) document?.formProvider?.addFormElementsToPage("checkboxfield-1", checkBoxFormConfigurationList) }
/** * Creates and attaches to the document a radio button form field with two radio button form elements. */ private fun createRadioButtonFormField() { val rectFRadioButtonFormConfiguration1 = RectF(30f, 500f, 60f, 470f) val radioButtonFormConfiguration1 = RadioButtonFormConfiguration.Builder(0, rectFRadioButtonFormConfiguration1) .select() .build()
val rectFRadioButtonFormConfiguration2 = RectF(30f, 450f, 60f, 420f) val radioButtonFormConfiguration2 = RadioButtonFormConfiguration.Builder(0, rectFRadioButtonFormConfiguration2) .deselect() .build()
val radioButtonFormConfigurationList = listOf(radioButtonFormConfiguration1, radioButtonFormConfiguration2) document?.formProvider?.addFormElementsToPage("radiobuttonfield-1", radioButtonFormConfigurationList) }
/** * Creates and attaches to the document a push button form field. */ private fun createPushButtonFormField() { val rectFPushButtonFormConfiguration = RectF(30f, 350f, 120f, 260f)
val bitmapFromAsset = getBitmapFromAsset(assets, "images/android.png") ?: return
val pushButtonFormConfiguration = PushButtonFormConfiguration.Builder( 0, rectFPushButtonFormConfiguration, bitmapFromAsset ) .setAction(UriAction("https://developer.android.com/index.html")) .build()
document?.formProvider?.addFormElementToPage("pushbuttonfield-1", pushButtonFormConfiguration) }
/** * Creates and attaches to the document a signature form field. */ private fun createSignatureFormField() { val rectFSignatureFormConfiguration = RectF(30f, 190f, 200f, 160f)
val signatureFormConfiguration = SignatureFormConfiguration.Builder(0, rectFSignatureFormConfiguration) .build()
document?.formProvider?.addFormElementToPage("signaturefield-1", signatureFormConfiguration) }
/** * Creates and attaches to the document a combo box form field. */ private fun createComboBoxFormField() { val rectFComboBoxFormConfiguration = RectF(350f, 650f, 520f, 620f) val comboBoxFormConfiguration = ComboBoxFormConfiguration.Builder(0, rectFComboBoxFormConfiguration) .setFormOptions( listOf( FormOption("L1", "42"), FormOption("L2", "43") ) ) .setCustomText("Custom text") .build()
document?.formProvider?.addFormElementToPage("comboboxfield-1", comboBoxFormConfiguration) }
/** * Creates and attaches to the document a list box form field. */ private fun createListBoxFormField() { val rectFListBoxFormConfiguration = RectF(350f, 500f, 520f, 420f) val listBoxFormConfiguration = ListBoxFormConfiguration.Builder(0, rectFListBoxFormConfiguration) .setFormOptions( listOf( FormOption("L1", "42"), FormOption("L2", "43"), FormOption("L3", "44"), FormOption("L4", "45") ) ) .setMultiSelectionEnabled(true) .setSelectedIndexes(listOf(1, 2)) .build()
document?.formProvider?.addFormElementToPage("listboxfield-1", listBoxFormConfiguration) }
@Suppress("SameParameterValue") private fun getBitmapFromAsset(assets: AssetManager, path: String): Bitmap? { var inputStream: InputStream? = null var bitmap: Bitmap? try { inputStream = assets.open(path) bitmap = BitmapFactory.decodeStream(inputStream) } catch (e: IOException) { bitmap = null } finally { if (inputStream != null) { try { inputStream.close() } catch (ignored: IOException) { } } } return bitmap }
override fun onDestroy() { super.onDestroy() getFormElementsDisposable?.dispose() }}
This code sample is an example that illustrates how to use our SDK. Please adapt it to your specific use case.