Skip to content

Commit 74d3635

Browse files
author
PSPDFKit
committed
Release 2.12.0
1 parent 8b4d9ef commit 74d3635

File tree

66 files changed

+5790
-18639
lines changed

Some content is hidden

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

66 files changed

+5790
-18639
lines changed

ACKNOWLEDGEMENTS.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3815,6 +3815,32 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY
38153815
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
38163816
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38173817

3818+
--------------------------------------------------------------------------------
3819+
## swift-perception (https://github.com/pointfreeco/swift-perception)
3820+
3821+
MIT License
3822+
3823+
Copyright (c) 2023 Point-Free
3824+
3825+
Permission is hereby granted, free of charge, to any person obtaining a copy
3826+
of this software and associated documentation files (the "Software"), to deal
3827+
in the Software without restriction, including without limitation the rights
3828+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
3829+
copies of the Software, and to permit persons to whom the Software is
3830+
furnished to do so, subject to the following conditions:
3831+
3832+
The above copyright notice and this permission notice shall be included in all
3833+
copies or substantial portions of the Software.
3834+
3835+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
3836+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
3837+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
3838+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
3839+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
3840+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3841+
SOFTWARE.
3842+
3843+
38183844
--------------------------------------------------------------------------------
38193845
## Tesseract (https://tesseract-ocr.github.io/)
38203846

CHANGELOG.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,18 @@
11
## Newest Release
22

3+
### 2.12.0 - 01 Aug 2024
4+
5+
- Adds APIs belonging to the `PDFDocument` interface, moving them away from the global namespace. (J#HYB-406)
6+
- Adds support for using `React.RefObject` as `PSPDFKitView` ref property. (J#HYB-444)
7+
- Updates for PSPDFKit 2024.3.1 for Android.
8+
- Updates for PSPDFKit 13.8.0 for iOS.
9+
- Fixes an issue where the `PSPDFKitView` sometimes failed to load the document on React Native Android. (J#HYB-397)
10+
- Fixes an issue where Instant JSON containing widgets was not applied using the `addAnnotations` API on iOS. (J#HYB-413)
11+
- Fixes an issue where password protected documents could not be saved after annotation changes were made. (J#HYB-454)
12+
- Fixes an issue where the `onDocumentLoaded` callback was not called reliably on iOS. (J#HYB-480)
13+
14+
## Previous Releases
15+
316
### 2.11.0 - 07 Jun 2024
417

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

12-
## Previous Releases
13-
1425
### 2.10.0 - 06 May 2024
1526

1627
- Adds the ability to define annotation behavior using flags. (J#HYB-283)

android/.settings/org.eclipse.buildship.core.prefs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
arguments=--init-script /var/folders/3v/qy3ssjxs2m7d97yc60nrl2l00000gn/T/d146c9752a26f79b52047fb6dc6ed385d064e120494f96f08ca63a317c41f94c.gradle --init-script /var/folders/3v/qy3ssjxs2m7d97yc60nrl2l00000gn/T/52cde0cfcf3e28b8b7510e992210d9614505e0911af0c190bd590d7158574963.gradle
1+
arguments=--init-script /var/folders/3v/qy3ssjxs2m7d97yc60nrl2l00000gn/T/db3b08fc4a9ef609cb16b96b200fa13e563f396e9bb1ed0905fdab7bc3bc513b.gradle --init-script /var/folders/3v/qy3ssjxs2m7d97yc60nrl2l00000gn/T/52cde0cfcf3e28b8b7510e992210d9614505e0911af0c190bd590d7158574963.gradle
22
auto.sync=false
33
build.scans.enabled=false
44
connection.gradle.distribution=GRADLE_DISTRIBUTION(VERSION(8.1.1))

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 = '2024.2.1'
18+
PSPDFKIT_VERSION = '2024.3.1'
1919
}
2020

2121
buildscript {

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

Lines changed: 267 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,37 @@
11
package com.pspdfkit.react
22

3+
import android.net.Uri
4+
import com.facebook.react.bridge.Arguments
35
import com.facebook.react.bridge.Promise
46
import com.facebook.react.bridge.ReactApplicationContext
57
import com.facebook.react.bridge.ReactContextBaseJavaModule
68
import com.facebook.react.bridge.ReactMethod
9+
import com.facebook.react.bridge.ReadableArray
10+
import com.facebook.react.bridge.ReadableMap
711
import com.facebook.react.module.annotations.ReactModule
12+
import com.pspdfkit.annotations.Annotation
13+
import com.pspdfkit.annotations.AnnotationProvider.ALL_ANNOTATION_TYPES
14+
import com.pspdfkit.annotations.AnnotationType
815
import com.pspdfkit.document.PdfDocument
16+
import com.pspdfkit.document.formatters.DocumentJsonFormatter
17+
import com.pspdfkit.document.formatters.XfdfFormatter
18+
import com.pspdfkit.document.providers.ContentResolverDataProvider
19+
import com.pspdfkit.document.providers.DataProvider
20+
import com.pspdfkit.internal.model.ImageDocumentImpl
21+
import com.pspdfkit.react.helper.ConversionHelpers.getAnnotationTypeFromString
22+
import com.pspdfkit.react.helper.DocumentJsonDataProvider
23+
import com.pspdfkit.react.helper.JsonUtilities
24+
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
25+
import io.reactivex.rxjava3.schedulers.Schedulers
26+
import org.json.JSONObject
27+
import java.io.ByteArrayOutputStream
28+
import java.util.EnumSet
929

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

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

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

44+
private fun getDocumentConfiguration(reference: Int): MutableMap<String, Any>? {
45+
return this.documentConfigurations[reference]
46+
}
47+
2348
fun setDocument(document: PdfDocument, reference: Int) {
2449
this.documents[reference] = document
2550
}
2651

52+
fun updateDocumentConfiguration(key: String, value: Any, reference: Int) {
53+
val currentConfiguration = documentConfigurations[reference]
54+
currentConfiguration?.set(key, value)
55+
}
56+
2757
@ReactMethod fun getDocumentId(reference: Int, promise: Promise) {
2858
try {
29-
// Using uid here until Android exposes the documentId property.
30-
promise.resolve(this.getDocument(reference)?.uid)
59+
promise.resolve(this.getDocument(reference)?.documentIdString)
3160
} catch (e: Throwable) {
3261
promise.reject("getDocumentId error", e)
3362
}
@@ -51,6 +80,242 @@ class PDFDocumentModule(reactContext: ReactApplicationContext) : ReactContextBas
5180
}
5281
}
5382

83+
@ReactMethod fun save(reference: Int, promise: Promise) {
84+
try {
85+
this.getDocument(reference)?.let {
86+
if (it is ImageDocumentImpl.ImagePdfDocumentWrapper) {
87+
val metadata = this.getDocumentConfiguration(reference)?.get("imageSaveMode")?.equals("flattenAndEmbed") == true
88+
if (it.imageDocument.saveIfModified(metadata)) {
89+
promise.resolve(true)
90+
}
91+
} else {
92+
it.saveIfModified()
93+
promise.resolve(true)
94+
}
95+
}
96+
promise.reject("save error", RuntimeException("Could not save document"))
97+
} catch (e: Throwable) {
98+
promise.reject("save error", e)
99+
}
100+
}
101+
102+
@ReactMethod fun getAllUnsavedAnnotations(reference: Int, promise: Promise) {
103+
try {
104+
this.getDocument(reference)?.let {
105+
val outputStream = ByteArrayOutputStream()
106+
DocumentJsonFormatter.exportDocumentJsonAsync(it, outputStream)
107+
.subscribeOn(Schedulers.io())
108+
.observeOn(AndroidSchedulers.mainThread())
109+
.subscribe(
110+
{
111+
val json = JSONObject(outputStream.toString())
112+
val jsonMap = JsonUtilities.jsonObjectToMap(json)
113+
val nativeMap = Arguments.makeNativeMap(jsonMap)
114+
promise.resolve(nativeMap)
115+
}, { e ->
116+
promise.reject(RuntimeException(e))
117+
}
118+
)
119+
}
120+
} catch (e: Throwable) {
121+
promise.reject("getAllUnsavedAnnotations error", e)
122+
}
123+
}
124+
125+
@ReactMethod fun getAnnotations(reference: Int, type: String?, promise: Promise) {
126+
try {
127+
this.getDocument(reference)?.let {
128+
it.annotationProvider.getAllAnnotationsOfTypeAsync(if (type == null) ALL_ANNOTATION_TYPES else getAnnotationTypeFromString(type))
129+
.toList()
130+
.subscribeOn(Schedulers.io())
131+
.observeOn(AndroidSchedulers.mainThread())
132+
.subscribe(
133+
{ annotations ->
134+
var annotationsSerialized: ArrayList<Map<String, Any>> = ArrayList()
135+
for (annotation in annotations) {
136+
if (annotation.type == AnnotationType.POPUP) {
137+
continue
138+
}
139+
val annotationInstantJSON = JSONObject(annotation.toInstantJson())
140+
val annotationMap = JsonUtilities.jsonObjectToMap(annotationInstantJSON)
141+
annotationMap["uuid"] = annotation.uuid
142+
annotationsSerialized.add(annotationMap)
143+
}
144+
val nativeList = Arguments.makeNativeArray(annotationsSerialized)
145+
promise.resolve(nativeList)
146+
}, { e ->
147+
promise.reject(RuntimeException(e))
148+
}
149+
)
150+
}
151+
} catch (e: Throwable) {
152+
promise.reject("getAnnotations error", e)
153+
}
154+
}
155+
156+
@ReactMethod fun getAnnotationsForPage(reference: Int, pageIndex: Int, type: String?, promise: Promise) {
157+
try {
158+
this.getDocument(reference)?.let {
159+
160+
if (pageIndex > it.pageCount-1) {
161+
promise.reject(RuntimeException("Specified page index is out of bounds"))
162+
return
163+
}
164+
165+
it.annotationProvider.getAllAnnotationsOfTypeAsync(if (type == null) EnumSet.allOf(AnnotationType::class.java) else
166+
getAnnotationTypeFromString(type), pageIndex, 1)
167+
.toList()
168+
.subscribeOn(Schedulers.io())
169+
.observeOn(AndroidSchedulers.mainThread())
170+
.subscribe(
171+
{ annotations ->
172+
var annotationsSerialized: ArrayList<Map<String, Any>> = ArrayList()
173+
for (annotation in annotations) {
174+
if (annotation.type == AnnotationType.POPUP) {
175+
continue
176+
}
177+
val annotationInstantJSON = JSONObject(annotation.toInstantJson())
178+
val annotationMap = JsonUtilities.jsonObjectToMap(annotationInstantJSON)
179+
annotationMap["uuid"] = annotation.uuid
180+
annotationsSerialized.add(annotationMap)
181+
}
182+
val nativeList = Arguments.makeNativeArray(annotationsSerialized)
183+
promise.resolve(nativeList)
184+
}, { e ->
185+
promise.reject(RuntimeException(e))
186+
}
187+
)
188+
}
189+
} catch (e: Throwable) {
190+
promise.reject("getAnnotationsForPage error", e)
191+
}
192+
}
193+
194+
@ReactMethod fun removeAnnotations(reference: Int, instantJSON: ReadableArray, promise: Promise) {
195+
try {
196+
this.getDocument(reference)?.let {
197+
198+
val instantJSONArray: List<Map<String, Any>> = instantJSON.toArrayList().filterIsInstance<Map<String, Any>>()
199+
var annotationsToDelete: ArrayList<Annotation> = ArrayList()
200+
it.annotationProvider.getAllAnnotationsOfTypeAsync(ALL_ANNOTATION_TYPES)
201+
.toList()
202+
.subscribeOn(Schedulers.io())
203+
.observeOn(AndroidSchedulers.mainThread())
204+
.subscribe(
205+
{ annotations ->
206+
for (annotation in annotations) {
207+
for (instantJSONAnnotation in instantJSONArray) {
208+
if (annotation.name == instantJSONAnnotation["name"] ||
209+
annotation.uuid == instantJSONAnnotation["uuid"]) {
210+
annotationsToDelete.add(annotation)
211+
}
212+
}
213+
}
214+
215+
for (annotation in annotationsToDelete) {
216+
it.annotationProvider.removeAnnotationFromPage(annotation)
217+
}
218+
promise.resolve(true)
219+
}, { e ->
220+
promise.reject(RuntimeException(e))
221+
}
222+
)
223+
}
224+
} catch (e: Throwable) {
225+
promise.reject("removeAnnotations error", e)
226+
}
227+
}
228+
229+
@ReactMethod fun addAnnotations(reference: Int, instantJSON: ReadableMap, promise: Promise) {
230+
try {
231+
this.getDocument(reference)?.let {
232+
val json = JSONObject(instantJSON.toHashMap())
233+
val dataProvider: DataProvider = DocumentJsonDataProvider(json)
234+
DocumentJsonFormatter.importDocumentJsonAsync(it, dataProvider)
235+
.subscribeOn(Schedulers.io())
236+
.observeOn(AndroidSchedulers.mainThread())
237+
.subscribe({
238+
promise.resolve(true)
239+
}, { e ->
240+
promise.reject(RuntimeException(e))
241+
})
242+
}
243+
} catch (e: Throwable) {
244+
promise.reject("addAnnotations error", e)
245+
}
246+
}
247+
248+
@ReactMethod fun importXFDF(reference: Int, filePath: String, promise: Promise) {
249+
try {
250+
this.getDocument(reference)?.let {
251+
var importPath = filePath;
252+
if (Uri.parse(importPath).scheme == null) {
253+
importPath = "file:///$filePath";
254+
}
255+
256+
XfdfFormatter.parseXfdfAsync(it, ContentResolverDataProvider((Uri.parse(importPath))))
257+
.subscribeOn(Schedulers.io())
258+
.observeOn(AndroidSchedulers.mainThread())
259+
.subscribe(
260+
{ annotations ->
261+
for (annotation in annotations) {
262+
it.annotationProvider.addAnnotationToPage(annotation)
263+
}
264+
val result = JSONObject()
265+
result.put("success", true)
266+
val jsonMap = JsonUtilities.jsonObjectToMap(result)
267+
val nativeMap = Arguments.makeNativeMap(jsonMap)
268+
promise.resolve(nativeMap)
269+
}, { e ->
270+
promise.reject("importXFDF error", e)
271+
})
272+
}
273+
} catch (e: Throwable) {
274+
promise.reject("importXFDF error", e)
275+
}
276+
}
277+
278+
@ReactMethod fun exportXFDF(reference: Int, filePath: String, promise: Promise) {
279+
try {
280+
this.getDocument(reference)?.let {
281+
var exportPath = filePath;
282+
if (Uri.parse(exportPath).scheme == null) {
283+
exportPath = "file:///$filePath";
284+
}
285+
286+
val outputStream = reactApplicationContext.contentResolver.openOutputStream(Uri.parse(exportPath))
287+
if (outputStream == null) {
288+
promise.reject("exportXFDF error", RuntimeException("Could not write to supplied file path error"))
289+
return
290+
}
291+
292+
val allAnnotations = it.annotationProvider.getAllAnnotationsOfType(ALL_ANNOTATION_TYPES)
293+
val allFormFields = it.formProvider.formFields
294+
295+
XfdfFormatter.writeXfdfAsync(it, allAnnotations, allFormFields, outputStream)
296+
XfdfFormatter.parseXfdfAsync(it, ContentResolverDataProvider((Uri.parse(exportPath))))
297+
.subscribeOn(Schedulers.io())
298+
.observeOn(AndroidSchedulers.mainThread())
299+
.subscribe(
300+
{ annotations ->
301+
for (annotation in annotations) {
302+
it.annotationProvider.addAnnotationToPage(annotation)
303+
}
304+
val result = JSONObject()
305+
result.put("success", true)
306+
result.put("filePath", filePath)
307+
val jsonMap = JsonUtilities.jsonObjectToMap(result)
308+
val nativeMap = Arguments.makeNativeMap(jsonMap)
309+
promise.resolve(nativeMap)
310+
}, { e ->
311+
promise.reject("exportXFDF error", e)
312+
})
313+
}
314+
} catch (e: Throwable) {
315+
promise.reject("exportXFDF error", e)
316+
}
317+
}
318+
54319
companion object {
55320
const val NAME = "PDFDocumentManager"
56321
}

0 commit comments

Comments
 (0)