Sfoglia il codice sorgente

PDFTool(Android) - 1.新增Kotlin版本Samples示例

liuxiaolong 1 anno fa
parent
commit
8d30fde8c5
65 ha cambiato i file con 3487 aggiunte e 0 eliminazioni
  1. 1 0
      Samples_kotlin/.gitignore
  2. 66 0
      Samples_kotlin/build.gradle
  3. 21 0
      Samples_kotlin/proguard-rules.pro
  4. 54 0
      Samples_kotlin/src/main/AndroidManifest.xml
  5. BIN
      Samples_kotlin/src/main/assets/Annotations.pdf
  6. BIN
      Samples_kotlin/src/main/assets/Bird.wav
  7. BIN
      Samples_kotlin/src/main/assets/ComPDFKit.png
  8. BIN
      Samples_kotlin/src/main/assets/CommonFivePage.pdf
  9. BIN
      Samples_kotlin/src/main/assets/ImageExtractTest.pdf
  10. BIN
      Samples_kotlin/src/main/assets/text.pdf
  11. 100 0
      Samples_kotlin/src/main/java/com/compdfkit/samples/PDFSamples.kt
  12. 66 0
      Samples_kotlin/src/main/java/com/compdfkit/samples/SampleApplication.kt
  13. 94 0
      Samples_kotlin/src/main/java/com/compdfkit/samples/SampleDetailActivity.kt
  14. 38 0
      Samples_kotlin/src/main/java/com/compdfkit/samples/SampleListActivity.kt
  15. 50 0
      Samples_kotlin/src/main/java/com/compdfkit/samples/SampleListAdapter.kt
  16. 74 0
      Samples_kotlin/src/main/java/com/compdfkit/samples/samples/AnnotationImportExportTest.kt
  17. 560 0
      Samples_kotlin/src/main/java/com/compdfkit/samples/samples/AnnotationTest.kt
  18. 114 0
      Samples_kotlin/src/main/java/com/compdfkit/samples/samples/BackgroundTest.kt
  19. 99 0
      Samples_kotlin/src/main/java/com/compdfkit/samples/samples/BatesTest.kt
  20. 51 0
      Samples_kotlin/src/main/java/com/compdfkit/samples/samples/BookmarkTest.kt
  21. 80 0
      Samples_kotlin/src/main/java/com/compdfkit/samples/samples/DocumentInfoTest.kt
  22. 139 0
      Samples_kotlin/src/main/java/com/compdfkit/samples/samples/EncryptTest.kt
  23. 55 0
      Samples_kotlin/src/main/java/com/compdfkit/samples/samples/FlattenTest.kt
  24. 146 0
      Samples_kotlin/src/main/java/com/compdfkit/samples/samples/HeaderFooterTest.kt
  25. 47 0
      Samples_kotlin/src/main/java/com/compdfkit/samples/samples/ImageExtractTest.kt
  26. 330 0
      Samples_kotlin/src/main/java/com/compdfkit/samples/samples/InteractiveFormsTest.kt
  27. 100 0
      Samples_kotlin/src/main/java/com/compdfkit/samples/samples/OutlineTest.kt
  28. 63 0
      Samples_kotlin/src/main/java/com/compdfkit/samples/samples/PDFATest.kt
  29. 199 0
      Samples_kotlin/src/main/java/com/compdfkit/samples/samples/PDFPageTest.kt
  30. 57 0
      Samples_kotlin/src/main/java/com/compdfkit/samples/samples/PDFRedactTest.kt
  31. 67 0
      Samples_kotlin/src/main/java/com/compdfkit/samples/samples/PDFToImageTest.kt
  32. 95 0
      Samples_kotlin/src/main/java/com/compdfkit/samples/samples/TextExtractTest.kt
  33. 111 0
      Samples_kotlin/src/main/java/com/compdfkit/samples/samples/TextSearchTest.kt
  34. 154 0
      Samples_kotlin/src/main/java/com/compdfkit/samples/samples/WatermarkTest.kt
  35. 11 0
      Samples_kotlin/src/main/java/com/compdfkit/samples/util/CPDFGlideModule.kt
  36. 31 0
      Samples_kotlin/src/main/java/com/compdfkit/samples/util/DateUtil.kt
  37. 112 0
      Samples_kotlin/src/main/java/com/compdfkit/samples/util/FileUtils.kt
  38. 43 0
      Samples_kotlin/src/main/java/com/compdfkit/samples/util/LoggingOutputListener.kt
  39. 22 0
      Samples_kotlin/src/main/java/com/compdfkit/samples/util/OutputListener.kt
  40. 5 0
      Samples_kotlin/src/main/res/drawable/baseline_arrow_back_24.xml
  41. 18 0
      Samples_kotlin/src/main/res/layout/activity_sample_list.xml
  42. 65 0
      Samples_kotlin/src/main/res/layout/fragment_sample_detail.xml
  43. 22 0
      Samples_kotlin/src/main/res/layout/layout_sample_list_item.xml
  44. 5 0
      Samples_kotlin/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
  45. 5 0
      Samples_kotlin/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
  46. BIN
      Samples_kotlin/src/main/res/mipmap-hdpi/ic_launcher.png
  47. BIN
      Samples_kotlin/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
  48. BIN
      Samples_kotlin/src/main/res/mipmap-hdpi/ic_launcher_round.png
  49. BIN
      Samples_kotlin/src/main/res/mipmap-mdpi/ic_launcher.png
  50. BIN
      Samples_kotlin/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
  51. BIN
      Samples_kotlin/src/main/res/mipmap-mdpi/ic_launcher_round.png
  52. BIN
      Samples_kotlin/src/main/res/mipmap-xhdpi/ic_launcher.png
  53. BIN
      Samples_kotlin/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
  54. BIN
      Samples_kotlin/src/main/res/mipmap-xhdpi/ic_launcher_round.png
  55. BIN
      Samples_kotlin/src/main/res/mipmap-xxhdpi/ic_launcher.png
  56. BIN
      Samples_kotlin/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
  57. BIN
      Samples_kotlin/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
  58. BIN
      Samples_kotlin/src/main/res/mipmap-xxxhdpi/ic_launcher.png
  59. BIN
      Samples_kotlin/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
  60. BIN
      Samples_kotlin/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
  61. 7 0
      Samples_kotlin/src/main/res/values-night/themes.xml
  62. 5 0
      Samples_kotlin/src/main/res/values/colors.xml
  63. 71 0
      Samples_kotlin/src/main/res/values/strings.xml
  64. 9 0
      Samples_kotlin/src/main/res/values/themes.xml
  65. 25 0
      Samples_kotlin/src/main/res/xml/tools_file_paths.xml

+ 1 - 0
Samples_kotlin/.gitignore

@@ -0,0 +1 @@
+/build

+ 66 - 0
Samples_kotlin/build.gradle

@@ -0,0 +1,66 @@
+plugins {
+    id 'com.android.application'
+    id 'org.jetbrains.kotlin.android'
+}
+
+android {
+    namespace 'com.compdfkit.samples'
+    compileSdk rootProject.ext.android.COMPILESDK
+
+    defaultConfig {
+        applicationId "com.compdfkit.samples"
+        minSdk rootProject.ext.android.MINSDK
+        targetSdk rootProject.ext.android.TARGETSDK
+        versionCode rootProject.ext.android.VERSIONCODE as int
+        versionName rootProject.ext.sdk.COMPDFKit_SDK_VERSION
+
+    }
+
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+            android.applicationVariants.all { variant ->
+                variant.outputs.all {
+                    if (outputFileName.toLowerCase().endsWith('unsigned.apk')) {
+                        outputFileName = "Samples_ComPDFKit.apk"
+                    }
+                }
+            }
+        }
+        debug {
+            android.applicationVariants.all { variant ->
+                variant.outputs.all {
+                    if (outputFileName.toLowerCase().endsWith('debug.apk')) {
+                        outputFileName = "Samples_ComPDFKit.apk"
+                    }
+                }
+            }
+        }
+    }
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+    kotlinOptions {
+        jvmTarget = '1.8'
+    }
+}
+
+dependencies {
+
+    implementation 'androidx.appcompat:appcompat:1.6.1'
+    implementation 'com.google.android.material:material:1.9.0'
+    implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
+    implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.8.0"))
+    implementation "androidx.core:core-ktx:1.10.0"
+    implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.6.1"
+
+    api project(path:':ComPDFKit_Repo:compdfkit')
+    api project(path:':ComPDFKit_Repo:compdfkit-ui')
+    api 'com.github.bumptech.glide:glide:4.15.1'
+    annotationProcessor 'com.github.bumptech.glide:compiler:4.15.1'
+
+    api 'androidx.documentfile:documentfile:1.0.1'
+
+}

+ 21 - 0
Samples_kotlin/proguard-rules.pro

@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile

File diff suppressed because it is too large
+ 54 - 0
Samples_kotlin/src/main/AndroidManifest.xml


BIN
Samples_kotlin/src/main/assets/Annotations.pdf


BIN
Samples_kotlin/src/main/assets/Bird.wav


BIN
Samples_kotlin/src/main/assets/ComPDFKit.png


BIN
Samples_kotlin/src/main/assets/CommonFivePage.pdf


BIN
Samples_kotlin/src/main/assets/ImageExtractTest.pdf


BIN
Samples_kotlin/src/main/assets/text.pdf


+ 100 - 0
Samples_kotlin/src/main/java/com/compdfkit/samples/PDFSamples.kt

@@ -0,0 +1,100 @@
+/**
+ * Copyright © 2014-2023 PDF Technologies, Inc. All Rights Reserved.
+ *
+ *
+ * THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
+ * AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE ComPDFKit LICENSE AGREEMENT.
+ * UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
+ * This notice may not be removed from this file.
+ */
+package com.compdfkit.samples
+
+import android.content.Context
+import android.os.Environment
+import androidx.annotation.StringRes
+import com.compdfkit.core.common.CPDFDocumentException
+import com.compdfkit.core.document.CPDFDocument
+import com.compdfkit.samples.util.OutputListener
+import java.io.File
+
+abstract class PDFSamples {
+
+    @JvmField
+    protected var outputListener: OutputListener? = null
+
+    val outputFileList: ArrayList<String> = ArrayList()
+
+    var title = "{title}"
+
+    var description = "{description}"
+
+    fun setTitle(@StringRes titleResId: Int) {
+        title = context().getString(titleResId)
+    }
+
+    fun setDescription(@StringRes descriptionResId: Int) {
+        description = context().getString(descriptionResId)
+    }
+
+    fun getOutputFileList(): MutableList<String> = outputFileList
+
+    val outputFileNames: Array<String?>
+        get() {
+            val names = arrayOfNulls<String>(outputFileList.size)
+            outputFileList.toArray(names)
+            for (i in names.indices) {
+                val file = File(names[i])
+                names[i] = file.name
+            }
+            return names
+        }
+
+    fun addFileList(file: String) {
+        outputFileList.add(file)
+    }
+
+    protected fun printHead() {
+        val head: String = context().getString(R.string.sample_header, title)
+        outputListener?.println(head)
+    }
+
+    protected fun printFooter() {
+        val footer: String = context().getString(R.string.sample_footer)
+        outputListener?.println("\n${footer}")
+        printDividingLine()
+    }
+
+    protected fun printDividingLine() {
+        outputListener?.println("--------------------------------------------")
+    }
+
+    open fun run(outputListener: OutputListener?) {
+        this.outputListener = outputListener
+        outputFileList.clear()
+    }
+
+    protected open fun saveSamplePDF(document: CPDFDocument, file: File, close: Boolean = true) {
+        try {
+            file.parentFile?.mkdirs()
+            document.saveAs(file.absolutePath, false)
+            if (file.exists()) {
+                getOutputFileList().add(file.absolutePath)
+            }
+            if (close) {
+                document.close()
+            }
+        } catch (e: CPDFDocumentException) {
+            e.printStackTrace()
+        }
+    }
+
+    protected fun outputDir(): File? {
+        return context().getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS)
+    }
+
+    protected fun context(): Context = SampleApplication.instance
+
+    companion object {
+        protected const val INPUT_PATH = "TestFiles/"
+    }
+}

+ 66 - 0
Samples_kotlin/src/main/java/com/compdfkit/samples/SampleApplication.kt

@@ -0,0 +1,66 @@
+/**
+ * Copyright © 2014-2023 PDF Technologies, Inc. All Rights Reserved.
+ *
+ * THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
+ * AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE ComPDFKit LICENSE AGREEMENT.
+ * UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
+ * This notice may not be removed from this file.
+ */
+package com.compdfkit.samples
+
+import android.app.Application
+import com.compdfkit.samples.samples.AnnotationImportExportTest
+import com.compdfkit.samples.samples.AnnotationTest
+import com.compdfkit.samples.samples.BackgroundTest
+import com.compdfkit.samples.samples.BatesTest
+import com.compdfkit.samples.samples.BookmarkTest
+import com.compdfkit.samples.samples.DocumentInfoTest
+import com.compdfkit.samples.samples.EncryptTest
+import com.compdfkit.samples.samples.FlattenTest
+import com.compdfkit.samples.samples.HeaderFooterTest
+import com.compdfkit.samples.samples.ImageExtractTest
+import com.compdfkit.samples.samples.InteractiveFormsTest
+import com.compdfkit.samples.samples.OutlineTest
+import com.compdfkit.samples.samples.PDFATest
+import com.compdfkit.samples.samples.PDFPageTest
+import com.compdfkit.samples.samples.PDFRedactTest
+import com.compdfkit.samples.samples.PDFToImageTest
+import com.compdfkit.samples.samples.TextExtractTest
+import com.compdfkit.samples.samples.TextSearchTest
+import com.compdfkit.samples.samples.WatermarkTest
+
+class SampleApplication : Application() {
+
+    @JvmField
+    var samplesList: MutableList<PDFSamples> = ArrayList()
+
+    override fun onCreate() {
+        super.onCreate()
+        instance = this
+        samplesList.clear()
+        samplesList.add(BookmarkTest())
+        samplesList.add(OutlineTest())
+        samplesList.add(PDFToImageTest())
+        samplesList.add(TextSearchTest())
+        samplesList.add(AnnotationTest())
+        samplesList.add(AnnotationImportExportTest())
+        samplesList.add(InteractiveFormsTest())
+        samplesList.add(PDFPageTest())
+        samplesList.add(ImageExtractTest())
+        samplesList.add(TextExtractTest())
+        samplesList.add(DocumentInfoTest())
+        samplesList.add(WatermarkTest())
+        samplesList.add(BackgroundTest())
+        samplesList.add(HeaderFooterTest())
+        samplesList.add(BatesTest())
+        samplesList.add(PDFRedactTest())
+        samplesList.add(EncryptTest())
+        samplesList.add(PDFATest())
+        samplesList.add(FlattenTest())
+    }
+
+    companion object {
+        lateinit var instance: SampleApplication
+            private set
+    }
+}

+ 94 - 0
Samples_kotlin/src/main/java/com/compdfkit/samples/SampleDetailActivity.kt

@@ -0,0 +1,94 @@
+/**
+ * Copyright © 2014-2023 PDF Technologies, Inc. All Rights Reserved.
+ *
+ *
+ * THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
+ * AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE ComPDFKit LICENSE AGREEMENT.
+ * UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
+ * This notice may not be removed from this file.
+ */
+package com.compdfkit.samples
+
+import android.content.Context
+import android.content.DialogInterface
+import android.content.Intent
+import android.os.Bundle
+import android.widget.ScrollView
+import androidx.appcompat.app.AlertDialog
+import androidx.appcompat.app.AppCompatActivity
+import androidx.appcompat.widget.AppCompatTextView
+import androidx.lifecycle.lifecycleScope
+import com.compdfkit.samples.util.FileUtils
+import com.compdfkit.samples.util.LoggingOutputListener
+import com.google.android.material.button.MaterialButton
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import java.io.File
+
+open class SampleDetailActivity : AppCompatActivity() {
+
+    private var pdfSamples: PDFSamples? = null
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(R.layout.fragment_sample_detail)
+        val btnRun = findViewById<MaterialButton>(R.id.btn_run)
+        val btnOpenFiles = findViewById<MaterialButton>(R.id.btn_open_files)
+        val logTextView = findViewById<AppCompatTextView>(R.id.tv_logging)
+        val scrollView = findViewById<ScrollView>(R.id.scroll_view)
+        val tvDescription = findViewById<AppCompatTextView>(R.id.tv_description)
+        val outputListener = LoggingOutputListener(logTextView, scrollView)
+        if (intent.hasExtra(EXTRA_SAMPLE_ID)) {
+            pdfSamples = SampleApplication.instance.samplesList[intent.getIntExtra(EXTRA_SAMPLE_ID, 0)]
+            supportActionBar?.let {
+                it.setIcon(R.drawable.baseline_arrow_back_24)
+                it.setHomeButtonEnabled(true)
+                it.setDisplayHomeAsUpEnabled(true)
+                it.title = pdfSamples!!.title
+                tvDescription.text = pdfSamples!!.description
+            }
+            pdfSamples?.outputFileList?.clear()
+        }
+        btnRun.setOnClickListener {
+            lifecycleScope.launch(Dispatchers.IO) {
+                pdfSamples?.run(outputListener)
+            }
+        }
+        btnOpenFiles.setOnClickListener {
+            pdfSamples?.let { pdfSamples ->
+                pdfSamples.outputFileList?.let {
+                    AlertDialog.Builder(this)
+                            .apply {
+                                setTitle(R.string.choose_a_file_to_open)
+                                setItems(pdfSamples.outputFileNames) { dialog: DialogInterface?, which: Int ->
+                                    val filePath = it[which]
+                                    var mimeType = "application/pdf"
+                                    if (filePath.endsWith(".pdf")) {
+                                        mimeType = "application/pdf"
+                                    } else if (filePath.endsWith("png") || filePath.endsWith("jpg")) {
+                                        mimeType = "image/*"
+                                    } else if (filePath.endsWith(".xfdf")) {
+                                        mimeType = "application/vnd.adobe.xfdf"
+                                    }
+                                    FileUtils.shareFile(this@SampleDetailActivity, "Open", mimeType, File(filePath))
+                                }
+                            }.create().show()
+                }
+            }
+        }
+    }
+
+    override fun onSupportNavigateUp(): Boolean {
+        onBackPressed()
+        return super.onSupportNavigateUp()
+    }
+
+    companion object {
+        protected const val EXTRA_SAMPLE_ID = "SAMPLE_ID"
+        fun openDetail(context: Context, sampleId: Int) {
+            context.startActivity(Intent(context, SampleDetailActivity::class.java).apply {
+                putExtra(EXTRA_SAMPLE_ID, sampleId)
+            })
+        }
+    }
+}

+ 38 - 0
Samples_kotlin/src/main/java/com/compdfkit/samples/SampleListActivity.kt

@@ -0,0 +1,38 @@
+/**
+ * Copyright © 2014-2023 PDF Technologies, Inc. All Rights Reserved.
+ *
+ *
+ * THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
+ * AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE ComPDFKit LICENSE AGREEMENT.
+ * UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
+ * This notice may not be removed from this file.
+ */
+package com.compdfkit.samples
+
+import android.os.Bundle
+import android.os.Environment
+import androidx.appcompat.app.AppCompatActivity
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import com.compdfkit.samples.util.FileUtils
+
+class SampleListActivity : AppCompatActivity() {
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(R.layout.activity_sample_list)
+        val file = getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS)
+        file?.let {
+            FileUtils.deleteFile(it)
+        }
+        supportActionBar?.setTitle(R.string.samples)
+        val recyclerView = findViewById<RecyclerView>(R.id.rv_sample_list)
+        val listAdapter = SampleListAdapter().apply {
+            onItemClickListener = { position ->
+                SampleDetailActivity.openDetail(this@SampleListActivity, position)
+            }
+        }
+        recyclerView.layoutManager = LinearLayoutManager(this)
+        recyclerView.adapter = listAdapter
+    }
+}

+ 50 - 0
Samples_kotlin/src/main/java/com/compdfkit/samples/SampleListAdapter.kt

@@ -0,0 +1,50 @@
+/**
+ * Copyright © 2014-2023 PDF Technologies, Inc. All Rights Reserved.
+ *
+ * THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
+ * AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE ComPDFKit LICENSE AGREEMENT.
+ * UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
+ * This notice may not be removed from this file.
+ */
+package com.compdfkit.samples
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.appcompat.widget.AppCompatTextView
+import androidx.recyclerview.widget.RecyclerView
+
+class SampleListAdapter : RecyclerView.Adapter<SampleListAdapter.SampleListViewHolder>() {
+
+    private val list: MutableList<PDFSamples> = ArrayList()
+
+    var onItemClickListener : ((position : Int) -> Unit)? = null
+
+    init {
+        list.addAll(SampleApplication.instance.samplesList)
+    }
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SampleListViewHolder {
+        return SampleListViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.layout_sample_list_item, parent, false))
+    }
+
+    override fun onBindViewHolder(holder: SampleListViewHolder, position: Int) {
+        holder.tvSampleTitle.text = list[holder.adapterPosition].title
+        holder.itemView.setOnClickListener {
+            onItemClickListener?.invoke(holder.adapterPosition)
+        }
+    }
+
+    override fun getItemCount(): Int {
+        return list.size
+    }
+
+    class SampleListViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
+        val tvSampleTitle: AppCompatTextView
+
+        init {
+            tvSampleTitle = itemView.findViewById(R.id.tv_sample_title)
+        }
+    }
+
+}

+ 74 - 0
Samples_kotlin/src/main/java/com/compdfkit/samples/samples/AnnotationImportExportTest.kt

@@ -0,0 +1,74 @@
+/**
+ * Copyright © 2014-2023 PDF Technologies, Inc. All Rights Reserved.
+ *
+ * THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
+ * AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE ComPDFKit LICENSE AGREEMENT.
+ * UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
+ * This notice may not be removed from this file.
+ */
+package com.compdfkit.samples.samples
+
+import com.compdfkit.core.document.CPDFDocument
+import com.compdfkit.samples.PDFSamples
+import com.compdfkit.samples.R
+import com.compdfkit.samples.SampleApplication
+import com.compdfkit.samples.util.FileUtils.getAssetsTempFile
+import com.compdfkit.samples.util.OutputListener
+import java.io.File
+
+class AnnotationImportExportTest : PDFSamples() {
+
+    init {
+        setTitle(R.string.annotation_import_export_test_title)
+        setDescription(R.string.annotation_import_export_test_desc)
+    }
+
+    override fun run(outputListener: OutputListener?) {
+        super.run(outputListener)
+        printHead()
+        exportAnnotation()
+        importAnnotation()
+        printFooter()
+    }
+
+    /**
+     * Samples 1 : export pdf document annotations
+     */
+    private fun exportAnnotation() {
+        printDividingLine()
+        // Open the pdf document containing comments that needs to be exported
+        val document = CPDFDocument(context())
+        document.open(getAssetsTempFile(context(), "Annotations.pdf"))
+        // Set annotations export path, cache directory
+        val exportFile = File(outputDir(), "AnnotationImportExportTest/ExportAnnotationTest.xfdf")
+        val cacheDir = File(context().cacheDir, "AnnotationImportExportTest/")
+        // To create a directory, please ensure that the path exists.
+        exportFile.parentFile?.mkdirs()
+        cacheDir.mkdirs()
+        document.exportAnnotations(exportFile.absolutePath, cacheDir.absolutePath)
+        document.close()
+        if (exportFile.exists()) {
+            getOutputFileList().add(exportFile.absolutePath)
+        }
+        outputListener?.println("Done.")
+        outputListener?.println("Done. Results saved in ExportAnnotationTest.xfdf")
+    }
+
+    /**
+     * Samples 2 : Import a previously exported comment file into a blank document
+     */
+    private fun importAnnotation() {
+        // Open the pdf document containing comments that needs to be exported
+        val document = CPDFDocument(context())
+        document.open(getAssetsTempFile(context(), "CommonFivePage.pdf"))
+        // Get imported annotation files
+        val importFile = File(outputDir(), "AnnotationImportExportTest/ExportAnnotationTest.xfdf")
+        val cacheDir = File(SampleApplication.instance.cacheDir, "AnnotationImportExportTest/")
+        cacheDir.mkdirs()
+        document.importAnnotations(importFile.absolutePath, cacheDir.absolutePath)
+        val file = File(outputDir(), "AnnotationImportExportTest/ImportAnnotationTest.pdf")
+        saveSamplePDF(document, file, true)
+        outputListener?.println("Done.")
+        outputListener?.println("Done. Results saved in ImportAnnotationTest.pdf")
+    }
+}

+ 560 - 0
Samples_kotlin/src/main/java/com/compdfkit/samples/samples/AnnotationTest.kt

@@ -0,0 +1,560 @@
+/**
+ * Copyright © 2014-2023 PDF Technologies, Inc. All Rights Reserved.
+ *
+ *
+ * THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
+ * AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE ComPDFKit LICENSE AGREEMENT.
+ * UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
+ * This notice may not be removed from this file.
+ */
+package com.compdfkit.samples.samples
+
+import android.graphics.BitmapFactory
+import android.graphics.Color
+import android.graphics.PointF
+import android.graphics.RectF
+import com.compdfkit.core.annotation.CPDFAnnotation
+import com.compdfkit.core.annotation.CPDFBorderStyle
+import com.compdfkit.core.annotation.CPDFCircleAnnotation
+import com.compdfkit.core.annotation.CPDFFreetextAnnotation
+import com.compdfkit.core.annotation.CPDFHighlightAnnotation
+import com.compdfkit.core.annotation.CPDFInkAnnotation
+import com.compdfkit.core.annotation.CPDFLineAnnotation
+import com.compdfkit.core.annotation.CPDFLinkAnnotation
+import com.compdfkit.core.annotation.CPDFSoundAnnotation
+import com.compdfkit.core.annotation.CPDFSquareAnnotation
+import com.compdfkit.core.annotation.CPDFStampAnnotation
+import com.compdfkit.core.annotation.CPDFStampAnnotation.StandardStamp
+import com.compdfkit.core.annotation.CPDFStampAnnotation.TextStamp
+import com.compdfkit.core.annotation.CPDFTextAnnotation
+import com.compdfkit.core.annotation.CPDFTextAttribute
+import com.compdfkit.core.document.CPDFDestination
+import com.compdfkit.core.document.CPDFDocument
+import com.compdfkit.core.document.action.CPDFGoToAction
+import com.compdfkit.core.page.CPDFPage
+import com.compdfkit.samples.PDFSamples
+import com.compdfkit.samples.R
+import com.compdfkit.samples.util.FileUtils.getAssetsTempFile
+import com.compdfkit.samples.util.OutputListener
+import java.io.File
+import java.text.SimpleDateFormat
+import java.util.Date
+import kotlin.math.abs
+
+class AnnotationTest : PDFSamples() {
+    init {
+        setTitle(R.string.annotation_test_title)
+        setDescription(R.string.annotation_test_desc)
+    }
+
+    override fun run(outputListener: OutputListener?) {
+        super.run(outputListener)
+        printHead()
+        val document = CPDFDocument(context())
+        document.open(getAssetsTempFile(context(), "CommonFivePage.pdf"))
+        printDividingLine()
+        //------------------------------------------
+        //Samples 1 : add freetext annotation
+        addFreeText(document)
+
+        //------------------------------------------
+        //Samples 2 : add ink annotation
+        addInk(document)
+
+        //------------------------------------------
+        //Samples 3 : add line annotation
+        addLine(document)
+
+        //------------------------------------------
+        //Samples 4 : add circle annotation
+        addCircleShape(document)
+
+        //------------------------------------------
+        //Samples 5 : add square annotation
+        addSquareShape(document)
+
+        //------------------------------------------
+        //Samples 6 : add highlight(markup) annotation
+        addHighlight(document)
+
+        //------------------------------------------
+        //Samples 7 : add link annotation
+        addLink(document)
+
+        //------------------------------------------
+        //Samples 8 : add note annotation
+        addNote(document)
+
+        //------------------------------------------
+        //Samples 8 : add sound annotation
+        addSound(document)
+
+        //------------------------------------------
+        //Samples 9 : add stamp annotation
+        addStamp(document)
+        val file = File(outputDir(), "AnnotationTest/CreateAnnotationTest.pdf")
+        saveSamplePDF(document, file, true)
+        outputListener!!.println("Done.")
+        outputListener.println("\nDone. Result saved in CreateAnnotationTest.pdf")
+        printDividingLine()
+
+        //------------------------------------------
+        //Samples 10 : print annotation list info
+        printAnnotationList()
+
+        //------------------------------------------
+        //Samples 11 : delete annotation
+        deleteAnnotation()
+        printFooter()
+    }
+
+    /**
+     * Samples 1 : add freetext annotation
+     *
+     * @param document
+     */
+    private fun addFreeText(document: CPDFDocument) {
+        // Insert the free text annotation into the first page of the PDF document.
+        val page1 = document.pageAtIndex(0)
+        (page1.addAnnot(CPDFAnnotation.Type.FREETEXT) as CPDFFreetextAnnotation).apply {
+            rect = getConvertRect(page1, RectF(10f, 200f, 160f, 570f))
+            // set text alignment
+            freetextAlignment = CPDFFreetextAnnotation.Alignment.ALIGNMENT_LEFT
+            // Set text font, bold, italic, color and font size
+            freetextDa = CPDFTextAttribute(CPDFTextAttribute.FontNameHelper.obtainFontName(
+                    CPDFTextAttribute.FontNameHelper.FontType.Courier, false, false
+            ), 12f, Color.RED)
+            // set text color opacity
+            alpha = 255
+            content = """
+             Some swift brown fox snatched a gray hare out of the air by freezing it with an angry glare.
+             Aha!
+             And there was much rejoicing!
+             """
+            updateAp()
+        }
+    }
+
+    /**
+     * Samples 2 : add ink annotation
+     *
+     * @param document
+     */
+    private fun addInk(document: CPDFDocument) {
+        // Insert the ink annotation into the first page of the PDF document.
+        val page1 = document.pageAtIndex(0)
+        val mDrawing = arrayListOf(
+                arrayListOf(
+                        PointF(100F, 100F),
+                        PointF(110F, 110F),
+                        PointF(120F, 120F)
+                ),
+                arrayListOf(
+                        PointF(115F, 115F),
+                        PointF(130F, 130F),
+                        PointF(160F, 160F)
+                )
+        )
+        val scaleValue = 1F
+        val borderWidth = 5F
+        val inkAnnotation = page1.addAnnot(CPDFAnnotation.Type.INK) as CPDFInkAnnotation
+        inkAnnotation.color = Color.RED
+        inkAnnotation.alpha = 255
+        inkAnnotation.borderWidth = borderWidth
+
+        var rect: RectF? = null
+        val size = document.getPageSize(0)
+        if (size.isEmpty) {
+            return
+        }
+        val lineCount = mDrawing.size
+        val path: Array<Array<PointF?>?> = arrayOfNulls(lineCount)
+        for (lineIndex in 0 until lineCount) {
+            val line = mDrawing[lineIndex]
+            val pointCount = line.size
+            val linePath = arrayOfNulls<PointF>(pointCount)
+            for (pointIndex in 0 until pointCount) {
+                val point = line[pointIndex]
+                // Calculate the smallest Rect that the Path is surrounded by
+                if (rect == null) {
+                    rect = RectF(point.x, point.y, point.x, point.y)
+                } else {
+                    rect.union(point.x, point.y)
+                }
+                // Calculate the coordinate points that are converted to the Page and stored in the linePath collection
+                linePath[pointIndex] = page1.convertPointToPage(false, size.width(), size.height(), point)
+            }
+            path[lineIndex] = linePath
+        }
+        val dx = borderWidth / scaleValue / 2
+        rect?.inset(-dx, -dx)
+        rect?.set(page1.convertRectToPage(false, size.width(), size.height(), rect))
+        inkAnnotation.inkPath = path
+        inkAnnotation.rect = rect
+        inkAnnotation.updateAp()
+        mDrawing.clear()
+    }
+
+    /**
+     * Samples 3 : add line shape annotation
+     *
+     * @param document
+     */
+    private fun addLine(document: CPDFDocument) {
+        // Add a green dotted line annotation
+        val page2 = document.pageAtIndex(1)
+        (page2.addAnnot(CPDFAnnotation.Type.LINE) as CPDFLineAnnotation).apply {
+            // line start point
+            val startPoint = PointF(200F, 100F)
+            // line end point
+            val endPoint = PointF(50F, 300F)
+            // Get the position of the line on the page
+            rect = kotlin.run {
+                val area = RectF()
+                convertLinePoint(page2, startPoint, endPoint, area)
+                area
+            }
+            setLinePoints(startPoint, endPoint)
+            // Sets the arrowhead shape at both ends of the line
+            setLineType(CPDFLineAnnotation.LineType.LINETYPE_NONE, CPDFLineAnnotation.LineType.LINETYPE_NONE)
+            // Set line to dash and spacer width
+            borderStyle = CPDFBorderStyle(CPDFBorderStyle.Style.Border_Dashed, 10F, floatArrayOf(8F, 4F))
+            // set line width
+            borderWidth = 3F
+            borderAlpha = 255
+            borderColor = Color.GREEN
+            updateAp()
+        }
+
+
+        //Add a solid red line annotation with an arrow type
+        (page2.addAnnot(CPDFAnnotation.Type.LINE) as CPDFLineAnnotation).apply {
+            val startPoint2 = PointF(200F, 350F)
+            val endPoint2 = PointF(50F, 550F)
+            rect = kotlin.run {
+                val area2 = RectF()
+                convertLinePoint(page2, startPoint2, endPoint2, area2)
+                area2
+            }
+            setLinePoints(startPoint2, endPoint2)
+            // Set the start position of the arrow as circle and the end of the arrow as the arrow type
+            setLineType(CPDFLineAnnotation.LineType.LINETYPE_CIRCLE, CPDFLineAnnotation.LineType.LINETYPE_ARROW)
+            // set line to solid
+            borderStyle = CPDFBorderStyle(CPDFBorderStyle.Style.Border_Solid, 10f, floatArrayOf(8f, 0f))
+            borderWidth = 5F
+            borderAlpha = 255
+            borderColor = Color.RED
+            updateAp()
+        }
+
+        // Add a solid red line annotation
+        (page2.addAnnot(CPDFAnnotation.Type.LINE) as CPDFLineAnnotation).apply {
+            val startPoint3 = PointF(400F, 100F)
+            val endPoint3 = PointF(250F, 300F)
+            rect = kotlin.run {
+                val area3 = RectF()
+                convertLinePoint(page2, startPoint3, endPoint3, area3)
+                area3
+            }
+            setLinePoints(startPoint3, endPoint3)
+            setLineType(CPDFLineAnnotation.LineType.LINETYPE_NONE, CPDFLineAnnotation.LineType.LINETYPE_NONE)
+            borderStyle = CPDFBorderStyle(CPDFBorderStyle.Style.Border_Solid, 10F, floatArrayOf(8F, 0F))
+            borderWidth = 5F
+            borderAlpha = 255
+            borderColor = Color.BLUE
+            updateAp()
+        }
+    }
+
+    private fun convertLinePoint(page: CPDFPage, startPoint: PointF, endPoint: PointF, area: RectF) {
+        val pageSize = page.size
+        val lineWidth = 10F
+
+        val minX = startPoint.x.coerceAtMost(endPoint.x) - lineWidth * 2
+        val minY = startPoint.y.coerceAtMost(endPoint.y) - lineWidth * 2
+        val maxX = startPoint.x.coerceAtLeast(endPoint.x) + lineWidth * 2
+        val maxY = startPoint.y.coerceAtLeast(endPoint.y) + lineWidth * 2
+
+        area.set(minX, minY, maxX, maxY)
+        area.set(page.convertRectToPage(false, pageSize.width(), pageSize.height(), area))
+        startPoint.set(page.convertPointToPage(false, pageSize.width(), pageSize.height(), startPoint))
+        endPoint.set(page.convertPointToPage(false, pageSize.width(), pageSize.height(), endPoint))
+    }
+
+    /**
+     * Samples 4 : add circle shape annotation
+     *
+     * @param document
+     */
+    private fun addCircleShape(document: CPDFDocument) {
+        // Add a circular annotation with a green border and blue fill
+        val page3 = document.pageAtIndex(2)
+        (page3.addAnnot(CPDFAnnotation.Type.CIRCLE) as CPDFCircleAnnotation).apply {
+            val insertRect = getConvertRect(page3, RectF(50F, 50F, 150F, 150F))
+            rect = insertRect
+            // set border color
+            borderColor = Color.parseColor("#3863F1")
+            // Set border to solid line
+            borderStyle = CPDFBorderStyle(CPDFBorderStyle.Style.Border_Solid, 10F, floatArrayOf(8.0F, 0F)).apply {
+                borderWidth = 5F
+            }
+            borderAlpha = 255
+            fillColor = Color.parseColor("#31BC98")
+            fillAlpha = 255
+            updateAp()
+        }
+
+        //Add a circular shape annotation with a dotted border.
+        (page3.addAnnot(CPDFAnnotation.Type.CIRCLE) as CPDFCircleAnnotation).apply {
+            rect = getConvertRect(page3, RectF(50F, 200F, 150F, 300F))
+            borderColor = Color.parseColor("#3863F1")
+            borderStyle = CPDFBorderStyle(CPDFBorderStyle.Style.Border_Dashed, 10F, floatArrayOf(8.0F, 4F)).apply {
+                borderWidth = 5F
+            }
+            borderAlpha = 127
+            fillColor = Color.parseColor("#31BC98")
+            fillAlpha = 127
+            updateAp()
+        }
+    }
+
+    /**
+     * Samples 5 : add square shape annotation
+     *
+     * @param document
+     */
+    private fun addSquareShape(document: CPDFDocument) {
+        // Add a rectangle with a blue border and green fill
+        val page3 = document.pageAtIndex(2)
+        (page3.addAnnot(CPDFAnnotation.Type.SQUARE) as CPDFSquareAnnotation).apply {
+            rect = getConvertRect(page3, RectF(50F, 350F, 300F, 450F))
+            borderColor = Color.parseColor("#3863F1")
+            borderStyle = CPDFBorderStyle(CPDFBorderStyle.Style.Border_Solid, 10F, floatArrayOf(8.0F, 0F)).apply {
+                borderWidth = 10F
+            }
+            borderAlpha = 255
+            fillColor = Color.parseColor("#31BC98")
+            fillAlpha = 255
+            updateAp()
+        }
+
+        // Add a rectangle with a blue dotted border and a green fill with 50% transparency
+        // Add a rectangle with a blue dotted border and a green fill with 50% transparency
+        val pageSize = page3.size
+        // Add a rectangle with a blue dotted border and a green fill with 50% transparency
+        val squareAnnotation2 = page3.addAnnot(CPDFAnnotation.Type.SQUARE) as CPDFSquareAnnotation
+        var insertRect2: RectF? = RectF(50F, 500F, 300F, 600F)
+        insertRect2 = page3.convertRectToPage(false, pageSize.width(), pageSize.height(), insertRect2)
+        squareAnnotation2.rect = insertRect2
+        squareAnnotation2.borderColor = Color.parseColor("#3863F1")
+        val borderStyle2 = CPDFBorderStyle(CPDFBorderStyle.Style.Border_Dashed, 10f, floatArrayOf(8.0f, 4f))
+        borderStyle2.borderWidth = 10f
+        squareAnnotation2.borderStyle = borderStyle2
+        squareAnnotation2.borderAlpha = 127
+        squareAnnotation2.fillColor = Color.parseColor("#31BC98")
+        squareAnnotation2.fillAlpha = 127
+        squareAnnotation2.updateAp()
+    }
+
+    /**
+     * Samples 6 : add high annotation
+     * Here is a demonstration of searching out keywords in the search page and adding highlighted annotation
+     *
+     * @param document
+     */
+    private fun addHighlight(document: CPDFDocument) {
+        //Also search for the `Page` keyword in the 3rd of the document
+        val pdfPage = document.pageAtIndex(3)
+        //Then, add a highlight annotation for the specific area.
+        (pdfPage.addAnnot(CPDFAnnotation.Type.HIGHLIGHT) as CPDFHighlightAnnotation).apply {
+            var annotRect = RectF(315F, 258F, 372F, 288F)
+            color = Color.YELLOW
+            alpha = 255 / 2
+            quadRects = kotlin.run {
+                annotRect = pdfPage.convertRectToPage(false, pdfPage.size.width(), pdfPage.size.height(), annotRect)
+                arrayOf(annotRect)
+            }
+            markedText = "Page"
+            rect = annotRect
+            updateAp()
+            outputListener!!.println(annotRect.toString())
+        }
+    }
+
+    /**
+     * Samples 7 : add link annotation
+     *
+     * @param document
+     */
+    private fun addLink(document: CPDFDocument) {
+        val page = document.pageAtIndex(3)
+        (page.addAnnot(CPDFAnnotation.Type.LINK) as CPDFLinkAnnotation).apply {
+            rect = getConvertRect(page, RectF(50F, 50F, 150F, 150F))
+            val firstPageHeight = document.getPageSize(0).height()
+            // Add page jump link action
+            linkAction = CPDFGoToAction().apply {
+                val destination = CPDFDestination(1, 0F, firstPageHeight, 1F)
+                setDestination(document, destination)
+            }
+            updateAp()
+        }
+    }
+
+    /**
+     * Samples 8 : add note annotation
+     *
+     * @param document
+     */
+    private fun addNote(document: CPDFDocument) {
+        val page = document.pageAtIndex(3)
+        val textAnnotation = page.addAnnot(CPDFAnnotation.Type.TEXT) as CPDFTextAnnotation
+        //get the actual size of the page you want to insert
+        val insertRect = getConvertRect(page, RectF(50F, 200F, 100F, 250F))
+        textAnnotation.rect = insertRect
+        textAnnotation.content = "ComPDFKit"
+        textAnnotation.updateAp()
+    }
+
+    /**
+     * Samples 9 : add sound annotation
+     *
+     * @param document
+     */
+    private fun addSound(document: CPDFDocument) {
+        val page = document.pageAtIndex(3)
+        val soundAnnotation = page.addAnnot(CPDFAnnotation.Type.SOUND) as CPDFSoundAnnotation
+        val insertRect = getConvertRect(page, RectF(50F, 300F, 100F, 350F))
+        soundAnnotation.rect = insertRect
+        soundAnnotation.setSoundPath(getAssetsTempFile(context(), "Bird.wav"))
+        soundAnnotation.updateAp()
+    }
+
+    /**
+     * Samples 10 : add stamp annotation
+     *
+     * @param document
+     */
+    private fun addStamp(document: CPDFDocument) {
+        // add standard stamp annotation
+        var yOffset = 50
+        var lastOffset = 0F
+        for (i in StandardStamp.values().indices) {
+            val page = document.pageAtIndex(4)
+            val standardStamp = StandardStamp.values()[i]
+            if (standardStamp == null || standardStamp == StandardStamp.UNKNOWN) {
+                continue
+            }
+            // add Standard stamp
+            val standard = page.addAnnot(CPDFAnnotation.Type.STAMP) as CPDFStampAnnotation
+            standard.standardStamp = standardStamp
+            val pageSize = page.size
+            val insertRect = standard.rect
+            insertRect.set(page.convertRectFromPage(false, pageSize.width(), pageSize.height(), insertRect))
+            val defaultWidth = 100F
+            var x = 50
+            if (i == 10) {
+                lastOffset = 50F
+            }
+            if (i >= 10) {
+                x = 150
+            }
+            yOffset = lastOffset.toInt() + 10
+            val vertex = PointF(x.toFloat(), yOffset.toFloat())
+            insertRect[vertex.x, vertex.y, vertex.x + defaultWidth] = vertex.y + defaultWidth * abs(insertRect.height() / insertRect.width())
+            lastOffset = insertRect.bottom
+            standard.rect = page.convertRectToPage(false, pageSize.width(), pageSize.height(), insertRect)
+            standard.updateAp()
+        }
+
+        //add text stamp annotations
+        val page = document.pageAtIndex(4)
+        val stampAnnotation = page.addAnnot(CPDFAnnotation.Type.STAMP) as CPDFStampAnnotation
+        val df = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
+        val date = df.format(Date())
+        stampAnnotation.textStamp = TextStamp(
+                "ComPDFKit", date, CPDFStampAnnotation.TextStampShape.TEXTSTAMP_RECT.id,
+                CPDFStampAnnotation.TextStampColor.TEXTSTAMP_GREEN.id)
+        val pageSize = page.size
+        val insertRect = stampAnnotation.rect
+        insertRect.set(page.convertRectFromPage(false, pageSize.width(), pageSize.height(), insertRect))
+        val defaultWidth = 150F
+        val vertex = PointF(300F, 50F)
+        insertRect[vertex.x, vertex.y, vertex.x + defaultWidth] = vertex.y + defaultWidth * abs(insertRect.height() / insertRect.width())
+        stampAnnotation.rect = page.convertRectToPage(false, pageSize.width(), pageSize.height(), insertRect)
+        stampAnnotation.updateAp()
+
+        // add image stamp annotations
+        val standard = page.addAnnot(CPDFAnnotation.Type.STAMP) as CPDFStampAnnotation
+        val imagePath = getAssetsTempFile(context(), "ComPDFKit.png")
+        val options = BitmapFactory.Options()
+        options.inJustDecodeBounds = true
+        BitmapFactory.decodeFile(imagePath, options)
+
+        standard.rect = kotlin.run {
+            val imageDefaultWidth = 100F
+            val imageVertex = PointF(300F, 300F)
+            val imageInsertRect = RectF(imageVertex.x, imageVertex.y, imageVertex.x + imageDefaultWidth, imageVertex.y + imageDefaultWidth * options.outHeight / options.outWidth)
+            val pageSize1 = page.size
+            imageInsertRect.set(page.convertRectToPage(false, pageSize1.width(), pageSize1.height(), imageInsertRect))
+            imageInsertRect
+        }
+        if (imagePath?.endsWith("png", true) == true) {
+            val tmpOptions = BitmapFactory.Options()
+            tmpOptions.inMutable = true
+            val bitmap = BitmapFactory.decodeFile(imagePath, tmpOptions)
+            standard.updateApWithBitmap(bitmap)
+            bitmap.recycle()
+        } else {
+            standard.setImageStamp(imagePath)
+            standard.updateAp()
+        }
+    }
+
+    private fun getConvertRect(page: CPDFPage, rectF: RectF): RectF {
+        val size = page.size
+        return page.convertRectToPage(false, size.width(), size.height(), rectF)
+    }
+
+    /**
+     * Samples 10 : print annotation list info
+     */
+    private fun printAnnotationList() {
+        printDividingLine()
+        val sampleFile = File(outputDir(), "AnnotationTest/CreateAnnotationTest.pdf")
+        val document = CPDFDocument(context())
+        document.open(sampleFile.absolutePath)
+        for (i in 0 until document.pageCount) {
+            val page = document.pageAtIndex(i)
+            for (annotation in page.annotations) {
+                outputListener?.println("Page: " + (i + 1))
+                outputListener?.println("Annot Type: " + annotation.type.name)
+                val position = page.convertRectFromPage(false, document.getPageSize(i).width(),
+                        document.getPageSize(i).height(), annotation.rect)
+                outputListener?.println(String.format("Position: %d, %d, %d, %d", position.left.toInt(), position.top.toInt(), position.right.toInt(), position.bottom.toInt()))
+                printDividingLine()
+            }
+        }
+        document.close()
+    }
+
+    /**
+     * Samples 11 : delete annotation
+     */
+    private fun deleteAnnotation() {
+        val sampleFile = File(outputDir(), "AnnotationTest/CreateAnnotationTest.pdf")
+        val document = CPDFDocument(context())
+        document.open(sampleFile.absolutePath)
+        for (i in 0 until document.pageCount) {
+            val page = document.pageAtIndex(i)
+            val annotations = page.annotations
+            if (annotations != null && annotations.size > 0) {
+                page.deleteAnnotation(annotations[0])
+                break
+            }
+        }
+        val file = File(outputDir(), "AnnotationTest/DeleteAnnotationTest.pdf")
+        saveSamplePDF(document, file, true)
+        outputListener?.println("Done.")
+        outputListener?.println("Done. Results saved in DeleteAnnotationTest.pdf")
+    }
+}

+ 114 - 0
Samples_kotlin/src/main/java/com/compdfkit/samples/samples/BackgroundTest.kt

@@ -0,0 +1,114 @@
+/**
+ * Copyright © 2014-2023 PDF Technologies, Inc. All Rights Reserved.
+ *
+ * THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
+ * AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE ComPDFKit LICENSE AGREEMENT.
+ * UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
+ * This notice may not be removed from this file.
+ */
+package com.compdfkit.samples.samples
+
+import android.graphics.BitmapFactory
+import android.graphics.Color
+import com.compdfkit.core.annotation.CPDFImageScaleType
+import com.compdfkit.core.document.CPDFBackground
+import com.compdfkit.core.document.CPDFDocument
+import com.compdfkit.samples.PDFSamples
+import com.compdfkit.samples.R
+import com.compdfkit.samples.util.FileUtils.getAssetsTempFile
+import com.compdfkit.samples.util.OutputListener
+import java.io.File
+
+class BackgroundTest : PDFSamples() {
+    init {
+        setTitle(R.string.background_test_title)
+        setDescription(R.string.background_test_desc)
+    }
+
+    override fun run(outputListener: OutputListener?) {
+        super.run(outputListener)
+        printHead()
+        addColorBackground()
+        addImageBackground()
+        deleteBackground()
+        printFooter()
+    }
+
+    private fun addColorBackground() {
+        outputListener?.println()
+        outputListener?.println("Samples 1 : Set the document background color")
+        val document = CPDFDocument(context())
+        document.open(getAssetsTempFile(context(), "CommonFivePage.pdf"))
+        document.background.apply {
+            type = CPDFBackground.Type.Color
+            color = Color.RED
+            opacity = 1f
+            pages = "0,1,2,3,4"
+            printBackgroundInfo(this)
+            update()
+            release()
+        }
+        val file = File(outputDir(), "backgroundTest/AddColorBackgroundTest.pdf")
+        saveSamplePDF(document, file, true)
+        outputListener?.println("Done. Result saved in AddColorBackgroundTest.pdf")
+        printDividingLine()
+    }
+
+    private fun addImageBackground() {
+        outputListener!!.println("Samples 2 : Set the document background image")
+        val document = CPDFDocument(context())
+        document.open(getAssetsTempFile(context(), "CommonFivePage.pdf"))
+        document.background.apply {
+            type = CPDFBackground.Type.Image
+            val backgroundImage = BitmapFactory.decodeResource(context().resources, R.mipmap.ic_launcher_foreground)
+            setImage(backgroundImage, CPDFImageScaleType.SCALETYPE_center)
+            opacity = 1F
+            pages = "0,1,2,3,4"
+            printBackgroundInfo(this)
+            update()
+            release()
+        }
+        val file = File(outputDir(), "backgroundTest/AddImageBackgroundTest.pdf")
+        saveSamplePDF(document, file, true)
+        outputListener?.println("Done. Result saved in AddImageBackgroundTest.pdf")
+        printDividingLine()
+    }
+
+    /**
+     * Sample 3 : Delete document background
+     */
+    private fun deleteBackground() {
+        outputListener?.println("Samples 3 : Delete document background")
+        val document = CPDFDocument(context())
+        val sampleFile = File(outputDir(),
+                "backgroundTest/AddColorBackgroundTest.pdf")
+        document.open(sampleFile.absolutePath)
+        //remove all pages background
+        document.background.clear()
+        //update pages
+        //background.setPages("0,2,3,4");
+        //background.update();
+        //background.release();
+        val file = File(outputDir(), "backgroundTest/DeleteBackgroundTest.pdf")
+        saveSamplePDF(document, file, true)
+        outputListener?.println("Done. Result saved in DeleteBackgroundTest.pdf")
+    }
+
+    private fun printBackgroundInfo(background: CPDFBackground) {
+        outputListener?.println("Type : ${background.type.name}")
+        if (background.type == CPDFBackground.Type.Color) {
+            outputListener?.println(String.format("Color : red:%d, green:%d, blue:%d, alpha:%d",
+                    Color.red(background.color),
+                    Color.green(background.color),
+                    Color.blue(background.color),
+                    Color.alpha(background.color)))
+        }
+        outputListener?.println("Opacity : ${background.opacity}")
+        outputListener?.println("Rotation : ${background.rotation}")
+        outputListener?.println("Vertalign : ${background.vertalign.name}")
+        outputListener?.println("Horizalign : ${background.horizalign.name}")
+        outputListener?.println("VertOffset : ${background.xOffset}")
+        outputListener?.println("HorizOffset : ${background.yOffset}")
+        outputListener?.println("Pages : ${background.pages}")
+    }
+}

+ 99 - 0
Samples_kotlin/src/main/java/com/compdfkit/samples/samples/BatesTest.kt

@@ -0,0 +1,99 @@
+/**
+ * Copyright © 2014-2023 PDF Technologies, Inc. All Rights Reserved.
+ *
+ * THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
+ * AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE ComPDFKit LICENSE AGREEMENT.
+ * UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
+ * This notice may not be removed from this file.
+ */
+package com.compdfkit.samples.samples
+
+import android.graphics.Color
+import com.compdfkit.core.document.CPDFDocument
+import com.compdfkit.samples.PDFSamples
+import com.compdfkit.samples.R
+import com.compdfkit.samples.util.FileUtils.getAssetsTempFile
+import com.compdfkit.samples.util.OutputListener
+import java.io.File
+
+class BatesTest : PDFSamples() {
+    init {
+        setTitle(R.string.bates_test_title)
+        setDescription(R.string.bates_test_desc)
+    }
+
+    override fun run(outputListener: OutputListener?) {
+        super.run(outputListener)
+        printHead()
+        addCommonBates()
+        editDateBates()
+        clearBates()
+        printFooter()
+    }
+
+    /**
+     * Samples 1 : Add bates
+     */
+    private fun addCommonBates() {
+        printDividingLine()
+        outputListener?.println("Samples 1 : Add Bates")
+        val document = CPDFDocument(context())
+        document.open(getAssetsTempFile(context(), "CommonFivePage.pdf"))
+        val bates = document.bates
+        val num = 6
+        for (i in 0 until num) {
+            outputListener?.println("\nText : <<#3#5#Prefix-#-Suffix>>")
+            bates.setText(i, "<<#3#5#Prefix-#-Suffix>>")
+            bates.setTextColor(i, Color.RED)
+            bates.setFontSize(i, 14F)
+            when (i) {
+                0 -> outputListener?.println("Location: Top Left")
+                1 -> outputListener?.println("Location: Top Middle")
+                2 -> outputListener?.println("Location: Top Right")
+                3 -> outputListener?.println("Location: Botton Left")
+                4 -> outputListener?.println("Location: Botton Middle")
+                else -> outputListener?.println("Location: Botton Right")
+            }
+        }
+        bates.pages = "0,1,2,3,4"
+        bates.update()
+        val file = File(outputDir(), "BatesTest/AddBatesTest.pdf")
+        saveSamplePDF(document, file, true)
+        outputListener?.println("Done. Results saved in 1 AddBatesTest.pdf")
+        printDividingLine()
+    }
+
+    /**
+     * Samples 2: edit bates
+     */
+    private fun editDateBates() {
+        outputListener?.println("Samples 2 : edit bates")
+        val document = CPDFDocument(context())
+        val file = File(outputDir(), "BatesTest/AddBatesTest.pdf")
+        document.open(file.absolutePath)
+        val bates = document.bates
+        outputListener?.println("Get old bates 0 succeeded, text is ${bates.getText(0)}")
+        outputListener?.println("Change bates 0 succeeded, new text is <<#3#1#ComPDFKit-#-ComPDFKit>>")
+        bates.setText(0, "<<#3#1#ComPDFKit-#-ComPDFKit>>")
+        bates.update()
+        val resultsFile = File(outputDir(), "BatesTest/EditBatesTest.pdf")
+        saveSamplePDF(document, resultsFile, false)
+        outputListener?.println("Done. Results saved in EditBatesTest.pdf")
+        printDividingLine()
+    }
+
+    /**
+     * samples 3 : clear bates
+     */
+    private fun clearBates() {
+        outputListener?.println("Samples 3 : clear bates")
+        val document = CPDFDocument(context())
+        val file = File(outputDir(), "BatesTest/AddBatesTest.pdf")
+        document.open(file.absolutePath)
+        val bates = document.bates.clear()
+        val resultsFile = File(outputDir(), "BatesTest/ClearBatesTest.pdf")
+        saveSamplePDF(document, resultsFile, false)
+        outputListener?.println("Done. Results saved in ClearBatesTest.pdf")
+        printDividingLine()
+    }
+}

+ 51 - 0
Samples_kotlin/src/main/java/com/compdfkit/samples/samples/BookmarkTest.kt

@@ -0,0 +1,51 @@
+/**
+ * Copyright © 2014-2023 PDF Technologies, Inc. All Rights Reserved.
+ *
+ *
+ * THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
+ * AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE ComPDFKit LICENSE AGREEMENT.
+ * UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
+ * This notice may not be removed from this file.
+ */
+package com.compdfkit.samples.samples
+
+import com.compdfkit.core.common.CPDFDate
+import com.compdfkit.core.document.CPDFBookmark
+import com.compdfkit.core.document.CPDFDocument
+import com.compdfkit.core.utils.TTimeUtil
+import com.compdfkit.samples.PDFSamples
+import com.compdfkit.samples.R
+import com.compdfkit.samples.util.FileUtils.getAssetsTempFile
+import com.compdfkit.samples.util.OutputListener
+import java.io.File
+
+class BookmarkTest : PDFSamples() {
+    init {
+        setTitle(R.string.bookmark_test_title)
+        setDescription(R.string.bookmark_test_desc)
+    }
+
+    override fun run(outputListener: OutputListener?) {
+        super.run(outputListener)
+        printHead()
+        outputListener?.println()
+        // Open test pdf document
+        val document = CPDFDocument(context())
+        document.open(getAssetsTempFile(context(), "CommonFivePage.pdf"))
+        // Add a bookmark that jumps to the second page of the document
+        document.addBookmark(CPDFBookmark(
+                1, "my bookmark", CPDFDate.toStandardDate(TTimeUtil.getCurrentDate())
+        ))
+        val list = document.bookmarks
+        if (list != null && list.size > 0) {
+            for (cpdfBookmark in list) {
+                outputListener?.println("Go to page " + (cpdfBookmark.pageIndex + 1))
+            }
+        }
+        val file = File(outputDir(), "BookmarkTest/CreateBookmarkTest.pdf")
+        outputListener?.println("Done.")
+        outputListener?.println("Done. Result saved in CreateBookmarkTest.pdf")
+        saveSamplePDF(document, file, true)
+        printFooter()
+    }
+}

+ 80 - 0
Samples_kotlin/src/main/java/com/compdfkit/samples/samples/DocumentInfoTest.kt

@@ -0,0 +1,80 @@
+/**
+ * Copyright © 2014-2023 PDF Technologies, Inc. All Rights Reserved.
+ *
+ * THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
+ * AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE ComPDFKit LICENSE AGREEMENT.
+ * UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
+ * This notice may not be removed from this file.
+ */
+package com.compdfkit.samples.samples
+
+import com.compdfkit.core.document.CPDFDocument
+import com.compdfkit.samples.PDFSamples
+import com.compdfkit.samples.R
+import com.compdfkit.samples.util.DateUtil.transformPDFDate
+import com.compdfkit.samples.util.FileUtils.getAssetsTempFile
+import com.compdfkit.samples.util.OutputListener
+
+class DocumentInfoTest : PDFSamples() {
+    init {
+        setTitle(R.string.document_test_title)
+        setDescription(R.string.document_test_desc)
+    }
+
+    override fun run(outputListener: OutputListener?) {
+        super.run(outputListener)
+        printHead()
+        val document = CPDFDocument(context())
+        document.open(getAssetsTempFile(context(), "CommonFivePage.pdf"))
+        outputListener?.println()
+        // Obtain basic information of pdf documents
+        val cpdfInfo = document.info
+        outputListener?.println("FileName : ${document.fileName}")
+        outputListener?.println("FileSize : ${getDocumentSize(document)}")
+        outputListener?.println("Title : ${cpdfInfo.title}")
+        outputListener?.println("Author : ${cpdfInfo.author}")
+        outputListener?.println("Subject : ${cpdfInfo.subject}")
+        outputListener?.println("Keywords : ${cpdfInfo.keywords}")
+        outputListener?.println("Version : ${document.majorVersion}")
+        outputListener?.println("PageCount : ${document.pageCount}")
+        outputListener?.println("Creator : ${cpdfInfo.creator}")
+        outputListener?.println("CreationDate : ${transformPDFDate(cpdfInfo.creationDate)}")
+        outputListener?.println("ModificationDate : ${transformPDFDate(cpdfInfo.modificationDate)}")
+
+        // Get pdf document permission information
+        val permissionInfo = document.permissionsInfo
+        outputListener?.println("Printing : ${permissionInfo.isAllowsPrinting}")
+        outputListener?.println("Content Copying : ${permissionInfo.isAllowsCopying}")
+        outputListener?.println("Document Change : ${permissionInfo.isAllowsDocumentChanges}")
+        outputListener?.println("Document Assembly : ${permissionInfo.isAllowsDocumentAssembly}")
+        outputListener?.println("Document Commenting : ${permissionInfo.isAllowsCommenting}")
+        outputListener?.println("Document Filling of form field : ${permissionInfo.isAllowsFormFieldEntry}")
+        document.close()
+        printFooter()
+    }
+
+    private fun getDocumentSize(document: CPDFDocument): String {
+        val MB = (1024 * 1024).toLong()
+        val KB = 1024
+        var fileSize = 0L
+        try {
+            val p = document.context.contentResolver.openFileDescriptor(document.uri, "r")
+            fileSize = p?.statSize ?:0L
+            p?.close()
+        } catch (e: Exception) {
+            e.printStackTrace()
+        }
+        val size: Float
+        var unit = " M"
+        if (fileSize > MB) {
+            size = fileSize.toFloat() / (1024 * 1024)
+        } else if (fileSize > KB) {
+            size = fileSize.toFloat() / 1024
+            unit = " KB"
+        } else {
+            size = fileSize.toFloat()
+            unit = " B"
+        }
+        return String.format("%.2f", size) + unit
+    }
+}

+ 139 - 0
Samples_kotlin/src/main/java/com/compdfkit/samples/samples/EncryptTest.kt

@@ -0,0 +1,139 @@
+/**
+ * Copyright © 2014-2023 PDF Technologies, Inc. All Rights Reserved.
+ *
+ *
+ * THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
+ * AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE ComPDFKit LICENSE AGREEMENT.
+ * UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
+ * This notice may not be removed from this file.
+ */
+package com.compdfkit.samples.samples
+
+import com.compdfkit.core.common.CPDFDocumentException
+import com.compdfkit.core.document.CPDFDocument
+import com.compdfkit.core.document.CPDFDocument.PDFDocumentEncryptAlgo
+import com.compdfkit.samples.PDFSamples
+import com.compdfkit.samples.R
+import com.compdfkit.samples.util.FileUtils.getAssetsTempFile
+import com.compdfkit.samples.util.OutputListener
+import java.io.File
+
+class EncryptTest : PDFSamples() {
+    init {
+        setTitle(R.string.encrypt_test_title)
+        setDescription(R.string.encrypt_test_desc)
+    }
+
+    override fun run(outputListener: OutputListener?) {
+        super.run(outputListener)
+        printHead()
+        printDividingLine()
+        outputListener?.println("Samples 1 : RC4 encrypt")
+        encryptDocument(PDFDocumentEncryptAlgo.PDFDocumentRC4, "EncryptUseRC4Test.pdf")
+        outputListener?.println("Samples 2 : AES128 encrypt")
+        encryptDocument(PDFDocumentEncryptAlgo.PDFDocumentAES128, "EncryptUseAES128Test.pdf")
+        outputListener?.println("Samples 3 : AES236 encrypt")
+        encryptDocument(PDFDocumentEncryptAlgo.PDFDocumentAES128, "EncryptUseAES256Test.pdf")
+        outputListener?.println("Samples 4 : NoEncryptAlgo encrypt")
+        encryptDocument(PDFDocumentEncryptAlgo.PDFDocumentAES128, "EncryptUseNoEncryptAlgoTest.pdf")
+        outputListener?.println("Encrypt by user password done.")
+        encryptOwnerPassword()
+        encryptAllPassword()
+        decryptDocument()
+        printFooter()
+    }
+
+    /**
+     * Encrypt document
+     */
+    private fun encryptDocument(documentEncryptAlgo: PDFDocumentEncryptAlgo, fileName: String) {
+        val document = CPDFDocument(context())
+        document.open(getAssetsTempFile(context(), "CommonFivePage.pdf"))
+        document.setUserPassword("User")
+        document.encryptAlgorithm = documentEncryptAlgo
+        val resultsFile = File(outputDir(), "EncryptTest/$fileName")
+        saveSamplePDF(document, resultsFile, true)
+        outputListener?.println("User password is : User")
+        outputListener?.println("Done. Results saved in $fileName")
+        outputListener?.println()
+    }
+
+    private fun encryptOwnerPassword() {
+        printDividingLine()
+        outputListener?.println("Samples 5 : Encrypt by owner passwords")
+        val document = CPDFDocument(context())
+        document.open(getAssetsTempFile(context(), "CommonFivePage.pdf"))
+        document.encryptAlgorithm = PDFDocumentEncryptAlgo.PDFDocumentAES256
+        document.setOwnerPassword("owner")
+        val resultsFile = File(outputDir(), "EncryptTest/EncryptByOwnerPasswordsTest.pdf")
+        saveSamplePDF(document, resultsFile, true)
+        outputListener?.println("Owner password is : owner")
+        outputListener?.println("Done. Results saved in EncryptByOwnerPasswordsTest.pdf")
+        outputListener?.println()
+    }
+
+    private fun encryptAllPassword() {
+        printDividingLine()
+        outputListener?.println("Samples 6 : Encrypt by Both user and owner passwords")
+        val document = CPDFDocument(context())
+        document.open(getAssetsTempFile(context(), "CommonFivePage.pdf"))
+        document.setUserPassword("User")
+        document.encryptAlgorithm = PDFDocumentEncryptAlgo.PDFDocumentAES256
+        document.setOwnerPassword("owner")
+        val resultsFile = File(outputDir(), "EncryptTest/EncryptByAllPasswordsTest.pdf")
+        saveSamplePDF(document, resultsFile, true)
+        outputListener?.println("User password is : User")
+        outputListener?.println("Owner password is : owner")
+        outputListener?.println("Done. Results saved in EncryptByAllPasswordsTest.pdf")
+        outputListener?.println()
+    }
+
+    private fun decryptDocument() {
+        outputListener?.println("Samples 7 : encrypt document")
+        val document = CPDFDocument(context())
+        val file = File(outputDir(), "EncryptTest/EncryptByAllPasswordsTest.pdf")
+        outputListener?.println("Unlock with user password")
+        document.open(file.absolutePath, "User")
+        document.isEncrypted
+        outputListener?.println("Document is ${if (document.isEncrypted) "locked" else "unlocked"}")
+        val info = document.permissionsInfo
+        outputListener?.println("AllowsPrinting : ${info.isAllowsPrinting}")
+        outputListener?.println("AllowsCopy : ${info.isAllowsCopying}")
+        document.close()
+        val newDocument = CPDFDocument(context())
+        outputListener?.println("Unlock with owner password")
+        newDocument.open(file.absolutePath, "owner")
+        val info1 = newDocument.permissionsInfo
+        outputListener?.println("AllowsPrinting : ${info1.isAllowsPrinting}")
+        outputListener?.println("AllowsCopy : ${info1.isAllowsCopying}")
+        outputListener?.println("Unlock done.")
+        printDividingLine()
+        try {
+            val decryptTestFile = File(outputDir(), "EncryptTest/DecryptTest.pdf")
+            decryptTestFile.parentFile?.mkdirs()
+            newDocument.saveAs(decryptTestFile.absolutePath, true)
+            if (decryptTestFile.exists()) {
+                getOutputFileList().add(decryptTestFile.absolutePath)
+            }
+            newDocument.close()
+            outputListener?.println("Document decrypt done.")
+            outputListener?.println("Decrypt done.")
+        } catch (_: CPDFDocumentException) {
+        }
+    }
+
+    override fun saveSamplePDF(document: CPDFDocument, file: File, close: Boolean) {
+        try {
+            file.parentFile?.mkdirs()
+            document.saveAs(file.absolutePath, false)
+            if (file.exists()) {
+                getOutputFileList().add(file.absolutePath)
+            }
+            if (close) {
+                document.close()
+            }
+        } catch (e: CPDFDocumentException) {
+            e.printStackTrace()
+        }
+    }
+}

+ 55 - 0
Samples_kotlin/src/main/java/com/compdfkit/samples/samples/FlattenTest.kt

@@ -0,0 +1,55 @@
+/**
+ * Copyright © 2014-2023 PDF Technologies, Inc. All Rights Reserved.
+ *
+ * THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
+ * AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE ComPDFKit LICENSE AGREEMENT.
+ * UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
+ * This notice may not be removed from this file.
+ */
+package com.compdfkit.samples.samples
+
+import com.compdfkit.core.document.CPDFDocument
+import com.compdfkit.core.page.CPDFPage
+import com.compdfkit.samples.PDFSamples
+import com.compdfkit.samples.R
+import com.compdfkit.samples.util.FileUtils.getAssetsTempFile
+import com.compdfkit.samples.util.OutputListener
+import java.io.File
+
+class FlattenTest : PDFSamples() {
+    init {
+        setTitle(R.string.flatten_test_title)
+        setDescription(R.string.flatten_test_desc)
+    }
+
+    override fun run(outputListener: OutputListener?) {
+        super.run(outputListener)
+        printHead()
+        outputListener?.println()
+        val document = CPDFDocument(context())
+        document.open(getAssetsTempFile(context(), "Annotations.pdf"))
+        // Get the total number of comments in the pdf document
+        var annotationCount = 0
+        for (i in 0 until document.pageCount) {
+            annotationCount += document.pageAtIndex(i).annotCount
+        }
+        outputListener?.println("$annotationCount annotations in the file.")
+        // Flatten processing of pdf pages
+        document.flattenAllPages(CPDFPage.PDFFlattenOption.FLAT_NORMALDISPLAY)
+        // save document
+        val file = File(outputDir(), "Flatten/FlattenTest.pdf")
+        saveSamplePDF(document, file, true)
+        outputListener?.println("Done. Result saved in FlattenTest.pdf")
+        val newFileDocument = CPDFDocument(context())
+        newFileDocument.open(file.absolutePath)
+        //Open the document again and get the number of annotations in the document
+        annotationCount = 0
+        for (i in 0 until newFileDocument.pageCount) {
+            annotationCount += newFileDocument.pageAtIndex(i).annotCount
+        }
+        newFileDocument.close()
+        outputListener?.println("$annotationCount annotations in the new file.")
+        printDividingLine()
+        printFooter()
+    }
+}

+ 146 - 0
Samples_kotlin/src/main/java/com/compdfkit/samples/samples/HeaderFooterTest.kt

@@ -0,0 +1,146 @@
+/**
+ * Copyright © 2014-2023 PDF Technologies, Inc. All Rights Reserved.
+ *
+ *
+ * THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
+ * AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE ComPDFKit LICENSE AGREEMENT.
+ * UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
+ * This notice may not be removed from this file.
+ */
+package com.compdfkit.samples.samples
+
+import android.graphics.Color
+import com.compdfkit.core.document.CPDFDocument
+import com.compdfkit.samples.PDFSamples
+import com.compdfkit.samples.R
+import com.compdfkit.samples.util.FileUtils.getAssetsTempFile
+import com.compdfkit.samples.util.OutputListener
+import java.io.File
+
+class HeaderFooterTest : PDFSamples() {
+    init {
+        setTitle(R.string.header_footer_test_title)
+        setDescription(R.string.header_footer_test_desc)
+    }
+
+    override fun run(outputListener: OutputListener?) {
+        super.run(outputListener)
+        printHead()
+        addCommonHeaderFooter()
+        addPageHeaderFooter()
+        editHeaderFooter()
+        clearHeaderFooterTest()
+        printFooter()
+    }
+
+    /**
+     * samples 1 : add header
+     */
+    private fun addCommonHeaderFooter() {
+        printDividingLine()
+        outputListener?.println("Samples 1 : Insert common header footer")
+        val document = CPDFDocument(context())
+        document.open(getAssetsTempFile(context(), "CommonFivePage.pdf"))
+        val headerFooter = document.headerFooter
+        val headerStr = "ComPDFKit"
+        // index 0 : top left
+        // index 1 : top middle
+        // index 2 : top right
+        val index = 3
+        for (i in 0 until index) {
+            outputListener!!.println("Text: $headerStr")
+            headerFooter.setText(i, headerStr)
+            headerFooter.setTextColor(i, Color.RED)
+            headerFooter.setFontSize(i, 14F)
+            when (i) {
+                0 -> outputListener?.println("Location: Top Left")
+                1 -> outputListener?.println("Location: Top Middle")
+                else -> outputListener?.println("Location: Top Right")
+            }
+            outputListener?.println()
+        }
+        headerFooter.pages = "0,1,2,3,4"
+        headerFooter.update()
+        val file = File(outputDir(), "HeaderFooterTest/AddCommonHeaderFooter.pdf")
+        saveSamplePDF(document, file, false)
+        outputListener?.println("Done. Results saved in AddCommonHeaderFooter.pdf")
+        printDividingLine()
+    }
+
+    private fun addPageHeaderFooter() {
+        outputListener?.println("Samples 2 : Insert page header footer")
+        val document = CPDFDocument(context())
+        document.open(getAssetsTempFile(context(), "CommonFivePage.pdf"))
+        val headerFooter = document.headerFooter
+        val headerStr = "ComPDFKit"
+        for (i in 0 until document.pageCount) {
+            val pageNumber = i + 1
+            val index = 6
+            // index 0 : top left
+            // index 1 : top middle
+            // index 2 : top right
+            // index 3 : bottom left
+            // index 4 : bottom middle
+            // index 5 : bottom right
+            for (i1 in 0 until index) {
+                if (i1 < 3) {
+                    outputListener?.println("Text: $headerStr")
+                    headerFooter.setText(i1, headerStr)
+                    headerFooter.setTextColor(i1, Color.BLACK)
+                } else {
+                    outputListener?.println("Text: 0$pageNumber")
+                    headerFooter.setText(i1, "0$pageNumber")
+                    headerFooter.setTextColor(i1, Color.RED)
+                }
+                when (i1) {
+                    0 -> outputListener?.println("Location: Top Left")
+                    1 -> outputListener?.println("Location: Top Middle")
+                    2 -> outputListener?.println("Location: Top Right")
+                    3 -> outputListener?.println("Location: Bottom Left")
+                    4 -> outputListener?.println("Location: Bottom Middle")
+                    else -> outputListener?.println("Location: Bottom Right")
+                }
+                headerFooter.setFontSize(i, 14f)
+            }
+            headerFooter.pages = "" + i
+            headerFooter.update()
+        }
+        val file = File(outputDir(), "HeaderFooterTest/AddPageHeaderFooter.pdf")
+        saveSamplePDF(document, file, false)
+        outputListener?.println("Done. Results saved in AddPageHeaderFooter.pdf")
+        printDividingLine()
+    }
+
+    /**
+     * samples 3 : edit top left header
+     */
+    private fun editHeaderFooter() {
+        outputListener?.println("Samples 3 : Edit top left header")
+        val document = CPDFDocument(context())
+        val file = File(outputDir(), "HeaderFooterTest/AddCommonHeaderFooter.pdf")
+        document.open(file.absolutePath)
+        val headerFooter = document.headerFooter
+        outputListener?.println("Get old head and footer 0 succeeded, text is ${headerFooter.getText(0)}")
+        outputListener?.println("Change head and footer 0 succeeded, new text is ComPDFKit Samples")
+        // change top left text
+        headerFooter.setText(0, "ComPDFKit Samples")
+        headerFooter.update()
+        val resultsFile = File(outputDir(), "HeaderFooterTest/EditHeaderFooterTest.pdf")
+        saveSamplePDF(document, resultsFile, false)
+        outputListener?.println("Done. Results saved in EditHeaderFooterTest.pdf")
+        printDividingLine()
+    }
+
+    private fun clearHeaderFooterTest() {
+        outputListener?.println("Samples 4 : Clean all header and footer")
+        val document = CPDFDocument(context())
+        val file = File(outputDir(), "HeaderFooterTest/AddCommonHeaderFooter.pdf")
+        document.open(file.absolutePath)
+        document.headerFooter.clear()
+        outputListener?.println("")
+        val resultsFile = File(outputDir(), "HeaderFooterTest/ClearHeaderFooterTest.pdf")
+        saveSamplePDF(document, resultsFile, false)
+        outputListener?.println("Done. Results saved in ClearHeaderFooterTest.pdf")
+        printDividingLine()
+    }
+}

+ 47 - 0
Samples_kotlin/src/main/java/com/compdfkit/samples/samples/ImageExtractTest.kt

@@ -0,0 +1,47 @@
+/**
+ * Copyright © 2014-2023 PDF Technologies, Inc. All Rights Reserved.
+ *
+ * THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
+ * AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE ComPDFKit LICENSE AGREEMENT.
+ * UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
+ * This notice may not be removed from this file.
+ */
+package com.compdfkit.samples.samples
+
+import com.compdfkit.core.document.CPDFDocument
+import com.compdfkit.samples.PDFSamples
+import com.compdfkit.samples.R
+import com.compdfkit.samples.util.FileUtils.getAssetsTempFile
+import com.compdfkit.samples.util.OutputListener
+import java.io.File
+
+class ImageExtractTest : PDFSamples() {
+    init {
+        setTitle(R.string.image_extract_test_title)
+        setDescription(R.string.image_extract_test_desc)
+    }
+
+    override fun run(outputListener: OutputListener?) {
+        super.run(outputListener)
+        printHead()
+        printDividingLine()
+        outputListener?.println("Samples 1: Extract all images in the document")
+        outputListener?.println("Opening the Samples PDF File")
+        val document = CPDFDocument(context())
+        document.open(getAssetsTempFile(context(), "ImageExtractTest.pdf"))
+        for (i in 0 until document.pageCount) {
+            val extractDir = File(outputDir(), "imageExtract/ImageExtractTest_$i/")
+            extractDir.delete()
+            document.pageAtIndex(i).extractImages(extractDir.absolutePath)
+            val images = extractDir.listFiles()
+            if (images != null && images.isNotEmpty()) {
+                for (image in images) {
+                    getOutputFileList().add(image.absolutePath)
+                    outputListener?.println(image.name)
+                }
+            }
+        }
+        document.close()
+        printFooter()
+    }
+}

+ 330 - 0
Samples_kotlin/src/main/java/com/compdfkit/samples/samples/InteractiveFormsTest.kt

@@ -0,0 +1,330 @@
+/**
+ * Copyright © 2014-2023 PDF Technologies, Inc. All Rights Reserved.
+ *
+ *
+ * THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
+ * AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE ComPDFKit LICENSE AGREEMENT.
+ * UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
+ * This notice may not be removed from this file.
+ */
+package com.compdfkit.samples.samples
+
+import android.graphics.Color
+import android.graphics.RectF
+import com.compdfkit.core.annotation.CPDFAnnotation
+import com.compdfkit.core.annotation.form.CPDFCheckboxWidget
+import com.compdfkit.core.annotation.form.CPDFComboboxWidget
+import com.compdfkit.core.annotation.form.CPDFListboxWidget
+import com.compdfkit.core.annotation.form.CPDFPushbuttonWidget
+import com.compdfkit.core.annotation.form.CPDFRadiobuttonWidget
+import com.compdfkit.core.annotation.form.CPDFSignatureWidget
+import com.compdfkit.core.annotation.form.CPDFTextWidget
+import com.compdfkit.core.annotation.form.CPDFWidget
+import com.compdfkit.core.annotation.form.CPDFWidget.WidgetType
+import com.compdfkit.core.annotation.form.CPDFWidgetItem
+import com.compdfkit.core.document.CPDFDestination
+import com.compdfkit.core.document.CPDFDocument
+import com.compdfkit.core.document.action.CPDFGoToAction
+import com.compdfkit.core.document.action.CPDFUriAction
+import com.compdfkit.samples.PDFSamples
+import com.compdfkit.samples.R
+import com.compdfkit.samples.SampleApplication
+import com.compdfkit.samples.util.OutputListener
+import java.io.File
+
+class InteractiveFormsTest : PDFSamples() {
+    init {
+        setTitle(R.string.interactive_forms_test_title)
+        setDescription(R.string.interactive_forms_test_desc)
+    }
+
+    override fun run(outputListener: OutputListener?) {
+        super.run(outputListener)
+        printHead()
+        val document = CPDFDocument.createDocument(context())
+        // Create a blank new page and add some form fields.
+        document.insertBlankPage(0, 595F, 842F)
+        document.insertBlankPage(1, 595F, 842F)
+
+        //---------------------------------
+        //Samples 1 : Programmatically create new Form Fields and Widget Annotations.
+        createTestForms(document)
+
+        //---------------------------------
+        //Samples 2 : Traverse all form fields in the document (and print out their names).
+        printFormsMessage(document)
+        val file = File(outputDir(), "forms/Create_From_Test.pdf")
+        saveSamplePDF(document, file, true)
+        deleteForm()
+        printFooter()
+    }
+
+    private fun createTestForms(document: CPDFDocument) {
+        // create new Form Fields and Widget Annotations.
+        val pageNumber = 0
+        val pageSize = document.getPageSize(pageNumber)
+        val cpdfPage = document.pageAtIndex(pageNumber)
+
+        //Insert a single-line TextField.
+        (cpdfPage.addFormWidget(WidgetType.Widget_TextField) as CPDFTextWidget).apply {
+            rect = kotlin.run {
+                val singleLineTextRect = RectF(28F, 32F, 237F, 75F)
+                cpdfPage.convertRectToPage(false, pageSize.width(), pageSize.height(), singleLineTextRect)
+            }
+            fieldName = "TextField1"
+            text = "Basic Text Field"
+            fontColor = Color.BLACK
+            fontSize = 15F
+            updateAp()
+        }
+
+        //Insert a multiline TextField.
+        val multiLineTextWidget = cpdfPage.addFormWidget(WidgetType.Widget_TextField) as CPDFTextWidget
+        multiLineTextWidget.apply {
+            rect = kotlin.run {
+                val multilineTextRect: RectF? = RectF(28F, 97F, 237F, 189F)
+                cpdfPage.convertRectToPage(false, pageSize.width(), pageSize.height(), multilineTextRect)
+            }
+            fieldName = "TextField2"
+            text = "Basic Text Field\nBasic Text Field\nBasic Text Field"
+            isMultiLine = true
+            fontColor = Color.BLACK
+            fontSize = 15f
+            updateAp()
+        }
+
+
+        //Insert a ListBox widget.
+        val listBoxWidget = cpdfPage.addFormWidget(WidgetType.Widget_ListBox) as CPDFListboxWidget
+        listBoxWidget.apply {
+            rect = kotlin.run {
+                val listBoxRect: RectF? = RectF(267F, 32F, 567F, 138F)
+                cpdfPage.convertRectToPage(false, pageSize.width(), pageSize.height(), listBoxRect)
+            }
+            fieldName = "ListBox1"
+            val listBoxItems = arrayOf(
+                    CPDFWidgetItem("List Box No.1", "List Box No.1"),
+                    CPDFWidgetItem("List Box No.2", "List Box No.2"),
+                    CPDFWidgetItem("List Box No.3", "List Box No.3"))
+            setOptionItems(listBoxItems)
+            selectedIndexes = intArrayOf(1)
+            updateAp()
+        }
+
+        //Insert a ComboBox Widget.
+        val comboBoxWidget = cpdfPage.addFormWidget(WidgetType.Widget_ComboBox) as CPDFComboboxWidget
+        comboBoxWidget.apply {
+            rect = kotlin.run {
+                val comboBoxRect: RectF? = RectF(267F, 143F, 567F, 189F)
+                cpdfPage.convertRectToPage(false, pageSize.width(), pageSize.height(), comboBoxRect)
+            }
+            fieldName = "ComboBox1"
+            val comboBoxItems = arrayOf(
+                    CPDFWidgetItem("Combo Box No.1", "Combo Box No.1"),
+                    CPDFWidgetItem("Combo Box No.2", "Combo Box No.2"),
+                    CPDFWidgetItem("Combo Box No.3", "Combo Box No.3"))
+            setOptionItems(comboBoxItems, intArrayOf(1))
+            updateAp()
+        }
+
+
+        //Insert a Form Signature Widget (unsigned)
+        val signatureWidget = cpdfPage.addFormWidget(WidgetType.Widget_SignatureFields) as CPDFSignatureWidget
+        signatureWidget.fieldName = "Signature1"
+        signatureWidget.rect = kotlin.run {
+            val signatureRect: RectF? = RectF(28F, 206F, 237F, 301F)
+            cpdfPage.convertRectToPage(false, pageSize.width(), pageSize.height(), signatureRect)
+        }
+        signatureWidget.updateAp()
+
+        //Insert a PushButton to jump to a page.
+        val pushButtonWidget1 = cpdfPage.addFormWidget(WidgetType.Widget_PushButton) as CPDFPushbuttonWidget
+        pushButtonWidget1.apply {
+            rect = kotlin.run {
+                val pushButton1Rect: RectF? = RectF(267F, 203F, 401F, 235F)
+                cpdfPage.convertRectToPage(false, pageSize.width(), pageSize.height(), pushButton1Rect)
+            }
+            fieldName = "PushButton1"
+            fontColor = Color.BLACK
+            fontSize = 15F
+            buttonTitle = "PushButton"
+            //set PushButton jump to a page action
+            buttonAction = CPDFGoToAction().apply {
+                setDestination(document, CPDFDestination(1, 0F, 842F, 0F))
+            }
+            updateAp()
+        }
+
+
+        //Insert a PushButton to jump to a website.
+        var pushButton2Rect: RectF? = RectF(433F, 203F, 567F, 235F)
+        pushButton2Rect = cpdfPage.convertRectToPage(false, pageSize.width(), pageSize.height(), pushButton2Rect)
+        val pushButtonWidget2 = cpdfPage.addFormWidget(WidgetType.Widget_PushButton) as CPDFPushbuttonWidget
+        pushButtonWidget2.rect = pushButton2Rect
+        pushButtonWidget2.fieldName = "PushButton2"
+        pushButtonWidget2.fontColor = Color.BLACK
+        pushButtonWidget2.fontSize = 15f
+        pushButtonWidget2.buttonTitle = "PushButton"
+        //set PushButton jump to a website
+        pushButtonWidget2.buttonAction = CPDFUriAction().apply {
+            uri = "https://www.compdf.com/"
+        }
+        pushButtonWidget2.updateAp()
+
+        //Insert CheckBox Widget
+        val checkboxWidget = cpdfPage.addFormWidget(WidgetType.Widget_CheckBox) as CPDFCheckboxWidget
+        checkboxWidget.rect = kotlin.run {
+            val checkBox1: RectF? = RectF(267F, 251F, 299F, 283F)
+            cpdfPage.convertRectToPage(false, pageSize.width(), pageSize.height(), checkBox1)
+        }
+        checkboxWidget.fieldName = "CheckBox1"
+        checkboxWidget.fillColor = Color.parseColor("#CCE5E5FF")
+        checkboxWidget.borderColor = Color.BLACK
+        checkboxWidget.borderWidth = 2F
+        checkboxWidget.isChecked = false
+        checkboxWidget.updateAp()
+
+        val checkboxWidget2 = cpdfPage.addFormWidget(WidgetType.Widget_CheckBox) as CPDFCheckboxWidget
+        checkboxWidget2.rect = kotlin.run {
+            val checkBox2: RectF? = RectF(326F, 251F, 358F, 283F)
+            cpdfPage.convertRectToPage(false, pageSize.width(), pageSize.height(), checkBox2)
+        }
+        checkboxWidget2.fieldName = "CheckBox1"
+        checkboxWidget2.fillColor = Color.parseColor("#CCE5E5FF")
+        checkboxWidget2.borderColor = Color.BLACK
+        checkboxWidget2.borderWidth = 2F
+        checkboxWidget2.isChecked = true
+        checkboxWidget2.updateAp()
+
+        //Insert RadioButton Widget
+        val radiobuttonWidget1 = cpdfPage.addFormWidget(WidgetType.Widget_RadioButton) as CPDFRadiobuttonWidget
+        radiobuttonWidget1.rect = kotlin.run {
+            val radioButton1: RectF? = RectF(385F, 251F, 424f, 290f)
+            cpdfPage.convertRectToPage(false, pageSize.width(), pageSize.height(), radioButton1)
+        }
+        radiobuttonWidget1.fieldName = "RadioButton"
+        radiobuttonWidget1.checkStyle = CPDFWidget.CheckStyle.CK_Circle
+        radiobuttonWidget1.fillColor = Color.parseColor("#CCE5E5FF")
+        radiobuttonWidget1.borderColor = Color.BLACK
+        radiobuttonWidget1.borderWidth = 2F
+        radiobuttonWidget1.isChecked = false
+        radiobuttonWidget1.fieldName = "RadioButton1"
+        radiobuttonWidget1.updateAp()
+
+        val radiobuttonWidget2 = cpdfPage.addFormWidget(WidgetType.Widget_RadioButton) as CPDFRadiobuttonWidget
+        radiobuttonWidget2.rect = kotlin.run {
+            val radioButton2: RectF? = RectF(450F, 251F, 489F, 290F)
+             cpdfPage.convertRectToPage(false, pageSize.width(), pageSize.height(), radioButton2)
+        }
+        radiobuttonWidget2.fieldName = "RadioButton"
+        radiobuttonWidget2.checkStyle = CPDFWidget.CheckStyle.CK_Circle
+        radiobuttonWidget2.fillColor = Color.parseColor("#CCE5E5FF")
+        radiobuttonWidget2.borderColor = Color.BLACK
+        radiobuttonWidget2.borderWidth = 2F
+        // Check the widget (by default it is unchecked).
+        radiobuttonWidget2.isChecked = true
+        radiobuttonWidget2.fieldName = "RadioButton1"
+        radiobuttonWidget2.updateAp()
+
+
+        val radiobuttonWidget3 = cpdfPage.addFormWidget(WidgetType.Widget_RadioButton) as CPDFRadiobuttonWidget
+        radiobuttonWidget3.rect = kotlin.run {
+            val radioButton3: RectF? = RectF(515F, 251F, 554F, 290F)
+            cpdfPage.convertRectToPage(false, pageSize.width(), pageSize.height(), radioButton3)
+        }
+        radiobuttonWidget3.fieldName = "RadioButton"
+        radiobuttonWidget3.checkStyle = CPDFWidget.CheckStyle.CK_Circle
+        radiobuttonWidget3.fillColor = Color.parseColor("#CCE5E5FF")
+        radiobuttonWidget3.borderColor = Color.BLACK
+        radiobuttonWidget3.borderWidth = 2F
+        radiobuttonWidget3.isChecked = false
+        radiobuttonWidget3.fieldName = "RadioButton1"
+        radiobuttonWidget3.updateAp()
+        outputListener?.println("Done.")
+        outputListener?.println("Done. Result saved in Create_Form_Test.pdf")
+        printDividingLine()
+    }
+
+    private fun deleteForm() {
+        val file = File(outputDir(), "forms/Create_From_Test.pdf")
+        val document = CPDFDocument(SampleApplication.instance)
+        document.open(file.absolutePath)
+        val page = document.pageAtIndex(0)
+        for (annotation in page.annotations) {
+            if (annotation.type == CPDFAnnotation.Type.WIDGET) {
+                page.deleteAnnotation(annotation)
+                break
+            }
+        }
+        val resultsForms = File(outputDir(), "forms/Delete_Form_Test.pdf")
+        saveSamplePDF(document, resultsForms, true)
+        outputListener?.println("Done.")
+        outputListener?.println("Done. Result saved in Delete_Form_Test.pdf")
+    }
+
+    private fun printFormsMessage(document: CPDFDocument) {
+        for (i in 0 until document.pageCount) {
+            val page = document.pageAtIndex(i)
+            for (annotation in page.annotations) {
+                when (annotation.type) {
+                    CPDFAnnotation.Type.WIDGET -> {
+                        val cpdfWidget = annotation as CPDFWidget
+                        outputListener?.println("Field name : ${cpdfWidget.fieldName}")
+                        when (cpdfWidget.widgetType) {
+                            WidgetType.Widget_TextField -> outputListener?.println("Field partial name : ${(cpdfWidget as CPDFTextWidget).text}")
+                            WidgetType.Widget_ListBox -> {
+                                val listBoxWidget = cpdfWidget as CPDFListboxWidget
+                                val options = listBoxWidget.options
+                                val selectedIndexs = listBoxWidget.selectedIndexes
+                                if (options != null && selectedIndexs != null) {
+                                    val selectItem = options[selectedIndexs[0]]
+                                    outputListener?.println("Field Select Item : ${selectItem.text}")
+                                }
+                            }
+
+                            WidgetType.Widget_ComboBox -> {
+                                val comboBoxWidget = cpdfWidget as CPDFComboboxWidget
+                                val comboBoxOptions = comboBoxWidget.options
+                                val selectedIndexs1 = comboBoxWidget.selectedIndexes
+                                if (comboBoxOptions != null && selectedIndexs1 != null) {
+                                    val selectItem = comboBoxOptions[selectedIndexs1[0]]
+                                    outputListener?.println("Field Select Item : ${selectItem.text}")
+                                }
+                            }
+
+                            WidgetType.Widget_SignatureFields -> {
+                                val signatureWidget = cpdfWidget as CPDFSignatureWidget
+                                outputListener?.println("Field isSigned : ${signatureWidget.isSigned}")
+                            }
+
+                            WidgetType.Widget_CheckBox -> outputListener?.println("Field isChecked : ${(cpdfWidget as CPDFCheckboxWidget).isChecked}")
+                            WidgetType.Widget_RadioButton -> outputListener?.println("Field isChecked : ${(cpdfWidget as CPDFRadiobuttonWidget).isChecked}")
+                            WidgetType.Widget_PushButton -> {
+                                val pushButtonWidget = cpdfWidget as CPDFPushbuttonWidget
+                                outputListener?.println("Field PushButton Title : ${pushButtonWidget.buttonTitle}")
+                                val cpdfAction = pushButtonWidget.buttonAction
+                                if (cpdfAction != null) {
+                                    if (cpdfAction is CPDFUriAction) {
+                                        outputListener?.println("Field PushButton Action : ${cpdfAction.uri}")
+                                    } else if (cpdfAction is CPDFGoToAction) {
+                                        outputListener?.println("Field PushButton Action : Jump to page ${(cpdfAction.getDestination(document).pageIndex + 1)} of the document")
+                                    }
+                                }
+                            }
+
+                            else -> {}
+                        }
+                        val widgetRect = cpdfWidget.rect
+                        val position = page.convertRectFromPage(false, document.getPageSize(i).width(),
+                                document.getPageSize(i).height(), widgetRect)
+                        outputListener?.println(String.format("Field Position : %d, %d, %d, %d", position.left.toInt(), position.top.toInt(), position.right.toInt(), position.bottom.toInt()))
+                        outputListener?.println("Widget type : ${cpdfWidget.widgetType.name}")
+                        printDividingLine()
+                    }
+
+                    else -> {}
+                }
+            }
+        }
+    }
+}

+ 100 - 0
Samples_kotlin/src/main/java/com/compdfkit/samples/samples/OutlineTest.kt

@@ -0,0 +1,100 @@
+/**
+ * Copyright © 2014-2023 PDF Technologies, Inc. All Rights Reserved.
+ *
+ * THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
+ * AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE ComPDFKit LICENSE AGREEMENT.
+ * UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
+ * This notice may not be removed from this file.
+ */
+package com.compdfkit.samples.samples
+
+import com.compdfkit.core.document.CPDFDestination
+import com.compdfkit.core.document.CPDFDocument
+import com.compdfkit.core.document.CPDFOutline
+import com.compdfkit.samples.PDFSamples
+import com.compdfkit.samples.R
+import com.compdfkit.samples.util.FileUtils.getAssetsTempFile
+import com.compdfkit.samples.util.OutputListener
+import java.io.File
+
+class OutlineTest : PDFSamples() {
+    init {
+        setTitle(R.string.outline_test_title)
+        setDescription(R.string.outline_test_desc)
+    }
+
+    override fun run(outputListener: OutputListener?) {
+        super.run(outputListener)
+        printHead()
+        createOutline()
+        printOutline()
+        printFooter()
+    }
+
+    /**
+     * Sample 1 : create outline test
+     */
+    private fun createOutline() {
+        outputListener?.println()
+        // Open test pdf document
+        val document = CPDFDocument(context())
+        document.open(getAssetsTempFile(context(), "CommonFivePage.pdf"))
+        val rootOutline: CPDFOutline = if (document.outlineRoot == null) {
+            document.newOutlineRoot()
+        } else {
+            document.outlineRoot
+        }
+        // add outline data
+        val child = insertOutline(rootOutline, "1. page1", 0)
+        // Add sub-outline data for first page outline
+        insertOutline(child, "1.1 page1_1", 0)
+        insertOutline(rootOutline, "2. page2", 1)
+        insertOutline(rootOutline, "3. page3", 2)
+        insertOutline(rootOutline, "4. page4", 3)
+        insertOutline(rootOutline, "5. page5", 4)
+        val file = File(outputDir(), "OutlineTest/CreateOutlineTest.pdf")
+        saveSamplePDF(document, file, true)
+        outputListener?.println("Done.")
+        outputListener?.println("Done. Results saved in CreateOutlineTest.pdf")
+    }
+
+    /**
+     * insert outline data
+     * @param rootOutline parent Outline
+     * @param title outline title
+     * @param pageIndex jump document page index
+     * @return CPDFOutline
+     */
+    private fun insertOutline(rootOutline: CPDFOutline, title: String, pageIndex: Int): CPDFOutline {
+        return rootOutline.insertChildAtIndex(pageIndex).apply {
+            this.title = title;
+            destination = CPDFDestination(pageIndex, 0F, 0F, 1F)
+        }
+    }
+
+    private fun printOutline() {
+        outputListener?.println()
+        val document = CPDFDocument(context())
+        val sampleFile = File(outputDir(), "OutlineTest/CreateOutlineTest.pdf")
+        document.open(sampleFile.absolutePath)
+        val outlineRoot = document.outlineRoot
+        if (outlineRoot != null) {
+            printChildOutline(outlineRoot.childList)
+        }
+    }
+
+    private fun printChildOutline(outlines: Array<CPDFOutline>) {
+        for (outline in outlines) {
+            val tab = StringBuilder()
+            if (outline.level > 1) {
+                for (i in 0 until outline.level) {
+                    tab.append("\t")
+                }
+            }
+            outputListener?.println(tab.toString() + "->" + outline.title)
+            if (outline.childList != null && outline.childList.size > 0) {
+                printChildOutline(outline.childList)
+            }
+        }
+    }
+}

+ 63 - 0
Samples_kotlin/src/main/java/com/compdfkit/samples/samples/PDFATest.kt

@@ -0,0 +1,63 @@
+/**
+ * Copyright © 2014-2023 PDF Technologies, Inc. All Rights Reserved.
+ *
+ * THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
+ * AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE ComPDFKit LICENSE AGREEMENT.
+ * UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
+ * This notice may not be removed from this file.
+ */
+package com.compdfkit.samples.samples
+
+import com.compdfkit.core.document.CPDFDocument
+import com.compdfkit.samples.PDFSamples
+import com.compdfkit.samples.R
+import com.compdfkit.samples.util.FileUtils.getAssetsTempFile
+import com.compdfkit.samples.util.OutputListener
+import java.io.File
+
+class PDFATest : PDFSamples() {
+    init {
+        setTitle(R.string.pdf_a_test_title)
+        setDescription(R.string.pdf_a_test_desc)
+    }
+
+    override fun run(outputListener: OutputListener?) {
+        super.run(outputListener)
+        printHead()
+        convertToPDFA1a()
+        convertToPDFA1b()
+        printFooter()
+    }
+
+    /**
+     * Samples 1 : convert pdf document to pdfA1a
+     */
+    private fun convertToPDFA1a() {
+        printDividingLine()
+        outputListener?.println("Samples 1 : convert to pdf A1a")
+        val document = CPDFDocument(context())
+        document.open(getAssetsTempFile(context(), "CommonFivePage.pdf"))
+        document.convertType(CPDFDocument.PDFDocumentType.PDFTypeA1a)
+        val file = File(outputDir(),
+                "PDFATest/PDFA1aTest.pdf")
+        saveSamplePDF(document, file, true)
+        outputListener?.println("Convert to PDF/A-1a done")
+        outputListener?.println("Done. Result saved in PDFA1aTest.pdf")
+        printDividingLine()
+    }
+
+    /**
+     * Samples 2 : convert pdf document to pdfA1b
+     */
+    private fun convertToPDFA1b() {
+        outputListener?.println("Samples 1 : convert to pdf A1b")
+        val document = CPDFDocument(context())
+        document.open(getAssetsTempFile(context(), "CommonFivePage.pdf"))
+        document.convertType(CPDFDocument.PDFDocumentType.PDFTypeA1b)
+        val file = File(outputDir(), "PDFATest/PDFA1bTest.pdf")
+        saveSamplePDF(document, file, true)
+        outputListener?.println("Convert to PDF/A-1b done")
+        outputListener?.println("Done. Result saved in PDFA1bTest.pdf")
+        printDividingLine()
+    }
+}

+ 199 - 0
Samples_kotlin/src/main/java/com/compdfkit/samples/samples/PDFPageTest.kt

@@ -0,0 +1,199 @@
+/**
+ * Copyright © 2014-2023 PDF Technologies, Inc. All Rights Reserved.
+ *
+ *
+ * THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
+ * AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE ComPDFKit LICENSE AGREEMENT.
+ * UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
+ * This notice may not be removed from this file.
+ */
+package com.compdfkit.samples.samples
+
+import com.compdfkit.core.document.CPDFDocument
+import com.compdfkit.samples.PDFSamples
+import com.compdfkit.samples.R
+import com.compdfkit.samples.util.FileUtils.getAssetsTempFile
+import com.compdfkit.samples.util.OutputListener
+import java.io.File
+
+class PDFPageTest : PDFSamples() {
+    init {
+        setTitle(R.string.pdf_page_test_title)
+        setDescription(R.string.pdf_page_test_desc)
+    }
+
+    override fun run(outputListener: OutputListener?) {
+        super.run(outputListener)
+        printHead()
+        insertBlankPage()
+        insertPdfPage()
+        splitPages()
+        mergePages()
+        deletePages()
+        rotatePage()
+        replacePages()
+        extractPages()
+        printFooter()
+    }
+
+    /**
+     * Samples - 1: Insert a blank A4-sized page into the sample document
+     */
+    private fun insertBlankPage() {
+        printDividingLine()
+        outputListener?.println("Samples 1: Insert a blank A4-sized page into the sample document")
+        outputListener?.println("Opening the Samples PDF File")
+        val document = CPDFDocument(context())
+        document.open(getAssetsTempFile(context(), "CommonFivePage.pdf"))
+        document.insertBlankPage(1, 595F, 842F)
+        outputListener?.println("Insert PageIndex : 1")
+        outputListener?.println("Size : 595*842")
+        val file = File(outputDir(), "pdfPage/Insert_Blank_Page.pdf")
+        saveSamplePDF(document, file, true)
+        outputListener?.println("Done. Result saved in Insert_Blank_Page.pdf")
+        printDividingLine()
+    }
+
+    /**
+     * Samples - 2: Import pages from another document into the example document
+     */
+    private fun insertPdfPage() {
+        outputListener?.println("Samples 2: Import pages from another document into the example document")
+        outputListener?.println("Opening the Samples PDF File")
+        val document = CPDFDocument(context())
+        document.open(getAssetsTempFile(context(), "CommonFivePage.pdf"))
+        outputListener?.println("Open the document to be imported")
+        //Open the document to be imported
+        val document2 = CPDFDocument(context())
+        document2.open(getAssetsTempFile(context(), "text.pdf"))
+        document.importPages(document2, intArrayOf(0), 1)
+        val file = File(outputDir(), "pdfPage/Import_Document_Page.pdf")
+        saveSamplePDF(document, file, true)
+        outputListener?.println("Done. Result saved in Import_Document_Page.pdf")
+        printDividingLine()
+    }
+
+    /**
+     * Samples - 3: Split a PDF document into multiple pages
+     */
+    private fun splitPages() {
+        outputListener?.println("Samples 3: Split a PDF document into multiple pages")
+        outputListener?.println("Opening the Samples PDF File")
+        val document = CPDFDocument(context())
+        document.open(getAssetsTempFile(context(), "CommonFivePage.pdf"))
+        for (i in 0 until document.pageCount) {
+            val newDocument = CPDFDocument.createDocument(context())
+            newDocument.importPages(document, intArrayOf(i), 0)
+            val file = File(outputDir(), "pdfPage/CommonFivePage_Split_Page_" + (i + 1) + ".pdf")
+            outputListener?.println("Done. Result saved in \nCommonFivePage_Split_Page_" + (i + 1) + ".pdf")
+            saveSamplePDF(newDocument, file, true)
+        }
+        document.close()
+        outputListener?.println("Done!")
+        printDividingLine()
+    }
+
+    /**
+     * Samples - 4: Merge split documents
+     */
+    private fun mergePages() {
+        outputListener?.println("Samples 4: Merge split documents")
+        val pageNum = 5
+        val document = CPDFDocument.createDocument(context())
+        for (i in 0 until pageNum) {
+            val file = File(outputDir(), "pdfPage/CommonFivePage_Split_Page_" + (i + 1) + ".pdf")
+            if (file.exists()) {
+                outputListener?.println("Opening " + file.name)
+                val newDocument = CPDFDocument(context())
+                newDocument.open(file.absolutePath)
+                document.importPages(newDocument, intArrayOf(0), i)
+            }
+        }
+        val file = File(outputDir(), "pdfPage/Merge_Pages.pdf")
+        saveSamplePDF(document, file, true)
+        outputListener?.println("Done. Result saved in\nMerge_Pages.pdf")
+        printDividingLine()
+    }
+
+    /**
+     * Samples - 5: Delete the specified page of the document
+     */
+    private fun deletePages() {
+        outputListener?.println("Samples 5: Delete the specified page of the document")
+        outputListener?.println("Opening the Samples PDF File")
+        val document = CPDFDocument(context())
+        document.open(getAssetsTempFile(context(), "CommonFivePage.pdf"))
+        val evenNumbers = getEvenNumbers(1, document.pageCount - 1)
+        document.removePages(evenNumbers)
+        val file = File(outputDir(), "pdfPage/Remove_Page.pdf")
+        saveSamplePDF(document, file, true)
+        outputListener?.println("Done. Result saved in\nRemove_Page.pdf")
+        printDividingLine()
+    }
+
+    /**
+     * Samples - 6: Rotate document pages
+     */
+    private fun rotatePage() {
+        outputListener?.println("Samples 6: Rotate document pages")
+        outputListener?.println("Opening the Samples PDF File")
+        val document = CPDFDocument(context())
+        document.open(getAssetsTempFile(context(), "CommonFivePage.pdf"))
+        document.pageAtIndex(0).rotation = 90
+        val file = File(outputDir(), "pdfPage/Rotate_Pages.pdf")
+        saveSamplePDF(document, file, true)
+        outputListener?.println("Done. Result saved in\nRotate_Pages.pdf")
+        printDividingLine()
+    }
+
+    /**
+     * Samples - 7: Replace specified pages of example documentation with other documentation specified pages
+     */
+    private fun replacePages() {
+        outputListener?.println("Samples 7: Replace specified pages of example documentation with other documentation specified pages")
+        outputListener?.println("Opening the Samples PDF File")
+        val document = CPDFDocument(context())
+        document.open(getAssetsTempFile(context(), "CommonFivePage.pdf"))
+        document.removePages(intArrayOf(1))
+        //open second pdf Document
+        val document2 = CPDFDocument(context())
+        document2.open(getAssetsTempFile(context(), "text.pdf"))
+        document.importPages(document2, intArrayOf(0), 1)
+        val file = File(outputDir(), "pdfPage/Replace_Pages.pdf")
+        saveSamplePDF(document, file, true)
+        outputListener?.println("Done. Result saved in\nReplace_Pages.pdf")
+        printDividingLine()
+    }
+
+    /**
+     * Samples - 8: Extract specific pages of a document
+     */
+    private fun extractPages() {
+        outputListener?.println("Samples 8: Extract specific pages of a document")
+        outputListener?.println("Opening the Samples PDF File")
+        val document = CPDFDocument(context())
+        document.open(getAssetsTempFile(context(), "CommonFivePage.pdf"))
+        val newDocument = CPDFDocument.createDocument(context())
+        newDocument.importPages(document, intArrayOf(1), 0)
+        val file = File(outputDir(), "pdfPage/ExtractPages.pdf")
+        outputListener?.println("Done. Result saved in \nCommonFivePage_Extract_Page_1.pdf")
+        saveSamplePDF(newDocument, file, true)
+        document.close()
+        printDividingLine()
+    }
+
+    companion object {
+        fun getEvenNumbers(start: Int, end: Int): IntArray {
+            val size = (end - start) / 2 + 1
+            val evenNumbers = IntArray(size)
+            var index = 0
+            for (i in start..end) {
+                if (i % 2 != 0) {
+                    evenNumbers[index] = i
+                    index++
+                }
+            }
+            return evenNumbers
+        }
+    }
+}

+ 57 - 0
Samples_kotlin/src/main/java/com/compdfkit/samples/samples/PDFRedactTest.kt

@@ -0,0 +1,57 @@
+/**
+ * Copyright © 2014-2023 PDF Technologies, Inc. All Rights Reserved.
+ *
+ *
+ * THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
+ * AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE ComPDFKit LICENSE AGREEMENT.
+ * UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
+ * This notice may not be removed from this file.
+ */
+package com.compdfkit.samples.samples
+
+import android.graphics.Color
+import android.graphics.RectF
+import com.compdfkit.core.annotation.CPDFAnnotation
+import com.compdfkit.core.annotation.CPDFRedactAnnotation
+import com.compdfkit.core.annotation.CPDFTextAttribute
+import com.compdfkit.core.document.CPDFDocument
+import com.compdfkit.samples.PDFSamples
+import com.compdfkit.samples.R
+import com.compdfkit.samples.util.FileUtils.getAssetsTempFile
+import com.compdfkit.samples.util.OutputListener
+import java.io.File
+
+class PDFRedactTest : PDFSamples() {
+    init {
+        setTitle(R.string.redact_test_title)
+        setDescription(R.string.redact_test_desc)
+    }
+
+    override fun run(outputListener: OutputListener?) {
+        super.run(outputListener)
+        printHead()
+        outputListener?.println()
+        outputListener?.println("The text need to be redact is : Page1")
+        outputListener?.println("Text in the redacted area is :")
+        val document = CPDFDocument(context())
+        document.open(getAssetsTempFile(context(), "CommonFivePage.pdf"))
+        val pdfPage = document.pageAtIndex(0)
+        val redactAnnotation = pdfPage.addAnnot(CPDFAnnotation.Type.REDACT) as CPDFRedactAnnotation
+        val pageSize = pdfPage.size
+        redactAnnotation.rect = kotlin.run {
+            val insertRect = RectF(300F, 240F, 400F, 320F)
+            //coordinate conversion
+            pdfPage.convertRectToPage(false, pageSize.width(), pageSize.height(), insertRect)
+        }
+        val textAttribute = CPDFTextAttribute(
+                CPDFTextAttribute.FontNameHelper.obtainFontName(CPDFTextAttribute.FontNameHelper.FontType.Helvetica, false, false), 14f, Color.YELLOW)
+        redactAnnotation.textDa = textAttribute
+        redactAnnotation.fillColor = Color.RED
+        redactAnnotation.applyRedaction()
+        val resultsFile = File(outputDir(), "RedactTest/RedactTest.pdf")
+        saveSamplePDF(document, resultsFile, false)
+        outputListener?.println("Done. Results saved in RedactTest.pdf")
+        printDividingLine()
+        printFooter()
+    }
+}

+ 67 - 0
Samples_kotlin/src/main/java/com/compdfkit/samples/samples/PDFToImageTest.kt

@@ -0,0 +1,67 @@
+/**
+ * Copyright © 2014-2023 PDF Technologies, Inc. All Rights Reserved.
+ *
+ *
+ * THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
+ * AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE ComPDFKit LICENSE AGREEMENT.
+ * UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
+ * This notice may not be removed from this file.
+ */
+package com.compdfkit.samples.samples
+
+import android.graphics.Bitmap
+import android.graphics.Color
+import com.compdfkit.core.document.CPDFDocument
+import com.compdfkit.samples.PDFSamples
+import com.compdfkit.samples.R
+import com.compdfkit.samples.util.FileUtils.getAssetsTempFile
+import com.compdfkit.samples.util.FileUtils.getNameWithoutExtension
+import com.compdfkit.samples.util.OutputListener
+import com.compdfkit.ui.utils.CPDFBitmapUtils
+import java.io.File
+
+class PDFToImageTest : PDFSamples() {
+    init {
+        setTitle(R.string.pdf_to_image_test_title)
+        setDescription(R.string.pdf_to_image_test_desc)
+    }
+
+    override fun run(outputListener: OutputListener?) {
+        super.run(outputListener)
+        printHead()
+        outputListener?.println()
+        val document = CPDFDocument(context())
+        val assetsName = "CommonFivePage.pdf"
+        document.open(getAssetsTempFile(context(), assetsName))
+        // Render each page of the document as an image
+        for (i in 0 until document.pageCount) {
+            // page size
+            val size = document.getPageSize(i)
+            val bitmap = Bitmap.createBitmap(size.width().toInt(), size.height().toInt(), Bitmap.Config.RGB_565)
+            val success = document.renderPageAtIndex(
+                    bitmap,
+                    i, size.width().toInt(), size.height().toInt(),
+                    0,
+                    0, size.width().toInt(), size.height().toInt(),
+                    Color.WHITE,
+                    255,
+                    0,
+                    true,
+                    true
+            )
+            if (success) {
+                val file = File(outputDir(), "PDFToImageTest/" + getNameWithoutExtension(assetsName) + "/PDFToImageTest" + i + ".png")
+                file.parentFile?.mkdirs()
+                // save bitmap
+                CPDFBitmapUtils.saveBitmap(bitmap, file.absolutePath)
+                outputListener?.println("Done.")
+                if (file.exists()) {
+                    outputListener?.println("Done. Results saved in " + file.name)
+                    addFileList(file.absolutePath)
+                }
+            }
+        }
+        document.close()
+        printFooter()
+    }
+}

+ 95 - 0
Samples_kotlin/src/main/java/com/compdfkit/samples/samples/TextExtractTest.kt

@@ -0,0 +1,95 @@
+/**
+ * Copyright © 2014-2023 PDF Technologies, Inc. All Rights Reserved.
+ *
+ * THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
+ * AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE ComPDFKit LICENSE AGREEMENT.
+ * UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
+ * This notice may not be removed from this file.
+ */
+package com.compdfkit.samples.samples
+
+import android.graphics.RectF
+import com.compdfkit.core.document.CPDFDocument
+import com.compdfkit.core.page.CPDFTextRange
+import com.compdfkit.samples.PDFSamples
+import com.compdfkit.samples.R
+import com.compdfkit.samples.util.FileUtils.getAssetsTempFile
+import com.compdfkit.samples.util.OutputListener
+
+class TextExtractTest : PDFSamples() {
+    init {
+        setTitle(R.string.text_extract_test_title)
+        setDescription(R.string.text_extract_test_desc)
+    }
+
+    override fun run(outputListener: OutputListener?) {
+        super.run(outputListener)
+        printHead()
+        extractPageText()
+        extractAllPageText()
+        extractRectRangeText()
+        printFooter()
+    }
+
+    /**
+     * Samples 1: Extract all text content in the specified page
+     */
+    private fun extractPageText() {
+        outputListener?.println("Samples 1: Extract all text content in the specified page")
+        outputListener?.println("Opening the Samples PDF File")
+        outputListener?.println("The text content of the first page of the document:")
+        printDividingLine()
+        outputListener?.println("Text : ")
+        val document = CPDFDocument(context())
+        document.open(getAssetsTempFile(context(), "text.pdf"))
+        val page = document.pageAtIndex(0)
+        val textPage = page.textPage
+        val pageText = textPage.getText(CPDFTextRange(0, textPage.countChars - 1))
+        outputListener?.print(pageText)
+        outputListener?.println("\nDone!")
+        printDividingLine()
+    }
+
+    /**
+     * Samples 2: Extract all text content of the document
+     */
+    private fun extractAllPageText() {
+        outputListener?.println("Samples 2: Extract all text content of the document")
+        outputListener?.println("Opening the Samples PDF File")
+        printDividingLine()
+        outputListener?.println("Text : ")
+        val document = CPDFDocument(context())
+        document.open(getAssetsTempFile(context(), "text.pdf"))
+        for (i in 0 until document.pageCount) {
+            val page = document.pageAtIndex(i)
+            val textPage = page.textPage
+            val pageText = textPage.getText(CPDFTextRange(0, textPage.countChars - 1))
+            outputListener?.print(pageText)
+        }
+        outputListener?.println("Done!")
+        printDividingLine()
+    }
+
+    private fun extractRectRangeText() {
+        outputListener?.println("Samples 3: Extract Select Rect Range Text")
+        outputListener?.println("Opening the Samples PDF File")
+        val document = CPDFDocument(context())
+        document.open(getAssetsTempFile(context(), "text.pdf"))
+        val page = document.pageAtIndex(0)
+        val textPage = page.textPage
+        // Extract the text within the range of (0, 0, 500, 500) on the first page
+        var selectRect: RectF? = RectF(0f, 0f, 300f, 300f)
+        val size = page.size
+        selectRect = page.convertRectFromPage(false, size.width(), size.height(), selectRect)
+        val selections = textPage.getSelectionsByLineForRect(selectRect)
+        outputListener?.println("Range : (0, 0, 300, 300)")
+        outputListener?.println("Text : ")
+        for (i in selections.indices) {
+            val textSelection = selections[i] ?: continue
+            val text = textPage.getText(textSelection.textRange)
+            outputListener?.print(text)
+        }
+        outputListener?.println("\nDone!")
+        printDividingLine()
+    }
+}

+ 111 - 0
Samples_kotlin/src/main/java/com/compdfkit/samples/samples/TextSearchTest.kt

@@ -0,0 +1,111 @@
+/**
+ * Copyright © 2014-2023 PDF Technologies, Inc. All Rights Reserved.
+ *
+ *
+ * THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
+ * AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE ComPDFKit LICENSE AGREEMENT.
+ * UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
+ * This notice may not be removed from this file.
+ */
+package com.compdfkit.samples.samples
+
+import android.graphics.Color
+import android.graphics.RectF
+import android.os.Handler
+import android.os.Looper
+import android.text.TextUtils
+import com.compdfkit.core.annotation.CPDFAnnotation
+import com.compdfkit.core.annotation.CPDFHighlightAnnotation
+import com.compdfkit.core.document.CPDFDocument
+import com.compdfkit.core.page.CPDFTextRange
+import com.compdfkit.core.page.CPDFTextSearcher
+import com.compdfkit.samples.PDFSamples
+import com.compdfkit.samples.R
+import com.compdfkit.samples.util.FileUtils.getAssetsTempFile
+import com.compdfkit.samples.util.OutputListener
+import com.compdfkit.ui.reader.CPDFReaderView
+import java.io.File
+
+class TextSearchTest : PDFSamples() {
+    private val handler = Handler(Looper.getMainLooper())
+
+    init {
+        setTitle(R.string.text_search_test_title)
+        setDescription(R.string.text_search_test_desc)
+    }
+
+    override fun run(outputListener: OutputListener?) {
+        super.run(outputListener)
+        handler.post {
+            printHead()
+            outputListener?.println()
+            val document = CPDFDocument(context())
+            document.open(getAssetsTempFile(context(), "text.pdf"))
+            val readerView = CPDFReaderView(context())
+            readerView.pdfDocument = document
+            readerView.reloadPages()
+            val keywords = "PDF"
+            // Search for all relevant text in a document based on a keyword
+            val list = startSearch(readerView, keywords)
+            if (list != null && list.isNotEmpty()) {
+                val textRange = list[0]
+                val pdfPage = document.pageAtIndex(0)
+                val pdfTextPage = pdfPage.textPage
+                val textSelectionArr = pdfTextPage.getSelectionsByTextForRange(textRange)
+                //Then, add a highlight annotation for the specific area.
+                val annotRect = RectF()
+                val highlightAnnotation = pdfPage.addAnnot(CPDFAnnotation.Type.HIGHLIGHT) as CPDFHighlightAnnotation
+                highlightAnnotation.color = Color.YELLOW
+                highlightAnnotation.alpha = 255 / 2
+                val quadRects = arrayOfNulls<RectF>(textSelectionArr.size)
+                val markedTextSb = StringBuilder()
+                val len = textSelectionArr.size
+                for (i in 0 until len) {
+                    val textSelection = textSelectionArr[i] ?: continue
+                    val rect = RectF(textSelection.rectF)
+                    if (annotRect.isEmpty) {
+                        annotRect.set(rect)
+                    } else {
+                        annotRect.union(rect)
+                    }
+                    quadRects[i] = RectF(textSelection.rectF)
+                    val text = pdfTextPage.getText(textSelection.textRange)
+                    if (!TextUtils.isEmpty(text)) {
+                        markedTextSb.append(text)
+                    }
+                }
+                highlightAnnotation.quadRects = quadRects
+                highlightAnnotation.markedText = markedTextSb.toString()
+                highlightAnnotation.rect = annotRect
+                highlightAnnotation.updateAp()
+            }
+            outputListener?.println("the key `" + keywords + "` have " + list!!.size + " results")
+            val file = File(outputDir(), "TextSearchTest/TextSearchResults.pdf")
+            saveSamplePDF(document, file, false)
+            outputListener?.println("Done. Results saved in TextSearchResults.pdf")
+            printFooter()
+        }
+    }
+
+    companion object {
+        fun startSearch(readerView: CPDFReaderView, keywords: String?): List<CPDFTextRange>? {
+            val textSearcher = readerView.textSearcher ?: return null
+            val document = readerView.pdfDocument ?: return null
+            textSearcher.setSearchConfig(keywords, CPDFTextSearcher.PDFSearchOptions.PDFSearchCaseInsensitive)
+            val searchTextInfoList: MutableList<CPDFTextRange> = ArrayList()
+            for (i in 0 until document.pageCount) {
+                val page = document.pageAtIndex(i)
+                val textPage = page.textPage
+                if (null == textPage || !textPage.isValid) {
+                    continue
+                }
+                val searchPageContent: List<CPDFTextRange> = textSearcher.searchKeyword(i)
+                if (searchPageContent.isNotEmpty()) {
+                    searchTextInfoList.addAll(searchPageContent)
+                }
+                page.close()
+            }
+            return searchTextInfoList
+        }
+    }
+}

+ 154 - 0
Samples_kotlin/src/main/java/com/compdfkit/samples/samples/WatermarkTest.kt

@@ -0,0 +1,154 @@
+/**
+ * Copyright © 2014-2023 PDF Technologies, Inc. All Rights Reserved.
+ *
+ *
+ * THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
+ * AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE ComPDFKit LICENSE AGREEMENT.
+ * UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
+ * This notice may not be removed from this file.
+ */
+package com.compdfkit.samples.samples
+
+import android.graphics.BitmapFactory
+import android.graphics.Color
+import com.compdfkit.core.document.CPDFDocument
+import com.compdfkit.core.watermark.CPDFWatermark
+import com.compdfkit.samples.PDFSamples
+import com.compdfkit.samples.R
+import com.compdfkit.samples.util.FileUtils.getAssetsTempFile
+import com.compdfkit.samples.util.OutputListener
+import java.io.File
+
+class WatermarkTest : PDFSamples() {
+    init {
+        setTitle(R.string.watermark_test_title)
+        setDescription(R.string.watermark_test_desc)
+    }
+
+    override fun run(outputListener: OutputListener?) {
+        super.run(outputListener)
+        printHead()
+        addTextWatermark()
+        addImageWatermark()
+        addTilesWatermark()
+        deleteWatermark()
+        printFooter()
+    }
+
+    /**
+     * Samples 1 : Insert a text watermark in the center of all pages of the document
+     */
+    private fun addTextWatermark() {
+        printDividingLine()
+        outputListener?.println("Sample 1 : Insert Text Watermark")
+        val document = CPDFDocument(context())
+        document.open(getAssetsTempFile(context(), "CommonFivePage.pdf"))
+        document.createWatermark(CPDFWatermark.Type.WATERMARK_TYPE_TEXT).apply {
+            text = "ComPDFKit"
+            textRGBColor = Color.RED
+            fontSize = 30F
+            opacity = 0.5F
+            rotation = 45F
+            vertalign = CPDFWatermark.Vertalign.WATERMARK_VERTALIGN_CENTER
+            horizalign = CPDFWatermark.Horizalign.WATERMARK_HORIZALIGN_CENTER
+            vertOffset = 0F
+            horizOffset = 0F
+            pages = "0,1,2,3,4"
+            printWatermarkInfo(this)
+            update()
+        }.release()
+        val file = File(outputDir(), "watermarkTest/AddTextWatermarkTest.pdf")
+        saveSamplePDF(document, file, true)
+        outputListener?.println("Done. Result saved in AddTextWatermarkTest.pdf")
+        printDividingLine()
+    }
+
+    /**
+     * Samples 2 : Insert a picture watermark in the center of all pages of the document
+     */
+    private fun addImageWatermark() {
+        outputListener?.println("Sample 2 : Insert Image Watermark")
+        val document = CPDFDocument(context())
+        document.open(getAssetsTempFile(context(), "CommonFivePage.pdf"))
+        document.createWatermark(CPDFWatermark.Type.WATERMARK_TYPE_IMG).apply {
+            val watermarkImage = BitmapFactory.decodeResource(context().resources, R.mipmap.ic_launcher_foreground)
+            setImage(watermarkImage, 100, 100)
+            opacity = 1F
+            rotation = 20F
+            vertalign = CPDFWatermark.Vertalign.WATERMARK_VERTALIGN_CENTER
+            horizalign = CPDFWatermark.Horizalign.WATERMARK_HORIZALIGN_CENTER
+            vertOffset = 0F
+            horizOffset = 0F
+            pages = "0,1,2,3,4"
+            printWatermarkInfo(this)
+            update()
+        }.release()
+        val file = File(outputDir(), "watermarkTest/AddImageWatermarkTest.pdf")
+        saveSamplePDF(document, file, true)
+        outputListener?.println("Done. Result saved in AddImageWatermarkTest.pdf")
+        printDividingLine()
+    }
+
+    /**
+     * Samples 3 : Insert a tiled text watermark on all pages of the document
+     */
+    private fun addTilesWatermark() {
+        outputListener?.println("Sample 3 : Insert Text Tiles Watermark")
+        val document = CPDFDocument(context())
+        document.open(getAssetsTempFile(context(), "CommonFivePage.pdf"))
+        document.createWatermark(CPDFWatermark.Type.WATERMARK_TYPE_TEXT).apply {
+            text = "ComPDFKit"
+            textRGBColor = Color.RED
+            fontSize = 30F
+            opacity = 0.5F
+            rotation = 45F
+            vertalign = CPDFWatermark.Vertalign.WATERMARK_VERTALIGN_CENTER
+            horizalign = CPDFWatermark.Horizalign.WATERMARK_HORIZALIGN_CENTER
+            vertOffset = 0F
+            horizOffset = 0F
+            pages = "0,1,2,3,4"
+            isFullScreen = true
+            horizontalSpacing = 100F
+            verticalSpacing = 100F
+            printWatermarkInfo(this)
+            update()
+        }.release()
+        val file = File(outputDir(), "watermarkTest/AddTilesWatermarkTest.pdf")
+        saveSamplePDF(document, file, true)
+        outputListener?.println("Done. Result saved in AddTilesWatermarkTest.pdf")
+        printDividingLine()
+    }
+
+    private fun deleteWatermark() {
+        outputListener?.println("Sample 4 : Delete Watermark")
+        val document = CPDFDocument(context())
+        val sampleFile = File(outputDir(), "watermarkTest/AddTextWatermarkTest.pdf")
+        document.open(sampleFile.absolutePath)
+        document.getWatermark(0)?.clear()
+        val file = File(outputDir(), "watermarkTest/DeleteWatermarkTest.pdf")
+        saveSamplePDF(document, file, true)
+        outputListener?.println("Done. Result saved in DeleteWatermarkTest.pdf")
+        printDividingLine()
+    }
+
+    private fun printWatermarkInfo(watermark: CPDFWatermark) {
+        if (watermark.type == CPDFWatermark.Type.WATERMARK_TYPE_TEXT) {
+            outputListener?.println("Text : " + watermark.text)
+            outputListener?.println(String.format("Color : red:%d, green:%d, blue:%d, alpha:%d",
+                    Color.red(watermark.textRGBColor),
+                    Color.green(watermark.textRGBColor),
+                    Color.blue(watermark.textRGBColor),
+                    Color.alpha(watermark.textRGBColor)))
+            outputListener?.println("FontSize : ${watermark.fontSize}")
+        }
+        outputListener?.println("Opacity : ${watermark.opacity}")
+        outputListener?.println("Rotation : ${watermark.rotation}")
+        outputListener?.println("Vertalign : ${watermark.vertalign.name}")
+        outputListener?.println("Horizalign : ${watermark.horizalign.name}")
+        outputListener?.println("VertOffset : ${watermark.vertOffset}")
+        outputListener?.println("HorizOffset : ${watermark.horizOffset}")
+        outputListener?.println("Pages : ${watermark.pages}")
+        outputListener?.println("VerticalSpacing : ${watermark.verticalSpacing}")
+        outputListener?.println("HorizontalSpacing : ${watermark.horizontalSpacing}")
+    }
+}

+ 11 - 0
Samples_kotlin/src/main/java/com/compdfkit/samples/util/CPDFGlideModule.kt

@@ -0,0 +1,11 @@
+package com.compdfkit.samples.util
+
+import com.bumptech.glide.annotation.GlideModule
+import com.bumptech.glide.module.AppGlideModule
+
+@GlideModule
+class CPDFGlideModule : AppGlideModule() {
+    override fun isManifestParsingEnabled(): Boolean {
+        return false
+    }
+}

+ 31 - 0
Samples_kotlin/src/main/java/com/compdfkit/samples/util/DateUtil.kt

@@ -0,0 +1,31 @@
+/**
+ * Copyright © 2014-2023 PDF Technologies, Inc. All Rights Reserved.
+ *
+ * THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
+ * AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE ComPDFKit LICENSE AGREEMENT.
+ * UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
+ * This notice may not be removed from this file.
+ */
+package com.compdfkit.samples.util
+
+import android.text.TextUtils
+
+object DateUtil {
+
+    fun transformPDFDate(inputDate: String): String {
+        return try {
+            if (TextUtils.isEmpty(inputDate) || inputDate.length < 16) {
+                return ""
+            }
+            val year = inputDate.substring(2, 6)
+            val month = inputDate.substring(6, 8)
+            val day = inputDate.substring(8, 10)
+            val hour = inputDate.substring(10, 12)
+            val minute = inputDate.substring(12, 14)
+            val second = inputDate.substring(14, 16)
+            "$year-$month-$day $hour:$minute:$second"
+        } catch (e: Exception) {
+            inputDate
+        }
+    }
+}

+ 112 - 0
Samples_kotlin/src/main/java/com/compdfkit/samples/util/FileUtils.kt

@@ -0,0 +1,112 @@
+/**
+ * Copyright © 2014-2023 PDF Technologies, Inc. All Rights Reserved.
+ *
+ *
+ * THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
+ * AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE ComPDFKit LICENSE AGREEMENT.
+ * UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
+ * This notice may not be removed from this file.
+ */
+package com.compdfkit.samples.util
+
+import android.content.ActivityNotFoundException
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
+import android.os.Build
+import androidx.core.content.FileProvider
+import java.io.File
+import java.io.FileOutputStream
+import java.io.IOException
+
+object FileUtils {
+
+    fun shareFile(context: Context, title: String?, type: String?, file: File?) {
+        try {
+            val intent = Intent(Intent.ACTION_VIEW).apply {
+                putExtra(Intent.EXTRA_SUBJECT, title)
+                val uri = getUriBySystem(context, file)
+                putExtra(Intent.EXTRA_STREAM, uri)
+                setDataAndType(uri, type)
+                flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION
+            }
+            context.startActivity(Intent.createChooser(intent, title))
+        } catch (e: ActivityNotFoundException) {
+            e.printStackTrace()
+        }
+    }
+
+    fun getUriBySystem(context: Context, file: File?): Uri? {
+        return try {
+            if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
+                Uri.fromFile(file)
+            } else {
+                FileProvider.getUriForFile(context, "${context.packageName}.fileprovider", file!!)
+            }
+        } catch (e: Exception) {
+            null
+        }
+    }
+
+    fun getAssetsTempFile(context: Context, assetsName: String): String? {
+        return copyFileFromAssets(context, assetsName, context.cacheDir.absolutePath, assetsName, true)
+    }
+
+    fun getNameWithoutExtension(name: String): String {
+        val index = name.lastIndexOf(".")
+        return if (index == -1) name else name.substring(0, index)
+    }
+
+    fun copyFileFromAssets(context: Context,
+                           assetName: String?,
+                           savePath: String,
+                           saveName: String,
+                           overwriteExisting: Boolean): String? {
+        //if save path folder not exists, create directory
+        val dir = File(savePath)
+        if (!dir.exists()) {
+            if (!dir.mkdirs()) {
+                return null
+            }
+        }
+
+        // 拷贝文件
+        val filename = "$savePath/$saveName"
+        val file = File(filename)
+        if (file.exists()) {
+            file.delete()
+        }
+        return if (!file.exists() || overwriteExisting) {
+            try {
+                val inStream = context.assets.open(assetName!!)
+                val fileOutputStream = FileOutputStream(filename)
+                var byteRead: Int
+                val buffer = ByteArray(1024)
+                while (inStream.read(buffer).also { byteRead = it } != -1) {
+                    fileOutputStream.write(buffer, 0, byteRead)
+                }
+                fileOutputStream.flush()
+                inStream.close()
+                fileOutputStream.close()
+            } catch (e: IOException) {
+                e.printStackTrace()
+            }
+            file.absolutePath
+        } else {
+            file.absolutePath
+        }
+    }
+
+    fun deleteFile(file: File) {
+        if (file.isDirectory) {
+            val files = file.listFiles()
+            for (i in files.indices) {
+                val f = files[i]
+                deleteFile(f)
+            }
+            file.delete() //如要保留文件夹,只删除文件,请注释这行
+        } else if (file.exists()) {
+            file.delete()
+        }
+    }
+}

+ 43 - 0
Samples_kotlin/src/main/java/com/compdfkit/samples/util/LoggingOutputListener.kt

@@ -0,0 +1,43 @@
+/**
+ * Copyright © 2014-2023 PDF Technologies, Inc. All Rights Reserved.
+ *
+ * THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
+ * AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE ComPDFKit LICENSE AGREEMENT.
+ * UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
+ * This notice may not be removed from this file.
+ */
+package com.compdfkit.samples.util
+
+import android.view.View
+import android.widget.ScrollView
+import androidx.appcompat.widget.AppCompatTextView
+
+class LoggingOutputListener(private val logTextview: AppCompatTextView,
+                            private val scrollView: ScrollView) : OutputListener {
+
+    override fun print(output: String?) {
+        scrollView.post {
+            logTextview.append(output)
+            scrollView.fullScroll(View.FOCUS_DOWN)
+        }
+    }
+
+    override fun print() {
+        logTextview.append("")
+    }
+
+    override fun println(output: String?) {
+        scrollView.post {
+            logTextview.append("$output \n")
+            scrollView.fullScroll(View.FOCUS_DOWN)
+        }
+    }
+
+    override fun println() {
+        logTextview.post { logTextview.append("\n") }
+    }
+
+    override fun printError(errorMessage: String?) {
+        println(errorMessage)
+    }
+}

+ 22 - 0
Samples_kotlin/src/main/java/com/compdfkit/samples/util/OutputListener.kt

@@ -0,0 +1,22 @@
+/**
+ * Copyright © 2014-2023 PDF Technologies, Inc. All Rights Reserved.
+ *
+ * THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
+ * AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE ComPDFKit LICENSE AGREEMENT.
+ * UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
+ * This notice may not be removed from this file.
+ */
+package com.compdfkit.samples.util
+
+interface OutputListener {
+
+    fun print(output: String?)
+
+    fun print()
+
+    fun println(output: String?)
+
+    fun println()
+
+    fun printError(errorMessage: String?)
+}

+ 5 - 0
Samples_kotlin/src/main/res/drawable/baseline_arrow_back_24.xml

@@ -0,0 +1,5 @@
+<vector android:autoMirrored="true" android:height="24dp"
+    android:tint="#000000" android:viewportHeight="24"
+    android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="@android:color/white" android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/>
+</vector>

+ 18 - 0
Samples_kotlin/src/main/res/layout/activity_sample_list.xml

@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".SampleListActivity">
+
+
+    <androidx.recyclerview.widget.RecyclerView
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:id="@+id/rv_sample_list"
+        tools:listitem="@layout/layout_sample_list_item"
+        />
+
+
+
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 65 - 0
Samples_kotlin/src/main/res/layout/fragment_sample_detail.xml

@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:padding="8dp">
+
+
+    <androidx.appcompat.widget.AppCompatTextView
+        android:id="@+id/tv_description"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        tools:text="Sample Description" />
+
+    <com.google.android.material.button.MaterialButton
+        android:id="@+id/btn_run"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="4dp"
+        android:text="@string/run"
+        app:cornerRadius="4dp"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/tv_description"
+
+        />
+
+    <com.google.android.material.button.MaterialButton
+        android:id="@+id/btn_open_files"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="8dp"
+        android:text="@string/open_files"
+        app:cornerRadius="4dp"
+        app:layout_constraintStart_toEndOf="@id/btn_run"
+        app:layout_constraintTop_toTopOf="@id/btn_run" />
+
+
+    <ScrollView
+        android:id="@+id/scroll_view"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_marginTop="8dp"
+        android:fillViewport="true"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/btn_run">
+
+        <androidx.appcompat.widget.AppCompatTextView
+            android:id="@+id/tv_logging"
+            style="?android:attr/textAppearanceSmall"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textIsSelectable="true"
+            android:background="#80E4E0E0"
+            android:padding="4dp"
+            tools:text="Done!" />
+
+
+    </ScrollView>
+
+
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 22 - 0
Samples_kotlin/src/main/res/layout/layout_sample_list_item.xml

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_marginHorizontal="8dp"
+    android:background="?attr/selectableItemBackground"
+    android:orientation="vertical">
+
+    <androidx.appcompat.widget.AppCompatTextView
+        android:id="@+id/tv_sample_title"
+        android:layout_width="match_parent"
+        android:layout_height="55dp"
+        android:gravity="center_vertical"
+        android:textSize="16sp"
+        tools:text="FormsTest" />
+
+    <View
+        android:layout_width="match_parent"
+        android:layout_height="0.5dp"
+        android:background="?attr/colorControlHighlight" />
+</LinearLayout>

+ 5 - 0
Samples_kotlin/src/main/res/mipmap-anydpi-v26/ic_launcher.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@android:color/white"/>
+    <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
+</adaptive-icon>

+ 5 - 0
Samples_kotlin/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@color/white"/>
+    <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
+</adaptive-icon>

BIN
Samples_kotlin/src/main/res/mipmap-hdpi/ic_launcher.png


BIN
Samples_kotlin/src/main/res/mipmap-hdpi/ic_launcher_foreground.png


BIN
Samples_kotlin/src/main/res/mipmap-hdpi/ic_launcher_round.png


BIN
Samples_kotlin/src/main/res/mipmap-mdpi/ic_launcher.png


BIN
Samples_kotlin/src/main/res/mipmap-mdpi/ic_launcher_foreground.png


BIN
Samples_kotlin/src/main/res/mipmap-mdpi/ic_launcher_round.png


BIN
Samples_kotlin/src/main/res/mipmap-xhdpi/ic_launcher.png


BIN
Samples_kotlin/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png


BIN
Samples_kotlin/src/main/res/mipmap-xhdpi/ic_launcher_round.png


BIN
Samples_kotlin/src/main/res/mipmap-xxhdpi/ic_launcher.png


BIN
Samples_kotlin/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png


BIN
Samples_kotlin/src/main/res/mipmap-xxhdpi/ic_launcher_round.png


BIN
Samples_kotlin/src/main/res/mipmap-xxxhdpi/ic_launcher.png


BIN
Samples_kotlin/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png


BIN
Samples_kotlin/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png


+ 7 - 0
Samples_kotlin/src/main/res/values-night/themes.xml

@@ -0,0 +1,7 @@
+<resources>
+    <!-- Base application theme. -->
+    <style name="Base.Theme.Compdfkit_android_demo" parent="Theme.Material3.DayNight">
+        <!-- Customize your dark theme here. -->
+        <!-- <item name="colorPrimary">@color/my_dark_primary</item> -->
+    </style>
+</resources>

+ 5 - 0
Samples_kotlin/src/main/res/values/colors.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <color name="black">#FF000000</color>
+    <color name="white">#FFFFFFFF</color>
+</resources>

File diff suppressed because it is too large
+ 71 - 0
Samples_kotlin/src/main/res/values/strings.xml


+ 9 - 0
Samples_kotlin/src/main/res/values/themes.xml

@@ -0,0 +1,9 @@
+<resources>
+    <!-- Base application theme. -->
+    <style name="Base.Theme.Compdfkit_android_demo" parent="Theme.Material3.DayNight">
+        <!-- Customize your light theme here. -->
+        <!-- <item name="colorPrimary">@color/my_light_primary</item> -->
+    </style>
+
+    <style name="Theme.Compdfkit_android_demo" parent="Base.Theme.Compdfkit_android_demo" />
+</resources>

+ 25 - 0
Samples_kotlin/src/main/res/xml/tools_file_paths.xml

@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<paths>
+
+    <cache-path name="compdfkit/"
+        path="." />
+
+    <cache-path name="media/"
+        path="compdfkit/temp/" />
+
+    <files-path
+        name="media/"
+        path="."/>
+
+    <files-path name="media/"
+        path="compdfkit/temp/" />
+
+    <files-path
+        name="file/"
+        path="."/>
+
+    <external-path
+        name="compdf/"
+        path="."/>
+
+</paths>