Skip to content

Commit dc6971a

Browse files
author
PSPDFKit
committed
Release 2.9.1
1 parent c2affd4 commit dc6971a

27 files changed

+433
-98
lines changed

CHANGELOG.md

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
## Newest Release
22

3+
### 2.9.1 - 12 Apr 2024
4+
5+
- Adds the ability to import and export annotations from XFDF files. (J#HYB-293)
6+
- Updates for PSPDFKit 2024.2.1 for Android.
7+
- Fixes issue where password input UI for password-protected documents wasn't shown on Android. (J#HYB-285)
8+
9+
## Previous Releases
10+
311
### 2.9.0 - 22 Mar 2024
412

513
- Adds new getConfiguration method to retrieve current PSPDFKitView configuration options. (J#HYB-192)
@@ -14,8 +22,6 @@
1422
- Fixes signatureSavingStrategy configuration option to save signature if enabled. (J#HYB-210)
1523
- Fixes `spreadFitting` configuration option behaviour on iOS. (J#HYB-222)
1624

17-
## Previous Releases
18-
1925
### 2.8.1 - 27 Feb 2024
2026

2127
- Updates for PSPDFKit 13.3.1 for iOS. (#43565)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
connection.project.dir=../../../android
2+
eclipse.preferences.version=1

android/build.gradle

+2-8
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
* Contains gradle configuration constants
1616
*/
1717
ext {
18-
PSPDFKIT_VERSION = '2024.1.2'
18+
PSPDFKIT_VERSION = '2024.2.1'
1919
}
2020

2121
buildscript {
@@ -70,13 +70,7 @@ dependencies {
7070
api("com.pspdfkit:pspdfkit:${PSPDFKIT_VERSION}") {
7171
exclude group: 'com.google.auto.value', module: 'auto-value'
7272
}
73-
74-
implementation "androidx.compose.material:material:1.5.4"
75-
implementation "androidx.constraintlayout:constraintlayout:2.1.4"
76-
implementation "androidx.constraintlayout:constraintlayout-compose:1.0.1"
77-
implementation "androidx.compose.foundation:foundation:1.5.4"
78-
implementation "androidx.compose.ui:ui:1.5.4"
79-
73+
8074
implementation "com.facebook.react:react-native:+"
8175
implementation 'com.squareup.okhttp3:okhttp:4.9.2'
8276
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"

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

-60
Original file line numberDiff line numberDiff line change
@@ -267,66 +267,6 @@ public void processAnnotations(@NonNull final String processingMode,
267267
});
268268
}
269269

270-
@ReactMethod
271-
public void setMeasurementScale(@Nullable final Scale scale) {
272-
if (resumedActivity instanceof PdfActivity) {
273-
final PdfActivity activity = (PdfActivity) resumedActivity;
274-
activity.runOnUiThread(new Runnable() {
275-
@Override
276-
public void run() {
277-
PdfDocument document = activity.getDocument();
278-
if (document != null && scale != null) {
279-
document.setMeasurementScale(scale);
280-
} else {
281-
activity.getPdfFragment().addDocumentListener(new SimpleDocumentListener() {
282-
@Override
283-
public void onDocumentLoaded(@NonNull PdfDocument document) {
284-
activity.getPdfFragment().removeDocumentListener(this);
285-
}
286-
});
287-
}
288-
}
289-
});
290-
} else {
291-
onPdfActivityOpenedTask = new Runnable() {
292-
@Override
293-
public void run() {
294-
setMeasurementScale(scale);
295-
}
296-
};
297-
}
298-
}
299-
@ReactMethod
300-
public void setMeasurementPrecision(@Nullable final MeasurementPrecision floatPrecision) {
301-
if (resumedActivity instanceof PdfActivity) {
302-
final PdfActivity activity = (PdfActivity) resumedActivity;
303-
activity.runOnUiThread(new Runnable() {
304-
@Override
305-
public void run() {
306-
PdfDocument document = activity.getDocument();
307-
MeasurementPrecision precision = floatPrecision;
308-
if (document != null && precision != null) {
309-
document.setMeasurementPrecision(precision);
310-
} else {
311-
activity.getPdfFragment().addDocumentListener(new SimpleDocumentListener() {
312-
@Override
313-
public void onDocumentLoaded(@NonNull PdfDocument document) {
314-
activity.getPdfFragment().removeDocumentListener(this);
315-
}
316-
});
317-
}
318-
}
319-
});
320-
} else {
321-
onPdfActivityOpenedTask = new Runnable() {
322-
@Override
323-
public void run() {
324-
setMeasurementPrecision(floatPrecision);
325-
}
326-
};
327-
}
328-
}
329-
330270
private static PdfProcessorTask.AnnotationProcessingMode getProcessingModeFromString(@NonNull final String mode) {
331271
if ("print".equalsIgnoreCase(mode)) {
332272
return PdfProcessorTask.AnnotationProcessingMode.PRINT;

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

+16
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ public class ReactPdfViewManager extends ViewGroupManager<PdfView> {
7171
public static final int COMMAND_SET_TOOLBAR = 19;
7272
public static final int COMMAND_SET_MEASUREMENT_VALUE_CONFIGURATIONS = 21;
7373
public static final int COMMAND_GET_MEASUREMENT_VALUE_CONFIGURATIONS = 22;
74+
public static final int COMMAND_IMPORT_XFDF = 23;
75+
public static final int COMMAND_EXPORT_XFDF = 24;
7476

7577
private final CompositeDisposable annotationDisposables = new CompositeDisposable();
7678

@@ -123,6 +125,8 @@ public Map<String, Integer> getCommandsMap() {
123125
commandMap.put("getMeasurementValueConfigurations", COMMAND_GET_MEASUREMENT_VALUE_CONFIGURATIONS);
124126
commandMap.put("getConfiguration", COMMAND_GET_CONFIGURATION);
125127
commandMap.put("setToolbar", COMMAND_SET_TOOLBAR);
128+
commandMap.put("importXFDF", COMMAND_IMPORT_XFDF);
129+
commandMap.put("exportXFDF", COMMAND_EXPORT_XFDF);
126130
return commandMap;
127131
}
128132

@@ -421,6 +425,18 @@ public void accept(List<Annotation> annotations) {
421425
setToolbar(root,args.getMap(0));
422426
}
423427
break;
428+
case COMMAND_IMPORT_XFDF:
429+
if (args != null && args.size() == 2) {
430+
final int requestId = args.getInt(0);
431+
root.importXFDF(requestId, args.getString(1));
432+
}
433+
break;
434+
case COMMAND_EXPORT_XFDF:
435+
if (args != null && args.size() == 2) {
436+
final int requestId = args.getInt(0);
437+
root.exportXFDF(requestId, args.getString(1));
438+
}
439+
break;
424440
}
425441
}
426442

android/src/main/java/com/pspdfkit/views/PdfView.java

+88-15
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import com.facebook.react.uimanager.events.EventDispatcher;
3939
import com.pspdfkit.PSPDFKit;
4040
import com.pspdfkit.annotations.Annotation;
41+
import com.pspdfkit.annotations.AnnotationProvider;
4142
import com.pspdfkit.annotations.AnnotationType;
4243
import com.pspdfkit.annotations.configuration.AnnotationConfiguration;
4344
import com.pspdfkit.annotations.configuration.FreeTextAnnotationConfiguration;
@@ -49,10 +50,14 @@
4950
import com.pspdfkit.document.PdfDocument;
5051
import com.pspdfkit.document.PdfDocumentLoader;
5152
import com.pspdfkit.document.formatters.DocumentJsonFormatter;
53+
import com.pspdfkit.document.formatters.XfdfFormatter;
54+
import com.pspdfkit.document.providers.ContentResolverDataProvider;
5255
import com.pspdfkit.document.providers.DataProvider;
56+
import com.pspdfkit.exceptions.InvalidPasswordException;
5357
import com.pspdfkit.forms.ChoiceFormElement;
5458
import com.pspdfkit.forms.ComboBoxFormElement;
5559
import com.pspdfkit.forms.EditableButtonFormElement;
60+
import com.pspdfkit.forms.FormField;
5661
import com.pspdfkit.forms.TextFormElement;
5762
import com.pspdfkit.listeners.OnVisibilityChangedListener;
5863
import com.pspdfkit.listeners.SimpleDocumentListener;
@@ -90,6 +95,8 @@
9095

9196
import java.io.ByteArrayOutputStream;
9297
import java.io.File;
98+
import java.io.FileNotFoundException;
99+
import java.io.OutputStream;
93100
import java.util.ArrayList;
94101
import java.util.Collections;
95102
import java.util.List;
@@ -226,7 +233,7 @@ public void inject(FragmentManager fragmentManager, EventDispatcher eventDispatc
226233

227234
public void setFragmentTag(String fragmentTag) {
228235
this.fragmentTag = fragmentTag;
229-
setupFragment();
236+
setupFragment(false);
230237
}
231238

232239
public void setInitialConfiguration(PdfActivityConfiguration configuration) {
@@ -253,7 +260,7 @@ public void setConfiguration(PdfActivityConfiguration configuration) {
253260
removeFragment(false);
254261
}
255262
this.configuration = configuration;
256-
setupFragment();
263+
setupFragment(false);
257264
}
258265

259266
public PdfActivityConfiguration getConfiguration() {
@@ -271,7 +278,7 @@ public void setCustomToolbarItems(final ArrayList toolbarItems) {
271278

272279
public void setAnnotationConfiguration(final Map<AnnotationType,AnnotationConfiguration> annotationsConfigurations) {
273280
this.annotationsConfigurations = annotationsConfigurations;
274-
setupFragment();
281+
setupFragment(false);
275282
}
276283

277284
public void setDocumentPassword(@Nullable String documentPassword) {
@@ -307,10 +314,10 @@ public void setDocument(@Nullable String documentPath) {
307314
.observeOn(AndroidSchedulers.mainThread())
308315
.subscribe(imageDocument -> {
309316
PdfView.this.document = imageDocument.getDocument();
310-
setupFragment();
317+
setupFragment(false);
311318
}, throwable -> {
312319
PdfView.this.document = null;
313-
setupFragment();
320+
setupFragment(false);
314321
eventDispatcher.dispatchEvent(new PdfViewDocumentLoadFailedEvent(getId(), throwable.getMessage()));
315322
});
316323
} else {
@@ -319,18 +326,21 @@ public void setDocument(@Nullable String documentPath) {
319326
.observeOn(AndroidSchedulers.mainThread())
320327
.subscribe(pdfDocument -> {
321328
PdfView.this.document = pdfDocument;
322-
setupFragment();
329+
setupFragment(false);
323330
}, throwable -> {
324-
PdfView.this.document = null;
325-
setupFragment();
326-
eventDispatcher.dispatchEvent(new PdfViewDocumentLoadFailedEvent(getId(), throwable.getMessage()));
331+
// The Android SDK will present password UI, do not emit an error.
332+
if (!(throwable instanceof InvalidPasswordException)) {
333+
PdfView.this.document = null;
334+
eventDispatcher.dispatchEvent(new PdfViewDocumentLoadFailedEvent(getId(), throwable.getMessage()));
335+
}
336+
setupFragment(true);
327337
});
328338
}
329339
}
330340

331341
public void setPageIndex(int pageIndex) {
332342
this.pageIndex = pageIndex;
333-
setupFragment();
343+
setupFragment(false);
334344
}
335345

336346
public void setDisableDefaultActionForTappedAnnotations(boolean disableDefaultActionForTappedAnnotations) {
@@ -440,8 +450,8 @@ public void setHideDefaultToolbar(boolean hideDefaultToolbar) {
440450
}));
441451
}
442452

443-
private void setupFragment() {
444-
if (fragmentTag != null && configuration != null && document != null) {
453+
private void setupFragment(boolean recreate) {
454+
if (fragmentTag != null && configuration != null && (document != null || recreate == true)) {
445455
PdfUiFragment pdfFragment = (PdfUiFragment) fragmentManager.findFragmentByTag(fragmentTag);
446456
if (pdfFragment != null &&
447457
(pdfFragment.getArguments() == null ||
@@ -454,10 +464,16 @@ private void setupFragment() {
454464
}
455465

456466
if (pdfFragment == null) {
457-
pdfFragment = PdfUiFragmentBuilder.fromDocumentDescriptor(getContext(), DocumentDescriptor.fromDocument(document))
467+
if (recreate == true) {
468+
pdfFragment = PdfUiFragmentBuilder.fromUri(getContext(), Uri.parse(documentPath)).fragmentClass(ReactPdfUiFragment.class).build();
469+
} else if (document != null) {
470+
pdfFragment = PdfUiFragmentBuilder.fromDocumentDescriptor(getContext(), DocumentDescriptor.fromDocument(document))
458471
.configuration(configuration)
459472
.fragmentClass(ReactPdfUiFragment.class)
460473
.build();
474+
} else {
475+
return;
476+
}
461477
// We put our internal id so we can track if this fragment belongs to us, used to handle orphaned fragments after hot reloads.
462478
pdfFragment.getArguments().putInt(ARG_ROOT_ID, internalId);
463479
prepareFragment(pdfFragment, true);
@@ -710,8 +726,6 @@ public Single<List<Annotation>> getAllAnnotations(@Nullable final String type) {
710726
.toList();
711727
}
712728

713-
714-
715729
public Disposable addAnnotation(final int requestId, ReadableMap annotation) {
716730
return getCurrentPdfFragment().map(PdfFragment::getDocument).subscribeOn(Schedulers.io())
717731
.map(pdfDocument -> {
@@ -881,6 +895,65 @@ public Maybe<Boolean> setFormFieldValue(@NonNull String formElementName, @NonNul
881895
});
882896
}
883897

898+
public Disposable importXFDF(final int requestId, String filePath) {
899+
900+
if (Uri.parse(filePath).getScheme() == null) {
901+
filePath = FILE_SCHEME + filePath;
902+
}
903+
if (fragment == null || fragment.getDocument() == null) {
904+
return null;
905+
}
906+
907+
return XfdfFormatter.parseXfdfAsync(fragment.getDocument(), new ContentResolverDataProvider(Uri.parse(filePath)))
908+
.subscribeOn(Schedulers.io())
909+
.observeOn(AndroidSchedulers.mainThread())
910+
.subscribe(annotations -> {
911+
for (Annotation annotation : annotations) {
912+
fragment.getDocument().getAnnotationProvider().addAnnotationToPage(annotation);
913+
}
914+
JSONObject result = new JSONObject();
915+
result.put("success", true);
916+
eventDispatcher.dispatchEvent(new PdfViewDataReturnedEvent(getId(), requestId, result));
917+
}, throwable -> {
918+
eventDispatcher.dispatchEvent(new PdfViewDataReturnedEvent(getId(), requestId, throwable));
919+
});
920+
}
921+
922+
public Disposable exportXFDF(final int requestId, String filePath) {
923+
924+
if (Uri.parse(filePath).getScheme() == null) {
925+
filePath = FILE_SCHEME + filePath;
926+
}
927+
if (fragment == null || fragment.getDocument() == null) {
928+
return null;
929+
}
930+
931+
try {
932+
final OutputStream outputStream = getContext().getContentResolver().openOutputStream(Uri.parse(filePath));
933+
if (outputStream == null) return null;
934+
935+
List<Annotation> annotations = fragment.getDocument().getAnnotationProvider().getAllAnnotationsOfType(AnnotationProvider.ALL_ANNOTATION_TYPES);
936+
List<FormField> formFields = fragment.getDocument().getFormProvider().getFormFields();
937+
938+
String finalFilePath = filePath;
939+
return XfdfFormatter.writeXfdfAsync(fragment.getDocument(), annotations, formFields, outputStream)
940+
.subscribeOn(Schedulers.io())
941+
.observeOn(AndroidSchedulers.mainThread())
942+
.subscribe(() -> {
943+
JSONObject result = new JSONObject();
944+
result.put("success", true);
945+
result.put("filePath", finalFilePath);
946+
eventDispatcher.dispatchEvent(new PdfViewDataReturnedEvent(getId(), requestId, result));
947+
}, throwable -> {
948+
eventDispatcher.dispatchEvent(new PdfViewDataReturnedEvent(getId(), requestId, throwable));
949+
}
950+
);
951+
} catch (final FileNotFoundException ignored) {
952+
eventDispatcher.dispatchEvent(new PdfViewDataReturnedEvent(getId(), requestId, ignored));
953+
}
954+
return null;
955+
}
956+
884957
public JSONObject convertConfiguration() {
885958
try {
886959
JSONObject config = new JSONObject();

0 commit comments

Comments
 (0)