Browse Source

compdfkit(rn) - android sdk v2.2.0-SNAPSHOT, android端新增交互接口

liuxiaolong 3 months ago
parent
commit
630dc47db6

File diff suppressed because it is too large
+ 15 - 8
README.md


+ 5 - 3
android/build.gradle

@@ -81,6 +81,7 @@ repositories {
   maven {
     url 'https://s01.oss.sonatype.org/content/repositories/snapshots/'
   }
+  mavenLocal()
 }
 
 
@@ -88,9 +89,10 @@ dependencies {
   compileOnly fileTree(include: ['*.jar','*.aar'], dir: 'libs')
   implementation "com.facebook.react:react-native:+"
 
-  api "com.compdf:compdfkit:2.1.3"
-  api "com.compdf:compdfkit-ui:2.1.3"
-  api "com.compdf:compdfkit-tools:2.1.3"
+//  api "com.compdf:compdfkit:2.1.3"
+//  api "com.compdf:compdfkit-ui:2.1.3"
+  api "com.compdf:compdfkit-tools:2.2.0-SNAPSHOT"
+//  api "com.compdf:compdfkit-tools:2.2.0-SNAPSHOT"
   api 'com.github.bumptech.glide:glide:4.15.1'
   annotationProcessor 'com.github.bumptech.glide:compiler:4.15.1'
   api 'androidx.documentfile:documentfile:1.0.1'

+ 11 - 0
android/src/main/java/com/compdfkitpdf/reactnative/CompdfkitPdfModule.java

@@ -190,4 +190,15 @@ public class CompdfkitPdfModule extends ReactContextBaseJavaModule {
     }
   }
 
+  /**
+   * Delete the saved signature file from the annotation signature list.
+   * @param promise
+   */
+  @ReactMethod
+  public void removeSignFileList(Promise promise){
+    File dirFile = new File(mReactContext.getFilesDir(), CFileUtils.SIGNATURE_FOLDER);
+    boolean deleteResult = CFileUtils.deleteDirectory(dirFile.getAbsolutePath());
+    promise.resolve(deleteResult);
+  }
+
 }

+ 83 - 0
android/src/main/java/com/compdfkitpdf/reactnative/modules/CPDFViewModule.java

@@ -9,6 +9,7 @@
 
 package com.compdfkitpdf.reactnative.modules;
 
+import android.text.TextUtils;
 import android.util.Log;
 import androidx.annotation.NonNull;
 import com.compdfkitpdf.reactnative.viewmanager.CPDFViewManager;
@@ -16,6 +17,9 @@ 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.uimanager.UIBlock;
+import com.facebook.react.uimanager.UIManagerModule;
 
 
 public class CPDFViewModule extends ReactContextBaseJavaModule {
@@ -53,4 +57,83 @@ public class CPDFViewModule extends ReactContextBaseJavaModule {
       }
     });
   }
+
+  @ReactMethod
+  public void setMargins(int tag, ReadableArray margins) {
+    UIManagerModule uiManager = getReactApplicationContext().getNativeModule(UIManagerModule.class);
+    if (uiManager != null) {
+      uiManager.addUIBlock(nativeViewHierarchyManager -> {
+          int left = margins.getInt(0);
+          int top = margins.getInt(1);
+          int right = margins.getInt(2);
+          int bottom = margins.getInt(3);
+          mPDFViewInstance.setMargins(tag, left, top, right, bottom);
+      });
+    }
+  }
+
+  @ReactMethod
+  public void removeAllAnnotations(int tag, final Promise promise) {
+    uiBlock(nativeViewHierarchyManager -> {
+      boolean result = mPDFViewInstance.removeAllAnnotations(tag);
+      promise.resolve(result);
+    });
+  }
+
+  @ReactMethod
+  public void importAnnotations(int tag, String xfdfFile, final Promise promise) {
+    uiBlock(nativeViewHierarchyManager -> {
+      try {
+        boolean result = mPDFViewInstance.importAnnotations(tag, xfdfFile);
+        promise.resolve(result);
+      } catch (Exception e) {
+        promise.reject(e);
+      }
+    });
+  }
+
+  @ReactMethod
+  public void exportAnnotations(int tag, final Promise promise) {
+    uiBlock(nativeViewHierarchyManager -> {
+      try {
+        String result = mPDFViewInstance.exportAnnotations(tag);
+        if (!TextUtils.isEmpty(result)){
+          promise.resolve(result);
+        }else {
+          promise.reject(new Throwable("Export annotations failed."));
+        }
+      }catch (Exception e){
+        promise.reject(e);
+      }
+    });
+  }
+
+  @ReactMethod
+  public void setDisplayPageIndex(int tag, int pageIndex){
+    uiBlock(nativeViewHierarchyManager -> {
+      mPDFViewInstance.setDisplayPageIndex(tag, pageIndex);
+    });
+  }
+
+  @ReactMethod
+  public void getCurrentPageIndex(int tag, Promise promise){
+    uiBlock(nativeViewHierarchyManager -> {
+      promise.resolve(mPDFViewInstance.getCurrentPageIndex(tag));
+    });
+  }
+
+  @ReactMethod
+  public void hasChange(int tag, Promise promise){
+    uiBlock(nativeViewHierarchyManager -> {
+      promise.resolve(mPDFViewInstance.hasChange(tag));
+    });
+  }
+
+  private void uiBlock(UIBlock uiBlock){
+    UIManagerModule uiManager = getReactApplicationContext().getNativeModule(UIManagerModule.class);
+    if (uiManager != null) {
+      uiManager.addUIBlock(uiBlock);
+    }
+  }
+
 }

+ 27 - 0
android/src/main/java/com/compdfkitpdf/reactnative/util/CPDFDocumentUtil.java

@@ -1,7 +1,13 @@
 package com.compdfkitpdf.reactnative.util;
 
 import android.content.Context;
+import android.net.Uri;
+import android.util.Log;
+import com.compdfkit.core.utils.TFileUtils;
+import com.compdfkit.tools.common.pdf.CPDFDocumentActivity;
 import com.compdfkit.tools.common.utils.CFileUtils;
+import com.compdfkit.tools.common.utils.CUriUtil;
+import java.io.File;
 
 public class CPDFDocumentUtil {
 
@@ -16,4 +22,25 @@ public class CPDFDocumentUtil {
       String fileName = strs[strs.length -1];
       return CFileUtils.getAssetsTempFile(context, assetsPath, fileName);
   }
+
+  public static String getImportAnnotationPath(Context context, String pathOrUri) {
+    if (pathOrUri.startsWith(ASSETS_SCHEME)) {
+      String assetsPath = pathOrUri.replace(ASSETS_SCHEME + "/","");
+      String[] strs = pathOrUri.split("/");
+      String fileName = strs[strs.length -1];
+      return CFileUtils.getAssetsTempFile(context, assetsPath, fileName);
+    } else if (pathOrUri.startsWith(CONTENT_SCHEME)) {
+      Uri uri = Uri.parse(pathOrUri);
+      String fileName = CUriUtil.getUriFileName(context, uri);
+      String dir = new File(context.getCacheDir(), CFileUtils.CACHE_FOLDER + File.separator + "xfdfFile").getAbsolutePath();
+      // Get the saved file path
+      return CFileUtils.copyFileToInternalDirectory(context, uri, dir, fileName);
+    } else if (pathOrUri.startsWith(FILE_SCHEME)) {
+      Uri uri = Uri.parse(pathOrUri);
+      return uri.getPath();
+    } else {
+      return pathOrUri;
+    }
+  }
+
 }

+ 65 - 0
android/src/main/java/com/compdfkitpdf/reactnative/view/CPDFView.java

@@ -13,6 +13,7 @@ import android.content.Context;
 import android.graphics.Rect;
 import android.net.Uri;
 import android.text.TextUtils;
+import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
@@ -21,8 +22,17 @@ import androidx.annotation.NonNull;
 import androidx.fragment.app.FragmentManager;
 import com.compdfkit.tools.common.pdf.CPDFDocumentFragment;
 import com.compdfkit.tools.common.pdf.config.CPDFConfiguration;
+import com.compdfkit.tools.common.views.pdfview.CPDFIReaderViewCallback;
+import com.compdfkit.ui.reader.CPDFReaderView;
 import com.compdfkitpdf.reactnative.util.CPDFDocumentUtil;
+import com.facebook.react.bridge.Arguments;
+import com.facebook.react.bridge.ReactContext;
+import com.facebook.react.bridge.WritableMap;
+import com.facebook.react.modules.core.DeviceEventManagerModule;
 import com.facebook.react.uimanager.ThemedReactContext;
+import com.facebook.react.uimanager.events.RCTEventEmitter;
+import java.util.HashMap;
+import java.util.Map;
 
 
 public class CPDFView extends FrameLayout {
@@ -43,7 +53,10 @@ public class CPDFView extends FrameLayout {
 
   private CPDFConfiguration configuration;
 
+  private ThemedReactContext themedReactContext;
+
   public void setup(ThemedReactContext reactContext, FragmentManager fragmentManager) {
+    this.themedReactContext = reactContext;
     this.fragmentManager = fragmentManager;
     int width = ViewGroup.LayoutParams.MATCH_PARENT;
     int height = ViewGroup.LayoutParams.MATCH_PARENT;
@@ -100,18 +113,40 @@ public class CPDFView extends FrameLayout {
       View fragmentView = documentFragment.getView();
       addView(fragmentView, ViewGroup.LayoutParams.MATCH_PARENT,
         ViewGroup.LayoutParams.MATCH_PARENT);
+      documentFragment.initDocument(()->{
+        documentFragment.pdfView.addReaderViewCallback(new CPDFIReaderViewCallback() {
+          @Override
+          public void onMoveToChild(int pageIndex) {
+            super.onMoveToChild(pageIndex);
+            WritableMap params = Arguments.createMap();
+            params.putInt("onPageChanged", pageIndex);
+            onReceiveNativeEvent(params);
+          }
+        });
+        documentFragment.pdfView.setSaveCallback((s, uri) -> {
+          WritableMap event = Arguments.createMap();
+          event.putString("saveDocument", "saveDocument");
+          event.putString("path", s);
+          event.putString("uri", uri.toString());
+          onReceiveNativeEvent(event);
+        }, e -> {
+
+        });
+      });
     }
   }
 
   @Override
   protected void onAttachedToWindow() {
     super.onAttachedToWindow();
+    Log.i("ComPDFKit", "CPDFView-onAttachedToWindow()");
     getViewTreeObserver().addOnGlobalLayoutListener(mOnGlobalLayoutListener);
   }
 
   @Override
   protected void onDetachedFromWindow() {
     super.onDetachedFromWindow();
+    Log.i("ComPDFKit", "CPDFView-onDetachedFromWindow()");
     getViewTreeObserver().removeOnGlobalLayoutListener(mOnGlobalLayoutListener);
   }
 
@@ -146,10 +181,40 @@ public class CPDFView extends FrameLayout {
     layout(getLeft(), getTop(), getRight(), getBottom());
   };
 
+  public CPDFReaderView getCPDFReaderView() {
+    return documentFragment.pdfView.getCPdfReaderView();
+  }
+
 
   @Override
   public void requestLayout() {
     super.requestLayout();
     post(mLayoutRunnable);
   }
+
+  public void onReceiveNativeEvent(String key, String message) {
+    ReactContext reactContext = (ReactContext) getContext();
+    WritableMap event = Arguments.createMap();
+    event.putString(key, message);
+    reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(
+      getId(),
+      "topChange",
+      event);
+  }
+
+  public void onReceiveNativeEvent(String key, int message) {
+    WritableMap event = Arguments.createMap();
+    event.putInt(key, message);
+    themedReactContext.getJSModule(RCTEventEmitter.class).receiveEvent(
+      getId(),
+      "topChange",
+      event);
+  }
+
+  public void onReceiveNativeEvent(WritableMap event) {
+    themedReactContext.getJSModule(RCTEventEmitter.class).receiveEvent(
+      getId(),
+      "topChange",
+      event);
+  }
 }

+ 97 - 3
android/src/main/java/com/compdfkitpdf/reactnative/viewmanager/CPDFViewManager.java

@@ -20,10 +20,14 @@ import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.fragment.app.FragmentActivity;
 
+import com.compdfkit.core.document.CPDFDocument;
 import com.compdfkit.tools.common.pdf.CPDFConfigurationUtils;
 import com.compdfkit.tools.common.pdf.config.CPDFConfiguration;
+import com.compdfkit.tools.common.utils.CFileUtils;
 import com.compdfkit.tools.common.views.pdfview.CPDFViewCtrl.COnSaveCallback;
 import com.compdfkit.tools.common.views.pdfview.CPDFViewCtrl.COnSaveError;
+import com.compdfkit.ui.reader.CPDFReaderView;
+import com.compdfkitpdf.reactnative.util.CPDFDocumentUtil;
 import com.compdfkitpdf.reactnative.view.CPDFView;
 import com.facebook.react.bridge.ReactApplicationContext;
 import com.facebook.react.bridge.ReadableArray;
@@ -31,6 +35,7 @@ import com.facebook.react.bridge.ReadableMap;
 import com.facebook.react.uimanager.ThemedReactContext;
 import com.facebook.react.uimanager.ViewGroupManager;
 import com.facebook.react.uimanager.annotations.ReactProp;
+import java.io.File;
 import java.util.Map;
 
 public class CPDFViewManager extends ViewGroupManager<CPDFView> {
@@ -80,6 +85,11 @@ public class CPDFViewManager extends ViewGroupManager<CPDFView> {
     }
   }
 
+  @Override
+  public boolean needsCustomLayoutForChildren() {
+    return true;
+  }
+
   @ReactProp(name = "document")
   public void setDocument(CPDFView pdfView, String document) {
     Log.d(TAG, "CPDFViewManager-setDocument()");
@@ -100,6 +110,7 @@ public class CPDFViewManager extends ViewGroupManager<CPDFView> {
     pdfView.setConfiguration(configuration);
   }
 
+
   public void save(int tag, COnSaveCallback saveCallback, COnSaveError error) {
     CPDFView pdfView = mDocumentViews.get(tag);
     if (pdfView != null) {
@@ -109,9 +120,92 @@ public class CPDFViewManager extends ViewGroupManager<CPDFView> {
     }
   }
 
-  @Override
-  public boolean needsCustomLayoutForChildren() {
-    return true;
+  public void setMargins(int tag, int left, int top, int right, int bottom) {
+    CPDFView pdfView = mDocumentViews.get(tag);
+    if (pdfView != null) {
+      CPDFReaderView readerView = pdfView.documentFragment.pdfView.getCPdfReaderView();
+      readerView.setReaderViewTopMargin(top);
+      readerView.setReaderViewBottomMargin(bottom);
+      readerView.setReaderViewHorizontalMargin(left, right);
+      readerView.reloadPages();
+    }
+  }
+
+  public boolean removeAllAnnotations(int tag){
+    try {
+      CPDFView pdfView = mDocumentViews.get(tag);
+      if (pdfView != null) {
+        CPDFReaderView readerView = pdfView.documentFragment.pdfView.getCPdfReaderView();
+        boolean result = readerView.getPDFDocument().removeAllAnnotations();
+        if (result){
+          readerView.invalidateAllChildren();
+        }
+        return result;
+      }
+      return false;
+    }catch (Exception e){
+      return false;
+    }
+  }
+
+  public boolean importAnnotations(int tag, String xfdfFilePath) throws Exception{
+    String xfdf = CPDFDocumentUtil.getImportAnnotationPath(reactContext, xfdfFilePath);
+    Log.i("ComPDFKitRN", "xfdf:" + xfdf);
+    CPDFView pdfView = mDocumentViews.get(tag);
+    CPDFReaderView readerView = pdfView.documentFragment.pdfView.getCPdfReaderView();
+    CPDFDocument document = readerView.getPDFDocument();
+    File file = new File(xfdf);
+    if (!file.exists()) {
+      throw new Exception("File not found: " + xfdf);
+    }
+    File cacheFile = new File(reactContext.getCacheDir(),
+      CFileUtils.CACHE_FOLDER + File.separator + "importAnnotCache/"
+        + CFileUtils.getFileNameNoExtension(document.getFileName()));
+    cacheFile.mkdirs();
+    boolean importResult = document.importAnnotations(xfdf,
+      cacheFile.getAbsolutePath());
+    readerView.reloadPages();
+    return importResult;
+  }
+
+  public String exportAnnotations(int tag) throws Exception {
+    CPDFView pdfView = mDocumentViews.get(tag);
+    CPDFReaderView readerView = pdfView.documentFragment.pdfView.getCPdfReaderView();
+    CPDFDocument document = readerView.getPDFDocument();
+    // export file directory
+    File dirFile = new File(reactContext.getFilesDir(), "compdfkit/annotation/export/");
+    dirFile.mkdirs();
+    // export file name
+    String fileName = CFileUtils.getFileNameNoExtension(document.getFileName());
+    // export file cache file
+    File cacheFile = new File(reactContext.getCacheDir(),
+      CFileUtils.CACHE_FOLDER + File.separator + "exportAnnotCache/" + fileName);
+    cacheFile.mkdirs();
+    // export file
+    File saveFile = new File(dirFile, fileName + ".xfdf");
+    saveFile = CFileUtils.renameNameSuffix(saveFile);
+    boolean exportResult = document.exportAnnotations(saveFile.getAbsolutePath(),
+      cacheFile.getAbsolutePath());
+    if (exportResult) {
+      return saveFile.getAbsolutePath();
+    } else {
+      return null;
+    }
+  }
+
+  public void setDisplayPageIndex(int tag, int pageIndex) {
+    CPDFView pdfView = mDocumentViews.get(tag);
+    pdfView.getCPDFReaderView().setDisplayPageIndex(pageIndex);
+  }
+
+  public int getCurrentPageIndex(int tag) {
+    CPDFView pdfView = mDocumentViews.get(tag);
+    return pdfView.getCPDFReaderView().getPageNum();
+  }
+
+  public boolean hasChange(int tag){
+    CPDFView pdfView = mDocumentViews.get(tag);
+    return pdfView.getCPDFReaderView().getPDFDocument().hasChanges();
   }
 
 }

File diff suppressed because it is too large
+ 11 - 3
example/App.tsx


+ 1 - 0
example/android/app/build.gradle

@@ -123,6 +123,7 @@ android {
 
 dependencies {
   implementation fileTree(include: ['*.jar','*.aar'], dir: 'libs')
+  api "com.compdf:compdfkit:2.2.0-SNAPSHOT"
 
     // The version of react-native is set by the React Native Gradle Plugin
     implementation("com.facebook.react:react-android")

File diff suppressed because it is too large
+ 32 - 0
example/android/app/src/main/assets/test.xfdf


BIN
example/android/app/src/main/assets/test_pdf.pdf


BIN
example/android/app/src/main/assets/test_pdf_SetPassword.pdf


+ 4 - 0
example/android/build.gradle

@@ -13,6 +13,8 @@ buildscript {
     maven {
       url 'https://s01.oss.sonatype.org/content/repositories/snapshots/'
     }
+    mavenLocal()
+
   }
   dependencies {
     classpath("com.android.tools.build:gradle")
@@ -28,6 +30,8 @@ allprojects {
     maven {
       url 'https://s01.oss.sonatype.org/content/repositories/snapshots/'
     }
+    mavenLocal()
+
   }
 }
 

+ 4 - 4
example/ios/CompdfkitPdfExample.xcodeproj/project.pbxproj

@@ -491,7 +491,7 @@
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CLANG_ENABLE_MODULES = YES;
 				CURRENT_PROJECT_VERSION = 1;
-				DEVELOPMENT_TEAM = 4GGQPGRTSV;
+				DEVELOPMENT_TEAM = "";
 				ENABLE_BITCODE = NO;
 				ENABLE_USER_SCRIPT_SANDBOXING = NO;
 				INFOPLIST_FILE = CompdfkitPdfExample/Info.plist;
@@ -507,7 +507,7 @@
 					"-ObjC",
 					"-lc++",
 				);
-				PRODUCT_BUNDLE_IDENTIFIER = com.compdfkit.reactnative.example;
+				PRODUCT_BUNDLE_IDENTIFIER = com.neointelligence.FieldWorker;
 				PRODUCT_NAME = ComPDFKit_RN;
 				SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
 				SUPPORTS_MACCATALYST = NO;
@@ -526,7 +526,7 @@
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CLANG_ENABLE_MODULES = YES;
 				CURRENT_PROJECT_VERSION = 1;
-				DEVELOPMENT_TEAM = 4GGQPGRTSV;
+				DEVELOPMENT_TEAM = "";
 				ENABLE_USER_SCRIPT_SANDBOXING = NO;
 				INFOPLIST_FILE = CompdfkitPdfExample/Info.plist;
 				IPHONEOS_DEPLOYMENT_TARGET = 13.4;
@@ -541,7 +541,7 @@
 					"-ObjC",
 					"-lc++",
 				);
-				PRODUCT_BUNDLE_IDENTIFIER = com.compdfkit.reactnative.example;
+				PRODUCT_BUNDLE_IDENTIFIER = com.neointelligence.FieldWorker;
 				PRODUCT_NAME = ComPDFKit_RN;
 				SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
 				SUPPORTS_MACCATALYST = NO;

+ 8 - 20
example/ios/CompdfkitPdfExample/Info.plist

@@ -28,31 +28,19 @@
 	<dict>
 		<key>NSAllowsArbitraryLoads</key>
 		<true/>
-		<key>NSAllowsLocalNetworking</key>
-		<true/>
 	</dict>
+	<key>NSCameraUsageDescription</key>
+	<string>Your consent is required before you could access the function.</string>
 	<key>NSLocationWhenInUseUsageDescription</key>
 	<string></string>
+	<key>NSMicrophoneUsageDescription</key>
+	<string>Your consent is required before you could access the function.</string>
+	<key>NSPhotoLibraryAddUsageDescription</key>
+	<string>Your consent is required before you could access the function.</string>
+	<key>NSPhotoLibraryUsageDescription</key>
+	<string>Your consent is required before you could access the function.</string>
 	<key>UILaunchStoryboardName</key>
 	<string>LaunchScreen</string>
-  
-  <key>NSCameraUsageDescription</key>
-  <string>Your consent is required before you could access the function.</string>
-
-  <key>NSMicrophoneUsageDescription</key>
-  <string>Your consent is required before you could access the function.</string>
-
-  <key>NSPhotoLibraryAddUsageDescription</key>
-  <string>Your consent is required before you could access the function.</string>
-
-  <key>NSPhotoLibraryUsageDescription</key>
-  <string>Your consent is required before you could access the function.</string>
-    
-  <key>NSAppTransportSecurity</key>
-    <dict>
-      <key>NSAllowsArbitraryLoads</key>
-      <true/>
-    </dict>
 	<key>UIRequiredDeviceCapabilities</key>
 	<array>
 		<string>arm64</string>

+ 3 - 2
example/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@compdfkit_pdf_sdk/react_native-example",
-  "version": "2.1.3-2",
-  "versionCode": "11",
+  "version": "2.2.0",
+  "versionCode": "12",
   "private": true,
   "scripts": {
     "android": "react-native run-android",
@@ -16,6 +16,7 @@
     "react": "18.2.0",
     "react-native": "0.74.0",
     "react-native-document-picker": "^9.1.0",
+    "react-native-popup-menu": "^0.16.1",
     "react-native-safe-area-context": "^4.10.7",
     "react-native-screens": "^3.32.0",
     "react-navigation": "^5.0.0"

+ 5 - 1
example/src/CPDFReaderViewExample.tsx

@@ -13,7 +13,7 @@ import { CPDFReaderView, ComPDFKit, CPDFToolbarAction } from '@compdfkit_pdf_sdk
 import { useNavigation, useRoute, RouteProp } from '@react-navigation/native';
 
 type RootStackParamList = {
-    CPDFReaderViewExample: { document?: string }; // 定义 `document` 参数
+    CPDFReaderViewExample: { document?: string }; 
 };
 
 type CPDFReaderViewExampleScreenRouteProp = RouteProp<
@@ -64,9 +64,13 @@ const CPDFReaderViewExampleScreen = () => {
             document={samplePDF}
             configuration={ComPDFKit.getDefaultConfig({
                 toolbarConfig: {
+                    mainToolbarVisible: true,
                     iosLeftBarAvailableActions:[
                         CPDFToolbarAction.THUMBNAIL
                       ]
+                },
+                readerViewConfig :{
+                    margins: [10,20,30,20]
                 }
             })}
         />

+ 9 - 1
example/src/examples.tsx

@@ -67,6 +67,14 @@ const examples = [
             } catch (err) {
             }
         }
-    }
+    },
+    {
+        key: 'item5',
+        title: 'CPDFReaderView Conctrol Example',
+        description: 'Examples of functions that can be used with the PDF component',
+        action: (component: any)  => {
+            component.props.navigation.navigate('CPDFReaderViewConctrolExample');
+        }
+    },
 ];
 export default examples;

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "@compdfkit_pdf_sdk/react_native",
-  "version": "2.1.3-2",
+  "version": "2.2.0",
   "description": "ComPDFKit for React Native is a comprehensive SDK that allows you to quickly add PDF functionality to Android, iOS, and React Native applications.",
   "main": "./src/index.tsx",
   "source": "src/index",

+ 18 - 0
src/configuration/CPDFConfiguration.ts

@@ -51,6 +51,13 @@ export class CPDFConfiguration {
      * Configuration for top toolbar functionality
      */
     toolbarConfig?: {
+
+        /**
+         * Set whether to display the top toolbar
+         * 
+         * Default: true
+         */
+        mainToolbarVisible?: boolean,
         /**
          * Top toolbar actions for Android platform
          *
@@ -406,6 +413,17 @@ export class CPDFConfiguration {
          * Spacing between each page of the PDF, default 10px.
          */
         pageSpacing?: number,
+        
+        /**
+         * Sets the outer margins for the PDF reading view.
+         * - `index 0`: margin left
+         * - `index 1`: margin top
+         * - `index 2`: margin right
+         * - `index 3`: margin bottom
+         * 
+         * **Default:** `[0,0,0,0]`
+         */
+        margins?: number[],
         /**
          * Page scale value, default 1.0.
          */

+ 15 - 0
src/index.tsx

@@ -119,6 +119,18 @@ declare module 'react-native' {
        *
        */
       openDocument: (document: string, password: string, configuration: string) => void;
+
+      /**
+       * Delete the saved signature file from the annotation signature list
+       * 
+       * @example
+       * ComPDFKit.removeSignFileList().then((result : boolean) => {
+       *  console.log('ComPDFKit removeSignFileList:', result)
+       * })
+       * 
+       * @returns 
+       */
+      removeSignFileList : () => Promise<boolean>;
     };
   }
 }
@@ -130,6 +142,7 @@ interface ComPDFKit {
   init_(license: string): Promise<boolean>;
   initialize(androidOnlineLicense: string, iosOnlineLicense: string): Promise<boolean>;
   openDocument(document: string, password: string, configurationJson: string): void;
+  removeSignFileList() : Promise<boolean>;
 }
 
 const ComPDFKit = NativeModules.ComPDFKit
@@ -170,6 +183,7 @@ function getDefaultConfig(overrides : Partial<CPDFConfiguration> = {}) : string
       ]
     },
     toolbarConfig: {
+      mainToolbarVisible : true,
       androidAvailableActions: [
         CPDFToolbarAction.THUMBNAIL,
         CPDFToolbarAction.SEARCH,
@@ -414,6 +428,7 @@ function getDefaultConfig(overrides : Partial<CPDFConfiguration> = {}) : string
       enableSliderBar: true,
       enablePageIndicator: true,
       pageScale: 1.0,
+      margins: [0,0,0,0],
       pageSpacing: 10,
       pageSameWidth: true
     },

+ 175 - 1
src/view/CPDFReaderView.tsx

@@ -8,7 +8,7 @@
  */
 
 import React, { PureComponent } from 'react';
-import PropTypes from 'prop-types';
+import PropTypes, { Requireable, Validator } from 'prop-types';
 import { findNodeHandle, requireNativeComponent, NativeModules } from 'react-native';
 import { ViewPropTypes } from 'deprecated-react-native-prop-types';
 const { CPDFViewManager } = NativeModules;
@@ -17,6 +17,8 @@ const { CPDFViewManager } = NativeModules;
  * ComPDFKit PDF UI Component.
  * 
  * @example
+ * const pdfReaderRef = useRef<CPDFReaderView>(null);
+ * 
  *  <CPDFReaderView
  *      ref={pdfReaderRef}
  *      document={samplePDF}
@@ -29,12 +31,29 @@ const propTypes = {
   configuration: PropTypes.string.isRequired,
   document: PropTypes.string.isRequired,
   password: PropTypes.string,
+  onPageChanged : func<(pageIndex: number) => void>(),
+  saveDocument : func<() => void>(),
   ...ViewPropTypes,
 }
 
 // Generates the prop types for TypeScript users, from PropTypes.
 type CPDFReaderViewProps = PropTypes.InferProps<typeof propTypes>;
 
+function func<T>(): Requireable<T> {
+
+  let validator: Validator<T> = function (props: { [key: string]: any }, propName: string, componentName: string, location: string, propFullName: string): Error | null {
+    if (typeof props[propName] !== "function" && typeof props[propName] !== "undefined") {
+      return new Error(`Invalid prop \`${propName}\` of type \`${typeof props[propName]}\` supplied to \`${componentName}\`, expected a function.`);
+    }
+    return null;
+  }
+
+  const t: Requireable<T> = validator as Requireable<T>;
+  t.isRequired = validator as Validator<NonNullable<T>>;
+  return t;
+}
+
+
 export class CPDFReaderView extends PureComponent<CPDFReaderViewProps, any> {
 
   _viewerRef: any;
@@ -49,8 +68,23 @@ export class CPDFReaderView extends PureComponent<CPDFReaderViewProps, any> {
     this._viewerRef = ref;
   }
 
+  onChange = (event : any) => {
+    if (event.nativeEvent.onPageChanged){
+      if(this.props.onPageChanged){
+        this.props.onPageChanged(event.nativeEvent.onPageChanged);
+      }
+    } else if(event.nativeEvent.saveDocument){
+      if(this.props.saveDocument){
+        this.props.saveDocument();
+      }
+    }
+  }
+
   /**
    * Save the document and return whether it is saved successfully.
+   * @example
+   * const saveResult = await pdfReaderRef.current.save();
+   * 
    * @returns true or false
    */
   save = (): Promise<boolean> => {
@@ -61,11 +95,151 @@ export class CPDFReaderView extends PureComponent<CPDFReaderViewProps, any> {
     return Promise.resolve(false);
   }
 
+  /**
+   * Set the reading area spacing.
+   * @example
+   * await pdfReaderRef.current?.setMargins(10,10,10,10);
+   * 
+   * @param left 
+   * @param top 
+   * @param right 
+   * @param bottom 
+   * @returns 
+   */
+  setMargins = (left: number, top: number, right: number, bottom: number) => {
+    const tag = findNodeHandle(this._viewerRef);
+    if (!tag) {
+      console.error('Unable to find the native view reference');
+      return;
+    }
+    CPDFViewManager.setMargins(tag, [left, top, right, bottom]);
+  };
+
+  /**
+   * Delete all comments in the current document
+   * @example
+   * const removeResult = await pdfReaderRef.current?.removeAllAnnotations();
+   * 
+   * @returns 
+   */
+  removeAllAnnotations = () : Promise<boolean> => {
+    const tag = findNodeHandle(this._viewerRef);
+    if (!tag) {
+      console.error('Unable to find the native view reference');
+      return Promise.resolve(false);
+    }
+    return CPDFViewManager.removeAllAnnotations(tag);
+  }
+
+  /**
+   * Imports annotations from the specified XFDF file into the current PDF document.
+   * @example
+   * // Android - assets file
+   * const testXfdf = 'file:///android_asset/test.xfdf';
+   * const importResult = await pdfReaderRef.current?.importAnnotations(testXfdf);
+   * 
+   * // Android - file path
+   * const testXfdf = '/data/user/0/com.compdfkit.reactnative.example/xxx/xxx.xfdf';
+   * const importResult = await pdfReaderRef.current?.importAnnotations(testXfdf);
+   * 
+   * // Android - Uri
+   * const xfdfUri = 'content://xxxx'
+   * const importResult = await pdfReaderRef.current?.importAnnotations(xfdfUri);
+   * 
+   * // iOS 
+   * 
+   * 
+   * 
+   * @param xfdfFile Path of the XFDF file to be imported.
+   * @returns true if the import is successful; otherwise, false.
+   */
+  importAnnotations = (xfdfFile : string) : Promise<boolean> => {
+    const tag = findNodeHandle(this._viewerRef);
+    if (!tag) {
+      console.error('Unable to find the native view reference');
+      return Promise.resolve(false);
+    }
+    return CPDFViewManager.importAnnotations(tag, xfdfFile);
+  }
+
+  /**
+   * Exports annotations from the current PDF document to an XFDF file.
+   * 
+   * @example
+   * const exportXfdfFilePath = await pdfReaderRef.current?.exportAnnotations();
+   * 
+   * @returns The path of the XFDF file if export is successful; an empty string if the export fails.
+   */
+  exportAnnotations = () : Promise<string> =>{
+    const tag = findNodeHandle(this._viewerRef);
+    if (!tag) {
+      console.error('Unable to find the native view reference');
+      return Promise.reject("Unable to find the native view reference")
+    }
+    return CPDFViewManager.exportAnnotations(tag);
+  }
+
+  /**
+   * Jump to the index page.
+   * 
+   * @example
+   * await pdfReaderRef.current?.setDisplayPageIndex(1);
+   * 
+   * @param pageIndex The index of the page to jump.
+   * @returns 
+   */
+  setDisplayPageIndex = (pageIndex : number) => {
+    const tag = findNodeHandle(this._viewerRef);
+    if (!tag) {
+      console.error('Unable to find the native view reference');
+      return Promise.reject("Unable to find the native view reference")
+    }
+    return CPDFViewManager.setDisplayPageIndex(tag, pageIndex);
+  }
+
+  /**
+   * get current page index
+   * 
+   * @example
+   * const pageIndex = await pdfReaderRef.current?.getCurrentPageIndex();
+   * 
+   * @returns 
+   */
+  getCurrentPageIndex = () : Promise<number> =>{
+    const tag = findNodeHandle(this._viewerRef);
+    if (!tag) {
+      console.error('Unable to find the native view reference');
+      return Promise.reject("Unable to find the native view reference")
+    }
+    return CPDFViewManager.getCurrentPageIndex(tag);
+  }
+
+/**
+   * Checks whether the document has been modified
+   * 
+   * @example
+   * const hasChange = await pdfReaderRef.current?.hasChange();
+   * 
+   * @returns {Promise<boolean>} Returns a Promise indicating if the document has been modified.  
+   *          `true`: The document has been modified,   
+   *          `false`: The document has not been modified.   
+   *          If the native view reference cannot be found, a rejected Promise will be returned.
+   */
+  hasChange = () : Promise<boolean> => {
+    const tag = findNodeHandle(this._viewerRef);
+    if (!tag) {
+      console.error('Unable to find the native view reference');
+      return Promise.reject("Unable to find the native view reference")
+    }
+    return CPDFViewManager.hasChange(tag);
+  }
+
   render() {
     return (
       <RCTCPDFReaderView
         ref={(ref) => {this._viewerRef = ref}}
         style={{ flex: 1 }}
+        onChange={this.onChange}
         {...this.props}
       />
     )