Bookmark Highlighting
Shows how to use the drawable API to put a indicator on all bookmarked pages in the thumbnail bar and thumbnail grid.
/* * Copyright © 2020-2026 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.annotation.SuppressLintimport android.content.Contextimport android.graphics.Canvasimport android.graphics.ColorFilterimport android.graphics.PixelFormatimport android.graphics.Rectimport android.graphics.drawable.Drawableimport android.net.Uriimport androidx.core.content.ContextCompatimport com.pspdfkit.bookmarks.Bookmarkimport com.pspdfkit.catalog.Rimport com.pspdfkit.catalog.SdkExampleimport com.pspdfkit.catalog.tasks.ExtractAssetTaskimport com.pspdfkit.configuration.activity.PdfActivityConfigurationimport com.pspdfkit.configuration.activity.ThumbnailBarModeimport com.pspdfkit.document.PdfDocumentimport com.pspdfkit.ui.PdfActivityimport com.pspdfkit.ui.PdfActivityIntentBuilderimport com.pspdfkit.ui.drawable.PdfDrawableimport com.pspdfkit.ui.drawable.PdfDrawableProviderimport io.reactivex.rxjava3.android.schedulers.AndroidSchedulersimport io.reactivex.rxjava3.disposables.Disposable
/** * Example showing how to use the drawable API to put a drawn highlight on all pages that contain a bookmark. */class BookmarkHighlightingExample(private val context: Context) : SdkExample(context, R.string.bookmarkHighlightingExampleTitle, R.string.bookmarkHighlightingExampleDescription) {
override fun launchExample(context: Context, configuration: PdfActivityConfiguration.Builder) { // This uses larger thumbnails making our bookmark indicator more easily visible. configuration .setThumbnailBarMode(ThumbnailBarMode.THUMBNAIL_BAR_MODE_SCROLLABLE) .title("Bookmark Indicator")
// Start the activity once the example document has been extracted from the app's assets. ExtractAssetTask.extract(BOOKMARK_DOCUMENT, title, context) { documentFile -> val intent = PdfActivityIntentBuilder.fromUri(context, Uri.fromFile(documentFile)) .configuration(configuration.build()) .activityClass(BookmarkHighlightingActivity::class) .build()
// Start the BookmarkHighlightingActivity for the extracted document. context.startActivity(intent) } }
companion object { const val BOOKMARK_DOCUMENT = "bookmark_highlight_example.pdf" }}
class BookmarkHighlightingActivity : PdfActivity() {
/** List of bookmarks that current exist in the document. */ private val currentBookmarks = mutableListOf<Bookmark>()
/** Drawable provider that will put a bookmark icon on bookmarked pages. */ private val drawableProvider = object : PdfDrawableProvider() { override suspend fun getDrawablesForPage(context: Context, document: PdfDocument, pageIndex: Int): List<PdfDrawable> { if (currentBookmarks.any { it.pageIndex == pageIndex }) { // If there's bookmark for the given page index, add our drawable. return listOf(BookmarkDrawable(this@BookmarkHighlightingActivity)) } else { // Otherwise we draw nothing. return emptyList() } } }
/** Used to stop the bookmark loading process when closing the activity. */ private var bookmarkLoadingDisposable: Disposable? = null
override fun onDocumentLoaded(document: PdfDocument) { super.onDocumentLoaded(document) // When the document is initially loaded we prepare the first set of bookmarks to be displayed. bookmarkLoadingDisposable = document .bookmarkProvider .bookmarksAsync .firstOrError() .observeOn(AndroidSchedulers.mainThread()) .subscribe { bookmarks -> updateBookmarkDrawables(bookmarks) }
// Afterwards we subscribe to be updated whenever the list of bookmarks change. document.bookmarkProvider.addBookmarkListener { // This is always called on the UI thread so no need for special concurrency handling. updateBookmarkDrawables(it) } }
private fun updateBookmarkDrawables(newBookmarks: List<Bookmark>) { currentBookmarks.clear() currentBookmarks.addAll(newBookmarks)
// We need to add and remove the provider so the drawables are correctly redrawn. pspdfKitViews.thumbnailBarView?.apply { removeDrawableProvider(drawableProvider) addDrawableProvider(drawableProvider) } pspdfKitViews.thumbnailGridView?.apply { removeDrawableProvider(drawableProvider) addDrawableProvider(drawableProvider) } }
override fun onDestroy() { super.onDestroy() // Make sure to dispose of this before closing the activity. bookmarkLoadingDisposable?.dispose() }}
/** * An implementation of [PdfDrawable], which shows an indicator on bookmarked pages. */private class BookmarkDrawable(private val context: Context) : PdfDrawable() {
// This has to be non null, otherwise this whole example will do nothing. private val bookmarkDrawable: Drawable = ContextCompat.getDrawable(context, R.drawable.ic_bookmark_highlight)!!
/** * This will draw our bookmarkDrawable on to the page. */ @SuppressLint("CanvasSize") override fun draw(canvas: Canvas) { // First we convert our DP constants to their respective pixel sizes. val baseSize = BASE_SIZE_DP * context.resources.displayMetrics.density val bookmarkSize = BOOKMARK_SIZE_DP * context.resources.displayMetrics.density val margin = MARGIN_DP * context.resources.displayMetrics.density
// We use the canvas size here since that will be the size of the entire page in the thumbnail bar / grid. // Based on this size we calculate the real size our bookmark icon will be. val ratio = canvas.width / baseSize val effectiveBookmarkSize = bookmarkSize * ratio val effectiveMargin = margin * ratio
// Then we place the bookmark in top right edge, with some margin to the right, // and moving it up a bit so it sits flush with the page edge. bookmarkDrawable.bounds = Rect( (canvas.width - effectiveBookmarkSize - effectiveMargin).toInt(), -effectiveMargin.toInt(), (canvas.width - effectiveMargin).toInt(), effectiveBookmarkSize.toInt() )
// Finally we simply draw it onto the page. bookmarkDrawable.draw(canvas) }
override fun setAlpha(alpha: Int) { // Not required. }
@Deprecated("Deprecated in Java") override fun getOpacity(): Int { return PixelFormat.TRANSLUCENT }
override fun setColorFilter(colorFilter: ColorFilter?) { // Not required. }
companion object { /** The width in DP at which the bookmark icon will have it's size, used to scale depending on context. */ const val BASE_SIZE_DP = 100
/** The size of the bookmark icon assuming the size of the canvas is BASE_SIZE_DP. */ const val BOOKMARK_SIZE_DP = 24
/** The margin to apply to the bookmark icon. */ const val MARGIN_DP = 8 }}This code sample is an example that illustrates how to use our SDK. Please adapt it to your specific use case.