Generate PDF report in Kotlin for Android
Generate a PDF out of multiple document files, add new pages, and protect the new PDF document with a password. Get additional resources by visiting Android PDF editor library.
/* * 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. */
// We're temporarily suppressing the ProgressDialog being deprecated warning.// Issue: https://github.com/PSPDFKit/PSPDFKit/issues/32215@file:Suppress("DEPRECATION")
package com.pspdfkit.catalog.examples.kotlin
import android.app.ProgressDialogimport android.content.Contextimport android.graphics.BitmapFactoryimport android.graphics.Colorimport android.graphics.Paintimport android.graphics.RectFimport android.net.Uriimport android.text.TextPaintimport com.pspdfkit.annotations.FreeTextAnnotationimport com.pspdfkit.annotations.StampAnnotationimport com.pspdfkit.annotations.appearance.AssetAppearanceStreamGeneratorimport com.pspdfkit.catalog.Rimport com.pspdfkit.catalog.SdkExampleimport com.pspdfkit.configuration.activity.PdfActivityConfigurationimport com.pspdfkit.document.DocumentPermissionsimport com.pspdfkit.document.DocumentSaveOptionsimport com.pspdfkit.document.DocumentSourceimport com.pspdfkit.document.PdfDocumentimport com.pspdfkit.document.PdfDocumentLoaderimport com.pspdfkit.document.processor.NewPageimport com.pspdfkit.document.processor.PageCanvasimport com.pspdfkit.document.processor.PagePatternimport com.pspdfkit.document.processor.PdfProcessorimport com.pspdfkit.document.processor.PdfProcessorTaskimport com.pspdfkit.document.providers.AssetDataProviderimport com.pspdfkit.ui.PdfActivityimport com.pspdfkit.utils.Sizeimport io.reactivex.rxjava3.android.schedulers.AndroidSchedulersimport io.reactivex.rxjava3.core.Singleimport io.reactivex.rxjava3.disposables.Disposableimport io.reactivex.rxjava3.schedulers.Schedulersimport java.io.Fileimport java.io.IOExceptionimport java.util.EnumSet
/** * This example shows how to programmatically create a PDF report. */class GenerateReportExample(context: Context) : SdkExample(context, R.string.generateReportExampleTitle, R.string.generateReportExampleDescription) {
/** * Disposable used to cancel the [com.pspdfkit.document.processor.PdfProcessor] * report generation when canceling the progress dialog. */ private var generationDisposable: Disposable? = null
override fun launchExample(context: Context, configuration: PdfActivityConfiguration.Builder) { val dialog = ProgressDialog.show(context, "Generating Report", "Please wait", true, true) { generationDisposable?.dispose() }
generationDisposable = Single.fromCallable { // Open the document we are going to take the first and last page from. val sourceDocument = PdfDocumentLoader.openDocument(context, DocumentSource(AssetDataProvider("AnnualReport.pdf"))) val pageSize = sourceDocument.getPageSize(0)
// Create PDF processor task from the document. val task = PdfProcessorTask.fromDocument(sourceDocument)
// Keep only the first and the last page of the original document. // Keep only the first and the last page of the original document. val pagesToRemove = mutableSetOf<Int>() for (i in 1 until sourceDocument.pageCount - 1) { pagesToRemove.add(i) } task.removePages(pagesToRemove)
// Add a newly created single-paged document as the second page of the report val secondPageDocument = generateSecondPage(pageSize, context) task.addNewPage(NewPage.fromPage(secondPageDocument, 0).build(), 1)
// Add a new page with a pattern grid as the third page of the report. task.addNewPage( NewPage.patternPage(pageSize, PagePattern.GRID_5MM) .backgroundColor(Color.WHITE) .build(), 2 )
// Add a page from an existing document. val importDocument = PdfDocumentLoader.openDocument(context, DocumentSource(AssetDataProvider("Aviation.pdf"))) task.addNewPage(NewPage.fromPage(importDocument, 0).build(), 3)
// Scale the recently added page to the first page size task.resizePage(3, pageSize)
// Draw "Generated for John Doe. Page X" watermark on every page drawWatermark("John Doe", task, pageSize, 5)
// Flatten all annotations. task.changeAllAnnotations(PdfProcessorTask.AnnotationProcessingMode.FLATTEN)
// Only allow opening by users that know the password. val password = "password" val saveOptions = DocumentSaveOptions( password, EnumSet.of(DocumentPermissions.PRINTING), false, null )
// Finally create the resulting document. val generatedReportFile = File(context.getDir("documents", Context.MODE_PRIVATE), "generated-report.pdf") PdfProcessor.processDocument(task, generatedReportFile, saveOptions)
return@fromCallable Uri.fromFile(generatedReportFile) }.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .doFinally { dialog.cancel() } // Open the produced document in PdfActivity. .subscribe { uri -> PdfActivity.showDocument(context, uri, configuration.build()) } }
@Throws(IOException::class) private fun generateSecondPage(pageSize: Size, context: Context): PdfDocument { // Create a separate single-paged document, which will be added as the second page of the report.
// Start with the empty page. val task = PdfProcessorTask.newPage(NewPage.emptyPage(pageSize).backgroundColor(Color.WHITE).build())
// Invoke processor to create new document. val tempFile = File.createTempFile("second_page", null, context.cacheDir) PdfProcessor.processDocument(task, tempFile)
// Create the document which will be the report's second page val pageDocument = PdfDocumentLoader.openDocument(context, Uri.fromFile(tempFile))
// Create a free text annotation as the title of the page. FreeTextAnnotation( 0, RectF(228f, 1024f, 828f, 964f), "Some Annotations" ).apply { textSize = 40f
pageDocument.annotationProvider.addAnnotationToPage(this) }
// Create a vector stamp annotation. StampAnnotation( 0, RectF(50f, 724f, 250f, 524f), "Stamp with custom AP stream" ).apply { // Set PDF from assets containing vector logo as annotation's appearance stream generator. appearanceStreamGenerator = AssetAppearanceStreamGenerator("images/Nutrient_Logo.pdf")
pageDocument.annotationProvider.addAnnotationToPage(this) }
// Create a free-text annotation as a label of the vector stamp. FreeTextAnnotation( 0, RectF(67f, 520f, 667f, 420f), "The logo above is a vector stamp annotation." ).apply { textSize = 18f pageDocument.annotationProvider.addAnnotationToPage(this) }
// Create an image stamp annotation. val image = BitmapFactory.decodeStream(context.assets.open("images/android.png")) StampAnnotation( 0, RectF(60f, 400f, (60 + image.width / 4).toFloat(), (400 - image.height / 4).toFloat()), image ).let { pageDocument.annotationProvider.addAnnotationToPage(it) }
// Create a free-text annotation as a label of the image stamp FreeTextAnnotation( 0, RectF(67f, 240f, 667f, 160f), "The image above is an image stamp annotation." ).apply { textSize = 18f pageDocument.annotationProvider.addAnnotationToPage(this) }
// Flatten all annotations val flattenedTempFile = File.createTempFile("flattened_second_page", null, context.cacheDir) val flattenTask = PdfProcessorTask.fromDocument(pageDocument) flattenTask.changeAllAnnotations(PdfProcessorTask.AnnotationProcessingMode.FLATTEN) PdfProcessor.processDocument(flattenTask, flattenedTempFile)
return PdfDocumentLoader.openDocument(context, Uri.fromFile(flattenedTempFile)) }
private fun drawWatermark(name: String, task: PdfProcessorTask, pageSize: Size, pageCount: Int) { val textPaint = TextPaint().apply { textSize = 30f color = Color.argb(128, 255, 0, 0) textAlign = Paint.Align.CENTER }
for (pageIndex in 0 until pageCount) { val canvas = PageCanvas(pageSize) { canvas -> canvas.drawText( "Generated for $name. Page ${pageIndex + 1}", pageSize.width / 2, pageSize.height - 100, textPaint ) } task.addCanvasDrawingToPage(canvas, pageIndex) } }
override fun onDestroy() { super.onDestroy() generationDisposable?.dispose() }}
This code sample is an example that illustrates how to use our SDK. Please adapt it to your specific use case.