Save signed PDFs directly to a remote server on Android

Many apps need to let users sign PDFs (e.g. contracts, delivery confirmations) and save the signed version directly to a remote server without storing files locally.

This guide explains how to:

  1. Capture user annotations (like signatures).
  2. Flatten them into the PDF (making them permanent).
  3. Securely upload the modified PDF to your server.

Key challenge

  • By default, annotations exist as separate layers. Without flattening, uploaded PDFs may appear unsigned.

Solution

  1. Flatten annotations — Merge signatures into the PDF content.
  2. Process asynchronously — Avoid user interface (UI) freezes during PDF processing.
  3. Upload from cache — Temporarily save the modified PDF to the app’s cache (not device storage).

Implementation

To flatten annotations, use PdfProcessorTask to burn annotations into the PDF:

val task = PdfProcessorTask
.fromDocument(document)
.changeAllAnnotations(PdfProcessorTask.AnnotationProcessingMode.FLATTEN)

Next, generate the signed PDF in the app’s cache directory:

val outputFile = File(context.cacheDir, "signed.pdf")
PdfProcessor.processDocumentAsync(task, outputFile)
.subscribeOn(Schedulers.io())
.subscribe(
{ /* Track progress */ },
{ error -> /* Handle errors */ },
{
// Upload to server after processing.
lifecycleScope.launch(Dispatchers.IO) {
uploadToServer(outputFile)
// Clean up: Delete the cached file after upload.
outputFile.delete()
}
}
)

Finally, implement your server upload logic in uploadToServer(). Here’s an example using Retrofit:

private suspend fun uploadToServer(file: File) {
val requestBody = file.asRequestBody("application/pdf".toMediaType())
val part = MultipartBody.Part.createFormData("file", file.name, requestBody)
val response = yourApiService.uploadSignedPdf(part)
// Handle response.
}

Improvement ideas

Error handling

  • Use throwable.printStackTrace() to debug processing errors.
  • Retry failed uploads with exponential backoff.

Security

  • Validate server responses to ensure successful uploads.
  • Delete cached files after upload using outputFile.delete().

Improve the user experience

  • For large PDFs, add a progress indicator during processing/upload.

Resources