Переглянути джерело

ComPDFKit(RN) - iOS 端获取注释和表单信息的接口串接

yangliuhua 1 тиждень тому
батько
коміт
7add4d4742

+ 228 - 0
ios/RCTCPDFPageUtil.swift

@@ -0,0 +1,228 @@
+//
+//  RCTCPDFPageUtil.swift
+//  react-native-compdfkit-pdf
+//
+//  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 UIKit
+import ComPDFKit
+
+class RCTCPDFPageUtil: NSObject {
+    
+    private var page: CPDFPage?
+    
+    public var pageIndex: Int = 0
+    
+    init(page: CPDFPage? = nil) {
+        self.page = page
+    }
+    
+    //MARK: - Public Methods
+    
+    func getAnnotations() -> [Dictionary<String, Any>] {
+        var annotaionDicts:[Dictionary<String, Any>] = []
+        
+        let annoations = page?.annotations ?? []
+        
+        for  annoation in annoations {
+            var annotaionDict: [String : Any] = [:]
+            
+            let type: String = annoation.type
+            if annoation.type == "Widget" {
+                continue
+            }
+            
+            switch type {
+            case "Highlight", "Squiggly", "Underline", "Strikeout":
+                if let markupAnnotation = annoation as? CPDFMarkupAnnotation {
+                    let lowertype = lowercaseFirstLetter(of: type)
+                    
+                    annotaionDict["type"] = lowertype
+                    annotaionDict["title"] = markupAnnotation.userName()
+                    annotaionDict["page"] = pageIndex
+                    annotaionDict["content"] = markupAnnotation.contents
+                }
+            case "Circle":
+                if let circleAnnotation = annoation as? CPDFCircleAnnotation {
+                    let lowertype = lowercaseFirstLetter(of: type)
+                    
+                    annotaionDict["type"] = lowertype
+                    annotaionDict["title"] = circleAnnotation.userName()
+                    annotaionDict["page"] = pageIndex
+                    annotaionDict["content"] = circleAnnotation.contents
+                }
+            case "Square":
+                if let squareAnnotation = annoation as? CPDFSquareAnnotation {
+                    let lowertype = lowercaseFirstLetter(of: type)
+                    
+                    annotaionDict["type"] = lowertype
+                    annotaionDict["title"] = squareAnnotation.userName()
+                    annotaionDict["page"] = pageIndex
+                    annotaionDict["content"] = squareAnnotation.contents
+                }
+            case "Line", "Arrow":
+                if let lineAnnotation = annoation as? CPDFLineAnnotation {
+                    let lowertype = lowercaseFirstLetter(of: type)
+                    
+                    annotaionDict["type"] = lowertype
+                    annotaionDict["title"] = lineAnnotation.userName()
+                    annotaionDict["page"] = pageIndex
+                    annotaionDict["content"] = lineAnnotation.contents
+                }
+            case "Freehand":
+                if let inkAnnotation = annoation as? CPDFInkAnnotation {
+                    let lowertype = lowercaseFirstLetter(of: type)
+                    
+                    annotaionDict["type"] = lowertype
+                    annotaionDict["title"] = inkAnnotation.userName()
+                    annotaionDict["page"] = pageIndex
+                    annotaionDict["content"] = inkAnnotation.contents
+                }
+                
+            case "Note":
+                if let noteAnnotation = annoation as? CPDFTextAnnotation {
+                    let lowertype = lowercaseFirstLetter(of: type)
+                    
+                    annotaionDict["type"] = lowertype
+                    annotaionDict["title"] = noteAnnotation.userName()
+                    annotaionDict["page"] = pageIndex
+                    annotaionDict["content"] = noteAnnotation.contents
+                }
+                
+            case "FreeText":
+                if let freeTextAnnotation = annoation as? CPDFFreeTextAnnotation {
+                    let lowertype = lowercaseFirstLetter(of: type)
+                    
+                    annotaionDict["type"] = lowertype
+                    annotaionDict["title"] = freeTextAnnotation.userName()
+                    annotaionDict["page"] = pageIndex
+                    annotaionDict["content"] = freeTextAnnotation.contents
+                }
+                
+            case "Stamp", "Image":
+                if let stampAnnotation = annoation as? CPDFStampAnnotation {
+                    let lowertype = lowercaseFirstLetter(of: type)
+                    
+                    annotaionDict["type"] = lowertype
+                    annotaionDict["title"] = stampAnnotation.userName()
+                    annotaionDict["page"] = pageIndex
+                    annotaionDict["content"] = stampAnnotation.contents
+                }
+                
+            case "Link":
+                if let linkAnnotation = annoation as? CPDFLinkAnnotation {
+                    let lowertype = lowercaseFirstLetter(of: type)
+                    
+                    annotaionDict["type"] = lowertype
+                    annotaionDict["title"] = linkAnnotation.userName()
+                    annotaionDict["page"] = pageIndex
+                    annotaionDict["content"] = linkAnnotation.contents
+                }
+                
+            case "Media":
+                if let mediaAnnotation = annoation as? CPDFSoundAnnotation {
+                    let lowertype = lowercaseFirstLetter(of: type)
+                    
+                    annotaionDict["type"] = lowertype
+                    annotaionDict["title"] = mediaAnnotation.userName()
+                    annotaionDict["page"] = pageIndex
+                    annotaionDict["content"] = mediaAnnotation.contents
+                }
+            default:
+                print("Unhandled type: \(type)")
+            }
+            
+            annotaionDicts.append(annotaionDict)
+        }
+        
+        return annotaionDicts
+    }
+    
+    func getForms() -> [Dictionary<String, Any>] {
+        var formDicts:[Dictionary<String, Any>] = []
+        
+        let forms = page?.annotations ?? []
+        
+        for form in forms {
+            var formDict: [String : Any] = [:]
+            
+            let type: String = form.type
+            if form.type == "Widget" {
+                if let widgetAnnotation = form as? CPDFWidgetAnnotation {
+                    let widgetType: String = widgetAnnotation.widgetType
+                    
+                    switch widgetType {
+                    case "CheckBox", "RadioButton", "PushButton":
+                        if let buttonWidget = form as? CPDFButtonWidgetAnnotation {
+                            let lowertype = lowercaseFirstLetter(of: widgetType)
+                            
+                            formDict["type"] = lowertype
+                            formDict["title"] = buttonWidget.fieldName()
+                            formDict["page"] = pageIndex
+                            if widgetType != "PushButton" {
+                                let isOn = buttonWidget.state()
+                                if (isOn != 0) {
+                                    formDict["isChecked"] = true
+                                } else {
+                                    formDict["isChecked"] = false
+                                }
+                            }
+                        }
+                        
+                    case "TextField":
+                        if let textFieldWidget = form as? CPDFTextWidgetAnnotation {
+                            let lowertype = lowercaseFirstLetter(of: widgetType)
+                            
+                            formDict["type"] = lowertype
+                            formDict["title"] = textFieldWidget.fieldName()
+                            formDict["page"] = pageIndex
+                            formDict["text"] = textFieldWidget.stringValue
+                        }
+                        
+                    case "ListBox", "ComboBox":
+                        if let choiceWidget = form as? CPDFChoiceWidgetAnnotation {
+                            let lowertype = lowercaseFirstLetter(of: widgetType)
+                            
+                            formDict["type"] = lowertype
+                            formDict["title"] = choiceWidget.fieldName()
+                            formDict["page"] = pageIndex
+                        }
+                        
+                    case "SignatureFields":
+                        if let signatureWidget = form as? CPDFSignatureWidgetAnnotation {
+                            let lowertype = lowercaseFirstLetter(of: widgetType)
+                            
+                            formDict["type"] = lowertype
+                            formDict["title"] = signatureWidget.fieldName()
+                            formDict["page"] = pageIndex
+                        }
+                        
+                    default:
+                        print("Unhandled type: \(type)")
+                    }
+                    
+                    formDicts.append(formDict)
+                }
+            }
+        }
+        
+        return formDicts
+    }
+    
+    //MARK: - Private Methods
+    
+    func lowercaseFirstLetter(of string: String) -> String {
+        guard !string.isEmpty else { return string }
+        
+        let lowercaseString = string.prefix(1).lowercased() + string.dropFirst()
+        
+        return lowercaseString
+    }
+    
+}

+ 54 - 12
ios/RCTCPDFView.swift

@@ -82,6 +82,55 @@ class RCTCPDFView: UIView, CPDFViewBaseControllerDelete {
         }
     }
     
+    func insertPDFDocument(_ document: CPDFDocument, Pages pages: [Int], Position index: UInt) -> Bool {
+        if let pdfListView = self.pdfViewController?.pdfListView {
+            var _index: UInt = index
+            if index < 0 || index > pdfListView.document.pageCount {
+                if Int(index) == -1 {
+                    _index = pdfListView.document.pageCount
+                } else {
+                    return false
+                }
+            }
+            
+            var indexSet = IndexSet()
+            for page in pages {
+                indexSet.insert(IndexSet.Element(page+1))
+            }
+            
+            let success = pdfListView.document.importPages(indexSet, from: document, at: _index)
+            
+            return success
+        } else {
+            return false
+        }
+    }
+    
+    func extractPDFDocument(_ savePath: String, Pages pages: [Int]) -> Bool {
+        if let pdfListView = self.pdfViewController?.pdfListView {
+            var indexSet = IndexSet()
+            for page in pages {
+                indexSet.insert(page)
+            }
+            
+            let document = CPDFDocument()
+            document?.importPages(indexSet, from: pdfListView.document, at: 0)
+            
+            let success = document?.write(to: URL(fileURLWithPath: savePath), isSaveFontSubset: true) ?? false
+            
+            return success
+        } else {
+            return false
+        }
+    }
+    
+    func getValue<T>(from info: [String: Any]?, key: String, defaultValue: T) -> T {
+        guard let value = info?[key] as? T else {
+            return defaultValue
+        }
+        return value
+    }
+    
     // MARK: - Page Public Methods
     
     func getPage(_ index: UInt) -> CPDFPage {
@@ -673,21 +722,21 @@ class RCTCPDFView: UIView, CPDFViewBaseControllerDelete {
         }
     }
     
-    func importDocument(info : NSDictionary, completionHandler: @escaping (Bool) -> Void) {
+    func importDocument(_ filePath:URL, _ info : NSDictionary, completionHandler: @escaping (Bool) -> Void) {
         if let pdfListView = self.pdfViewController?.pdfListView {
             let _info = info as? [String: Any]
             
-            let filePath: String = self.getValue(from: _info, key: "file_path", defaultValue: "")
             let password: String = self.getValue(from: _info, key: "password", defaultValue: "")
             let pages: [Int] = self.getValue(from: _info, key: "pages", defaultValue: [])
             let insert_position: UInt = self.getValue(from: _info, key: "insert_position", defaultValue: 0)
             
-            let _document = CPDFDocument(url: URL(fileURLWithPath: filePath))
+            let _document = CPDFDocument(url: filePath)
+            
             if _document?.isLocked == true {
                 _document?.unlock(withPassword: password)
             }
             
-            let success = self.pdfViewController?.insertPDFDocument(_document!, Pages: pages, Position: insert_position) ?? false
+            let success = self.insertPDFDocument(_document!, Pages: pages, Position: insert_position)
             completionHandler(success)
         } else {
             completionHandler(false)
@@ -701,7 +750,7 @@ class RCTCPDFView: UIView, CPDFViewBaseControllerDelete {
             let savePath: String = self.getValue(from: _info, key: "save_path", defaultValue: "")
             let pages: [Int] = self.getValue(from: _info, key: "pages", defaultValue: [])
             
-            let success = pdfViewController?.extractPDFDocument(savePath, Pages: pages) ?? false
+            let success = self.extractPDFDocument(savePath, Pages: pages)
             
             completionHandler(success)
         } else {
@@ -709,13 +758,6 @@ class RCTCPDFView: UIView, CPDFViewBaseControllerDelete {
         }
     }
     
-    func getValue<T>(from info: [String: Any]?, key: String, defaultValue: T) -> T {
-        guard let value = info?[key] as? T else {
-            return defaultValue
-        }
-        return value
-    }
-    
     // MARK: - CPDFViewBaseControllerDelete
     
     func PDFViewBaseController(_ baseController: CPDFViewBaseController, SaveState success: Bool) {

+ 24 - 2
ios/RCTCPDFViewManager.swift

@@ -340,9 +340,9 @@ class RCTCPDFReaderView: RCTViewManager, RCTCPDFViewDelegate {
         })
     }
     
-    func importDocument(forCPDFViewTag tag : Int, info : NSDictionary, completionHandler: @escaping (Bool) -> Void) {
+    func importDocument(forCPDFViewTag tag : Int, filePath: URL, info : NSDictionary, completionHandler: @escaping (Bool) -> Void) {
         let rtcCPDFView = cpdfViews[tag]
-        rtcCPDFView?.importDocument(info: info, completionHandler: { success in
+        rtcCPDFView?.importDocument(filePath, info, completionHandler: { success in
             completionHandler(success)
         })
     }
@@ -354,6 +354,28 @@ class RCTCPDFReaderView: RCTViewManager, RCTCPDFViewDelegate {
         })
     }
     
+    func getAnnotations(forCPDFViewTag tag : Int, pageIndex: Int, completionHandler: @escaping ([Dictionary<String, Any>]) -> Void) {
+        let rtcCPDFView = cpdfViews[tag]
+        
+        let page = rtcCPDFView?.getPage(UInt(pageIndex))
+        let pageUtil = RCTCPDFPageUtil(page: page)
+        pageUtil.pageIndex = pageIndex
+        let annotations = pageUtil.getAnnotations()
+        
+        completionHandler(annotations)
+    }
+    
+    func getWidgets(forCPDFViewTag tag : Int, pageIndex: Int, completionHandler: @escaping ([Dictionary<String, Any>]) -> Void) {
+        let rtcCPDFView = cpdfViews[tag]
+        
+        let page = rtcCPDFView?.getPage(UInt(pageIndex))
+        let pageUtil = RCTCPDFPageUtil(page: page)
+        pageUtil.pageIndex = pageIndex
+        let widgets = pageUtil.getForms()
+        
+        completionHandler(widgets)
+    }
+    
     // MARK: - RCTCPDFViewDelegate
     
     func cpdfViewAttached(_ cpdfView: RCTCPDFView) {

+ 11 - 0
ios/RCTDocumentManager.m

@@ -202,6 +202,7 @@ RCT_EXTERN_METHOD(saveAs:(NSInteger)tag
                   withRejecter:(RCTPromiseRejectBlock)reject)
 
 RCT_EXTERN_METHOD(importDocument:(NSInteger)tag
+                  withFilePath:(NSURL *) filePath
                   withInfo:(NSDictionary) info
                   withResolver:(RCTPromiseResolveBlock)resolve
                   withRejecter:(RCTPromiseRejectBlock)reject)
@@ -211,6 +212,16 @@ RCT_EXTERN_METHOD(splitDocumentPages:(NSInteger)tag
                   withResolver:(RCTPromiseResolveBlock)resolve
                   withRejecter:(RCTPromiseRejectBlock)reject)
 
+RCT_EXTERN_METHOD(getAnnotations:(NSInteger)tag
+                  withPageIndex:(NSInteger) pageIndex
+                  withResolver:(RCTPromiseResolveBlock)resolve
+                  withRejecter:(RCTPromiseRejectBlock)reject)
+
+RCT_EXTERN_METHOD(getForms:(NSInteger)tag
+                  withPageIndex:(NSInteger) pageIndex
+                  withResolver:(RCTPromiseResolveBlock)resolve
+                  withRejecter:(RCTPromiseRejectBlock)reject)
+
 + (BOOL)requiresMainQueueSetup
 {
   return NO;

+ 23 - 3
ios/RCTDocumentManager.swift

@@ -525,11 +525,11 @@ class RCTDocumentManager: NSObject, RCTBridgeModule {
         }
     }
     
-    @objc(importDocument: withInfo: withResolver: withRejecter:)
-    func importDocument(forCPDFViewTag tag : Int, info: NSDictionary, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
+    @objc(importDocument: withFilePath: withInfo: withResolver: withRejecter:)
+    func importDocument(forCPDFViewTag tag : Int, filePath: URL, info: NSDictionary, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
         DispatchQueue.main.async {
             let reader = self.readerView()
-            reader.importDocument(forCPDFViewTag: tag, info: info) { success in
+            reader.importDocument(forCPDFViewTag: tag, filePath: filePath, info: info) { success in
                 resolve(success)
             }
         }
@@ -545,4 +545,24 @@ class RCTDocumentManager: NSObject, RCTBridgeModule {
         }
     }
     
+    @objc(getAnnotations: withPageIndex: withResolver: withRejecter:)
+    func getAnnotations(forCPDFViewTag tag : Int, pageIndex: Int, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
+        DispatchQueue.main.async {
+            let reader = self.readerView()
+            reader.getAnnotations(forCPDFViewTag: tag, pageIndex: pageIndex) { annotations in
+                resolve(annotations)
+            }
+        }
+    }
+    
+    @objc(getForms: withPageIndex: withResolver: withRejecter:)
+    func getForms(forCPDFViewTag tag : Int, pageIndex: Int, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
+        DispatchQueue.main.async {
+            let reader = self.readerView()
+            reader.getWidgets(forCPDFViewTag: tag, pageIndex: pageIndex) { widgets in
+                resolve(widgets)
+            }
+        }
+    }
+    
 }