3838import com .facebook .react .uimanager .events .EventDispatcher ;
3939import com .pspdfkit .PSPDFKit ;
4040import com .pspdfkit .annotations .Annotation ;
41+ import com .pspdfkit .annotations .AnnotationProvider ;
4142import com .pspdfkit .annotations .AnnotationType ;
4243import com .pspdfkit .annotations .configuration .AnnotationConfiguration ;
4344import com .pspdfkit .annotations .configuration .FreeTextAnnotationConfiguration ;
4950import com .pspdfkit .document .PdfDocument ;
5051import com .pspdfkit .document .PdfDocumentLoader ;
5152import com .pspdfkit .document .formatters .DocumentJsonFormatter ;
53+ import com .pspdfkit .document .formatters .XfdfFormatter ;
54+ import com .pspdfkit .document .providers .ContentResolverDataProvider ;
5255import com .pspdfkit .document .providers .DataProvider ;
56+ import com .pspdfkit .exceptions .InvalidPasswordException ;
5357import com .pspdfkit .forms .ChoiceFormElement ;
5458import com .pspdfkit .forms .ComboBoxFormElement ;
5559import com .pspdfkit .forms .EditableButtonFormElement ;
60+ import com .pspdfkit .forms .FormField ;
5661import com .pspdfkit .forms .TextFormElement ;
5762import com .pspdfkit .listeners .OnVisibilityChangedListener ;
5863import com .pspdfkit .listeners .SimpleDocumentListener ;
9095
9196import java .io .ByteArrayOutputStream ;
9297import java .io .File ;
98+ import java .io .FileNotFoundException ;
99+ import java .io .OutputStream ;
93100import java .util .ArrayList ;
94101import java .util .Collections ;
95102import 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