Browse Source

【2025】【OCR】工具OCR Area功能实现

lizhe 2 months ago
parent
commit
17971523ac

+ 83 - 49
PDF Office/PDF Master/KMClass/KMPDFViewController/KMMainViewController.swift

@@ -1797,10 +1797,7 @@ struct KMNMWCFlags {
             } else if listView.toolMode == .COCRToolMode {
                 popWindow.popType = .ocr
                 popWindow.OCRAction = { [weak self] in
-                    //                    let rect = self?.listView.currentSelectionRect() ?? CGRect.zero
-                    //                    let orgPage : CPDFPage = self?.listView.currentSelectionPage() ?? CPDFPage()
-                    //                    self?.cropPages(atIndexs: [orgPage.pageIndex()], to: [rect])
-                    //                    self?.closePopOperationWindow()
+                    self?.convertSelectionRectOCR(rect: self?.listView.currentSelectionRect() ?? CGRectZero)
                 }
             }
             popWindow.updatePDFViewCallback = {[weak self] in
@@ -2981,6 +2978,52 @@ struct KMNMWCFlags {
         self.view.window?.addChildWindow(winC.window!, ordered: .above)
     }
     
+    // MARK: -显示加密弹窗
+     
+    
+    //MARK: - Redact密文
+    func showRedactProperty(readactAnnotation: CPDFRedactAnnotation?) {
+        let properties = KMRedactPropertiesWindowController()
+        properties.readactAnnotation = readactAnnotation
+        self.km_beginSheet(windowC: properties)
+        
+    }
+    
+    //MARK: - 测量
+    func refreshMeasureInfo() {
+        if let annotation = listView.activeAnnotation {
+            if (listView.activeAnnotation.isKind(of: CPDFLineAnnotation.self)) {
+                if (!(listView.activeAnnotation as! CPDFLineAnnotation).isMeasure) {
+                    cancelMeasureType()
+                } else {
+                    if distanceMeasureInfoWindowController == nil {
+                        let measureInfo = CPDFDistanceMeasureInfo()
+                        distanceMeasureInfoWindowController = CDistanceMeasureInfoWindowController()
+                        distanceMeasureInfoWindowController?.measureInfo = measureInfo
+                        distanceMeasureInfoWindowController?.delegate = self
+                    }
+                }
+            } else if (!listView.activeAnnotation.isKind(of: CPDFPolygonAnnotation.self) && !listView.activeAnnotation.isKind(of: CPDFPolylineAnnotation.self)) {
+                cancelMeasureType()
+            } else if (listView.activeAnnotation.isKind(of: CPDFPolygonAnnotation.self) || listView.activeAnnotation.isKind(of: CPDFPolylineAnnotation.self)) {
+                if perimeterMeasureInfoWindowController == nil {
+                    let measureInfo = CPDFPerimeterMeasureInfo()
+                    perimeterMeasureInfoWindowController = CPerimeterMeasureInfoWindowController()
+                    perimeterMeasureInfoWindowController?.measureInfo = measureInfo
+                    perimeterMeasureInfoWindowController?.delegate = self
+                }
+                if areaMeasureInfoWindowController == nil {
+                    let measureInfo = CPDFAreaMeasureInfo()
+                    areaMeasureInfoWindowController = CAreaMeasureInfoWindowController()
+                    areaMeasureInfoWindowController?.measureInfo = measureInfo
+                    areaMeasureInfoWindowController?.delegate = self
+                }
+            }
+        } else {
+            cancelMeasureType()
+        }
+    }
+    
 }
 
 //MARK: Compress
@@ -3028,6 +3071,7 @@ extension KMMainViewController {
 
 //MARK: - OCR
 extension KMMainViewController {
+    //window
     func showOCRWindow() {
         if !IAPProductsManager.default().isAvailableAllFunction(){
             let winC = KMPurchaseCompareWindowController.sharedInstance()
@@ -3097,7 +3141,7 @@ extension KMMainViewController {
         
         KMOCRManager.manager.convertScanFile(document: document, model: model, progress: { progress in
             
-        }) { [weak self] document, error in
+        }) { [weak self] document, text, error in
             window.endLoading()
             window.km_quick_endSheet()
             if !model.saveAsPDF {
@@ -3116,7 +3160,7 @@ extension KMMainViewController {
         
         KMOCRManager.manager.convertOCR(document: document, model: model, progress: { progress in
             
-        }) { [weak self] document, error in
+        }) { [weak self] document, text, error  in
 //            self?.view.window?.windowController.endLoading()
 //            window.km_quick_endSheet()
             if !model.saveAsPDF {
@@ -3124,53 +3168,44 @@ extension KMMainViewController {
             }
         }
     }
-     
-    // MARK: -显示加密弹窗
-     
     
-    //MARK: - Redact密文
-    func showRedactProperty(readactAnnotation: CPDFRedactAnnotation?) {
-        let properties = KMRedactPropertiesWindowController()
-        properties.readactAnnotation = readactAnnotation
-        self.km_beginSheet(windowC: properties)
-        
+    func convertOCRSaveAsTXT(text: String) {
+        NSPanel.savePanel(NSWindow.currentWindow()) { panel in
+            let url: URL = self.listView.document.documentURL
+            panel.nameFieldStringValue = ""+url.deletingPathExtension().lastPathComponent+"_OCR"
+            panel.allowedFileTypes = ["txt"]
+        } completion: { [unowned self] response, url in
+            if (response == .cancel) {
+                return
+            }
+            let saveAsPDFFilePath = url?.path ?? ""
+            let outputURL = URL(fileURLWithPath: saveAsPDFFilePath)
+            try? text.write(to: outputURL, atomically: true, encoding: .utf8)
+        }
     }
     
-    //MARK: - 测量
-    func refreshMeasureInfo() {
-        if let annotation = listView.activeAnnotation {
-            if (listView.activeAnnotation.isKind(of: CPDFLineAnnotation.self)) {
-                if (!(listView.activeAnnotation as! CPDFLineAnnotation).isMeasure) {
-                    cancelMeasureType()
-                } else {
-                    if distanceMeasureInfoWindowController == nil {
-                        let measureInfo = CPDFDistanceMeasureInfo()
-                        distanceMeasureInfoWindowController = CDistanceMeasureInfoWindowController()
-                        distanceMeasureInfoWindowController?.measureInfo = measureInfo
-                        distanceMeasureInfoWindowController?.delegate = self
-                    }
-                }
-            } else if (!listView.activeAnnotation.isKind(of: CPDFPolygonAnnotation.self) && !listView.activeAnnotation.isKind(of: CPDFPolylineAnnotation.self)) {
-                cancelMeasureType()
-            } else if (listView.activeAnnotation.isKind(of: CPDFPolygonAnnotation.self) || listView.activeAnnotation.isKind(of: CPDFPolylineAnnotation.self)) {
-                if perimeterMeasureInfoWindowController == nil {
-                    let measureInfo = CPDFPerimeterMeasureInfo()
-                    perimeterMeasureInfoWindowController = CPerimeterMeasureInfoWindowController()
-                    perimeterMeasureInfoWindowController?.measureInfo = measureInfo
-                    perimeterMeasureInfoWindowController?.delegate = self
-                }
-                if areaMeasureInfoWindowController == nil {
-                    let measureInfo = CPDFAreaMeasureInfo()
-                    areaMeasureInfoWindowController = CAreaMeasureInfoWindowController()
-                    areaMeasureInfoWindowController?.measureInfo = measureInfo
-                    areaMeasureInfoWindowController?.delegate = self
-                }
+    func convertSelectionRectOCR(rect: NSRect) {
+        let rect = NSIntegralRect(rect)
+        let orgPage : CPDFPage = listView.currentSelectionPage() ?? CPDFPage()
+        
+        if let page : CPDFPage = orgPage.copy() as? CPDFPage {
+            page.setBounds(rect, for: .cropBox)
+            let image = page.thumbnail(of: rect.size) ?? NSImage()
+            guard let model = self.rightSideController?.tool_OCRController?.model else { return }
+            model.pageRange = [Int(orgPage.pageIndex())]
+            KMOCRManager.manager.convertOCR(images: [image], model: model, progress: { progress in
+                
+            }) { [weak self] document, text, error in
+                self?.rightSideController?.tool_OCRController?.model.text = text ?? ""
+                self?.rightSideController?.tool_OCRController?.reloadData()
+                //关闭窗口
+                self?.listView.selectionRect = NSZeroRect
+                self?.listView.selectionPageIndex = UInt(NSNotFound)
+                self?.closePopOperationWindow()
+                self?.listView.setNeedsDisplayForVisiblePages()
             }
-        } else {
-            cancelMeasureType()
         }
     }
-     
 }
 
 //MARK: - 代理方法
@@ -3740,8 +3775,7 @@ extension KMMainViewController: KMRightSideControllerDelegate {
     
     func kmRightSideControllerOCRDoneAction(_ controller: KMRightSideController, _ model: KMOCRModel) {
         if model.showType == .area {
-            
-            
+            self.convertOCRSaveAsTXT(text: model.text)
         } else {
             if model.saveAsPDF {
                 if model.saveType == .PDF {

+ 3 - 0
PDF Office/PDF Master/KMClass/KMPDFViewController/RightSideController/Views/OCR/Tool/Contoller/KMOCRController.swift

@@ -124,6 +124,9 @@ class KMOCRController: NSViewController {
         } else {
             pageView.isHidden = true
             areaView.isHidden = false
+            
+            areaView.reloadData()
+            
             areaTabProperty.state = .pressed
             saveButton.properties.buttonText = KMLocalizedString("Save as TXT")
             saveButton.properties.showRightIcon = false

+ 52 - 28
PDF Office/PDF Master/KMClass/KMPDFViewController/RightSideController/Views/OCR/Tool/Manager/KMOCRManager.swift

@@ -9,7 +9,7 @@ import Cocoa
 import ComPDFKit_Conversion
 import ComDocumentAIKit
 
-typealias KMOCRManagerOCRComplete = (_ document: CPDFDocument?, _ error: Error?) -> Void
+typealias KMOCRManagerOCRComplete = (_ document: CPDFDocument?, _ text: String?, _ error: Error?) -> Void
 typealias KMOCRManagerOCRProgress = ( _ progress: Float) -> Void
 
 let maxImageScale = 4.0
@@ -139,7 +139,7 @@ extension KMOCRManager: KMGOCRManagerDelegate {
         if (selctPageImages.count == 0) {
             fail()
         } else {
-            self.cancelRecognition()
+//            self.cancelRecognition()
             DispatchQueue.main.async {
                 self.OCRManger = KMGOCRManager()
                 if type == .google {
@@ -153,8 +153,22 @@ extension KMOCRManager: KMGOCRManagerDelegate {
         }
     }
     
-    func recognitionImages() {
+    func convertOCR(images: [NSImage], model: KMOCRModel, progress: @escaping KMOCRManagerOCRProgress, complete: @escaping KMOCRManagerOCRComplete) {
+        self.OCRComplete = complete
+        self.progress = progress
+        self.model = model
+        self.pageIndexs = model.pageRange
         
+        DispatchQueue.main.async {
+            self.OCRManger = KMGOCRManager()
+            if model.ocrType == .google {
+                self.OCRManger?.ocrType = .google
+            } else if model.ocrType == .apple {
+                self.OCRManger?.ocrType = .apple
+            }
+            self.OCRManger?.delegate = self
+            self.OCRManger?.recognitionImages(images, withLanguages: [model.language])
+        }
     }
     
     func dealWithResults(_ rlts: [KMGOCRResult]?, OCRImageAtIndex index: Int) {
@@ -164,7 +178,7 @@ extension KMOCRManager: KMGOCRManagerDelegate {
     
     func cancelRecognition() {
         let error = NSError(domain: "com.example.MyApp", code: 404, userInfo: [NSLocalizedDescriptionKey: "取消转换"])
-        OCRComplete?(nil, error)
+        OCRComplete?(nil, "", error)
         
         KMGOCRManager.default().delegate = nil
         OCRManger?.cancelRecognition()
@@ -184,21 +198,26 @@ extension KMOCRManager: KMGOCRManagerDelegate {
             resultArrays.append(self.ocrDictionary[keyS] as Any)
         }
         
-        if model.saveAsPDF {
-            if model.saveType == .PDF {
-                KMGOCRManager.default().createPDFFile(self.saveAsPDFFilePath, images: self.pageImages, results: resultArrays, scale: maxImageScale)
+        if model.showType == .page {
+            if model.saveAsPDF {
+                if model.saveType == .PDF {
+                    KMGOCRManager.default().createPDFFile(self.saveAsPDFFilePath, images: self.pageImages, results: resultArrays, scale: maxImageScale)
+                } else {
+                    self.saveTXT()
+                }
             } else {
-                self.saveTXT()
+                let foler = self.fetchOutputFolderPath()
+                let outputPath = foler + "/temp_OCR.pdf";
+                KMGOCRManager.default().createPDFFile(outputPath, images: self.pageImages, results: resultArrays, scale: maxImageScale)
+                
+                guard let document = self.document else {
+                    self.fail()
+                    return }
+                self.insertPDF(currentDocument: document, pageIndexs: self.pageIndexs, outputPth: outputPath)
             }
-        } else {
-            let foler = self.fetchOutputFolderPath()
-            let outputPath = foler + "/temp_OCR.pdf";
-            KMGOCRManager.default().createPDFFile(outputPath, images: self.pageImages, results: resultArrays, scale: maxImageScale)
-            
-            guard let document = self.document else {
-                self.fail()
-                return }
-            self.insertPDF(currentDocument: document, pageIndexs: self.pageIndexs, outputPth: outputPath)
+        } else if model.showType == .area {
+            let string = self.fetchTXT(ocrDictionary: ocrDictionary)
+            self.OCRComplete?(nil, string, nil)
         }
     }
     
@@ -239,7 +258,10 @@ extension KMOCRManager: KMGOCRManagerDelegate {
     }
     
     func gocrManager(_ manager: KMGOCRManager!, didFinishOCRImageAt index: Int, results: [KMGOCRResult]!) {
-        let pageIndex = self.pageIndexs[index]
+        var pageIndex = 0
+        if self.pageIndexs.count != 0 {
+            pageIndex = self.pageIndexs[index]
+        }
         self.dealWithResults(results, OCRImageAtIndex: pageIndex)
         print("进度 :\(Float(index) / Float(self.pageIndexs.count))")
     }
@@ -356,9 +378,16 @@ extension KMOCRManager: CPDFConverterDelegate {
 //MARK: complete
 extension KMOCRManager {
     func saveTXT() {
-        let url: URL = document?.documentURL ?? URL(string: "")!
-        let fileName = ""+url.deletingPathExtension().lastPathComponent+"_OCR"
+        let textString = self.fetchTXT(ocrDictionary: ocrDictionary)
+
+        let outputURL = URL(fileURLWithPath: self.saveAsPDFFilePath)
+        try? textString.write(to: outputURL, atomically: true, encoding: .utf8)
+        self.viewFileAtFinder(outputURL.path)
         
+        self.success(document: self.document ?? CPDFDocument())
+    }
+    
+    func fetchTXT(ocrDictionary: [NSNumber: Any]) -> String {
         let sortedKeys = ocrDictionary.keys.sorted(by: { ($0 ).compare($1 ) == .orderedAscending })
         var textString = ""
 
@@ -375,12 +404,7 @@ extension KMOCRManager {
             textString += "\n"
             textString += rStr
         }
-
-        let outputURL = URL(fileURLWithPath: self.saveAsPDFFilePath)
-        try? textString.write(to: outputURL, atomically: true, encoding: .utf8)
-        self.viewFileAtFinder(outputURL.path)
-        
-        self.success(document: self.document ?? CPDFDocument())
+        return textString
     }
     
     func insertPDF(currentDocument: CPDFDocument, pageIndexs: [Int], outputPth: String) {
@@ -495,10 +519,10 @@ extension KMOCRManager {
 extension KMOCRManager {
     func fail() {
         let error = NSError(domain: "com.example.MyApp", code: 404, userInfo: [NSLocalizedDescriptionKey: "转换失败"])
-        OCRComplete?(nil, error)
+        OCRComplete?(nil, "", error)
     }
     
     func success(document: CPDFDocument) {
-        self.OCRComplete?(document, nil)
+        self.OCRComplete?(document, "", nil)
     }
 }

+ 1 - 0
PDF Office/PDF Master/KMClass/KMPDFViewController/RightSideController/Views/OCR/Tool/Model/KMOCRModel.swift

@@ -33,5 +33,6 @@ class KMOCRModel: NSObject {
     var pageRangeString: String = ""
     var pageRangeType: KMPageRange = .current
     
+    var text: String = ""
 //    var document: CPDFDocument?
 }

+ 29 - 13
PDF Office/PDF Master/KMClass/KMPDFViewController/RightSideController/Views/OCR/Tool/View/Area/KMOCRAreaView.swift

@@ -90,6 +90,7 @@ class KMOCRAreaView: BaseXibView {
         OCRPlan2Button.reloadData()
         
         copyButton.properties = ComponentButtonProperty(type: .text_gray, size: .xxs, onlyIcon: true, icon: NSImage(named: "tools_copy"), keepPressState: false)
+        copyButton.setTarget(self, action: #selector(copyButtonAction(_:)))
         
         languageSelectButton.properties = ComponentSelectProperties(size: .s,
                                                               state: .normal,
@@ -117,6 +118,10 @@ class KMOCRAreaView: BaseXibView {
         } else {
             self.OCRPlan2Button.properties.checkboxType = .selected
         }
+        
+        self.textArea.properties.text = model.text
+        self.textArea.reloadData()
+        
         self.OCRPlan1Button.reloadData()
         self.OCRPlan2Button.reloadData()
         
@@ -166,25 +171,36 @@ extension KMOCRAreaView {
         
         callBack(self, model)
     }
+    
+    func copyButtonAction(_ sender: ComponentButton) {
+        let pasteboard = NSPasteboard.general
+        pasteboard.clearContents() // 清空剪切板
+        pasteboard.setString(self.textArea.properties.text, forType: .string)
+    }
 }
 
 extension KMOCRAreaView: ComponentTextareaDelegate {
-    
+    func componentTextareaTextDidEndEditing(_ view: ComponentTextarea) {
+        self.model.text = self.textArea.properties.text
+    }
 }
 
 extension KMOCRAreaView: ComponentSelectDelegate {
     func componentSelectDidSelect(view: ComponentSelect?, menuItemProperty: ComponentMenuitemProperty?) {
-//        if(view == fontNameSelect) {
-//            if menuItemProperty?.identifier == "1" {
-//                let familyName = fontNameSelect.properties.text ?? "Helvetica"
-//                let styles = CPDFFont.fontNames(forFamilyName: familyName)
-//
-//                compdfFont = CPDFFont(familyName: familyName, fontStyle: styles.first ?? "")
-//                annotationPopMode.setAnnotationFont(font: compdfFont ?? CPDFFont.init(familyName: "Helvetica", fontStyle: ""))
-//            } else if menuItemProperty?.identifier == "2"{ //字体样式
-//                compdfFont = CPDFFont(familyName: compdfFont?.familyName ?? "Helvetica", fontStyle: menuItemProperty?.text ?? "")
-//                annotationPopMode.setAnnotationFont(font: compdfFont ?? CPDFFont.init(familyName: "Helvetica", fontStyle: ""))
-//            }
-//        }
+        if (view == languageSelectButton) {
+            var languages: [String] = KMOCRManager.manager.getLanguages(type: model.ocrType)
+            let position = languages.firstIndex(of: model.language) ?? 0
+            self.selectIndex = position + 1
+            
+            let values: [String: String] = KMGOCRManager.languages()[position] as? [String : String] ?? [:]
+            let key = values[KMGOCRLanguageCodeKey]
+            
+            model.language = key ?? ""
+            if let unwrappedKey = key, let intValue = Int(unwrappedKey) {
+                model.languageType = COCRLanguage(rawValue: intValue) ?? .english
+            } else {
+                print("转换失败")
+            }
+        }
     }
 }

+ 2 - 2
PDF Office/PDF Master/KMClass/PDFListView/WindowController/KMNPopOperationWindowController.xib

@@ -1,8 +1,8 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="21507" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="22505" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
     <dependencies>
         <deployment identifier="macosx"/>
-        <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21507"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="22505"/>
         <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
     </dependencies>
     <objects>