Browse Source

【2025】【OCR】工具OCR 保存PDF功能实现

lizhe 2 months ago
parent
commit
4a7dd21158

+ 33 - 11
PDF Office/PDF Master/Class/Common/OC/OCR/KMGOCRManager.m

@@ -244,16 +244,37 @@ NSString * KMGOCRLanguageStringKey = @"KMGOCRLanguageStringKey";
              @{KMGOCRLanguageCodeKey:@"zh-Hans", KMGOCRLanguageStringKey:@"Chinese Simplified"}];
 }
 
+//static inline NSFont * FontWithSize(NSString *strChar, CGSize size) {
+//    CGFloat fontsize = 1.0;
+//    NSFont *font = [NSFont systemFontOfSize:fontsize];
+//    CGSize strSize = [strChar sizeWithAttributes:@{NSFontAttributeName:font}];
+//    while ((fontsize<127) && (strSize.width<size.width || strSize.height<size.height)) {
+//        fontsize += 1.0;
+//        font = [NSFont systemFontOfSize:fontsize];
+//        strSize = [strChar sizeWithAttributes:@{NSFontAttributeName:font}];
+//    }
+//    return [NSFont systemFontOfSize:fontsize-1];
+//}
+
 static inline NSFont * FontWithSize(NSString *strChar, CGSize size) {
-    CGFloat fontsize = 1.0;
-    NSFont *font = [NSFont systemFontOfSize:fontsize];
-    CGSize strSize = [strChar sizeWithAttributes:@{NSFontAttributeName:font}];
-    while ((fontsize<127) && (strSize.width<size.width || strSize.height<size.height)) {
-        fontsize += 1.0;
-        font = [NSFont systemFontOfSize:fontsize];
-        strSize = [strChar sizeWithAttributes:@{NSFontAttributeName:font}];
-    }
-    return [NSFont systemFontOfSize:fontsize-1];
+    CGFloat minFontSize = 1.0;
+    CGFloat maxFontSize = 127.0;
+    CGFloat bestFontSize = minFontSize;
+
+    while (minFontSize <= maxFontSize) {
+        CGFloat midFontSize = floor((minFontSize + maxFontSize) / 2.0);
+        NSFont *font = [NSFont systemFontOfSize:midFontSize];
+        CGSize strSize = [strChar sizeWithAttributes:@{NSFontAttributeName: font}];
+
+        if (strSize.width <= size.width && strSize.height <= size.height) {
+            bestFontSize = midFontSize; // 更新最优值
+            minFontSize = midFontSize + 1; // 尝试更大的字体
+        } else {
+            maxFontSize = midFontSize - 1; // 缩小搜索范围
+        }
+    }
+
+    return [NSFont systemFontOfSize:bestFontSize];
 }
 
 - (void)createPDFFile:(NSString *)filePath imagePaths:(NSArray *)paths results:(NSArray *)resultsArray scale:(CGFloat)scale {
@@ -366,6 +387,7 @@ static inline NSFont * FontWithSize(NSString *strChar, CGSize size) {
         
         CIImage *imageCIImage = [CIImage imageWithCGImage:image.CGImage];
         NSSize size = [imageCIImage extent].size;
+//        NSSize size = image.size;
         CGRect pageRect = CGRectMake(0, 0, size.width/scale, size.height/scale);
         
         CFMutableDictionaryRef pageDictionary = CFDictionaryCreateMutable(NULL,
@@ -391,7 +413,7 @@ static inline NSFont * FontWithSize(NSString *strChar, CGSize size) {
             results = resultsArray[i];
             CGFloat newScale = scale;
             if([KMGOCRManager defaultManager].OCRType == KMOCRType_Apple)
-                newScale = 1;
+                newScale = scale * 0.5;
             if (results.count == 1) {
                 KMGOCRResult *result = results[0];
                 NSRect bounds = NSMakeRect((result.textBounds.origin.x)/newScale,
@@ -409,7 +431,7 @@ static inline NSFont * FontWithSize(NSString *strChar, CGSize size) {
                                                (result.textBounds.size.width)/newScale,
                                                (result.textBounds.size.height)/newScale);
                     NSDictionary *dic = @{NSFontAttributeName:FontWithSize(result.text, CGSizeMake(result.textBounds.size.width/newScale, result.textBounds.size.height/newScale)),
-                                          NSForegroundColorAttributeName:[NSColor clearColor]};
+                                          NSForegroundColorAttributeName:[NSColor blackColor]};
                     [result.text drawInRect:bounds withAttributes:dic];
                 }
             }

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

@@ -17,21 +17,25 @@ let maxImageScale = 4.0
 class KMOCRManager: NSObject {
     static let manager = KMOCRManager()
     
-    private var converter: CPDFConverter?
     private var outputPath = ""
-
-    //Edit OCR
-    private var OCRComplete: KMOCRManagerOCRComplete?
-    private var progress: KMOCRManagerOCRProgress?
+    
+    var model: KMOCRModel = KMOCRModel()
     private var pageIndexs: [Int] = []
     private var document: CPDFDocument?
     private var saveAsPDFFilePath: String = ""
     
+    //Edit OCR
+    private var converter: CPDFConverter?
+    
     //Tool OCR
     private var OCRManger: KMGOCRManager?
     private var ocrDictionary:[NSNumber: Any] = [:]
     private var pageImages: [NSImage] = []
     
+    private var OCRComplete: KMOCRManagerOCRComplete?
+    private var progress: KMOCRManagerOCRProgress?
+    
+    
 //    var documentContainsImagePages: [String: Int] = [:]
 
     var type: KMOCRType = .google {
@@ -70,12 +74,14 @@ class KMOCRManager: NSObject {
 }
 
 //MARK: Tool OCR
-extension KMOCRManager {
+extension KMOCRManager: KMGOCRManagerDelegate {
     func convertOCR(document: CPDFDocument, model: KMOCRModel, progress: @escaping KMOCRManagerOCRProgress, complete: @escaping KMOCRManagerOCRComplete) {
         self.OCRComplete = complete
         self.progress = progress
+        self.model = model
+        
         //计算需要处理的页面
-        var pages:[Int] = self.fetchPageIndex(document: document, model: model)
+        let pages:[Int] = self.fetchPageIndex(document: document, model: model)
         model.pageRange = pages
         
         if model.saveAsPDF {
@@ -89,15 +95,15 @@ extension KMOCRManager {
                         return
                     }
                     self.saveAsPDFFilePath = url?.path ?? ""
-                    self.convertOCR(document: document, indexs: model.pageRange, language: model.languageType, type: model.ocrType, saveAsPDF: true)
+                    self.convertOCR(document: document, indexs: model.pageRange, language: model.language, type: model.ocrType, saveAsPDF: true)
                 }
             }
         } else {
-            self.convertOCR(document: document, indexs: model.pageRange, language: model.languageType, type: model.ocrType)
+            self.convertOCR(document: document, indexs: model.pageRange, language: model.language, type: model.ocrType)
         }
     }
     
-    func convertOCR(document: CPDFDocument, indexs: [Int], language: COCRLanguage, type: KMOCRType, saveAsPDF: Bool = false) {
+    func convertOCR(document: CPDFDocument, indexs: [Int], language: String, type: KMOCRType, saveAsPDF: Bool = false) {
         self.document = document
         self.pageIndexs = indexs
         
@@ -131,11 +137,8 @@ extension KMOCRManager {
                 self.OCRManger?.recognitionImages(selctPageImages, withLanguages: [language])
             }
         }
-        
-        
     }
     
-    
     func recognitionImages() {
         
     }
@@ -146,9 +149,9 @@ extension KMOCRManager {
         ocrDictionary[key] = rlts
 //        ocrDictionary.setObject(rlts as Any, forKey: key as NSCopying)
         
-        let sortedKeys = ocrDictionary.keys.sorted(by: { ($0 ).compare($1 ) == .orderedAscending })
-        var textString = ""
-        
+//        let sortedKeys = ocrDictionary.keys.sorted(by: { ($0 ).compare($1 ) == .orderedAscending })
+//        var textString = ""
+//
 //        for key in sortedKeys {
 //            let results: Array<KMGOCRResult> = ocrDictionary.object(forKey: key) as! Array<KMGOCRResult>
 //            var rStr = ""
@@ -171,13 +174,90 @@ extension KMOCRManager {
         KMGOCRManager.default().delegate = nil
         OCRManger?.cancelRecognition()
     }
+    
+    
+    //MARK: - KMGOCRManagerDelegate
+    func gocrManagerDidFinishOCR(_ manager: KMGOCRManager!) {
+//        self.batchesOCR()
+//        KMGOCRManager.default().createPDFFile(savePDFPath, imagePaths: self.pageImages, results: resultArrays, scale: KMImageScale)
+        var resultArrays: Array<Any> = []
+        var ocrIndexArrays: Array<Any> = []
+        let sortedKeys = self.ocrDictionary.keys.sorted(by: { $0.compare(($1)) == .orderedAscending })
+        for i in 0 ..< (sortedKeys.count ) {
+            let keyS = sortedKeys[i]
+            ocrIndexArrays.append(keyS as Any)
+            resultArrays.append(self.ocrDictionary[keyS] as Any)
+        }
+        
+        if model.saveAsPDF {
+            KMGOCRManager.default().createPDFFile(self.saveAsPDFFilePath, images: self.pageImages, results: resultArrays, scale: maxImageScale)
+        } 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)
+        }
+    }
+    
+//    KMGOCRManager.default().createPDFFile(savePDFPath, imagePaths: imagePath, results: resultArrays, scale: KMImageScale)
+//    if saveAccessCtr.openAutomaticButton.state == .on {
+//        self.cancelButtonAction("")
+//        NSDocumentController.shared.openDocument(withContentsOf: URL(fileURLWithPath: savePDFPath), display: true, completionHandler: {document,documentWasAlreadyOpen,error in
+//            
+//        })
+//    } else {
+//        self.viewFileAtFinder(savePDFPath)
+//    }
+//}
+//}
+//}
+//
+//func viewFileAtFinder(_ fileName: String) {
+//let workspace = NSWorkspace.shared
+//let url = URL(fileURLWithPath: fileName)
+//workspace.activateFileViewerSelecting([url])
+//}
+    func gocrManager(_ manager: KMGOCRManager!, didCancelOCRImageAt index: Int) {
+        
+    }
+    
+    func gocrManager(_ manager: KMGOCRManager!, didStartOCRImageAt index: Int) {
+//        DispatchQueue.main.async {
+//            self.pageRangeBox.isEnabled = false
+//            self.languageButton.isEnabled = false
+//            self.planComboBox.isEnabled = false
+//        }
+    }
+    
+    func gocrManager(_ manager: KMGOCRManager!, didFinishOCRImageAt index: Int, results: [KMGOCRResult]!) {
+        let pageIndex = self.pageIndexs[index]
+        self.dealWithResults(results, OCRImageAtIndex: pageIndex)
+    }
+    
+    func gocrManager(_ manager: KMGOCRManager!, didFailureOCRImageAt index: Int, error: Error!) {
+//        let pagenum = self.pageIndexs?[index] as! NSNumber
+//        if (error != nil) {
+//            self.errorOCRArrays?.append(pagenum)
+//        }
+//        self.dealWithResults([], OCRImageAtIndex: index)
+    }
+    
+    
+    
+    
+    
 }
 
 //MARK: Edit OCR
-extension KMOCRManager {
+extension KMOCRManager: CPDFConverterDelegate {
     func convertScanFile(document: CPDFDocument, model: KMOCRModel, progress: @escaping KMOCRManagerOCRProgress, complete: @escaping KMOCRManagerOCRComplete) {
         self.OCRComplete = complete
         self.progress = progress
+        self.model = model
         
         //计算需要处理的页面
         let pages:[Int] = self.fetchPageIndex(document: document, model: model)
@@ -206,30 +286,9 @@ extension KMOCRManager {
         self.document = document
         self.pageIndexs = index
         
-        let tempDirectory = NSTemporaryDirectory()
-        let outputPath = (tempDirectory as NSString).appendingPathComponent("com.compdfkit.conversion")
-        let finalPath = (outputPath as NSString).appendingPathComponent("/Searchable")
-
-        let fileManager = FileManager.default
-
-        // 检查文件是否存在并删除
-        if fileManager.fileExists(atPath: finalPath) {
-            do {
-                try fileManager.removeItem(atPath: finalPath)
-            } catch {
-                print("Failed to remove file: \(error)")
-            }
-        }
-
-        // 创建目录
-        do {
-            try fileManager.createDirectory(atPath: finalPath, withIntermediateDirectories: true, attributes: nil)
-        } catch {
-            print("Failed to create directory: \(error)")
-        }
-        
-        self.outputPath = finalPath.deletingPathExtension + "/temp_Searchable.pdf";
-        let inputPath = finalPath.deletingPathExtension + "/temp_InputSearchable.pdf";
+        let foler = self.fetchOutputFolderPath()
+        self.outputPath = foler + "/temp_Searchable.pdf";
+        let inputPath = foler + "/temp_InputSearchable.pdf";
         
         let isSuccess = document.write(toFile: inputPath)
         if isSuccess {
@@ -247,71 +306,27 @@ extension KMOCRManager {
             
             self.converter?.convert(toFilePath: self.outputPath, pageIndexs: indexs, options:txtOptions)
         } else {
-            let error = NSError(domain: "com.example.MyApp", code: 404, userInfo: [NSLocalizedDescriptionKey: "文件写入失败"])
-            OCRComplete?(nil, error)
+            self.fail()
         }
     }
-}
-
-//MARK: - KMGOCRManagerDelegate
-extension KMOCRManager: KMGOCRManagerDelegate {
-    func gocrManagerDidFinishOCR(_ manager: KMGOCRManager!) {
-//        self.batchesOCR()
-//        KMGOCRManager.default().createPDFFile(savePDFPath, imagePaths: self.pageImages, results: resultArrays, scale: KMImageScale)
-    }
-    
-    func gocrManager(_ manager: KMGOCRManager!, didCancelOCRImageAt index: Int) {
-        
-    }
-    
-    func gocrManager(_ manager: KMGOCRManager!, didStartOCRImageAt index: Int) {
-//        DispatchQueue.main.async {
-//            self.pageRangeBox.isEnabled = false
-//            self.languageButton.isEnabled = false
-//            self.planComboBox.isEnabled = false
-//        }
-    }
     
-    func gocrManager(_ manager: KMGOCRManager!, didFinishOCRImageAt index: Int, results: [KMGOCRResult]!) {
-        let pageIndex = self.pageIndexs[index]
-        self.dealWithResults(results, OCRImageAtIndex: pageIndex)
-    }
     
-    func gocrManager(_ manager: KMGOCRManager!, didFailureOCRImageAt index: Int, error: Error!) {
-//        let pagenum = self.pageIndexs?[index] as! NSNumber
-//        if (error != nil) {
-//            self.errorOCRArrays?.append(pagenum)
-//        }
-//        self.dealWithResults([], OCRImageAtIndex: index)
-    }
-    
-    
-    
-    
-    
-}
-
-//MARK: CPDFConverterDelegate
-extension KMOCRManager: CPDFConverterDelegate {
+    //MARK: CPDFConverterDelegate
     func converter(_ converter: CPDFConverter!, didEndConvert error: Error!) {
         print("结束转档")
         
         DispatchQueue.main.async { [weak self] in
-            var tempDocument: CPDFDocument = CPDFDocument(url: NSURL.fileURL(withPath: self?.outputPath ?? ""));
             if self?.saveAsPDFFilePath.count == 0 {
-                for i in 0..<(self?.pageIndexs.count ?? 0) {
-                    let index = self?.pageIndexs[i]
-                    let tempPage = tempDocument.page(at: UInt(i))
-                    self?.document?.removePage(at: UInt(index ?? 0))
-                    self?.document?.insertPageObject(tempPage, at: UInt(index ?? 0))
-                }
-                
-                self?.OCRComplete?(self?.document, error)
+                guard let document = self?.document else {
+                    self?.fail()
+                    return }
+                self?.insertPDF(currentDocument: document, pageIndexs: self?.pageIndexs ?? [], outputPth: self?.outputPath ?? "")
             } else {
+                var tempDocument: CPDFDocument = CPDFDocument(url: NSURL.fileURL(withPath: self?.outputPath ?? ""));
                 tempDocument.write(toFile: self?.saveAsPDFFilePath)
                 self?.saveAsPDFFilePath = ""
                 NSWorkspace.shared.activateFileViewerSelecting([URL(fileURLWithPath: self?.saveAsPDFFilePath ?? "")])
-                self?.OCRComplete?(tempDocument, error)
+                self?.success(document: tempDocument)
 //                if (openDocument) {
 //                    NSDocumentController.shared.openDocument(withContentsOf: fileURL, display: true) { document, result, error in }
 //                } else {
@@ -332,7 +347,20 @@ extension KMOCRManager: CPDFConverterDelegate {
     }
 }
 
+//MARK: complete
 extension KMOCRManager {
+    func insertPDF(currentDocument: CPDFDocument, pageIndexs: [Int], outputPth: String) {
+        let tempDocument: CPDFDocument = CPDFDocument(url: NSURL.fileURL(withPath: outputPth));
+        for i in 0..<(pageIndexs.count) {
+            let index = pageIndexs[i]
+            let tempPage = tempDocument.page(at: UInt(i))
+            currentDocument.removePage(at: UInt(index))
+            currentDocument.insertPageObject(tempPage, at: UInt(index))
+        }
+        
+        self.success(document: currentDocument)
+    }
+    
     func moveFileAndRename(from sourcePath: String, to destinationDirectory: String, with newFileName: String) {
         let fileManager = FileManager.default
         
@@ -403,6 +431,31 @@ extension KMOCRManager {
         
         return path!
     }
+    
+    func fetchOutputFolderPath() -> String {
+        let tempDirectory = NSTemporaryDirectory()
+        let outputPath = (tempDirectory as NSString).appendingPathComponent("com.compdfkit.conversion")
+        let finalPath = (outputPath as NSString).appendingPathComponent("/Searchable")
+
+        let fileManager = FileManager.default
+
+        // 检查文件是否存在并删除
+        if fileManager.fileExists(atPath: finalPath) {
+            do {
+                try fileManager.removeItem(atPath: finalPath)
+            } catch {
+                print("Failed to remove file: \(error)")
+            }
+        }
+
+        // 创建目录
+        do {
+            try fileManager.createDirectory(atPath: finalPath, withIntermediateDirectories: true, attributes: nil)
+        } catch {
+            print("Failed to create directory: \(error)")
+        }
+        return finalPath
+    }
 }
 
 extension KMOCRManager {
@@ -410,4 +463,8 @@ extension KMOCRManager {
         let error = NSError(domain: "com.example.MyApp", code: 404, userInfo: [NSLocalizedDescriptionKey: "转换失败"])
         OCRComplete?(nil, error)
     }
+    
+    func success(document: CPDFDocument) {
+        self.OCRComplete?(document, nil)
+    }
 }