Skip to content

Commit b928be3

Browse files
author
PSPDFKit
committed
Release 2.19.0
1 parent cc440a4 commit b928be3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+3226
-1714
lines changed

CHANGELOG.md

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
## 2.19.0 - 01 Jul 2025
2+
3+
- Adds the `androidRemoveStatusBarOffset` property to the `PDFConfiguration` object on Android. (J#HYB-802)
4+
- Adds the `iOSFileConflictResolution` option to `PDFConfiguration` to manage file conflict resolution on iOS. (J#HYB-825)
5+
- Adds the `iOSDocumentInfoOptions` option to `PDFConfiguration` to customize Document Info tabs on iOS. (J#HYB-826)
6+
- Adds the `setExcludedAnnotations` API to exclude specified annotations completely from selection. (J#HYB-823)
7+
- Adds the `bookmarksChanged` event to `NotificationCenter` to receive bookmark change events. (J#HYB-818)
8+
- Adds the `getBookmarks`, `addBookmarks` and `removeBookmarks` APIs to `PDFDocument` for bookmark management. (J#HYB-818)
9+
- Fixes an issue where the `enterAnnotationCreationMode` API does not honour annotation variant options. (J#HYB-814)
10+
- Fixes an issue where the `enterAnnotationCreationMode` API on iOS could not change tools when already active. (J#HYB-815)
11+
- Fixes an issue where `AnnotationsEvent.REMOVED` contained null `name` and `creatorName` properties on Android. (J#HYB-829)
12+
113
## 2.18.1 - 24 Jun 2025
214
- Updates to Nutrient Android SDK 10.4.0.
315
- Fixes an issue where the `enterAnnotationCreationMode` and `exitCurrentlyActiveMode` calls on Android resolved before being complete. (J#HYB-824)
@@ -6,13 +18,13 @@
618

719
## 2.18.0 - 20 May 2025
820

9-
- Adds the new `AIAssistantConfiguration` class to configure AI Assistant and also add new `aiAssistantButtonItem` option to Toolbar configuration. (J#HYB-743)
21+
- Adds the new `AIAssistantConfiguration` class to configure AI Assistant, and also adds the new `aiAssistantButtonItem` option to the toolbar configuration. (J#HYB-743)
1022
- Adds the new `getPageInfo` API to the `PDFDocument` class. (J#HYB-801)
1123
- Updates to Nutrient Android SDK 10.2.0.
1224
- Updates to Nutrient iOS SDK 14.8.0.
1325
- Fixes an issue where the `setLicenseKeys` API could cause a crash on Android if called too early during the application lifecycle. (J#HYB-790)
14-
- Fixes an issue where `NotificationCenter` events are not always delivered when running Release build configuration on iOS. (J#HYB-793)
15-
- Fixes an issue where toolbar button customization was not persisted on Android during component reload. (J#HYB-800)
26+
- Fixes an issue where `NotificationCenter` events aren't always delivered when running the Release build configuration on iOS. (J#HYB-793)
27+
- Fixes an issue where toolbar button customization wasn't persisted on Android during component reload. (J#HYB-800)
1628
- Fixes an issue where the `onAnnotationTapped` callback wasn't called reliably on Android. (J#HYB-805)
1729

1830
## 2.17.0 - 14 Apr 2025

android/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
* Contains gradle configuration constants
1616
*/
1717
ext {
18-
PSPDFKIT_VERSION = '10.4.0'
18+
PSPDFKIT_VERSION = '10.4.1'
1919
}
2020

2121
buildscript {

android/src/main/java/com/pspdfkit/react/ConfigurationAdapter.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -663,11 +663,10 @@ private void configureShowDefaultToolbar(final boolean showDefaultToolbar) {
663663
if (showDefaultToolbar) {
664664
// Set it back to the default, which is AUTOMATIC_HIDE_SINGLE
665665
configuration.setTabBarHidingMode(TabBarHidingMode.AUTOMATIC_HIDE_SINGLE);
666-
configuration.defaultToolbarEnabled(true);
667666
} else {
668667
configuration.setTabBarHidingMode(TabBarHidingMode.HIDE);
669-
configuration.defaultToolbarEnabled(false);
670668
}
669+
configuration.defaultToolbarEnabled(showDefaultToolbar);
671670
}
672671

673672
private void configureShowActionButtons(final boolean showActionButtons) {

android/src/main/java/com/pspdfkit/react/NutrientNotificationCenter.kt

Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ import android.os.Bundle
44
import android.graphics.PointF
55
import com.facebook.react.bridge.Arguments
66
import com.facebook.react.bridge.ReactContext
7+
import com.facebook.react.bridge.WritableArray
78
import com.facebook.react.bridge.WritableMap
89
import com.pspdfkit.PSPDFKit
910
import com.pspdfkit.analytics.AnalyticsClient
1011
import com.pspdfkit.annotations.Annotation
12+
import com.pspdfkit.bookmarks.Bookmark
1113
import com.pspdfkit.forms.ChoiceFormElement
1214
import com.pspdfkit.forms.ComboBoxFormElement
1315
import com.pspdfkit.forms.EditableButtonFormElement
@@ -38,7 +40,8 @@ enum class NotificationEvent(val value: String) {
3840
FORM_FIELD_VALUES_UPDATED("formFieldValuesUpdated"),
3941
FORM_FIELD_SELECTED("formFieldSelected"),
4042
FORM_FIELD_DESELECTED("formFieldDeselected"),
41-
ANALYTICS("analytics");
43+
ANALYTICS("analytics"),
44+
BOOKMARKS_CHANGED("bookmarksChanged");
4245
}
4346

4447
object NutrientNotificationCenter {
@@ -100,14 +103,15 @@ object NutrientNotificationCenter {
100103
sendEvent(NotificationEvent.DOCUMENT_SCROLLED.value, jsonData)
101104
}
102105

103-
fun didTapDocument(pointF: PointF, documentID: String) {
106+
fun didTapDocument(pointF: PointF, pageIndex: Int, documentID: String) {
104107
try {
105108
val pointMap = mapOf("x" to pointF.x, "y" to pointF.y)
106109
val nativePointMap = Arguments.makeNativeMap(pointMap)
107110

108111
val jsonData = Arguments.createMap()
109112
jsonData.putString("event", NotificationEvent.DOCUMENT_TAPPED.value)
110113
jsonData.putMap("point", nativePointMap)
114+
jsonData.putInt("pageIndex", pageIndex)
111115
jsonData.putString("documentID", documentID)
112116
sendEvent(NotificationEvent.DOCUMENT_TAPPED.value, jsonData)
113117
} catch (e: Exception) {
@@ -134,19 +138,22 @@ object NutrientNotificationCenter {
134138
}
135139
}
136140
"removed" -> {
137-
val annotationsList = mutableListOf<Map<String, Any>>()
138-
val annotationMap = HashMap<String, String>()
139-
annotation.name?.let { annotationMap["name"] = it }
140-
annotation.creator?.let { annotationMap["creatorName"] = it }
141-
annotationMap["uuid"] = annotation.uuid
142-
annotationsList.add(annotationMap)
143-
val nativeAnnotationsList = Arguments.makeNativeArray(annotationsList)
144-
145-
val jsonData = Arguments.createMap()
146-
jsonData.putString("event", NotificationEvent.ANNOTATIONS_REMOVED.value)
147-
jsonData.putArray("annotations", nativeAnnotationsList)
148-
jsonData.putString("documentID", documentID)
149-
sendEvent(NotificationEvent.ANNOTATIONS_REMOVED.value, jsonData)
141+
// Only emit event if annotation has a name and creator
142+
if (annotation.name != null && annotation.creator != null) {
143+
val annotationsList = mutableListOf<Map<String, Any>>()
144+
val annotationMap = HashMap<String, String>()
145+
annotation.name?.let { annotationMap["name"] = it }
146+
annotation.creator?.let { annotationMap["creatorName"] = it }
147+
annotationMap["uuid"] = annotation.uuid
148+
annotationsList.add(annotationMap)
149+
val nativeAnnotationsList = Arguments.makeNativeArray(annotationsList)
150+
151+
val jsonData = Arguments.createMap()
152+
jsonData.putString("event", NotificationEvent.ANNOTATIONS_REMOVED.value)
153+
jsonData.putArray("annotations", nativeAnnotationsList)
154+
jsonData.putString("documentID", documentID)
155+
sendEvent(NotificationEvent.ANNOTATIONS_REMOVED.value, jsonData)
156+
}
150157
}
151158
"added" -> {
152159
try {
@@ -167,6 +174,28 @@ object NutrientNotificationCenter {
167174
}
168175
}
169176

177+
fun bookmarksChanged(bookmarks: List<Bookmark>, documentID: String) {
178+
try {
179+
// Create a WritableArray to hold the bookmark maps
180+
val bookmarksArray: WritableArray = Arguments.createArray()
181+
182+
for (bookmark in bookmarks) {
183+
val bookmarkMap: WritableMap = Arguments.createMap()
184+
bookmarkMap.putString("identifier", bookmark.uuid)
185+
bookmark.pageIndex?.let { bookmarkMap.putInt("pageIndex", it) }
186+
bookmarksArray.pushMap(bookmarkMap)
187+
}
188+
189+
val jsonData = Arguments.createMap()
190+
jsonData.putString("event", NotificationEvent.BOOKMARKS_CHANGED.value)
191+
jsonData.putArray("bookmarks", bookmarksArray)
192+
jsonData.putString("documentID", documentID)
193+
sendEvent(NotificationEvent.BOOKMARKS_CHANGED.value, jsonData)
194+
} catch (e: Exception) {
195+
// Could not decode bookmark data
196+
}
197+
}
198+
170199
fun didSelectAnnotations(annotation: Annotation, documentID: String) {
171200
try {
172201
val annotationsList = mutableListOf<Map<String, Any>>()

android/src/main/java/com/pspdfkit/react/PDFDocumentModule.kt

Lines changed: 87 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import com.pspdfkit.forms.ComboBoxFormElement
2828
import com.pspdfkit.forms.EditableButtonFormElement
2929
import com.pspdfkit.forms.TextFormElement
3030
import com.pspdfkit.react.helper.AnnotationUtils
31+
import com.pspdfkit.react.helper.BookmarkUtils
3132
import com.pspdfkit.react.helper.ConversionHelpers.getAnnotationTypes
3233
import com.pspdfkit.react.helper.DocumentJsonDataProvider
3334
import com.pspdfkit.react.helper.FormUtils
@@ -288,40 +289,44 @@ class PDFDocumentModule(reactContext: ReactApplicationContext) : ReactContextBas
288289
// Can be removed after customer migration is complete.
289290

290291
if (instantJSON.type == ReadableType.Map) {
291-
applyInstantJSON(reference, instantJSON.asMap(), promise)
292+
instantJSON.asMap()?.let { applyInstantJSON(reference, it, promise) }
292293
return
293294
}
294295

295296
try {
296297
this.getDocument(reference)?.document?.let { document ->
297298
if (instantJSON.type == ReadableType.Array) {
298299
val instantJSONArray = instantJSON.asArray()
299-
val hasImageAnnotations = (0 until instantJSONArray.size()).any { i ->
300-
val annotation = instantJSONArray.getMap(i)
300+
val hasImageAnnotations = (0 until (instantJSONArray?.size() ?: 0)).any { i ->
301+
val annotation = instantJSONArray?.getMap(i)
301302
val hashMap = annotation?.toHashMap() as? Map<String, Any>
302303
hashMap?.containsKey("imageAttachmentId") == true
303304
}
304305

305306
if (hasImageAnnotations) {
306307
// If there are any image annotations, process them all at once
307308
val attachmentsJSONMap = attachments.asMap()
308-
val instantData = createInstantObject(instantJSONArray, attachmentsJSONMap)
309+
val instantData = createInstantObject(instantJSONArray!!,
310+
attachmentsJSONMap!!
311+
)
309312
if (instantData != null) {
310313
applyInstantJSON(reference, instantData, promise)
311314
return
312315
}
313316
} else {
314317
// Process non-image annotations directly
315-
for (i in 0 until instantJSONArray.size()) {
316-
try {
317-
val annotation = instantJSONArray.getMap(i)
318-
val hashMap = annotation!!.toHashMap() as Map<String, Any>
319-
document.annotationProvider.createAnnotationFromInstantJson(
320-
JSONObject(hashMap).toString()
321-
)
322-
} catch (e: Exception) {
323-
promise.reject("addAnnotations error", e)
324-
return
318+
if (instantJSONArray != null) {
319+
for (i in 0 until instantJSONArray.size()) {
320+
try {
321+
val annotation = instantJSONArray.getMap(i)
322+
val hashMap = annotation!!.toHashMap() as Map<*, *>
323+
document.annotationProvider.createAnnotationFromInstantJson(
324+
JSONObject(hashMap).toString()
325+
)
326+
} catch (e: Exception) {
327+
promise.reject("addAnnotations error", e)
328+
return
329+
}
325330
}
326331
}
327332
promise.resolve(true)
@@ -338,7 +343,7 @@ class PDFDocumentModule(reactContext: ReactApplicationContext) : ReactContextBas
338343
@ReactMethod fun applyInstantJSON(reference: Int, instantJSON: ReadableMap, promise: Promise) {
339344
try {
340345
this.getDocument(reference)?.document?.let {
341-
val json = JSONObject(instantJSON.toHashMap() as Map<*, *>?)
346+
val json = (instantJSON.toHashMap() as Map<*, *>?)?.let { it1 -> JSONObject(it1) }
342347
val dataProvider: DataProvider = DocumentJsonDataProvider(json)
343348
DocumentJsonFormatter.importDocumentJsonAsync(it, dataProvider)
344349
.subscribeOn(Schedulers.io())
@@ -467,12 +472,15 @@ class PDFDocumentModule(reactContext: ReactApplicationContext) : ReactContextBas
467472
}
468473
is ChoiceFormElement -> {
469474
if (value.type == ReadableType.Array) {
470-
val indices = value.asArray().toArrayList().filterIsInstance<Int>()
471-
formElement.selectedIndexes = indices
475+
val indices = value.asArray()?.toArrayList()
476+
?.filterIsInstance<Int>()
477+
if (indices != null) {
478+
formElement.selectedIndexes = indices
479+
}
472480
success = true
473481
} else if (value.type == ReadableType.String) {
474482
try {
475-
val index = value.asString().toInt()
483+
val index = value.asString()?.toInt()
476484
formElement.selectedIndexes = listOf(index)
477485
success = true
478486
} catch (e: NumberFormatException) {
@@ -486,7 +494,7 @@ class PDFDocumentModule(reactContext: ReactApplicationContext) : ReactContextBas
486494
}
487495
is TextFormElement -> {
488496
if (value.type == ReadableType.String) {
489-
formElement.setText(value.asString())
497+
value.asString()?.let { it1 -> formElement.setText(it1) }
490498
success = true
491499
}
492500
}
@@ -508,6 +516,66 @@ class PDFDocumentModule(reactContext: ReactApplicationContext) : ReactContextBas
508516
}
509517
}
510518

519+
@ReactMethod fun getBookmarks(reference: Int, promise: Promise) {
520+
try {
521+
this.getDocument(reference)?.document?.let { document ->
522+
val bookmarks = document.bookmarkProvider.bookmarks
523+
val bookmarksJSON = BookmarkUtils.bookmarksToJSON(bookmarks)
524+
promise.resolve(Arguments.makeNativeArray(bookmarksJSON))
525+
} ?: run {
526+
promise.reject("getBookmarks", "Document is nil", null)
527+
}
528+
} catch (e: Throwable) {
529+
promise.reject("getBookmarks error", e)
530+
}
531+
}
532+
533+
@ReactMethod fun addBookmarks(reference: Int, bookmarks: ReadableArray, promise: Promise) {
534+
try {
535+
val document = this.getDocument(reference)?.document
536+
if (document == null) {
537+
promise.reject("addBookmarks", "Document is nil", null)
538+
return
539+
}
540+
541+
// Convert JSON to Bookmark objects
542+
val bookmarkObjects = BookmarkUtils.JSONToBookmarks(bookmarks)
543+
544+
// Add each bookmark to the document
545+
for (bookmark in bookmarkObjects) {
546+
document.bookmarkProvider.addBookmark(bookmark)
547+
}
548+
549+
promise.resolve(true)
550+
551+
} catch (e: Throwable) {
552+
promise.reject("addBookmarks error", e)
553+
}
554+
}
555+
556+
@ReactMethod fun removeBookmarks(reference: Int, bookmarks: ReadableArray, promise: Promise) {
557+
try {
558+
val document = this.getDocument(reference)?.document
559+
if (document == null) {
560+
promise.reject("removeBookmarks", "Document is nil", null)
561+
return
562+
}
563+
564+
// Convert JSON to Bookmark objects
565+
val bookmarkObjects = BookmarkUtils.JSONToBookmarks(bookmarks)
566+
567+
// Remove each bookmark from the document
568+
for (bookmark in bookmarkObjects) {
569+
document.bookmarkProvider.removeBookmark(bookmark)
570+
}
571+
572+
promise.resolve(true)
573+
574+
} catch (e: Throwable) {
575+
promise.reject("removeBookmarks error", e)
576+
}
577+
}
578+
511579
companion object {
512580
const val NAME = "PDFDocumentManager"
513581
}

0 commit comments

Comments
 (0)