Adding watermarks to PDFs using Kotlin for Android
Add custom content, like images, text, and logos, as watermarks to your document pages. Get additional resources by visiting our guide on adding watermarks to PDFs 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.graphics.Canvasimport android.graphics.Colorimport android.graphics.ColorFilterimport android.graphics.Matriximport android.graphics.Paintimport android.graphics.PixelFormatimport android.graphics.PointFimport android.graphics.Rectimport android.graphics.RectFimport android.os.Bundleimport androidx.annotation.IntRangeimport androidx.annotation.UiThreadimport com.pspdfkit.catalog.Rimport com.pspdfkit.catalog.SdkExampleimport com.pspdfkit.configuration.activity.PdfActivityConfigurationimport com.pspdfkit.document.PdfDocumentimport com.pspdfkit.document.providers.AssetDataProviderimport com.pspdfkit.ui.PdfActivityimport com.pspdfkit.ui.PdfActivityIntentBuilderimport com.pspdfkit.ui.drawable.PdfDrawableimport com.pspdfkit.ui.drawable.PdfDrawableProvider
/** * This example shows how to create page watermarks using Drawable API. */class WatermarkExample(context: Context) : SdkExample(context, R.string.watermarkExampleTitle, R.string.watermarkExampleDescription) { override fun launchExample(context: Context, configuration: PdfActivityConfiguration.Builder) { val intent = PdfActivityIntentBuilder.fromDataProvider(context, AssetDataProvider(WELCOME_DOC)) .configuration(configuration.build()) .activityClass(WatermarkExampleActivity::class.java) .build() context.startActivity(intent) }}
/** * Example activity showing how to draw custom drawables on the [PdfFragment], * the [PdfThumbnailGrid], and the [PdfThumbnailBar] using Drawable API. */class WatermarkExampleActivity : PdfActivity() {
/** * Drawable provider that provides example watermarks. */ private val customTestDrawableProvider: PdfDrawableProvider = object : PdfDrawableProvider() { override fun getDrawablesForPage(context: Context, document: PdfDocument, @IntRange(from = 0) pageIndex: Int): List<PdfDrawable> { return listOf( // Text watermark, tilted by 45 degrees with a bottom-left corner at (350, 350) in PDF coordinates. WatermarkDrawable("Watermark", PointF(350f, 350f)), // Simple watermark consisting of 2 square shapes. TwoSquaresDrawable(RectF(0f, 400f, 400f, 0f)) ) } }
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState)
// Register our drawable provider on `PdfFragment` to provide drawables to document pages... requirePdfFragment().addDrawableProvider(customTestDrawableProvider)
// Also register drawable provider on thumbnail bar, thumbnail grid. pspdfKitViews.thumbnailBarView?.addDrawableProvider(customTestDrawableProvider) pspdfKitViews.thumbnailGridView?.addDrawableProvider(customTestDrawableProvider)
// Outline displays page previews in bookmarks list. Bookmarks are enabled in this example // so we need to register our drawable provider on the outline view too. pspdfKitViews.outlineView?.addDrawableProvider(customTestDrawableProvider) }}
/** * This is a simple Kotlin extension function on [android.graphics.Rect] * so we can have a fluent API for converting to [RectF]. */private fun Rect.toRectF(): RectF { return RectF(this)}
/** * An implementation of [PdfDrawable], which shows two semi-transparent squares in the bottom-left * corner of the page. */private class TwoSquaresDrawable(private val pageCoordinates: RectF) : PdfDrawable() {
private val redPaint = Paint().apply { color = Color.RED style = Paint.Style.FILL alpha = 50 }
private val bluePaint = Paint().apply { color = Color.BLUE style = Paint.Style.FILL alpha = 50 }
private val screenCoordinates = RectF()
/** * This method performs all the drawing required by this drawable. * Keep this method fast to maintain performant UI. */ override fun draw(canvas: Canvas) { val bounds = bounds.toRectF() canvas.drawRect( bounds.left, bounds.top, bounds.right - bounds.width() / 2f, bounds.bottom - bounds.height() / 2f, redPaint ) canvas.drawRect( bounds.left + bounds.width() / 2f, bounds.top + bounds.height() / 2f, bounds.right, bounds.bottom, bluePaint ) }
/** * Nutrient calls this method every time the page was moved or resized on screen. * It will provide a fresh transformation for calculating screen coordinates from * PDF coordinates. */ override fun updatePdfToViewTransformation(matrix: Matrix) { super.updatePdfToViewTransformation(matrix) updateScreenCoordinates() }
private fun updateScreenCoordinates() { // Calculate the screen coordinates by applying the PDF-to-view transformation. getPdfToPageTransformation().mapRect(screenCoordinates, pageCoordinates)
// Rounding out to ensure that content does not clip. val bounds = Rect() screenCoordinates.roundOut(bounds) this.bounds = bounds }
@UiThread override fun setAlpha(alpha: Int) { bluePaint.alpha = alpha redPaint.alpha = alpha invalidateSelf() }
@UiThread override fun setColorFilter(colorFilter: ColorFilter?) { bluePaint.colorFilter = colorFilter redPaint.colorFilter = colorFilter invalidateSelf() }
@Deprecated("Deprecated in Java") override fun getOpacity(): Int { return PixelFormat.TRANSLUCENT }}
/** * An implementation of [PdfDrawable], which shows a semi-transparent text tilted by 45 degrees. */private class WatermarkDrawable(private val text: String, startingPoint: PointF) : PdfDrawable() {
private val redPaint = Paint().apply { color = Color.RED style = Paint.Style.FILL alpha = 50 textSize = 100f }
private val pageCoordinates = RectF() private val screenCoordinates = RectF()
init { calculatePageCoordinates(text, startingPoint) }
private fun calculatePageCoordinates(text: String, point: PointF) { val textBounds = Rect() redPaint.getTextBounds(text, 0, text.length, textBounds) pageCoordinates.set( point.x, point.y + textBounds.height().toFloat(), point.x + textBounds.width().toFloat(), point.y ) }
private fun updateScreenCoordinates() { getPdfToPageTransformation().mapRect(screenCoordinates, pageCoordinates)
// Rounding out ensure no clipping of content. val bounds = bounds screenCoordinates.roundOut(bounds) this.bounds = bounds }
/** * This method performs all the drawing required by this drawable. * Keep this method fast to maintain performant UI. */ override fun draw(canvas: Canvas) { val bounds = bounds.toRectF() canvas.save()
// Rotate canvas by 45 degrees. canvas.rotate(-45f, bounds.left, bounds.bottom) // Recalculate text size to much new bounds. setTextSizeForWidth(redPaint, bounds.width(), text) // Draw the text on rotated canvas. canvas.drawText(text, bounds.left, bounds.bottom, redPaint)
canvas.restore() }
private fun setTextSizeForWidth( paint: Paint, desiredWidth: Float, text: String ) { // Pick a reasonably large value for the test. val testTextSize = 60f
// Get the bounds of the text, using our testTextSize. paint.textSize = testTextSize
val bounds = Rect() paint.getTextBounds(text, 0, text.length, bounds)
// Calculate the desired size as a proportion of our testTextSize. val desiredTextSize = testTextSize * desiredWidth / bounds.width()
// Set the paint for that size. paint.textSize = desiredTextSize }
/** * Nutrient calls this method every time the page was moved or resized on screen. * It will provide a fresh transformation for calculating screen coordinates from * PDF coordinates. */ override fun updatePdfToViewTransformation(matrix: Matrix) { super.updatePdfToViewTransformation(matrix) updateScreenCoordinates() }
@UiThread override fun setAlpha(alpha: Int) { redPaint.alpha = alpha invalidateSelf() }
@UiThread override fun setColorFilter(colorFilter: ColorFilter?) { redPaint.colorFilter = colorFilter invalidateSelf() }
@Deprecated("Deprecated in Java") override fun getOpacity(): Int { return PixelFormat.TRANSLUCENT }}
This code sample is an example that illustrates how to use our SDK. Please adapt it to your specific use case.