Ver código fonte

ComPDFKit(flutter) - 新增水印设置,CPDFDocument独立使用,新增使用示例

liuxiaolong 4 semanas atrás
pai
commit
41e0defb63

+ 5 - 1
android/build.gradle

@@ -5,6 +5,8 @@ buildscript {
     repositories {
         google()
         mavenCentral()
+        mavenLocal()
+
     }
 
     dependencies {
@@ -17,6 +19,8 @@ rootProject.allprojects {
         google()
         mavenCentral()
         mavenLocal()
+        maven { url 'https://s01.oss.sonatype.org/content/repositories/snapshots/' }
+
     }
 }
 
@@ -41,7 +45,7 @@ android {
         implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
 
         // dependencies compdfkit pdf sdk
-        api 'com.compdf:compdfkit-tools:2.2.1'
+        api 'com.compdf:compdfkit-tools:2.2.2-SNAPSHOT'
 
         testImplementation 'junit:junit:4.13.2'
         testImplementation 'org.mockito:mockito-core:5.0.0'

+ 2 - 0
android/src/main/java/com/compdfkit/flutter/compdfkit_flutter/constants/CPDFConstants.java

@@ -150,6 +150,8 @@ public class CPDFConstants {
     public static final String REMOVE_ALL_WATERMARKS = "remove_all_watermarks";
     public static final String GET_ENCRYPT_ALGORITHM = "get_encrypt_algorithm";
     public static final String SET_IMPORT_FONT_DIRECTORY = "set_import_font_directory";
+    public static final String CREATE_DOCUMENT_PLUGIN = "create_document_plugin";
+
   }
 
 }

+ 77 - 22
android/src/main/java/com/compdfkit/flutter/compdfkit_flutter/plugin/CPDFDocumentPlugin.java

@@ -40,9 +40,14 @@ import android.graphics.Bitmap;
 import android.graphics.Color;
 import android.net.Uri;
 import android.os.Environment;
+import android.print.PrintAttributes;
+import android.print.PrintAttributes.MediaSize;
+import android.print.PrintDocumentAdapter;
+import android.print.PrintManager;
 import android.text.TextUtils;
 import android.util.Log;
 import androidx.annotation.NonNull;
+import androidx.fragment.app.FragmentActivity;
 import com.bumptech.glide.request.target.Target;
 import com.compdfkit.core.common.CPDFDocumentException;
 import com.compdfkit.core.document.CPDFDocument;
@@ -58,8 +63,10 @@ import com.compdfkit.flutter.compdfkit_flutter.utils.FileUtils;
 import com.compdfkit.tools.common.utils.CFileUtils;
 import com.compdfkit.tools.common.utils.glide.GlideApp;
 import com.compdfkit.tools.common.utils.image.CBitmapUtil;
+import com.compdfkit.tools.common.utils.print.CPDFPrintUtils;
 import com.compdfkit.tools.common.utils.threadpools.CThreadPoolUtils;
 import com.compdfkit.tools.common.utils.threadpools.SimpleBackgroundTask;
+import com.compdfkit.tools.common.utils.viewutils.CViewUtils;
 import com.compdfkit.tools.common.views.pdfview.CPDFViewCtrl;
 import com.compdfkit.ui.reader.CPDFReaderView;
 import io.flutter.plugin.common.BinaryMessenger;
@@ -75,6 +82,8 @@ public class CPDFDocumentPlugin extends BaseMethodChannelPlugin {
 
   private CPDFViewCtrl pdfView;
 
+  private CPDFDocument document;
+
   public CPDFDocumentPlugin(Context context,
       BinaryMessenger binaryMessenger, String documentUid) {
     super(context, binaryMessenger);
@@ -83,8 +92,12 @@ public class CPDFDocumentPlugin extends BaseMethodChannelPlugin {
 
   public void setReaderView(CPDFViewCtrl pdfView) {
     this.pdfView = pdfView;
+    this.document = pdfView.getCPdfReaderView().getPDFDocument();
   }
 
+  public void setDocument(CPDFDocument cpdfDocument) {
+    this.document = cpdfDocument;
+  }
 
   @Override
   public String methodName() {
@@ -93,26 +106,55 @@ public class CPDFDocumentPlugin extends BaseMethodChannelPlugin {
 
   @Override
   public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
-    if (pdfView == null || pdfView.getCPdfReaderView().getPDFDocument() == null) {
+    if (document == null) {
       result.error("-1", "CPDFReaderView isnull or CPDFDocument is null", null);
       return;
     }
-    CPDFReaderView readerView = pdfView.getCPdfReaderView();
-    CPDFDocument document = readerView.getPDFDocument();
     switch (call.method) {
       case OPEN_DOCUMENT:
         String filePath = call.argument("filePath");
         String openPwd = call.argument("password");
         PDFDocumentError error;
+        Object object;
         if (filePath.startsWith(CONTENT_SCHEME) || filePath.startsWith(
             FileUtils.FILE_SCHEME)) {
-          pdfView.openPDF(Uri.parse(filePath), openPwd, () -> {
-            result.success(true);
-          });
+          object = Uri.parse(filePath);
+          error = document.open(Uri.parse(filePath), openPwd);
         } else {
-          pdfView.openPDF(filePath, openPwd, () -> {
-            result.success(true);
-          });
+          object = filePath;
+          error = document.open(filePath, openPwd);
+        }
+        switch (error){
+          case PDFDocumentErrorSuccess:
+            result.success("success");
+            break;
+          case PDFDocumentErrorPassword:
+            result.success("errorPassword");
+            break;
+          case PDFDocumentErrorFile:
+            result.success("errorFile");
+            break;
+          case PDFDocumentErrorPage:
+            result.success("errorPage");
+            break;
+          case PDFDocumentErrorFormat:
+            result.success("errorFormat");
+            break;
+          case PDFDocumentErrorUnknown:
+            result.success("unknown");
+            break;
+          case PDFDocumentErrorSecurity:
+            result.success("errorSecurity");
+            break;
+          case PDFDocumentNotVerifyLicense:
+            result.success("notVerifyLicense");
+            break;
+          case PDFDocumentErrorNoReadPermission:
+            result.success("noReadPermission");
+            break;
+        }
+        if (error == PDFDocumentError.PDFDocumentErrorSuccess && pdfView != null){
+          pdfView.setPDFDocument(document, object, error, null);
         }
         break;
       case GET_FILE_NAME:
@@ -159,7 +201,9 @@ public class CPDFDocumentPlugin extends BaseMethodChannelPlugin {
           cacheFile.mkdirs();
           boolean importResult = document.importAnnotations(xfdfFilePath,
               cacheFile.getAbsolutePath());
-          readerView.reloadPages();
+          if (pdfView != null) {
+            pdfView.getCPdfReaderView().reloadPages();
+          }
           result.success(importResult);
         } catch (Exception e) {
           e.printStackTrace();
@@ -190,8 +234,8 @@ public class CPDFDocumentPlugin extends BaseMethodChannelPlugin {
         break;
       case REMOVE_ALL_ANNOTATIONS:
         boolean deleteResult = document.removeAllAnnotations();
-        if (deleteResult) {
-          readerView.invalidateAllChildren();
+        if (deleteResult && pdfView != null) {
+          pdfView.getCPdfReaderView().invalidateAllChildren();
         }
         result.success(deleteResult);
         break;
@@ -211,6 +255,9 @@ public class CPDFDocumentPlugin extends BaseMethodChannelPlugin {
         String savePath = call.argument("save_path");
         boolean removeSecurity = call.argument("remove_security");
         boolean fontSubSet = call.argument("font_sub_set");
+        if (pdfView != null){
+          pdfView.exitEditMode();
+        }
         CThreadPoolUtils.getInstance().executeIO(() -> {
           try {
             boolean saveResult;
@@ -219,10 +266,15 @@ public class CPDFDocumentPlugin extends BaseMethodChannelPlugin {
             } else {
               saveResult = document.saveAs(savePath, removeSecurity, false, fontSubSet);
             }
-            if (document.shouleReloadDocument()) {
-              document.reload();
-            }
-            result.success(saveResult);
+            CThreadPoolUtils.getInstance().executeMain(()->{
+              if (document.shouleReloadDocument()) {
+                document.reload();
+                if (pdfView != null) {
+                  pdfView.getCPdfReaderView().reloadPages();
+                }
+              }
+              result.success(saveResult);
+            });
           } catch (CPDFDocumentException e) {
             e.printStackTrace();
             result.error("SAVE_FAIL",
@@ -232,9 +284,10 @@ public class CPDFDocumentPlugin extends BaseMethodChannelPlugin {
         });
         break;
       case PRINT:
-        String path = readerView.getPDFDocument().getAbsolutePath();
-        Uri uri = readerView.getPDFDocument().getUri();
-        CFileUtils.startPrint(context, path, uri);
+        FragmentActivity fragmentActivity = CViewUtils.getFragmentActivity(pdfView.getContext());
+        if (fragmentActivity != null){
+          CPDFPrintUtils.printCurrentDocument(fragmentActivity, document);
+        }
         break;
       case REMOVE_PASSWORD:
         CThreadPoolUtils.getInstance().executeIO(() -> {
@@ -326,8 +379,8 @@ public class CPDFDocumentPlugin extends BaseMethodChannelPlugin {
         }
         break;
       case CREATE_WATERMARK:
-        Object object = call.arguments;
-        Log.e("ComPDFKit-Flutter", "watermark:" + object.toString());
+        Object watermarkObj = call.arguments;
+        Log.e("ComPDFKit-Flutter", "watermark:" + watermarkObj.toString());
         createWatermark(call, result, pdfView, document);
         break;
       case REMOVE_ALL_WATERMARKS:
@@ -335,7 +388,9 @@ public class CPDFDocumentPlugin extends BaseMethodChannelPlugin {
             watermarkCount--) {
           document.getWatermark(watermarkCount - 1).clear();
         }
-        pdfView.getCPdfReaderView().reloadPages();
+        if (pdfView != null) {
+          pdfView.getCPdfReaderView().reloadPages();
+        }
         break;
       default:
         break;

+ 2 - 1
android/src/main/java/com/compdfkit/flutter/compdfkit_flutter/plugin/CPDFViewCtrlPlugin.java

@@ -263,7 +263,8 @@ public class CPDFViewCtrlPlugin extends BaseMethodChannelPlugin {
         documentFragment.showBOTA();
         break;
       case SHOW_ADD_WATERMARK_VIEW:
-        documentFragment.showAddWatermarkDialog();
+        boolean saveAsNewFile = (boolean) call.arguments;
+        documentFragment.showAddWatermarkDialog(saveAsNewFile);
         break;
       case SHOW_SECURITY_VIEW:
         documentFragment.showSecurityDialog();

+ 10 - 1
android/src/main/java/com/compdfkit/flutter/compdfkit_flutter/plugin/ComPDFKitSDKPlugin.java

@@ -9,6 +9,7 @@
 
 package com.compdfkit.flutter.compdfkit_flutter.plugin;
 
+import static com.compdfkit.flutter.compdfkit_flutter.constants.CPDFConstants.ChannelMethod.CREATE_DOCUMENT_PLUGIN;
 import static com.compdfkit.flutter.compdfkit_flutter.constants.CPDFConstants.ChannelMethod.CREATE_URI;
 import static com.compdfkit.flutter.compdfkit_flutter.constants.CPDFConstants.ChannelMethod.GET_TEMP_DIRECTORY;
 import static com.compdfkit.flutter.compdfkit_flutter.constants.CPDFConstants.ChannelMethod.INIT_SDK;
@@ -31,6 +32,7 @@ import android.util.Log;
 import androidx.annotation.NonNull;
 
 import androidx.annotation.Nullable;
+import com.compdfkit.core.document.CPDFDocument;
 import com.compdfkit.core.document.CPDFSdk;
 import com.compdfkit.core.font.CPDFFont;
 import com.compdfkit.flutter.compdfkit_flutter.utils.FileUtils;
@@ -135,6 +137,13 @@ public class ComPDFKitSDKPlugin extends BaseMethodChannelPlugin implements Plugi
                 CPDFSdk.setImportFontDir(importFontDir, addSysFont);
                 result.success(true);
                 break;
+            case CREATE_DOCUMENT_PLUGIN:
+                String id = (String) call.arguments;
+                CPDFDocumentPlugin documentPlugin = new CPDFDocumentPlugin(context, binaryMessenger, id);
+                documentPlugin.setDocument(new CPDFDocument(context));
+                documentPlugin.register();
+                result.success(true);
+                break;
             default:
                 break;
         }
@@ -145,13 +154,13 @@ public class ComPDFKitSDKPlugin extends BaseMethodChannelPlugin implements Plugi
         if (requestCode == REQUEST_CODE && resultCode == Activity.RESULT_OK){
             if(data != null && data.getData() != null){
                 Uri uri = data.getData();
+                CFileUtils.takeUriPermission(context, uri);
                 successResult(uri.toString());
             }
             return true;
         } else if (requestCode == REQUEST_CODE && resultCode != Activity.RESULT_OK){
             successResult(null);
         }
-
         return false;
     }
 

+ 5 - 2
example/android/app/src/main/AndroidManifest.xml

@@ -1,5 +1,6 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.compdfkit.flutter.example">
+  xmlns:tools="http://schemas.android.com/tools"
+  package="com.compdfkit.flutter.example">
 
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
@@ -10,8 +11,10 @@
         android:name="${applicationName}"
         android:icon="@mipmap/ic_launcher"
         android:label="ComPDFKit_flutter"
+      android:enableOnBackInvokedCallback="true"
         android:requestLegacyExternalStorage="true"
-        android:roundIcon="@mipmap/ic_launcher_round">
+        android:roundIcon="@mipmap/ic_launcher_round"
+      tools:targetApi="tiramisu">
         <activity
             android:name=".MainActivity"
             android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"

+ 9 - 9
example/ios/Runner.xcodeproj/project.pbxproj

@@ -489,11 +489,11 @@
 				CLANG_ENABLE_MODULES = YES;
 				CODE_SIGN_IDENTITY = "Apple Development";
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 2.2.0;
+				CURRENT_PROJECT_VERSION = 2.2.1;
 				DEVELOPMENT_TEAM = 4GGQPGRTSV;
 				ENABLE_BITCODE = NO;
-				FLUTTER_BUILD_NAME = 2.2.0;
-				FLUTTER_BUILD_NUMBER = 2.2.0;
+				FLUTTER_BUILD_NAME = 2.2.1;
+				FLUTTER_BUILD_NUMBER = 2.2.1;
 				INFOPLIST_FILE = Runner/Info.plist;
 				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
 				LD_RUNPATH_SEARCH_PATHS = (
@@ -679,11 +679,11 @@
 				CLANG_ENABLE_MODULES = YES;
 				CODE_SIGN_IDENTITY = "Apple Development";
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 2.2.0;
+				CURRENT_PROJECT_VERSION = 2.2.1;
 				DEVELOPMENT_TEAM = 4GGQPGRTSV;
 				ENABLE_BITCODE = NO;
-				FLUTTER_BUILD_NAME = 2.2.0;
-				FLUTTER_BUILD_NUMBER = 2.2.0;
+				FLUTTER_BUILD_NAME = 2.2.1;
+				FLUTTER_BUILD_NUMBER = 2.2.1;
 				INFOPLIST_FILE = Runner/Info.plist;
 				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
 				LD_RUNPATH_SEARCH_PATHS = (
@@ -708,11 +708,11 @@
 				CLANG_ENABLE_MODULES = YES;
 				CODE_SIGN_IDENTITY = "Apple Development";
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 2.2.0;
+				CURRENT_PROJECT_VERSION = 2.2.1;
 				DEVELOPMENT_TEAM = 4GGQPGRTSV;
 				ENABLE_BITCODE = NO;
-				FLUTTER_BUILD_NAME = 2.2.0;
-				FLUTTER_BUILD_NUMBER = 2.2.0;
+				FLUTTER_BUILD_NAME = 2.2.1;
+				FLUTTER_BUILD_NUMBER = 2.2.1;
 				INFOPLIST_FILE = Runner/Info.plist;
 				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
 				LD_RUNPATH_SEARCH_PATHS = (

+ 60 - 0
example/lib/cpdf_document_examples.dart

@@ -0,0 +1,60 @@
+/*
+ * Copyright © 2014-2025 PDF Technologies, Inc. All Rights Reserved.
+ *
+ * THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
+ * AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE ComPDFKit LICENSE AGREEMENT.
+ * UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
+ * This notice may not be removed from this file.
+ *
+ */
+
+import 'package:compdfkit_flutter_example/examples.dart';
+import 'package:compdfkit_flutter_example/examples/cpdf_document_open_pdf_example.dart';
+import 'package:compdfkit_flutter_example/examples/cpdf_document_save_as_example.dart';
+import 'package:flutter/material.dart';
+
+class CPDFDocumentExamples extends StatefulWidget {
+  const CPDFDocumentExamples({super.key});
+
+  @override
+  State<CPDFDocumentExamples> createState() => _CPDFDocumentExamplesState();
+}
+
+class _CPDFDocumentExamplesState extends State<CPDFDocumentExamples> {
+  @override
+  void initState() {
+    super.initState();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+        appBar: AppBar(
+          title: const Text('CPDFDocument Examples'),
+        ),
+        body: ListView.builder(
+            itemCount: examples(context).length,
+            itemBuilder: (context, index) {
+              return examples(context)[index];
+            }));
+  }
+
+  List<Widget> examples(BuildContext context) => [
+        ListTile(
+          title: const Text('Open Encrypt PDF'),
+          onTap: () {
+            goTo(const CPDFDocumentOpenPDFExample(), context);
+          },
+        ),
+        ListTile(
+          title: const Text('Save As Issues'),
+          onTap: () async {
+            String? document = await pickDocument();
+            if (document != null) {
+              debugPrint('document:${document}');
+              goTo(CPDFSaveAsExample(document: document), context);
+            }
+          },
+        ),
+      ];
+}

+ 6 - 2
example/lib/cpdf_reader_widget_controller_example.dart

@@ -120,6 +120,9 @@ class _CPDFReaderWidgetControllerExampleState
         // only android platform
         //   String? savePath = await ComPDFKit.createUri('aaa.pdf', childDirectoryName: 'compdfkit');
         bool saveResult = await controller.document.saveAs(savePath);
+        if(saveResult){
+          _controller?.document.open(savePath, '');
+        }
         debugPrint('ComPDFKit:saveAs:Result:$saveResult');
         debugPrint('ComPDFKit:saveAs:Path:$savePath');
         break;
@@ -174,7 +177,8 @@ class _CPDFReaderWidgetControllerExampleState
         String? path = await ComPDFKit.pickFile();
         if (path != null) {
           var document = controller.document;
-          document.open(path, "");
+          var error = await document.open(path, "");
+          debugPrint('ComPDFKit:Document: open:$error');
         }
         break;
       case "removeSignFileList":
@@ -198,7 +202,7 @@ class _CPDFReaderWidgetControllerExampleState
         await controller.showDisplaySettingView();
         break;
       case 'Watermark':
-        await controller.showAddWatermarkView();
+        await controller.showAddWatermarkView(saveAsNewFile: false);
         break;
       case 'Security':
         await controller.showSecurityView();

+ 7 - 0
example/lib/cpdf_reader_widget_example.dart

@@ -36,6 +36,13 @@ class _CPDFReaderWidgetExampleState extends State<CPDFReaderWidgetExample> {
                 Navigator.pop(context);
               },
               icon: const Icon(Icons.arrow_back)),
+          actions: [
+            IconButton(
+                onPressed: () async {
+                  await _controller.document.printDocument();
+                },
+                icon: Icon(Icons.print))
+          ],
         ),
         body: CPDFReaderWidget(
           document: widget.documentPath,

+ 5 - 1
example/lib/cpdf_reader_widget_security_example.dart

@@ -17,10 +17,13 @@ import 'package:file_picker/file_picker.dart';
 import 'package:flutter/material.dart';
 
 class CPDFReaderWidgetSecurityExample extends StatefulWidget {
+
   final String documentPath;
 
+  final String password;
+
   const CPDFReaderWidgetSecurityExample(
-      {super.key, required this.documentPath});
+      {super.key, required this.documentPath, this.password = ''});
 
   @override
   State<CPDFReaderWidgetSecurityExample> createState() =>
@@ -47,6 +50,7 @@ class _CPDFReaderWidgetSecurityExampleState
         ),
         body: CPDFReaderWidget(
           document: widget.documentPath,
+          password: widget.password,
           configuration: CPDFConfiguration(
               toolbarConfig: const CPDFToolbarConfig(
                   iosLeftBarAvailableActions: [CPDFToolbarAction.thumbnail])),

+ 10 - 1
example/lib/examples.dart

@@ -12,6 +12,7 @@ import 'dart:io';
 
 import 'package:compdfkit_flutter/compdfkit.dart';
 import 'package:compdfkit_flutter/configuration/cpdf_configuration.dart';
+import 'package:compdfkit_flutter_example/cpdf_document_examples.dart';
 import 'package:compdfkit_flutter_example/cpdf_reader_widget_controller_example.dart';
 import 'package:compdfkit_flutter_example/cpdf_reader_widget_dark_theme_example.dart';
 import 'package:compdfkit_flutter_example/cpdf_reader_widget_security_example.dart';
@@ -86,7 +87,15 @@ List<Widget> examples(BuildContext context) => [
               ComPDFKit.openDocument(path,
                   password: '', configuration: CPDFConfiguration());
             }
-          })
+          }),
+      _title(context, 'CPDFDocument Examples'),
+      FeatureItem(
+          title: 'CPDFDocument Example',
+          description:
+              'This example demonstrates how to use CPDFDocument independently for PDF document operations, including opening documents, importing and exporting XFDF annotation files, setting passwords, and more.',
+          onTap: () async {
+            goTo(const CPDFDocumentExamples(), context);
+          }),
     ];
 
 void showDocument(context) async {

+ 143 - 0
example/lib/examples/cpdf_document_open_pdf_example.dart

@@ -0,0 +1,143 @@
+/*
+ * Copyright © 2014-2025 PDF Technologies, Inc. All Rights Reserved.
+ *
+ * THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
+ * AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE ComPDFKit LICENSE AGREEMENT.
+ * UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
+ * This notice may not be removed from this file.
+ *
+ */
+
+import 'dart:io';
+
+import 'package:compdfkit_flutter/compdfkit.dart';
+import 'package:compdfkit_flutter/configuration/cpdf_configuration.dart';
+import 'package:compdfkit_flutter/configuration/cpdf_options.dart';
+import 'package:compdfkit_flutter/document/cpdf_document.dart';
+import 'package:compdfkit_flutter/widgets/cpdf_reader_widget.dart';
+import 'package:compdfkit_flutter_example/cpdf_reader_widget_security_example.dart';
+import 'package:compdfkit_flutter_example/examples.dart';
+import 'package:compdfkit_flutter_example/utils/file_util.dart';
+import 'package:flutter/material.dart';
+
+class CPDFDocumentOpenPDFExample extends StatefulWidget {
+  const CPDFDocumentOpenPDFExample({super.key});
+
+  @override
+  State<CPDFDocumentOpenPDFExample> createState() => _CPDFDocumentExampleState();
+}
+
+class _CPDFDocumentExampleState extends State<CPDFDocumentOpenPDFExample> {
+  TextEditingController _textEditingController = new TextEditingController();
+  List<String> logs = List.empty(growable: true);
+  late CPDFDocument document;
+  @override
+  void initState() {
+    super.initState();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      appBar: AppBar(
+        title: const Text('Open Encrypt PDF Example'),
+      ),
+      body: Column(
+        crossAxisAlignment: CrossAxisAlignment.start,
+        children: [
+          TextButton(
+              onPressed: () async {
+                clearLogs();
+                File encryptPDF = await extractAsset(context,shouldOverwrite: true,
+                    'pdfs/Password_compdfkit_Security_Sample_File.pdf');
+
+                applyLog('filePath:${encryptPDF.path}\n');
+                document = await CPDFDocument.createInstance();
+                var error = await document.open(encryptPDF.path, '');
+
+                applyLog('open result:${error.name}\n');
+                String? password = '';
+                if (error == CPDFDocumentError.errorPassword) {
+                  applyLog('show input password dialog\n');
+                  password = await _showInputPasswordDialog();
+                  if (password == null) {
+                    applyLog('input password is empty;\n');
+                    return;
+                  }
+                  applyLog('password:${password}\n');
+                  error = await document.open(encryptPDF.path, password);
+                }
+                applyLog('open result:$error\n');
+                if (error == CPDFDocumentError.success) {
+                  applyLog('go to CPDFReaderWidgetSecurityExample\n');
+                  goTo(
+                      CPDFReaderWidgetSecurityExample(
+                          documentPath: encryptPDF.path, password: password),
+                      context);
+                }
+              },
+              child: const Text('Open Document')),
+          Expanded(
+              child: Container(
+            margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 8),
+            padding: const EdgeInsets.all(8),
+            width: double.infinity,
+            decoration: const BoxDecoration(
+                color: Colors.black12,
+                borderRadius: BorderRadius.all(Radius.circular(8))),
+            child: Text(
+              logs.join(),
+              style: const TextStyle(fontSize: 12),
+            ),
+          ))
+        ],
+      ),
+    );
+  }
+
+  Future<String?> _showInputPasswordDialog() async {
+    _textEditingController.clear();
+    return await showDialog(
+        context: context,
+        builder: (context) {
+          return AlertDialog(
+            title: const Text('Input Password'),
+            content: TextField(
+              controller: _textEditingController,
+              textInputAction: TextInputAction.done,
+              decoration: const InputDecoration(hintText: 'compdfkit'),
+            ),
+            actions: [
+              TextButton(
+                  onPressed: () {
+                    Navigator.pop(context, null);
+                  },
+                  child: const Text('Cancel')),
+              TextButton(
+                  onPressed: () {
+                    Navigator.pop(context, _textEditingController.text);
+                  },
+                  child: const Text('OK'))
+            ],
+          );
+        });
+  }
+
+  void applyLog(String msg) {
+    setState(() {
+      logs.add(msg);
+    });
+  }
+
+  void clearLogs() {
+    setState(() {
+      logs.clear();
+    });
+  }
+
+  @override
+  void dispose() {
+    super.dispose();
+    _textEditingController.dispose();
+  }
+}

+ 120 - 0
example/lib/examples/cpdf_document_save_as_example.dart

@@ -0,0 +1,120 @@
+/*
+ * Copyright © 2014-2025 PDF Technologies, Inc. All Rights Reserved.
+ *
+ * THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
+ * AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE ComPDFKit LICENSE AGREEMENT.
+ * UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
+ * This notice may not be removed from this file.
+ *
+ */
+
+import 'dart:io';
+
+import 'package:compdfkit_flutter/compdfkit.dart';
+import 'package:compdfkit_flutter/configuration/cpdf_configuration.dart';
+import 'package:compdfkit_flutter/configuration/cpdf_options.dart';
+import 'package:compdfkit_flutter/document/cpdf_document.dart';
+import 'package:compdfkit_flutter/widgets/cpdf_reader_widget.dart';
+import 'package:compdfkit_flutter/widgets/cpdf_reader_widget_controller.dart';
+import 'package:compdfkit_flutter_example/cpdf_reader_widget_example.dart';
+import 'package:compdfkit_flutter_example/cpdf_reader_widget_security_example.dart';
+import 'package:compdfkit_flutter_example/examples.dart';
+import 'package:compdfkit_flutter_example/utils/file_util.dart';
+import 'package:flutter/material.dart';
+
+class CPDFSaveAsExample extends StatefulWidget {
+  final String document;
+
+  const CPDFSaveAsExample({super.key, required this.document});
+
+
+  @override
+  State<CPDFSaveAsExample> createState() => _CPDFSaveAsExampleState();
+}
+
+class _CPDFSaveAsExampleState extends State<CPDFSaveAsExample> {
+  late CPDFReaderWidgetController controller;
+
+  @override
+  void initState() {
+    super.initState();
+  }
+
+  void initPDFPath() async {}
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+        appBar: AppBar(
+          title: const Text('Save As issues'),
+          actions: [
+            IconButton(
+                onPressed: () async {
+                  // await controller.document.save();
+
+                  final tempDir = await ComPDFKit.getTemporaryDirectory();
+                  String savePath = '${tempDir.path}/test_2.pdf';
+                  bool saveResult = await controller.document.saveAs(savePath);
+                  if (saveResult) {
+                    var jump = await _showSaveResultDialog(savePath);
+                    if (jump != null) {
+                      goTo(CPDFReaderWidgetExample(documentPath: jump), context);
+                    }
+                  }
+                },
+                icon: const Icon(Icons.download)),
+            IconButton(
+                onPressed: () async {
+
+                  final tempDir = await ComPDFKit.getTemporaryDirectory();
+                  String savePath = '${tempDir.path}/test_2.pdf';
+                  final file = File(savePath);
+                  if(await file.exists()){
+                    await file.delete();
+                  }
+
+                  // File xfdfFile = await extractAsset(context, 'pdfs/test.xfdf');
+                  //
+                  // // android Uri:
+                  // //String xfdfFile = "content://xxx";
+                  //
+                  // bool result = await controller.document
+                  //     .importAnnotations(xfdfFile.path);
+                  // debugPrint('ComPDFKit:Document: importAnnotations:$result');
+                },
+                icon: Icon(Icons.settings_ethernet_rounded)),
+
+          ],
+        ),
+        body: CPDFReaderWidget(
+            document: widget.document,
+            configuration: CPDFConfiguration(),
+            onCreated: (constroller) {
+              setState(() {
+                this.controller = constroller;
+              });
+            }));
+  }
+
+  Future<String?> _showSaveResultDialog(String path) async {
+    return await showDialog(
+        context: context,
+        builder: (context) {
+          return AlertDialog(
+            title: const Text('Save Result'),
+            content: Text('Save Path:${path}'),
+            actions: [
+              TextButton(
+                  onPressed: () {
+                    Navigator.pop(context, null);
+                  },
+                  child: const Text('Cancel')),
+              TextButton(
+                  onPressed: () {
+                    Navigator.pop(context, path);
+                  },
+                  child: const Text('Jump'))
+            ],
+          );
+        });
+  }
+}

+ 1 - 1
example/pubspec.yaml

@@ -1,6 +1,6 @@
 name: compdfkit_flutter_example
 description: Demonstrates how to use the compdfkit_flutter plugin.
-version: 2.2.1
+version: 2.2.2
 homepage: https://www.compdf.com
 repository: https://github.com/ComPDFKit/compdfkit-pdf-sdk-flutter
 issue_tracker: https://www.compdf.com/support

+ 0 - 1
ios/Classes/reader/CPDFDocumentPlugin.swift

@@ -59,7 +59,6 @@ public class CPDFDocumentPlugin {
                     if pdfListView.document.isModified() == true {
                         isSuccess = pdfListView.document.write(to: pdfListView.document.documentURL)
                     }
-                    
                 } else {
                     if(pdfListView.document != nil) {
                         if pdfListView.document.isModified() == true {

+ 4 - 0
lib/compdfkit.dart

@@ -153,4 +153,8 @@ class ComPDFKit {
       return false;
     }
   }
+
+  static Future<bool> createDocumentInstance(String id) async {
+    return await _methodChannel.invokeMethod('create_document_plugin', id);
+  }
 }

+ 18 - 2
lib/configuration/cpdf_configuration.dart

@@ -426,12 +426,28 @@ class CPDFGlobalConfig {
   /// The default value is true. Saving font subsets may increase file size.
   final bool fileSaveExtraFontSubset;
 
+  final CPDFWatermarkConfig watermark;
+
   const CPDFGlobalConfig(
       {this.themeMode = CPDFThemeMode.system,
-      this.fileSaveExtraFontSubset = true});
+      this.fileSaveExtraFontSubset = true,
+      this.watermark = const CPDFWatermarkConfig()});
 
   Map<String, dynamic> toJson() => {
         "themeMode": themeMode.name,
-        "fileSaveExtraFontSubset": fileSaveExtraFontSubset
+        "fileSaveExtraFontSubset": fileSaveExtraFontSubset,
+        "watermark" : watermark.toJson()
       };
 }
+
+class CPDFWatermarkConfig{
+
+  final bool saveAsNewFile;
+
+  const CPDFWatermarkConfig({
+    this.saveAsNewFile = true});
+
+
+  Map<String, dynamic> toJson() => {
+    "saveAsNewFile": saveAsNewFile};
+}

+ 36 - 3
lib/document/cpdf_document.dart

@@ -10,6 +10,9 @@ import 'package:compdfkit_flutter/document/cpdf_watermark.dart';
 import 'package:flutter/services.dart';
 import 'package:flutter/widgets.dart';
 
+import '../compdfkit.dart';
+import '../util/cpdf_uuid_util.dart';
+
 /// A class to handle PDF documents without using [CPDFReaderWidget]
 ///
 /// example:
@@ -30,15 +33,38 @@ class CPDFDocument {
 
   get isValid => _isValid;
 
+  static Future<CPDFDocument> createInstance() async {
+    var id = CpdfUuidUtil.generateShortUniqueId();
+    bool success = await ComPDFKit.createDocumentInstance(id);
+    if (!success) {
+      throw Exception('Failed to create document instance');
+    }
+    return CPDFDocument._(id);
+  }
+
+  CPDFDocument._(String id) {
+    _channel = MethodChannel('com.compdfkit.flutter.document_$id');
+  }
+
   CPDFDocument.withController(int viewId)
       : _channel = MethodChannel('com.compdfkit.flutter.document_$viewId'),
         _isValid = true;
 
-  Future<bool> open(String filePath, String password) async {
-    return  await _channel.invokeMethod(
-        'open_document', {'filePath': filePath, 'password': password});
+  Future<CPDFDocumentError> open(String filePath, String password) async {
+    try{
+      var error = await _channel.invokeMethod(
+          'open_document', {'filePath': filePath, 'password': password});
+      var type =  CPDFDocumentError.values.where((e) => e.name == error).first;
+      if(type == CPDFDocumentError.success){
+        _isValid = true;
+      }
+      return type;
+    } on PlatformException catch (e) {
+      return CPDFDocumentError.unknown;
+    }
   }
 
+
   /// Gets the file name of the PDF document.
   ///
   /// example:
@@ -321,4 +347,11 @@ class CPDFDocument {
   Future<void> removeAllWatermarks() async{
     return await _channel.invokeMethod('remove_all_watermarks');
   }
+
+  Future<void> close() async {
+    if (!_isValid) return;
+    await _channel.invokeMethod('close');
+    debugPrint('ComPDFKit:CPDFDocument.close');
+    _isValid = false;
+  }
 }

+ 21 - 0
lib/util/cpdf_uuid_util.dart

@@ -0,0 +1,21 @@
+/*
+ * Copyright © 2014-2024 PDF Technologies, Inc. All Rights Reserved.
+ *
+ * THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
+ * AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE ComPDFKit LICENSE AGREEMENT.
+ * UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
+ * This notice may not be removed from this file.
+ *
+ */
+
+import 'dart:math';
+
+class CpdfUuidUtil {
+
+  static String generateShortUniqueId() {
+    final now = DateTime.now().millisecondsSinceEpoch ~/ 1000;
+    final random = Random().nextInt(1000);
+    return '$now$random';
+  }
+
+}

+ 2 - 2
lib/widgets/cpdf_reader_widget_controller.dart

@@ -430,8 +430,8 @@ class CPDFReaderWidgetController {
   /// ```dart
   /// await controller.showAddWatermarkView();
   /// ```
-  Future<void> showAddWatermarkView() async {
-    await _channel.invokeMethod('show_add_watermark_view');
+  Future<void> showAddWatermarkView({bool saveAsNewFile = true}) async {
+    await _channel.invokeMethod('show_add_watermark_view', saveAsNewFile);
   }
 
   /// Displays the document security settings view, allowing users to configure document security options.

+ 1 - 1
pubspec.yaml

@@ -1,6 +1,6 @@
 name: compdfkit_flutter
 description: ComPDFKit for Flutter is a comprehensive SDK that allows you to quickly add PDF functionality to Android and iOS Flutter applications.
-version: 2.2.1
+version: 2.2.2
 homepage: https://www.compdf.com
 repository: https://github.com/ComPDFKit/compdfkit-pdf-sdk-flutter
 issue_tracker: https://www.compdf.com/support