Skip to content

Commit

Permalink
Release 2.12.0
Browse files Browse the repository at this point in the history
  • Loading branch information
PSPDFKit committed Aug 1, 2024
1 parent 8b4d9ef commit 74d3635
Show file tree
Hide file tree
Showing 66 changed files with 5,790 additions and 18,639 deletions.
26 changes: 26 additions & 0 deletions ACKNOWLEDGEMENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -3815,6 +3815,32 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

--------------------------------------------------------------------------------
## swift-perception (https://github.com/pointfreeco/swift-perception)

MIT License

Copyright (c) 2023 Point-Free

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


--------------------------------------------------------------------------------
## Tesseract (https://tesseract-ocr.github.io/)

Expand Down
15 changes: 13 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
## Newest Release

### 2.12.0 - 01 Aug 2024

- Adds APIs belonging to the `PDFDocument` interface, moving them away from the global namespace. (J#HYB-406)
- Adds support for using `React.RefObject` as `PSPDFKitView` ref property. (J#HYB-444)
- Updates for PSPDFKit 2024.3.1 for Android.
- Updates for PSPDFKit 13.8.0 for iOS.
- Fixes an issue where the `PSPDFKitView` sometimes failed to load the document on React Native Android. (J#HYB-397)
- Fixes an issue where Instant JSON containing widgets was not applied using the `addAnnotations` API on iOS. (J#HYB-413)
- Fixes an issue where password protected documents could not be saved after annotation changes were made. (J#HYB-454)
- Fixes an issue where the `onDocumentLoaded` callback was not called reliably on iOS. (J#HYB-480)

## Previous Releases

### 2.11.0 - 07 Jun 2024

- Adds the ability to clear the document cache. (J#HYB-347)
Expand All @@ -9,8 +22,6 @@
- Fixes an issue where the annotation `uuid` isn't included in `onAnnotationTapped` callbacks. (J#HYB-374)
- Fixes an issue where Instant configuration wasn't applied when using the `presentInstant` API on iOS. (J#HYB-375)

## Previous Releases

### 2.10.0 - 06 May 2024

- Adds the ability to define annotation behavior using flags. (J#HYB-283)
Expand Down
2 changes: 1 addition & 1 deletion android/.settings/org.eclipse.buildship.core.prefs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
arguments=--init-script /var/folders/3v/qy3ssjxs2m7d97yc60nrl2l00000gn/T/d146c9752a26f79b52047fb6dc6ed385d064e120494f96f08ca63a317c41f94c.gradle --init-script /var/folders/3v/qy3ssjxs2m7d97yc60nrl2l00000gn/T/52cde0cfcf3e28b8b7510e992210d9614505e0911af0c190bd590d7158574963.gradle
arguments=--init-script /var/folders/3v/qy3ssjxs2m7d97yc60nrl2l00000gn/T/db3b08fc4a9ef609cb16b96b200fa13e563f396e9bb1ed0905fdab7bc3bc513b.gradle --init-script /var/folders/3v/qy3ssjxs2m7d97yc60nrl2l00000gn/T/52cde0cfcf3e28b8b7510e992210d9614505e0911af0c190bd590d7158574963.gradle
auto.sync=false
build.scans.enabled=false
connection.gradle.distribution=GRADLE_DISTRIBUTION(VERSION(8.1.1))
Expand Down
2 changes: 1 addition & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
* Contains gradle configuration constants
*/
ext {
PSPDFKIT_VERSION = '2024.2.1'
PSPDFKIT_VERSION = '2024.3.1'
}

buildscript {
Expand Down
269 changes: 267 additions & 2 deletions android/src/main/java/com/pspdfkit/react/PDFDocumentModule.kt
Original file line number Diff line number Diff line change
@@ -1,16 +1,37 @@
package com.pspdfkit.react

import android.net.Uri
import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.Promise
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod
import com.facebook.react.bridge.ReadableArray
import com.facebook.react.bridge.ReadableMap
import com.facebook.react.module.annotations.ReactModule
import com.pspdfkit.annotations.Annotation
import com.pspdfkit.annotations.AnnotationProvider.ALL_ANNOTATION_TYPES
import com.pspdfkit.annotations.AnnotationType
import com.pspdfkit.document.PdfDocument
import com.pspdfkit.document.formatters.DocumentJsonFormatter
import com.pspdfkit.document.formatters.XfdfFormatter
import com.pspdfkit.document.providers.ContentResolverDataProvider
import com.pspdfkit.document.providers.DataProvider
import com.pspdfkit.internal.model.ImageDocumentImpl
import com.pspdfkit.react.helper.ConversionHelpers.getAnnotationTypeFromString
import com.pspdfkit.react.helper.DocumentJsonDataProvider
import com.pspdfkit.react.helper.JsonUtilities
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers
import org.json.JSONObject
import java.io.ByteArrayOutputStream
import java.util.EnumSet

@ReactModule(name = PDFDocumentModule.NAME)
class PDFDocumentModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {

private var documents = mutableMapOf<Int, PdfDocument>()
private var documentConfigurations = mutableMapOf<Int, MutableMap<String, Any>>()

override fun getName(): String {
return NAME
Expand All @@ -20,14 +41,22 @@ class PDFDocumentModule(reactContext: ReactApplicationContext) : ReactContextBas
return this.documents[reference]
}

private fun getDocumentConfiguration(reference: Int): MutableMap<String, Any>? {
return this.documentConfigurations[reference]
}

fun setDocument(document: PdfDocument, reference: Int) {
this.documents[reference] = document
}

fun updateDocumentConfiguration(key: String, value: Any, reference: Int) {
val currentConfiguration = documentConfigurations[reference]
currentConfiguration?.set(key, value)
}

@ReactMethod fun getDocumentId(reference: Int, promise: Promise) {
try {
// Using uid here until Android exposes the documentId property.
promise.resolve(this.getDocument(reference)?.uid)
promise.resolve(this.getDocument(reference)?.documentIdString)
} catch (e: Throwable) {
promise.reject("getDocumentId error", e)
}
Expand All @@ -51,6 +80,242 @@ class PDFDocumentModule(reactContext: ReactApplicationContext) : ReactContextBas
}
}

@ReactMethod fun save(reference: Int, promise: Promise) {
try {
this.getDocument(reference)?.let {
if (it is ImageDocumentImpl.ImagePdfDocumentWrapper) {
val metadata = this.getDocumentConfiguration(reference)?.get("imageSaveMode")?.equals("flattenAndEmbed") == true
if (it.imageDocument.saveIfModified(metadata)) {
promise.resolve(true)
}
} else {
it.saveIfModified()
promise.resolve(true)
}
}
promise.reject("save error", RuntimeException("Could not save document"))
} catch (e: Throwable) {
promise.reject("save error", e)
}
}

@ReactMethod fun getAllUnsavedAnnotations(reference: Int, promise: Promise) {
try {
this.getDocument(reference)?.let {
val outputStream = ByteArrayOutputStream()
DocumentJsonFormatter.exportDocumentJsonAsync(it, outputStream)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
val json = JSONObject(outputStream.toString())
val jsonMap = JsonUtilities.jsonObjectToMap(json)
val nativeMap = Arguments.makeNativeMap(jsonMap)
promise.resolve(nativeMap)
}, { e ->
promise.reject(RuntimeException(e))
}
)
}
} catch (e: Throwable) {
promise.reject("getAllUnsavedAnnotations error", e)
}
}

@ReactMethod fun getAnnotations(reference: Int, type: String?, promise: Promise) {
try {
this.getDocument(reference)?.let {
it.annotationProvider.getAllAnnotationsOfTypeAsync(if (type == null) ALL_ANNOTATION_TYPES else getAnnotationTypeFromString(type))
.toList()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ annotations ->
var annotationsSerialized: ArrayList<Map<String, Any>> = ArrayList()
for (annotation in annotations) {
if (annotation.type == AnnotationType.POPUP) {
continue
}
val annotationInstantJSON = JSONObject(annotation.toInstantJson())
val annotationMap = JsonUtilities.jsonObjectToMap(annotationInstantJSON)
annotationMap["uuid"] = annotation.uuid
annotationsSerialized.add(annotationMap)
}
val nativeList = Arguments.makeNativeArray(annotationsSerialized)
promise.resolve(nativeList)
}, { e ->
promise.reject(RuntimeException(e))
}
)
}
} catch (e: Throwable) {
promise.reject("getAnnotations error", e)
}
}

@ReactMethod fun getAnnotationsForPage(reference: Int, pageIndex: Int, type: String?, promise: Promise) {
try {
this.getDocument(reference)?.let {

if (pageIndex > it.pageCount-1) {
promise.reject(RuntimeException("Specified page index is out of bounds"))
return
}

it.annotationProvider.getAllAnnotationsOfTypeAsync(if (type == null) EnumSet.allOf(AnnotationType::class.java) else
getAnnotationTypeFromString(type), pageIndex, 1)
.toList()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ annotations ->
var annotationsSerialized: ArrayList<Map<String, Any>> = ArrayList()
for (annotation in annotations) {
if (annotation.type == AnnotationType.POPUP) {
continue
}
val annotationInstantJSON = JSONObject(annotation.toInstantJson())
val annotationMap = JsonUtilities.jsonObjectToMap(annotationInstantJSON)
annotationMap["uuid"] = annotation.uuid
annotationsSerialized.add(annotationMap)
}
val nativeList = Arguments.makeNativeArray(annotationsSerialized)
promise.resolve(nativeList)
}, { e ->
promise.reject(RuntimeException(e))
}
)
}
} catch (e: Throwable) {
promise.reject("getAnnotationsForPage error", e)
}
}

@ReactMethod fun removeAnnotations(reference: Int, instantJSON: ReadableArray, promise: Promise) {
try {
this.getDocument(reference)?.let {

val instantJSONArray: List<Map<String, Any>> = instantJSON.toArrayList().filterIsInstance<Map<String, Any>>()
var annotationsToDelete: ArrayList<Annotation> = ArrayList()
it.annotationProvider.getAllAnnotationsOfTypeAsync(ALL_ANNOTATION_TYPES)
.toList()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ annotations ->
for (annotation in annotations) {
for (instantJSONAnnotation in instantJSONArray) {
if (annotation.name == instantJSONAnnotation["name"] ||
annotation.uuid == instantJSONAnnotation["uuid"]) {
annotationsToDelete.add(annotation)
}
}
}

for (annotation in annotationsToDelete) {
it.annotationProvider.removeAnnotationFromPage(annotation)
}
promise.resolve(true)
}, { e ->
promise.reject(RuntimeException(e))
}
)
}
} catch (e: Throwable) {
promise.reject("removeAnnotations error", e)
}
}

@ReactMethod fun addAnnotations(reference: Int, instantJSON: ReadableMap, promise: Promise) {
try {
this.getDocument(reference)?.let {
val json = JSONObject(instantJSON.toHashMap())
val dataProvider: DataProvider = DocumentJsonDataProvider(json)
DocumentJsonFormatter.importDocumentJsonAsync(it, dataProvider)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
promise.resolve(true)
}, { e ->
promise.reject(RuntimeException(e))
})
}
} catch (e: Throwable) {
promise.reject("addAnnotations error", e)
}
}

@ReactMethod fun importXFDF(reference: Int, filePath: String, promise: Promise) {
try {
this.getDocument(reference)?.let {
var importPath = filePath;
if (Uri.parse(importPath).scheme == null) {
importPath = "file:///$filePath";
}

XfdfFormatter.parseXfdfAsync(it, ContentResolverDataProvider((Uri.parse(importPath))))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ annotations ->
for (annotation in annotations) {
it.annotationProvider.addAnnotationToPage(annotation)
}
val result = JSONObject()
result.put("success", true)
val jsonMap = JsonUtilities.jsonObjectToMap(result)
val nativeMap = Arguments.makeNativeMap(jsonMap)
promise.resolve(nativeMap)
}, { e ->
promise.reject("importXFDF error", e)
})
}
} catch (e: Throwable) {
promise.reject("importXFDF error", e)
}
}

@ReactMethod fun exportXFDF(reference: Int, filePath: String, promise: Promise) {
try {
this.getDocument(reference)?.let {
var exportPath = filePath;
if (Uri.parse(exportPath).scheme == null) {
exportPath = "file:///$filePath";
}

val outputStream = reactApplicationContext.contentResolver.openOutputStream(Uri.parse(exportPath))
if (outputStream == null) {
promise.reject("exportXFDF error", RuntimeException("Could not write to supplied file path error"))
return
}

val allAnnotations = it.annotationProvider.getAllAnnotationsOfType(ALL_ANNOTATION_TYPES)
val allFormFields = it.formProvider.formFields

XfdfFormatter.writeXfdfAsync(it, allAnnotations, allFormFields, outputStream)
XfdfFormatter.parseXfdfAsync(it, ContentResolverDataProvider((Uri.parse(exportPath))))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ annotations ->
for (annotation in annotations) {
it.annotationProvider.addAnnotationToPage(annotation)
}
val result = JSONObject()
result.put("success", true)
result.put("filePath", filePath)
val jsonMap = JsonUtilities.jsonObjectToMap(result)
val nativeMap = Arguments.makeNativeMap(jsonMap)
promise.resolve(nativeMap)
}, { e ->
promise.reject("exportXFDF error", e)
})
}
} catch (e: Throwable) {
promise.reject("exportXFDF error", e)
}
}

companion object {
const val NAME = "PDFDocumentManager"
}
Expand Down
Loading

0 comments on commit 74d3635

Please sign in to comment.