Browse Source

【转档工具】- OCR功能

jiajie 1 year ago
parent
commit
87ce60950c

+ 100 - 0
PDF Office/PDF Master.xcodeproj/project.pbxproj

@@ -2304,6 +2304,12 @@
 		BB276A522B0376B400AB5578 /* KMBatchOperateRemoveHeaderFooterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB65A07F2AF8FE7A003A27A0 /* KMBatchOperateRemoveHeaderFooterViewController.swift */; };
 		BB276A532B03776000AB5578 /* KMConvertOperationQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBAC26A02AFDF54F00563A08 /* KMConvertOperationQueue.swift */; };
 		BB276A542B03776200AB5578 /* KMConvertOperationQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBAC26A02AFDF54F00563A08 /* KMConvertOperationQueue.swift */; };
+		BB276A582B038D1100AB5578 /* KMOCRPDFWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB276A572B038D1100AB5578 /* KMOCRPDFWindowController.swift */; };
+		BB276A592B038D1100AB5578 /* KMOCRPDFWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB276A572B038D1100AB5578 /* KMOCRPDFWindowController.swift */; };
+		BB276A5A2B038D1100AB5578 /* KMOCRPDFWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB276A572B038D1100AB5578 /* KMOCRPDFWindowController.swift */; };
+		BB276A5C2B038D3A00AB5578 /* KMOCRPDFWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = BB276A5B2B038D3A00AB5578 /* KMOCRPDFWindowController.xib */; };
+		BB276A5D2B038D3A00AB5578 /* KMOCRPDFWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = BB276A5B2B038D3A00AB5578 /* KMOCRPDFWindowController.xib */; };
+		BB276A5E2B038D3A00AB5578 /* KMOCRPDFWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = BB276A5B2B038D3A00AB5578 /* KMOCRPDFWindowController.xib */; };
 		BB2C6AC928F4085200478A33 /* CPDFListView.m in Sources */ = {isa = PBXBuildFile; fileRef = BB2C6AC828F4085200478A33 /* CPDFListView.m */; };
 		BB2C6ACE28F41B9F00478A33 /* CPDFListView.m in Sources */ = {isa = PBXBuildFile; fileRef = BB2C6AC828F4085200478A33 /* CPDFListView.m */; };
 		BB2C6ACF28F41BA000478A33 /* CPDFListView.m in Sources */ = {isa = PBXBuildFile; fileRef = BB2C6AC828F4085200478A33 /* CPDFListView.m */; };
@@ -2496,6 +2502,21 @@
 		BB49ED21293F527700C82CA2 /* KMConvertExcelSettingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB49ED20293F527700C82CA2 /* KMConvertExcelSettingView.swift */; };
 		BB49ED22293F527700C82CA2 /* KMConvertExcelSettingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB49ED20293F527700C82CA2 /* KMConvertExcelSettingView.swift */; };
 		BB49ED23293F527700C82CA2 /* KMConvertExcelSettingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB49ED20293F527700C82CA2 /* KMConvertExcelSettingView.swift */; };
+		BB4A948D2B04726A00940F8B /* KMOCTool.m in Sources */ = {isa = PBXBuildFile; fileRef = BB4A948C2B04726A00940F8B /* KMOCTool.m */; };
+		BB4A948E2B04726A00940F8B /* KMOCTool.m in Sources */ = {isa = PBXBuildFile; fileRef = BB4A948C2B04726A00940F8B /* KMOCTool.m */; };
+		BB4A948F2B04726A00940F8B /* KMOCTool.m in Sources */ = {isa = PBXBuildFile; fileRef = BB4A948C2B04726A00940F8B /* KMOCTool.m */; };
+		BB4A94912B04926700940F8B /* KMGOCROperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB4A94902B04926700940F8B /* KMGOCROperation.swift */; };
+		BB4A94922B04926700940F8B /* KMGOCROperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB4A94902B04926700940F8B /* KMGOCROperation.swift */; };
+		BB4A94932B04926700940F8B /* KMGOCROperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB4A94902B04926700940F8B /* KMGOCROperation.swift */; };
+		BB4A949B2B04B77F00940F8B /* KMOCROperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB4A949A2B04B77F00940F8B /* KMOCROperation.swift */; };
+		BB4A949C2B04B77F00940F8B /* KMOCROperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB4A949A2B04B77F00940F8B /* KMOCROperation.swift */; };
+		BB4A949D2B04B77F00940F8B /* KMOCROperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB4A949A2B04B77F00940F8B /* KMOCROperation.swift */; };
+		BB4A94A02B04D8EC00940F8B /* KMGOCRManager.m in Sources */ = {isa = PBXBuildFile; fileRef = BB4A949F2B04D8EC00940F8B /* KMGOCRManager.m */; };
+		BB4A94A12B04D8EC00940F8B /* KMGOCRManager.m in Sources */ = {isa = PBXBuildFile; fileRef = BB4A949F2B04D8EC00940F8B /* KMGOCRManager.m */; };
+		BB4A94A22B04D8EC00940F8B /* KMGOCRManager.m in Sources */ = {isa = PBXBuildFile; fileRef = BB4A949F2B04D8EC00940F8B /* KMGOCRManager.m */; };
+		BB4A94A42B04DA0C00940F8B /* KMGOCRManagerNew.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB4A94A32B04DA0C00940F8B /* KMGOCRManagerNew.swift */; };
+		BB4A94A52B04DA0C00940F8B /* KMGOCRManagerNew.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB4A94A32B04DA0C00940F8B /* KMGOCRManagerNew.swift */; };
+		BB4A94A62B04DA0C00940F8B /* KMGOCRManagerNew.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB4A94A32B04DA0C00940F8B /* KMGOCRManagerNew.swift */; };
 		BB4BD9CC2909026500A66A65 /* KMRightSideViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB4BD9CA2909026500A66A65 /* KMRightSideViewController.swift */; };
 		BB4BD9CD2909026500A66A65 /* KMRightSideViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB4BD9CA2909026500A66A65 /* KMRightSideViewController.swift */; };
 		BB4BD9CE2909026500A66A65 /* KMRightSideViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB4BD9CA2909026500A66A65 /* KMRightSideViewController.swift */; };
@@ -4658,6 +4679,8 @@
 		BB1EC7FD2967B26700EC0BC3 /* KMPDFEditViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = KMPDFEditViewController.xib; sourceTree = "<group>"; };
 		BB24D4A52977BE6700041659 /* KMRedactConfirmWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KMRedactConfirmWindowController.swift; sourceTree = "<group>"; };
 		BB24D4A62977BE6700041659 /* KMRedactConfirmWindowController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = KMRedactConfirmWindowController.xib; sourceTree = "<group>"; };
+		BB276A572B038D1100AB5578 /* KMOCRPDFWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KMOCRPDFWindowController.swift; sourceTree = "<group>"; };
+		BB276A5B2B038D3A00AB5578 /* KMOCRPDFWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = KMOCRPDFWindowController.xib; sourceTree = "<group>"; };
 		BB2C6A7C28F26CEF00478A33 /* PDF_Master-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "PDF_Master-Bridging-Header.h"; sourceTree = "<group>"; };
 		BB2C6A7D28F26CEF00478A33 /* PDF_Master Pro-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "PDF_Master Pro-Bridging-Header.h"; sourceTree = "<group>"; };
 		BB2C6A7E28F26CF000478A33 /* PDF_Master DMG-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "PDF_Master DMG-Bridging-Header.h"; sourceTree = "<group>"; };
@@ -4726,6 +4749,13 @@
 		BB49ED18293F4D4E00C82CA2 /* KMConvertCSVSettingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KMConvertCSVSettingView.swift; sourceTree = "<group>"; };
 		BB49ED1C293F4FB200C82CA2 /* KMConvertPPTsSettingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KMConvertPPTsSettingView.swift; sourceTree = "<group>"; };
 		BB49ED20293F527700C82CA2 /* KMConvertExcelSettingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KMConvertExcelSettingView.swift; sourceTree = "<group>"; };
+		BB4A948B2B04726A00940F8B /* KMOCTool.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KMOCTool.h; sourceTree = "<group>"; };
+		BB4A948C2B04726A00940F8B /* KMOCTool.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KMOCTool.m; sourceTree = "<group>"; };
+		BB4A94902B04926700940F8B /* KMGOCROperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KMGOCROperation.swift; sourceTree = "<group>"; };
+		BB4A949A2B04B77F00940F8B /* KMOCROperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KMOCROperation.swift; sourceTree = "<group>"; };
+		BB4A949E2B04D8EA00940F8B /* KMGOCRManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KMGOCRManager.h; sourceTree = "<group>"; };
+		BB4A949F2B04D8EC00940F8B /* KMGOCRManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KMGOCRManager.m; sourceTree = "<group>"; };
+		BB4A94A32B04DA0C00940F8B /* KMGOCRManagerNew.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KMGOCRManagerNew.swift; sourceTree = "<group>"; };
 		BB4BD9CA2909026500A66A65 /* KMRightSideViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KMRightSideViewController.swift; sourceTree = "<group>"; };
 		BB4BD9CB2909026500A66A65 /* KMRightSideViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = KMRightSideViewController.xib; sourceTree = "<group>"; };
 		BB4DD042299B291A00E80DF6 /* KMCloudNoNetworkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KMCloudNoNetworkView.swift; sourceTree = "<group>"; };
@@ -5782,6 +5812,7 @@
 				BB853C5F2AF87344009C20C1 /* RemoveWatermark */,
 				ADD1B6FF2946C87400C3FFF7 /* Tools */,
 				ADD1B6A82941E94D00C3FFF7 /* Print */,
+				BB276A562B038CF500AB5578 /* OCRNew */,
 				BB3EAEA9293E3BA200D92407 /* Convert */,
 				BB6DD80829347EFC001F0544 /* Secure */,
 				BBFE6E7B2930EB8F00142C01 /* Compress */,
@@ -7948,6 +7979,15 @@
 			path = OCPart;
 			sourceTree = "<group>";
 		};
+		BB276A562B038CF500AB5578 /* OCRNew */ = {
+			isa = PBXGroup;
+			children = (
+				BB4A94862B04565200940F8B /* Model */,
+				BB4A94842B0455AC00940F8B /* Controller */,
+			);
+			path = OCRNew;
+			sourceTree = "<group>";
+		};
 		BB2C6A7128F2652500478A33 /* Class */ = {
 			isa = PBXGroup;
 			children = (
@@ -8200,6 +8240,7 @@
 		BB3EAEA9293E3BA200D92407 /* Convert */ = {
 			isa = PBXGroup;
 			children = (
+				BB4A94852B0455CF00940F8B /* New Group */,
 				BB1A91682AFB7833005E5FD8 /* NewController */,
 				BB88E4522940458F002B3655 /* Common */,
 				BB88E43E2940342B002B3655 /* Library */,
@@ -8273,6 +8314,41 @@
 			path = View;
 			sourceTree = "<group>";
 		};
+		BB4A94842B0455AC00940F8B /* Controller */ = {
+			isa = PBXGroup;
+			children = (
+				BB276A572B038D1100AB5578 /* KMOCRPDFWindowController.swift */,
+				BB276A5B2B038D3A00AB5578 /* KMOCRPDFWindowController.xib */,
+			);
+			path = Controller;
+			sourceTree = "<group>";
+		};
+		BB4A94852B0455CF00940F8B /* New Group */ = {
+			isa = PBXGroup;
+			children = (
+			);
+			path = "New Group";
+			sourceTree = "<group>";
+		};
+		BB4A94862B04565200940F8B /* Model */ = {
+			isa = PBXGroup;
+			children = (
+				BB4A94A32B04DA0C00940F8B /* KMGOCRManagerNew.swift */,
+				BB4A94902B04926700940F8B /* KMGOCROperation.swift */,
+				BB4A949A2B04B77F00940F8B /* KMOCROperation.swift */,
+			);
+			path = Model;
+			sourceTree = "<group>";
+		};
+		BB4A94942B04B62800940F8B /* OCR */ = {
+			isa = PBXGroup;
+			children = (
+				BB4A949E2B04D8EA00940F8B /* KMGOCRManager.h */,
+				BB4A949F2B04D8EC00940F8B /* KMGOCRManager.m */,
+			);
+			path = OCR;
+			sourceTree = "<group>";
+		};
 		BB4DD040299B28E900E80DF6 /* CloudDocuments */ = {
 			isa = PBXGroup;
 			children = (
@@ -8365,6 +8441,7 @@
 		BB5F8A0329BB04EF00365ADB /* OC */ = {
 			isa = PBXGroup;
 			children = (
+				BB4A94942B04B62800940F8B /* OCR */,
 				BBC4F9FD2AEBC62D0098A1A8 /* Batch */,
 				BBA93D3029BF0ECD0044E0DD /* KeyChain */,
 				BB9695A529BDA3A800FD68D3 /* Property */,
@@ -9136,6 +9213,8 @@
 		BBC4F9FD2AEBC62D0098A1A8 /* Batch */ = {
 			isa = PBXGroup;
 			children = (
+				BB4A948B2B04726A00940F8B /* KMOCTool.h */,
+				BB4A948C2B04726A00940F8B /* KMOCTool.m */,
 			);
 			path = Batch;
 			sourceTree = "<group>";
@@ -10203,6 +10282,7 @@
 				BB96A0B42AFCD56B00559E24 /* KMToolCompareWindowController.xib in Resources */,
 				9F0CB49A29683E1000007028 /* KMPropertiesPanelTextSubVC.xib in Resources */,
 				BBB29BCE2AEA190D005F1B6B /* KMToolbarCustomViewController.xib in Resources */,
+				BB276A5C2B038D3A00AB5578 /* KMOCRPDFWindowController.xib in Resources */,
 				BBC70EA92AEA6EF800AC1585 /* KMToolbarCustomWindowController.xib in Resources */,
 				89DB5DAA291B8DE70029624F /* KMOutlineEditViewController.xib in Resources */,
 				BB003029298CF7EC002DD1A0 /* KMPreferenceDisplayController.xib in Resources */,
@@ -10567,6 +10647,7 @@
 				BBFE6E712930D9C600142C01 /* KMMergeSettingWindowController.xib in Resources */,
 				BB6347B92AF224E200F5438E /* KMConvertCollectionViewHeader.xib in Resources */,
 				ADD1B6FD2946C0D400C3FFF7 /* KMPrintChoosePageSizePamphletView.xib in Resources */,
+				BB276A5D2B038D3A00AB5578 /* KMOCRPDFWindowController.xib in Resources */,
 				ADF9ED3429A850D200C4A943 /* KMAccountInfoView.xib in Resources */,
 				ADE787AF2AA5AA50002EC85A /* KMAccountExceptionView.xib in Resources */,
 				89316826296D73CC0073EA59 /* KMSignatureAnnotationViewController.xib in Resources */,
@@ -10741,6 +10822,7 @@
 				AD1D48272AFB6BCB007AC1F0 /* KMMergeView.xib in Resources */,
 				ADB5E5172A371131007110A8 /* KMSubscribeWaterMarkWindowController.xib in Resources */,
 				ADB2D6EB294740F30029D2B3 /* KMPrintPaperSetWindowController.xib in Resources */,
+				BB276A5E2B038D3A00AB5578 /* KMOCRPDFWindowController.xib in Resources */,
 				AD0FA50029A8DD8700EDEB50 /* KMRegisterSuccessView.xib in Resources */,
 				BBFDFAAC2AF389B700E08AA2 /* PasswordWindowController.xib in Resources */,
 				894A00F72976314C0070ED83 /* KMPDFViewPanelSetViewController.xib in Resources */,
@@ -11225,6 +11307,7 @@
 				9F0CB5092986563300007028 /* KMDesignToken+BorderBottom.swift in Sources */,
 				9F1FE4E429406E4700E952CA /* GTMNSColor+Luminance.m in Sources */,
 				BBA5429C29F13A140041BAD0 /* KMMemorandumPattern.swift in Sources */,
+				BB276A582B038D1100AB5578 /* KMOCRPDFWindowController.swift in Sources */,
 				BB46CF4C2AFBB34900281EDF /* AutoSaveManager.swift in Sources */,
 				8942F7BF2923670F00389627 /* KMBOTAManagerClass.swift in Sources */,
 				89E4E7352964148E002DBA6F /* KMAnnotationPropertiesViewController.m in Sources */,
@@ -11241,6 +11324,7 @@
 				BB146FBA299DC0D100784A6A /* GTLRBase64.m in Sources */,
 				BB981E522AD4F638001988CA /* KMCoverButton.swift in Sources */,
 				9F53D5472AD664C300CCF9D8 /* CPDFListHoverAnnotationViewController.swift in Sources */,
+				BB4A949B2B04B77F00940F8B /* KMOCROperation.swift in Sources */,
 				BBF38A5E294F42FF0086D025 /* KMWatermarkAdjectiveStepper.swift in Sources */,
 				BB1EC7FE2967B26700EC0BC3 /* KMPDFEditViewController.swift in Sources */,
 				BB5F8A1F29BB15AD00365ADB /* KMEmailSubWindowController.m in Sources */,
@@ -11323,6 +11407,7 @@
 				9F3D818E29A22AD90087B5AD /* Date+KMExtensions.swift in Sources */,
 				89E4E76129656A88002DBA6F /* KMAnnotationStampViewController.m in Sources */,
 				89E4E755296427E5002DBA6F /* NSImage_SKExtensions.m in Sources */,
+				BB4A94A02B04D8EC00940F8B /* KMGOCRManager.m in Sources */,
 				BB6DD821293497B6001F0544 /* KMSecureEncryptModel.swift in Sources */,
 				ADBC373229CA95AA00D93208 /* KMComparativeModel.swift in Sources */,
 				9FCFEC802AD0E74C00EAD2CB /* KMPopMenuButtonCell.swift in Sources */,
@@ -11364,6 +11449,7 @@
 				BB8F4560295AA39F0037EA22 /* KMHeaderFooterModel.swift in Sources */,
 				BBB9B325299A5D6D004F3235 /* GTMKeychain_macOS.m in Sources */,
 				BB146FAE299DC0D100784A6A /* GTLRService.m in Sources */,
+				BB4A948D2B04726A00940F8B /* KMOCTool.m in Sources */,
 				BB0A551D2A30793F00B6E84B /* KMDesignTextField.swift in Sources */,
 				BBD1F798296FF78C00343885 /* KMPageEditSettingBaseModel.swift in Sources */,
 				9FAAA33E290FECA70046FFCE /* NSImage+QuickLook.swift in Sources */,
@@ -11454,6 +11540,7 @@
 				BBB9B32E299A5D6D004F3235 /* GTMAppAuthFetcherAuthorization.m in Sources */,
 				BB3198162AC55E6D00107371 /* CPDFDocument+KMExtension.swift in Sources */,
 				ADE86A802B02220700414DFA /* KMSecurityView.swift in Sources */,
+				BB4A94912B04926700940F8B /* KMGOCROperation.swift in Sources */,
 				BBEFD0242AFA065F003FABD8 /* KMBatchAddHeaderFooterOperation.swift in Sources */,
 				894A00F22976314C0070ED83 /* KMPDFViewPanelSetViewController.swift in Sources */,
 				BB135C2A29B6CD9A00FD5965 /* KMTools.swift in Sources */,
@@ -11810,6 +11897,7 @@
 				BB2F61502966A91D001CB369 /* KMBackgroundPropertyHomeController.swift in Sources */,
 				BB10FAEE2AFE233C00F18D65 /* KMLineInspector.swift in Sources */,
 				BBBB6CD22AD14A5F0035AA66 /* CPDFChoiceWidgetAnnotation+PDFListView.swift in Sources */,
+				BB4A94A42B04DA0C00940F8B /* KMGOCRManagerNew.swift in Sources */,
 				9F1FE3DE293EE51F00E952CA /* KMMainDocument.swift in Sources */,
 				9F0201962A1F352100C9B673 /* KMAITranslationConfirmWindowController.swift in Sources */,
 				AD85D1A42AF09864000F4D28 /* KMHomeQuickToolsWindowController.swift in Sources */,
@@ -11910,6 +11998,7 @@
 				8942F7C02923670F00389627 /* KMBOTAManagerClass.swift in Sources */,
 				ADAFDA772AEB5FCD00F084BC /* KMHomeHistoryCollectionItem.swift in Sources */,
 				BB897242294C028A0045787C /* KMWatermarkAdjectiveSegementControl.swift in Sources */,
+				BB4A949C2B04B77F00940F8B /* KMOCROperation.swift in Sources */,
 				9F1FE4B529406E4700E952CA /* CTBrowser.m in Sources */,
 				BB6719E62AD28527003D44D5 /* CPDFLineAnnotation+PDFListView.swift in Sources */,
 				9FDD0FAB29534FCC000C4DAD /* KMAliasLight.swift in Sources */,
@@ -12220,6 +12309,7 @@
 				9FAAA33F290FECA70046FFCE /* NSImage+QuickLook.swift in Sources */,
 				ADE3C1E529A5ABC200793B13 /* KMLoginWindowController.swift in Sources */,
 				9F0CB4D62986551600007028 /* KMDesignToken+Spacing.swift in Sources */,
+				BB4A948E2B04726A00940F8B /* KMOCTool.m in Sources */,
 				89E4E70F2963D62C002DBA6F /* KMAnnotationFontWindowController.m in Sources */,
 				F3732324292DFFFE0013862C /* CPDFListView+Extension.m in Sources */,
 				ADB5E5132A371131007110A8 /* KMSubscribeWaterMarkWindowController.swift in Sources */,
@@ -12293,6 +12383,7 @@
 				ADB2D6EE2947415E0029D2B3 /* KMPrintPaperSetView.swift in Sources */,
 				BB147006299DC0D100784A6A /* OIDFieldMapping.m in Sources */,
 				BB003016298CA446002DD1A0 /* KMPreferenceGeneralController.swift in Sources */,
+				BB4A94A12B04D8EC00940F8B /* KMGOCRManager.m in Sources */,
 				BBB9B32F299A5D6D004F3235 /* GTMAppAuthFetcherAuthorization.m in Sources */,
 				BB3EAEAE293E3D6000D92407 /* KMConvertBaseWindowController.swift in Sources */,
 				BB1BFF5E2AE9F1FF003EB179 /* KMBatchOperateBaseWindowController.swift in Sources */,
@@ -12303,6 +12394,7 @@
 				BB1331552AD7A6A1008F6791 /* KMFileAttribute.swift in Sources */,
 				BBD1F79D296FF7A600343885 /* KMPageEditSplitSettingModel.swift in Sources */,
 				BB1BFF8F2AEA547B003EB179 /* NSButton+CustomAppearance.swift in Sources */,
+				BB276A592B038D1100AB5578 /* KMOCRPDFWindowController.swift in Sources */,
 				ADDEEA632AD3A6E700EF675D /* KMPDFSignatureTextView.swift in Sources */,
 				BBC4F9F72AEB69940098A1A8 /* NSArray+Extension.swift in Sources */,
 				9FCFEC892AD0EF9900EAD2CB /* KMPopMenuButton.swift in Sources */,
@@ -12391,6 +12483,7 @@
 				9F72D2092994BDAF00DCACF1 /* KMNotificationVC.swift in Sources */,
 				BB4DD04C299B296500E80DF6 /* KMCloudPathControl.swift in Sources */,
 				AD867F9129D9554F00F00440 /* KMBOTAOutlineItem.swift in Sources */,
+				BB4A94A52B04DA0C00940F8B /* KMGOCRManagerNew.swift in Sources */,
 				BB0A823329C00400002C5C1B /* KMCommonEnum.swift in Sources */,
 				BB3198132AC5142900107371 /* NSMenu+KMExtension.swift in Sources */,
 				ADB2D6FA294882AE0029D2B3 /* KMTextFieldStepperView.swift in Sources */,
@@ -12449,6 +12542,7 @@
 				BB65A0482AF893DD003A27A0 /* KMGeneralPreferences.swift in Sources */,
 				BB146FB8299DC0D100784A6A /* GTLRRuntimeCommon.m in Sources */,
 				BB8B173D2907CDD9001C5EA5 /* NibLoadable.swift in Sources */,
+				BB4A94922B04926700940F8B /* KMGOCROperation.swift in Sources */,
 				9F0CB51A2986568000007028 /* KMDesignToken+BorderRadiusTopRight.swift in Sources */,
 				BB6DD80D29347F77001F0544 /* KMSecureEncryptWindowController.swift in Sources */,
 				9F0CB4682967E5CB00007028 /* KMPropertiesPanelSubViewController.swift in Sources */,
@@ -12797,6 +12891,7 @@
 				9FCFEC8A2AD0EF9900EAD2CB /* KMPopMenuButton.swift in Sources */,
 				BB6DD80E29347F77001F0544 /* KMSecureEncryptWindowController.swift in Sources */,
 				AD53B70029AC5FCD00D61E81 /* KMLightMemberToken.swift in Sources */,
+				BB4A94A22B04D8EC00940F8B /* KMGOCRManager.m in Sources */,
 				9F1FE4A129406E4700E952CA /* HoverButton.m in Sources */,
 				BBEFD0262AFA065F003FABD8 /* KMBatchAddHeaderFooterOperation.swift in Sources */,
 				BB8F4580295B00130037EA22 /* KMWatermarkAdjectiveTools.swift in Sources */,
@@ -12861,6 +12956,7 @@
 				BBD7FE062A1323F000F96075 /* KMCustomTableRowView.swift in Sources */,
 				9F0CB4C6298625F400007028 /* NSColor+KMExtensions.swift in Sources */,
 				BBC745F4295F0DD00072C2ED /* KMCropSettingPageSizeView.swift in Sources */,
+				BB4A949D2B04B77F00940F8B /* KMOCROperation.swift in Sources */,
 				BBEFD01E2AF9E5AA003FABD8 /* KMBatchOperateAddHeaderFooterViewController.swift in Sources */,
 				9F1FE50729406E4700E952CA /* CTTabStripDragController.m in Sources */,
 				ADE86A942B0226AD00414DFA /* KMRemovePasswordView.swift in Sources */,
@@ -13095,6 +13191,7 @@
 				9FDD0F822952FC36000C4DAD /* KMDesignToken.swift in Sources */,
 				BB1BFF6B2AEA02F8003EB179 /* KMBatchOperateSplitViewController.swift in Sources */,
 				BB7648EE29ECEEF400931039 /* KMAppearance.swift in Sources */,
+				BB4A94932B04926700940F8B /* KMGOCROperation.swift in Sources */,
 				9FDD0F8E2952FCE0000C4DAD /* KMGlobalParser.swift in Sources */,
 				BBC348352955A118008D2CD1 /* KMCreateBackgroundController.swift in Sources */,
 				89E4E757296427E5002DBA6F /* NSImage_SKExtensions.m in Sources */,
@@ -13121,6 +13218,7 @@
 				9F1F82EC2935D02E0092C4B4 /* KMComboBox.swift in Sources */,
 				8997012128F41AB8009AF911 /* KMLeftSideViewController.swift in Sources */,
 				BB90E4F42AF37F9F00B04B9F /* KMCustomViewButton.swift in Sources */,
+				BB4A948F2B04726A00940F8B /* KMOCTool.m in Sources */,
 				F35B484D29A4903300756255 /* NSPointerArray+PDFListView.m in Sources */,
 				BBC70EB62AEA847500AC1585 /* KMToolbarCustomViewController.swift in Sources */,
 				89752E062939DB42003FF08E /* KMToolbarViewController.swift in Sources */,
@@ -13312,6 +13410,7 @@
 				BB6B4C0E292F62B20071CA06 /* KMPDFThumbnialPageView.swift in Sources */,
 				9FCFEC922AD11E5F00EAD2CB /* KMSplitView.swift in Sources */,
 				ADD1B6B929420B2300C3FFF7 /* KMPrintChooseView.swift in Sources */,
+				BB276A5A2B038D1100AB5578 /* KMOCRPDFWindowController.swift in Sources */,
 				AD9527D92952ED970039D2BC /* KMPrintPresenter_C.swift in Sources */,
 				9F0CB4E32986556400007028 /* KMDesignToken+PaddingTop.swift in Sources */,
 				BB2EDF6F296ECE17003BCF58 /* KMPageEditInsertDirectionItemView.swift in Sources */,
@@ -13357,6 +13456,7 @@
 				AD867F9E29D9853200F00440 /* KMBOTAOutlineRowView.swift in Sources */,
 				BBC3482B29559B22008D2CD1 /* KMBackgroundListCell.swift in Sources */,
 				F34BF93729530708002C25A2 /* NSImage+PDFListView.m in Sources */,
+				BB4A94A62B04DA0C00940F8B /* KMGOCRManagerNew.swift in Sources */,
 				9FAAA32C290BD01D0046FFCE /* KMHomeHistoryFileViewController.swift in Sources */,
 				BB276A522B0376B400AB5578 /* KMBatchOperateRemoveHeaderFooterViewController.swift in Sources */,
 				BB8F4551295A9FA50037EA22 /* KMCreateHeaderFooterController.swift in Sources */,

+ 1 - 1
PDF Office/PDF Master/Class/Common/LineInspector/KMLineInspector.swift

@@ -65,7 +65,7 @@ class KMLineInspector: NSWindowController {
             return self._lineWidth
         }
         set {
-            if (fabs(self.lineWidth - newValue) > 0.00001) {
+            if (abs(self.lineWidth - newValue) > 0.00001) {
                 self._lineWidth = newValue
                 self._notifyChangeAction(.lineWidth)
             }

+ 17 - 0
PDF Office/PDF Master/Class/Common/OC/Batch/KMOCTool.h

@@ -0,0 +1,17 @@
+//
+//  KMOCTool.h
+//  PDF Master
+//
+//  Created by liujiajie on 2023/11/15.
+//
+
+#import <Foundation/Foundation.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface KMOCTool : NSObject
+
++(void)createPDFFile:(NSString *)filePath imagePaths:(NSArray *)paths results:(NSArray *)resultsArray scale:(CGFloat)scale;
+@end
+
+NS_ASSUME_NONNULL_END

+ 114 - 0
PDF Office/PDF Master/Class/Common/OC/Batch/KMOCTool.m

@@ -0,0 +1,114 @@
+//
+//  KMOCTool.m
+//  PDF Master
+//
+//  Created by liujiajie on 2023/11/15.
+//
+
+#import "KMOCTool.h"
+#import <Foundation/Foundation.h>
+#import <CoreGraphics/CoreGraphics.h>
+#import <Cocoa/Cocoa.h>
+#import <PDF_Master-Swift.h>
+
+@implementation KMOCTool
++ (void)createPDFFile:(NSString *)filePath imagePaths:(NSArray *)paths results:(NSArray *)resultsArray scale:(CGFloat)scale {
+    if (paths.count < 1) {
+        return;
+    }
+    
+    CFStringRef path = (__bridge CFStringRef)filePath;
+    CFURLRef url = CFURLCreateWithFileSystemPath(NULL, path, kCFURLPOSIXPathStyle, 0);
+    CFMutableDictionaryRef myDictionary = CFDictionaryCreateMutable(NULL,
+                                                                    0,
+                                                                    &kCFTypeDictionaryKeyCallBacks,
+                                                                    &kCFTypeDictionaryValueCallBacks);
+    CFDictionarySetValue(myDictionary, kCGPDFContextCreator, CFSTR("Kdan Mobile PDF Reader"));
+    
+    CGContextRef pdfContext = CGPDFContextCreateWithURL(url, &CGRectZero, myDictionary);
+    CGContextSetRGBFillColor(pdfContext, 1.0, 0.0, 0.0, 0.0);
+    CGContextSetTextDrawingMode(pdfContext, kCGTextFill);
+    
+    CFRelease(myDictionary);
+    CFRelease(url);
+    
+    for (int i=0; i<paths.count; i++) {
+        NSString *path = [paths objectAtIndex:i];
+        NSImage *image = [[NSImage alloc] initWithContentsOfFile:path];
+        CIImage *imageCIImage = [CIImage imageWithContentsOfURL:[NSURL fileURLWithPath:path]];
+        NSSize size = [imageCIImage extent].size;
+        CGRect pageRect = CGRectMake(0, 0, size.width/scale, size.height/scale);
+        
+        CFMutableDictionaryRef pageDictionary = CFDictionaryCreateMutable(NULL,
+                                                                          0,
+                                                                          &kCFTypeDictionaryKeyCallBacks,
+                                                                          &kCFTypeDictionaryValueCallBacks);
+        CFDataRef boxData = CFDataCreate(NULL,(const UInt8 *)&pageRect, sizeof (CGRect));
+        CFDictionarySetValue(pageDictionary, kCGPDFContextMediaBox, boxData);
+        CGPDFContextBeginPage (pdfContext, pageDictionary);
+
+        NSData *imageData = [NSData dataWithContentsOfFile:path];
+        CGImageSourceRef imageSource = CGImageSourceCreateWithData((CFDataRef)imageData, NULL);
+        CGImageRef imageRef = CGImageSourceCreateImageAtIndex(imageSource, 0, NULL);
+        CGContextSaveGState(pdfContext);
+        CGContextDrawImage(pdfContext, pageRect, imageRef);
+        CGContextRestoreGState(pdfContext);
+        CGImageRelease(imageRef);
+        
+        [NSGraphicsContext saveGraphicsState];
+        [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithCGContext:pdfContext flipped:NO]];
+        NSArray *results = nil;
+        if (i < resultsArray.count) {
+            results = resultsArray[i];
+            CGFloat newScale = scale;
+            if([KMGOCRManager defaultManager].OCRType == KMOCRType_Apple)
+                newScale = 1;
+            if (results.count == 1) {
+                KMGOCRResult *result = results[0];
+                NSRect bounds = NSMakeRect((result.textBounds.origin.x)/newScale,
+                                           pageRect.size.height-(result.textBounds.origin.y+result.textBounds.size.height)/newScale,
+                                           (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]};
+                [result.text drawInRect:bounds withAttributes:dic];
+            } else {
+                for (int i=1; i<results.count; i++) {
+                    KMGOCRResult *result = results[i];
+                    NSRect bounds = NSMakeRect((result.textBounds.origin.x)/newScale,
+                                               pageRect.size.height-(result.textBounds.origin.y+result.textBounds.size.height)/newScale,
+                                               (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]};
+                    [result.text drawInRect:bounds withAttributes:dic];
+                }
+            }
+        }
+        [NSGraphicsContext restoreGraphicsState];
+        
+        CGPDFContextEndPage (pdfContext);
+        
+        CFRelease(pageDictionary);
+        CFRelease(boxData);
+        image = nil;
+    }
+    
+    CGPDFContextClose(pdfContext);
+    CGContextRelease (pdfContext);
+}
+
+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];
+}
+
+
+@end

+ 87 - 0
PDF Office/PDF Master/Class/Common/OC/OCR/KMGOCRManager.h

@@ -0,0 +1,87 @@
+//
+//  KMGOCRManager.h
+//  
+//
+//
+//
+//
+
+#import <Foundation/Foundation.h>
+
+typedef NS_ENUM(NSUInteger, KMOCRType) {
+    KMOCRType_Google,
+    KMOCRType_Apple
+};
+
+extern NSString * KMGOCRLanguageCodeKey;
+extern NSString * KMGOCRLanguageStringKey;
+
+@interface KMGOCRResult : NSObject
+
+@property (nonatomic,retain) NSString *text;
+@property (nonatomic,retain) NSString *locale;
+@property (nonatomic,assign) CGRect    textBounds;
+
+@end
+
+@class KMGOCRManager;
+@protocol KMGOCRManagerDelegate <NSObject>
+@optional
+- (void)GOCRManagerDidStartOCR:(KMGOCRManager *)manager;
+- (void)GOCRManagerDidFinishOCR:(KMGOCRManager *)manager;
+
+- (void)GOCRManager:(KMGOCRManager *)manager didCancelOCRImageAtIndex:(NSInteger)index;
+
+- (void)GOCRManager:(KMGOCRManager *)manager didStartOCRImageAtIndex:(NSInteger)index;
+- (void)GOCRManager:(KMGOCRManager *)manager didFinishOCRImageAtIndex:(NSInteger)index results:(NSArray<KMGOCRResult *> *)results;
+- (void)CHOCRManager:(KMGOCRManager *)manager didFinishOCRImageAtIndex:(NSInteger)index results:(NSArray *)results;//17方案
+- (void)GOCRManager:(KMGOCRManager *)manager didFailureOCRImageAtIndex:(NSInteger)index error:(NSError *)error;
+
+@end
+
+@interface KMGOCRManager : NSObject
+
+@property (nonatomic,assign) id<KMGOCRManagerDelegate> delegate;
+
+@property (nonatomic,readonly) NSMutableArray *images;
+
+@property (nonatomic,assign) KMOCRType OCRType;
+
+@property (nonatomic,retain) NSMutableArray *selectedLanguages;
+
+/* isGOCR : GOCR=NO/OCR=YES */
+@property (nonatomic,assign) BOOL isOCR;
+
++ (KMGOCRManager *)defaultManager;
+
++ (NSArray *)languages;
+
+- (void)createPDFFile:(NSString *)filePath imagePaths:(NSArray *)paths results:(NSArray *)resultsArray scale:(CGFloat)scale;
+
+- (void)recognitionImages:(NSArray *)images withLanguages:(NSArray *)languages;
+//- (void)recognitionImages:(NSArray *)images withLanguages:(NSArray *)languages fileType:(NSString * _Nullable)fileType filePath:(NSURL *)filePath;
+/**
+   Google OCR 设置参数进行转档.
+   @param images 需要转档的NSImage 数组对象
+   @param languages 设置的识别s语言
+   @param fileType 设置转档的文件格式,可为空
+   @param filePath 设置保存文件的路径,可为空
+*/
+- (void)recognitionImages:(NSArray *)images withLanguages:(NSArray *)languages fileType:(NSString * _Nullable)fileType filePath:(NSURL * _Nullable)filePath;
+
+- (void)cancelRecognition;
+
+@end
+
+
+@interface KMOCROperationQueue : NSOperationQueue
+
++ (instancetype)sharedInstance;
+
+- (void)addOCROperation:(NSOperation *)op;
+
+#pragma mark - cancel
+
+- (void)cancelAll;
+
+@end

File diff suppressed because it is too large
+ 713 - 0
PDF Office/PDF Master/Class/Common/OC/OCR/KMGOCRManager.m


+ 1 - 0
PDF Office/PDF Master/Class/PDFTools/Convert/NewController/KMConvertWindowController.swift

@@ -903,6 +903,7 @@ class KMConvertWindowController: NSWindowController, NSTextFieldDelegate, NSWind
                 let pagesArray = NSMutableArray()
                 for pageNum in fileAttribute.fetchSelectPages() {
                     var tpage = self.pdfDocument?.page(at: UInt(truncating: pageNum) - 1)
+                    pagesArray.add(tpage as Any)
                 }
                 self.produceNewPDF(with: pagesArray as! [CPDFPage])
             } else {

+ 867 - 0
PDF Office/PDF Master/Class/PDFTools/OCRNew/Controller/KMOCRPDFWindowController.swift

@@ -0,0 +1,867 @@
+//
+//  KMOCRPDFWindowController.swift
+//  PDF Master
+//
+//  Created by liujiajie on 2023/11/14.
+//
+
+import Cocoa
+import PDFKit
+
+class KMOCRPDFWindowController: NSWindowController, NSWindowDelegate, KMGOCRManagerDelegate, NSPopoverDelegate,NSTextFieldDelegate{
+    var resultString: String = ""
+    var ocrDictionary: NSMutableDictionary?
+    var currentIndexPage: Int = 0{
+        didSet {
+            if PDFView.document.pageCount > currentIndexPage {
+                currentIndexPage = 0
+                self.currentPageLabel.stringValue = "\(currentIndexPage + 1)"
+                //                let page = self.PDFView.document.page(at: UInt(currentIndexPage))
+                self.PDFView.go(toPageIndex: currentIndexPage, animated: true)
+            }
+        }
+    }
+    var PDFView: CPDFView!
+    
+    @IBOutlet var prePdfBGView: NSView!
+    @IBOutlet var currentPageLabel: NSTextField!
+    @IBOutlet var totalPageLabel: NSTextField!
+    
+    @IBOutlet var pageLabel: NSTextField!
+    @IBOutlet var planLabel: NSTextField!
+    
+    @IBOutlet var ocrCopyButton: NSButton!
+    
+    @IBOutlet var pageRangeBox: NSComboBox!
+    @IBOutlet var planComboBox: NSPopUpButton!
+    
+    //@IBOutlet var NSProgressIndicator *progressIndicator;
+    @IBOutlet var progressControl: NSProgressIndicator!
+    @IBOutlet var failedBox: NSBox!
+    @IBOutlet var failedLabel: NSTextField!
+    
+    @IBOutlet var cancelButton: NSButton!
+    @IBOutlet var startButton: NSButton!
+    
+    @IBOutlet var languageLabel: NSTextField!
+    @IBOutlet var ocrResultLabel: NSTextField!
+    
+    @IBOutlet var languageButton: NSButton!
+    @IBOutlet var txtTextView: NSTextView!
+    
+    @IBOutlet var deleteButton: NSButton!
+    
+    @IBOutlet var emptyBox: NSBox!
+    
+    @IBOutlet var emptyLabel: NSTextField!
+    
+    var errorOCRArrays: Array<Any>?
+    
+    var pageIndexs: Array<Any>?
+    
+    var  password: String = ""
+    var pathFile: String = ""
+    
+    var pdfDocument: CPDFDocument?
+    
+    @IBOutlet var saveButton: NSButton!
+    @IBOutlet var box1: NSBox!
+    @IBOutlet var boxLabel1: NSTextField!
+    @IBOutlet var previewLabel: NSTextField!
+    @IBOutlet var savePDFButton: NSButton!
+    
+    var ocrCurrentIndex: Int = 0
+    var appleOCRManger: KMGOCRManager?
+    var googleOCRManger: KMGOCRManager?
+    
+    var savedFileName: String = ""
+    
+    func getOCRResrultsFolderPath() -> String {
+        var path = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.applicationSupportDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).last
+        path?.append("/")
+        path?.append(Bundle.main.bundleIdentifier!)
+        if (FileManager.default.fileExists(atPath: path!) == false) {
+            try?FileManager.default.createDirectory(atPath: path!, withIntermediateDirectories: false)
+        }
+        
+        path?.append("/")
+        path?.append("OCR_Resrults")
+        
+        return path!
+    }
+    
+    convenience init(cpdfDocument: CPDFDocument, pwd: String) {
+        self.init(windowNibName: "KMOCRPDFWindowController")
+        self.pdfDocument = cpdfDocument
+        self.password = pwd
+        if let lastPathComponent = cpdfDocument.documentURL?.lastPathComponent {
+            let str = lastPathComponent.deletingPathExtension
+            if str.count > 0 {
+                self.pathFile = str
+            }else {
+                self.pathFile = NSLocalizedString("Untitled", comment: "")
+            }
+        } else {
+            self.pathFile = NSLocalizedString("Untitled", comment: "")
+        }
+    }
+    
+    convenience init(filePath: String, pwd: String) {
+        self.init(windowNibName: "KMOCRPDFWindowController")
+        self.password = pwd
+        let pathExtension = filePath.lastPathComponent.customPathExtension
+        if  pathExtension.count > 0{
+            if pathExtension.lowercased() == "pdf" {
+                pdfDocument = CPDFDocument(url: URL(fileURLWithPath: filePath))
+            } else {
+                if let image = NSImage(contentsOf: URL(fileURLWithPath: filePath)) {
+                    self.pdfDocument = CPDFDocument()
+                    _ = pdfDocument?.km_insertPage(image.size, withImage: filePath, at: pdfDocument?.pageCount ?? 0)
+                }
+            }
+        }
+        self.pathFile = filePath.lastPathComponent.deletingPathExtension
+    }
+    deinit {
+        NotificationCenter.default.removeObserver(self)
+        KMGOCRManager.default().delegate = nil
+        appleOCRManger?.cancelRecognition()
+        appleOCRManger?.delegate = nil
+        googleOCRManger?.cancelRecognition()
+        googleOCRManger?.delegate = nil
+    }
+    override func windowDidLoad() {
+        super.windowDidLoad()
+        self.pageLabel.stringValue = NSLocalizedString("Page Range", comment: "")
+        self.planLabel.stringValue = NSLocalizedString("OCR Plan", comment: "")
+        
+        self.planComboBox.removeAllItems()
+        self.planComboBox.addItems(withTitles: [NSLocalizedString("Plan 1 (Online)", comment: ""), NSLocalizedString("Plan 2 (Offline)", comment: "")])
+        let plan = UserDefaults.standard.integer(forKey: "KMOCRCurrentPlanKey")
+        if plan == 0 {
+            KMGOCRManager.default().ocrType = .google
+            self.planComboBox.selectItem(at: 0)
+        } else {
+            KMGOCRManager.default().ocrType = .apple
+            self.planComboBox.selectItem(at: 1)
+        }
+        self.pageRangeBox.addItems(withObjectValues: [NSLocalizedString("All Pages", comment: ""), NSLocalizedString("Current Page", comment: ""), NSLocalizedString("Odd Pages Only", comment: ""), NSLocalizedString("Even Pages Only", comment: ""), NSLocalizedString("e.g. 1,3-5,10", comment: "")])
+        self.pageRangeBox.placeholderString = NSLocalizedString("e.g. 1,3-5,10", comment: "")
+        
+        self.pageRangeBox.selectItem(at: 0)
+        self.pageRangeBox.isEditable = false
+        if self.pageRangeBox.indexOfSelectedItem == 4 {
+            self.window?.makeFirstResponder(self.pageRangeBox)
+            self.pageRangeBox.stringValue = ""
+            self.pageRangeBox.isEditable = true
+        }
+        self.emptyLabel.stringValue = NSLocalizedString("Recognize text from Image-based or Scanned PDF with OCR", comment: "")
+        self.failedLabel.stringValue = NSLocalizedString("OCR failed. Please try to change the OCR Plan to \"Plan 2 (Offline)\"", comment: "")
+        self.txtTextView.textColor = NSColor.textColor
+        self.txtTextView.enclosingScrollView?.autohidesScrollers = true
+        self.ocrResultLabel.stringValue = NSLocalizedString("OCR Results", comment: "")
+        self.ocrCopyButton?.toolTip = KMLocalizedString("Copy and edit text from documents with OCR.", nil)
+        self.deleteButton?.toolTip = NSLocalizedString("Delete", comment: "")
+        self.deleteButton?.title = NSLocalizedString("Delete", comment: "")
+        self.ocrCopyButton?.title = NSLocalizedString("Copy", comment: "")
+        self.errorOCRArrays = []
+        self.pageIndexs = []
+        self.ocrCopyButton.isEnabled = false
+        self.deleteButton.isEnabled = false
+        self.saveButton.isEnabled = false
+        self.savePDFButton.isEnabled = false
+        self.boxLabel1.stringValue = NSLocalizedString("Settings", comment: "")
+        self.previewLabel.stringValue = NSLocalizedString("Preview", comment: "")
+        
+        self.saveButton.title = NSLocalizedString("Save as TXT", comment: "")
+        self.savePDFButton.title = NSLocalizedString("Save as PDF", comment: "")
+        self.saveButton.toolTip = NSLocalizedString("Export as a searchable PDF or text file.", comment: "")
+        self.savePDFButton.toolTip = NSLocalizedString("Export as a searchable PDF or text file.", comment: "")
+        
+        self.cancelButton.title = NSLocalizedString("Cancel", comment: "")
+        self.startButton.title = NSLocalizedString("OCR", comment: "")
+        self.languageLabel.stringValue = NSLocalizedString("Select OCR Language:", comment: "")
+        
+        self.updateLanguageButton(KMGOCRManager.default().selectedLanguages.value(forKeyPath: KMGOCRLanguageStringKey) as! [String])
+        
+        NotificationCenter.default.addObserver(self, selector: #selector(OCRSelectedLanguagesChangeNotification(notification:)), name: NSNotification.Name("KMOCRSelectedLanguagesChangeNotification"), object: nil)
+        NotificationCenter.default.addObserver(self, selector: #selector(OCRSelectedPlanChangeNotification(notification:)), name: NSNotification.Name("KMOCRSelectedPlanChangeNotification"), object: nil)
+        
+        self.progressControl.isHidden = true
+        emptyBox.isHidden = false
+        failedBox.isHidden = true
+        
+        self.PDFView.document = self.pdfDocument
+        self.PDFView.autoScales = true
+        self.reloadPDFData()
+        if !(self.pdfDocument?.isLocked ?? false) && ((self.pdfDocument?.unlock(withPassword: self.password)) != nil) {
+            
+        } else {
+            DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
+                
+                let com = PasswordWindowController(windowNibName: "PasswordWindowController")
+                com.fileURL = self.pdfDocument?.documentURL
+                NSWindow.currentWindow().km_beginSheet(windowC: com)
+                com.closeCallBack = { passwordString in
+                    if passwordString.count > 0 {
+                        self.pdfDocument?.unlock(withPassword: passwordString)
+                        self.password = passwordString
+                        self.reloadPDFData()
+                    } else {
+                        self.close()
+                    }
+                }
+            }
+        }
+        
+        NotificationCenter.default.addObserver(self, selector: #selector(PDFViewDocumentChangedNotification(notification:)), name: NSNotification.Name.PDFViewPageChanged, object: nil)
+    }
+    
+    func windowShouldClose(_ sender: NSWindow) -> Bool {
+        close()
+        return true
+    }
+    
+    override func close() {
+        if ((self.window?.isSheet) != nil) {
+            self.km_endSheet()
+        } else {
+            super.close()
+        }
+    }
+    
+    func reloadPDFData() { // 隐藏PDFView滑动条
+        self.PDFView.documentView().enclosingScrollView?.hasVerticalScroller = false
+        self.PDFView.documentView().enclosingScrollView?.hasHorizontalScroller = false
+        let pageCount: Int = Int(self.pdfDocument?.pageCount ?? 0)
+        let currentPageIndex = self.pdfDocument?.index(for: self.PDFView.currentPage())
+        self.currentPageLabel.stringValue = "\((currentPageIndex ?? 0) + 1)"
+        self.totalPageLabel.stringValue = "/ \(pageCount)"
+    }
+    //MARK: KMOCRSelectedLanguagesChangeNotification
+    @objc func OCRSelectedLanguagesChangeNotification(notification: NSNotification) {
+        if let selectedLanguages = notification.object as? [String] {
+            updateLanguageButton(selectedLanguages)
+        }
+    }
+    
+    @objc func OCRSelectedPlanChangeNotification(notification: NSNotification) {
+        let plan = UserDefaults.standard.integer(forKey: "KMOCRCurrentPlanKey")
+        if plan == 0 {
+            self.planComboBox.selectItem(at: 0)
+        } else {
+            self.planComboBox.selectItem(at: 1)
+        }
+        KMGOCRManager.default().selectedLanguages = NSMutableArray()
+        updateLanguageButton(KMGOCRManager.default().selectedLanguages.value(forKeyPath: KMGOCRLanguageStringKey) as! [String])
+    }
+    @objc func PDFViewDocumentChangedNotification(notification: NSNotification) {
+        let page: CPDFPage = self.PDFView.currentPage()
+        let pageIndex = self.PDFView.document.index(for: page)
+        self.currentPageLabel.stringValue = "\(pageIndex + 1)"
+    }
+    
+    func updateLanguageButton(_ languages: [String]) {
+        if languages.count < 1 {
+            self.languageButton.title = NSLocalizedString("Auto Detection", comment: "")
+            return
+        }
+        var languageName: String? = nil
+        if languages.count > 0 {
+            for i in 0..<languages.count {
+                let language = languages[i]
+                if i == 0 {
+                    languageName = language
+                } else {
+                    languageName = languageName?.appendingFormat(",%@", language)
+                }
+            }
+        } else {
+            languageName = ""
+        }
+        
+        self.languageButton.title = languageName ?? ""
+    }
+    func imageRep(withSize size: NSSize, scale: CGFloat, drawingHandler: (NSRect) -> Void) -> Any? {
+        let bmpImageRep = NSBitmapImageRep(bitmapDataPlanes: nil, pixelsWide: Int(size.width * scale), pixelsHigh: Int(size.height * scale), bitsPerSample: 8, samplesPerPixel: 4, hasAlpha: true, isPlanar: false, colorSpaceName: .calibratedRGB, bitmapFormat: .alphaFirst, bytesPerRow: 0, bitsPerPixel: 0)
+        //        bmpImageRep?.bitmapImageRepByRetaggingWithColorSpace = .sRGB
+        bmpImageRep?.size = size
+        NSGraphicsContext.saveGraphicsState()
+        NSGraphicsContext.current = NSGraphicsContext(bitmapImageRep: bmpImageRep!)
+        //        drawingHandler(.zero, size)
+        drawingHandler(NSRect(origin: NSZeroPoint, size: size))
+        NSGraphicsContext.restoreGraphicsState()
+        return bmpImageRep
+    }
+    func bitmapImage(withSize size: NSSize, drawingHandler: (NSRect) -> Void) -> NSImage? {
+        let image = NSImage(size: size)
+        let scale = KMImageScale
+        image.addRepresentation(imageRep(withSize: size, scale: scale, drawingHandler: drawingHandler) as! NSBitmapImageRep)
+        return image
+    }
+    func thumbnail(with page: CPDFPage) -> NSImage? {
+        let bounds = page.bounds(for: .cropBox)
+        var pageSize = bounds.size
+        var scale: CGFloat = 1.0
+        var thumbnailSize: NSSize
+        var pageRect = NSZeroRect
+        var image: NSImage?
+        let aSize = pageSize.width
+        if page.rotation % 180 == 90 {
+            pageSize = NSSize(width: pageSize.height, height: pageSize.width)
+        }
+        
+        if aSize > 0.0 {
+            if pageSize.height > pageSize.width {
+                thumbnailSize = NSSize(width: round(aSize * pageSize.width / pageSize.height), height: aSize)
+            } else {
+                thumbnailSize = NSSize(width: aSize, height: round(aSize * pageSize.height / pageSize.width))
+            }
+            let kKMMaxPixelsLimit = 75000000
+            let totalPixelNumber = Int((thumbnailSize.width * KMImageScale) * (thumbnailSize.height * KMImageScale))
+            if totalPixelNumber >= kKMMaxPixelsLimit {
+                let sizeScale = sqrt(Float(kKMMaxPixelsLimit - 1000) / Float(totalPixelNumber))
+                thumbnailSize = NSSize(width: floor(thumbnailSize.width * CGFloat(sizeScale)), height: floor(thumbnailSize.height * CGFloat(sizeScale)))
+            }
+            scale = max(thumbnailSize.width / pageSize.width, (thumbnailSize.height) / pageSize.height)
+        } else {
+            thumbnailSize = NSSize(width: pageSize.width, height: pageSize.height)
+        }
+        
+        if thumbnailSize.width.isNaN || thumbnailSize.height.isNaN || thumbnailSize.width == 0.0 || thumbnailSize.height == 0.0 {
+            thumbnailSize = NSSize(width: 186.0, height: 256.0)
+        }
+        
+        pageRect.size = thumbnailSize
+        
+        image = bitmapImage(withSize: thumbnailSize) { rect in
+            NSGraphicsContext.current?.imageInterpolation = .high
+            
+            NSGraphicsContext.saveGraphicsState()
+            NSColor.white.setFill()
+            pageRect.fill()
+            NSGraphicsContext.restoreGraphicsState()
+            
+            if abs(scale - 1.0) > 0.0 {
+                let transform = NSAffineTransform()
+                transform.scale(by: scale)
+                transform.concat()
+            }
+            
+            page.draw(with: .cropBox, to: (NSGraphicsContext.current as! CGContext))
+            
+            NSGraphicsContext.current?.imageInterpolation = .default
+        }
+        
+        return image
+    }
+    func savedName() -> String {
+        var resultArrays: Array<Any> = []
+        var ocrIndexArrays: Array<Any> = []
+        
+        let sortedKeys = self.ocrDictionary?.allKeys.sorted(by: {
+            ($0 as? String)?.compare($1 as? String ?? "") == .orderedAscending
+        })
+        for i in 0 ..< (sortedKeys?.count ?? 0) {
+            let keyS = sortedKeys?[i]
+            ocrIndexArrays.append(keyS as Any)
+            resultArrays.append(self.ocrDictionary?[keyS as Any] as Any)
+        }
+        
+        var fileName = self.savedFileName
+        if fileName.count > 50 {
+            fileName = String(fileName.prefix(50))
+        }
+        
+        if sortedKeys?.count ?? 0 > 1 {
+            fileName = "\(self.pathFile) Pages \(fileName) _OCR"
+        } else {
+            fileName = "\(self.pathFile) Page \(fileName) _OCR"
+        }
+        
+        var returnName = fileName
+        switch self.pageRangeBox.indexOfSelectedItem {
+        case 0:
+            returnName = "\(self.pathFile)_OCR"
+        case 2:
+            returnName = "\(self.pathFile) Odd_OCR"
+        case 3:
+            returnName = "\(self.pathFile) Even_OCR"
+        default:
+            break
+        }
+        
+        return returnName
+    }
+    func savePDF() {
+        var resultArrays: Array<Any> = []
+        var ocrIndexArrays: Array<Any> = []
+        let sortedKeys = self.ocrDictionary?.allKeys.sorted(by: { ($0 as? String)?.compare($1 as? String ?? "") == .orderedAscending })
+        for i in 0 ..< (sortedKeys?.count ?? 0) {
+            let keyS = sortedKeys?[i]
+            ocrIndexArrays.append(keyS as Any)
+            resultArrays.append(self.ocrDictionary?[keyS as Any] as Any)
+        }
+        
+        let fileName = self.savedFileName
+        
+        let saveAccessCtr = KMSavePanelAccessoryController()
+        let outputSavePanel = NSSavePanel()
+        outputSavePanel.allowedFileTypes = ["pdf"]
+        outputSavePanel.nameFieldStringValue = fileName
+        outputSavePanel.accessoryView = saveAccessCtr.view
+        outputSavePanel.beginSheetModal(for: self.window!) { [self] (result) in
+            if result == .OK {
+                
+                let savePDFPath = outputSavePanel.url?.path ?? ""
+                if !FileManager.default.fileExists(atPath: getOCRResrultsFolderPath()) {
+                    try? FileManager.default.createDirectory(atPath: getOCRResrultsFolderPath(), withIntermediateDirectories: false, attributes: nil)
+                }
+                var imagePath = [Any]()
+                for i in 0..<ocrIndexArrays.count {
+                    let rPath = (getOCRResrultsFolderPath() as NSString).appendingPathComponent("\(i).png")
+                    if let index = ocrIndexArrays[i] as? NSNumber, let page = self.pdfDocument?.page(at: UInt(index.intValue)), let image = self.thumbnail(with: page) {
+                        try? image.tiffRepresentation?.write(to: URL(fileURLWithPath: rPath), options: .atomic)
+                    }
+                    imagePath.append(rPath)
+                }
+                
+                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 saveText() { 
+        let sortedKeys = self.ocrDictionary?.allKeys.sorted(by: { ($0 as? String)?.compare($1 as? String ?? "") == .orderedAscending })
+        let fileName = self.savedFileName
+        
+        let saveAccessCtr = KMSavePanelAccessoryController()
+        let outputSavePanel = NSSavePanel()
+        outputSavePanel.allowedFileTypes = ["txt"]
+        outputSavePanel.nameFieldStringValue = fileName
+        outputSavePanel.accessoryView = saveAccessCtr.view
+        outputSavePanel.beginSheetModal(for: self.window!) { (result) in
+            if result == .OK {
+                let outputURL = outputSavePanel.url
+                try? self.txtTextView?.string.write(to: outputURL!, atomically: true, encoding: .utf8)
+                if saveAccessCtr.openAutomaticButton?.state == .on {
+                    NSWorkspace.shared.open(outputURL!)
+                } else {
+                    self.viewFileAtFinder(outputURL?.path ?? "")
+                }
+            }
+        }
+    }
+    func fileNameWithNums(_ nums: [NSNumber]) -> String {
+        var fileName: String? = nil
+        if nums.count > 0 {
+            if nums.count == 1 {
+                if let num = nums.first {
+                    let idx = num.intValue + 1
+                    return "\(idx)"
+                }
+            }
+            var sortIndex = NSSet()
+            for num in nums {
+                let idx = num.intValue + 1
+                //            sortIndex.insert(NSNumber(value: idx))
+                sortIndex.adding(NSNumber(value: idx))
+            }
+            let sort = NSSortDescriptor(key: nil, ascending: true)
+            let sortDesc = [sort]
+            let sortArray = sortIndex.sortedArray(using: sortDesc)
+            
+            var a = 0
+            var b = 0
+            if sortArray.count == 1 {
+                let num: NSNumber = sortArray.last as! NSNumber
+                fileName = "\(num.intValue)"
+                return fileName ?? ""
+            }
+            
+            for i in 0 ..< sortArray.count {
+                let num = sortArray[i]
+                if fileName?.count ?? 0 > 0 {
+                    if (num as AnyObject).intValue == b + 1 {
+                        b = (num as AnyObject).intValue
+                        if (i == sortArray.count - 1)  {
+                            fileName = (fileName ?? "") + "\(a)-\(b)"
+                        }
+                    } else {
+                        if a == b {
+                            fileName = (fileName ?? "") + "\(a),"
+                        } else {
+                            fileName = (fileName ?? "") + "\(a)-\(b),"
+                        }
+                        a = (num as AnyObject).intValue
+                        b = (num as AnyObject).intValue
+                        if (i == sortArray.count - 1)  {
+                            fileName = (fileName ?? "") + "\(a)"
+                        }
+                    }
+                } else {
+                    fileName = ""
+                    a = (num as AnyObject).intValue
+                    b = (num as AnyObject).intValue
+                }
+            }
+            return fileName ?? ""
+        }
+        return ""
+    }
+    
+    @IBAction func cancelButtonAction(_ sender: Any) {
+        KMGOCRManager.default().cancelRecognition()
+        self.close()
+    }
+    @IBAction func startButtonAction(_ sender: Any) {
+        self.ocrCurrentIndex = 0
+        txtTextView.string = ""
+        ocrDictionary = NSMutableDictionary()
+        let pdfDocument = self.pdfDocument
+        errorOCRArrays?.removeAll()
+        pageIndexs?.removeAll()
+        if self.pageRangeBox.indexOfSelectedItem == 0 {
+            for i in 0..<(pdfDocument?.pageCount ?? 0) {
+                pageIndexs?.append(NSNumber(value: i))
+            }
+        } else if self.pageRangeBox.indexOfSelectedItem == 1 {
+            let page = PDFView.currentPage()
+            let pageIndex = PDFView.document?.index(for: page)
+            pageIndexs?.append(NSNumber(value: pageIndex!))
+        } else if self.pageRangeBox.indexOfSelectedItem == 2 {
+            for i in 0..<(pdfDocument?.pageCount ?? 0) where i % 2 == 0 {
+                pageIndexs?.append(NSNumber(value: i))
+            }
+        } else if self.pageRangeBox.indexOfSelectedItem == 3 {
+            for i in 0..<(pdfDocument?.pageCount ?? 0) where i % 2 != 0 {
+                pageIndexs?.append(NSNumber(value: i))
+            }
+        } else {
+            let attribute = KMFileAttribute()
+            attribute.pdfDocument = pdfDocument
+            attribute.bAllPage = false
+            attribute.pagesString = self.pageRangeBox.stringValue
+            let selectPages = attribute.fetchSelectPages()
+            if selectPages.count > 0 {
+                for num in selectPages {
+                    pageIndexs?.append(NSNumber(value: num.intValue - 1))
+                }
+            }
+        }
+        batchesOCR()
+    }
+    func batchesOCR() { savedFileName = savedName()
+        let intervalOCR: UInt = 10
+        var selctPageImages = [AnyObject]()
+        
+        for i in 0..<intervalOCR {
+            if ocrCurrentIndex + Int(i) >= self.pageIndexs?.count ?? 0 { continue }
+            
+            autoreleasepool {
+                let index = self.pageIndexs?[self.ocrCurrentIndex + Int(i)] as! NSNumber
+                let page = self.pdfDocument?.page(at: UInt(index.intValue))
+                selctPageImages.append(thumbnail(with: page!)!/*.tiffRepresentation*/)
+            }
+        }
+        
+        if selctPageImages.count < 1 && ocrCurrentIndex == 0 {
+            let alert = NSAlert()
+            alert.alertStyle = .critical
+            alert.messageText = NSLocalizedString("Invalid page range or the page number is out of range. Please try again.", comment: "")
+            
+            if #available(OSX 10.11, *) {
+                alert.beginSheetModal(for: self.window!, completionHandler: nil)
+            } else {
+                alert.runModal()
+            }
+        }
+        
+        if selctPageImages.count < 1 {
+            return
+        }
+        
+        if appleOCRManger != nil {
+            appleOCRManger?.cancelRecognition()
+            appleOCRManger?.delegate = nil
+            appleOCRManger = nil
+        }
+        
+        if googleOCRManger != nil {
+            googleOCRManger?.cancelRecognition()
+            googleOCRManger?.delegate = nil
+            googleOCRManger = nil
+        }
+        
+        ocrCopyButton.isEnabled = false
+        saveButton.isEnabled = false
+        savePDFButton.isEnabled = false
+        startButton.isEnabled = false
+        deleteButton.isEnabled = false
+        progressControl.isHidden = false
+        progressControl.startAnimation(nil)
+        emptyBox.isHidden = true
+        failedBox.isHidden = true
+        
+        DispatchQueue.global(qos: .default).async {
+            DispatchQueue.main.async {
+                if self.planComboBox.indexOfSelectedItem == 0 {
+                    self.googleOCRManger = KMGOCRManager()
+                    self.googleOCRManger?.ocrType = .google
+                    let languages = KMGOCRManager.default().selectedLanguages.value(forKeyPath: KMGOCRLanguageCodeKey) as! [Any]
+                    self.googleOCRManger?.delegate = self
+                    self.googleOCRManger?.recognitionImages(selctPageImages, withLanguages: languages)
+                } else {
+                    self.appleOCRManger = KMGOCRManager()
+                    self.appleOCRManger?.ocrType = .apple
+                    let languages = KMGOCRManager.default().selectedLanguages.value(forKeyPath: KMGOCRLanguageCodeKey) as! [Any]
+                    self.appleOCRManger?.delegate = self
+                    self.appleOCRManger?.recognitionImages(selctPageImages, withLanguages: languages)
+                }
+            }
+        }
+    }
+    @IBAction func planSelectButtonAction(_ sender: NSPopUpButton) {
+        let plan = UserDefaults.standard.integer(forKey: "KMOCRCurrentPlanKey")
+        if plan != sender.indexOfSelectedItem {
+            KMGOCRManager.default().selectedLanguages = NSMutableArray()
+            updateLanguageButton(KMGOCRManager.default().selectedLanguages.value(forKeyPath: KMGOCRLanguageStringKey) as! [String])
+        }
+        UserDefaults.standard.set(sender.indexOfSelectedItem, forKey: "KMOCRCurrentPlanKey")
+        UserDefaults.standard.synchronize()
+        if self.planComboBox.indexOfSelectedItem == 0 {
+            KMGOCRManager.default().ocrType = .google
+        } else {
+            KMGOCRManager.default().ocrType = .apple
+        }
+        NotificationCenter.default.post(name: NSNotification.Name("KMOCRSelectedPlanChangeNotification"), object: nil)
+    }
+    @IBAction func languageButtonAction(_ sender: Any) {
+        //        let popover = NSPopover()
+        //        popover.delegate = self
+        //        popover.contentViewController = KMLanguageViewController(nibName: "KMLanguageViewController", bundle: Bundle.main)
+        //        popover.animates = true
+        //        popover.behavior = .transient
+        //        popover.show(relativeTo: (sender as! NSView).bounds, of: sender as! NSView, preferredEdge: .minX)
+    }
+    @IBAction func helpButtonAction(_ sender: Any) {
+        let helpController = NSViewController()
+        let textView = NSTextView(frame: NSRectToCGRect(NSMakeRect(0, 0, 300.0, 50.0)))
+        textView.backgroundColor = NSColor.clear
+        textView.isEditable = false
+        textView.layer?.cornerRadius = 6
+        let tStrAuto = NSLocalizedString("Choose automatic language detection for better OCR results.", comment: "")
+        textView.alignment = .justified
+        textView.string = "\n\(tStrAuto)"
+        helpController.view = textView
+        let popover = NSPopover()
+        popover.delegate = self
+        popover.contentViewController = helpController
+        popover.animates = true
+        popover.behavior = .transient
+        popover.show(relativeTo: (sender as! NSView).bounds, of: sender as! NSView, preferredEdge: .minY)
+    }
+    @IBAction func buttonItemClick_CopyTxt(_ sender: NSButton) {
+        let pasteboard = NSPasteboard.general
+        pasteboard.clearContents()
+        let str: NSPasteboardWriting = self.txtTextView.string as NSPasteboardWriting
+        pasteboard.writeObjects([str])
+        let _ = CustomAlertView.alertView(message: NSLocalizedString("Copy successful!", comment: ""), fromView: sender.superview!, withStyle: .black)
+    }
+    @IBAction func buttonItemClick_Delete(_ sender: NSButton) {
+        ocrCopyButton.isEnabled = false
+        saveButton.isEnabled = false
+        savePDFButton.isEnabled = false
+        deleteButton.isEnabled = false
+        emptyBox.isHidden = false
+        failedBox.isHidden = true
+        resultString = ""
+        txtTextView.string = ""
+        ocrDictionary = NSMutableDictionary()
+    }
+    @IBAction func boxItemClicked_PageRange(_ sender: NSButton) {
+        if 4 != pageRangeBox.indexOfSelectedItem {
+            self.window?.makeFirstResponder(self)
+            pageRangeBox.isEditable = false
+        } else {
+            pageRangeBox.stringValue = ""
+            pageRangeBox.isEditable = true
+            self.window?.makeFirstResponder(pageRangeBox)
+        }
+    }
+    
+    @IBAction func nextButtonAction(_ sender: NSButton) {
+        self.PDFView.goToNextPage(nil)
+        self.reloadPDFData()
+    }
+    @IBAction func previousButtonAction(_ sender: NSButton) {
+        self.PDFView.goToPreviousPage(nil)
+        self.reloadPDFData()
+    }
+    @IBAction func buttonClicked_SaveButton(_ sender: NSButton) {
+        self.saveText()
+    }
+    @IBAction func savePDFButtonAction(_ sender: NSButton) {
+        self.savePDF()
+    }
+    func controlTextDidEndEditing(_ notification: Notification) {
+        guard let textField = notification.object as? NSTextField else { return }
+        if textField == currentPageLabel {
+            let index = Int(currentPageLabel.stringValue) ?? 0
+            let pageCount = pdfDocument?.pageCount
+            let currentPageIndex = pdfDocument?.index(for: PDFView.currentPage())
+            if index > 0 && index <= pageCount ?? 0 {
+                PDFView.go(to: pdfDocument?.page(at: UInt(index-1))!)
+                reloadPDFData()
+            } else {
+                currentPageLabel.stringValue = "(currentPageIndex+1)"
+            }
+        }
+    }
+    func controlTextDidChange(_ notification: Notification) {
+        guard let textField = notification.object as? NSTextField else { return }
+        if textField == currentPageLabel {
+            let string = textField.formatter?.string(for: NSNumber(value: Int(textField.stringValue) ?? 0))
+            textField.stringValue = string ?? ""
+        }
+    }
+    func popoverDidClose(_ notification: Notification) {
+        
+    }
+    
+    func gocrManagerDidStartOCR(_ manager: KMGOCRManager!) {
+        
+    }
+    //MARK: KMGOCRManagerDelegate
+    func gocrManagerDidFinishOCR(_ manager: KMGOCRManager!) {
+        self.batchesOCR()
+    }
+    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]!) {
+        self.dealWithResults(results, OCRImageAtIndex: index)
+    }
+    func gocrManager(_ manager: KMGOCRManager!, didFailureOCRImageAt index: Int, error: Error!) {
+        let pagenum = self.pageIndexs?[index] as! NSNumber
+        self.errorOCRArrays?.append(pagenum)
+        self.dealWithResults([], OCRImageAtIndex: index)
+    }
+    
+    func dealWithResults(_ rlts: [KMGOCRResult]?, OCRImageAtIndex index: Int) {
+        if index >= self.pageIndexs?.count ?? 0 {
+            return
+        }
+        
+        let key = self.pageIndexs?[self.ocrCurrentIndex] as! NSNumber
+        
+        if ocrDictionary == nil {
+            ocrDictionary = NSMutableDictionary()
+        }
+        
+        ocrDictionary?.setObject(rlts as Any, forKey: key)
+        
+        let sortedKeys = self.ocrDictionary?.allKeys.sorted(by: { ($0 as! NSNumber).compare($1 as! NSNumber) == .orderedAscending })
+        var textString = ""
+        
+        for key in sortedKeys! {
+            let results: Array<KMGOCRResult> = self.ocrDictionary?.object(forKey: key) as! Array<KMGOCRResult>
+            var rStr = ""
+            if results.count > 0 {
+                rStr = results[0].text
+            }
+            if textString.count > 0 {
+                textString += "\n\n"
+            }
+            textString += String(format: NSLocalizedString("Page %ld", comment: ""), (key as! NSNumber).intValue + 1)
+            textString += "\n"
+            textString += rStr
+        }
+        
+        self.txtTextView.string = textString
+        self.resultString = textString
+        
+        self.ocrCurrentIndex += 1
+        
+        if self.ocrCurrentIndex >= (self.pageIndexs?.count ?? 0) - 1 {
+            self.progressControl.stopAnimation(nil)
+            self.progressControl.isHidden = true
+            self.saveButton.isEnabled = true
+            self.savePDFButton.isEnabled = true
+            self.ocrCopyButton.isEnabled = true
+            self.deleteButton.isEnabled = true
+            self.startButton.isEnabled = true
+            self.planComboBox.isEnabled = true
+            self.pageRangeBox.isEnabled = true
+            self.languageButton.isEnabled = true
+            
+            if self.errorOCRArrays?.count ?? 0 < 1 {
+                
+            } else if self.errorOCRArrays?.count == self.pageIndexs?.count {
+                if KMGOCRManager.default().ocrType == .google {
+                    self.failedLabel.stringValue = NSLocalizedString("OCR failed.Please try to change the OCR Plan to \"Plan 2 (Offline)\"", comment: "")
+                    self.failedBox.isHidden = false
+                } else {
+                    let alert = NSAlert()
+                    alert.alertStyle = NSAlert.Style.critical
+                    alert.messageText = ""
+                    if #available(macOS 10.15, *) {
+                        alert.informativeText = NSLocalizedString("Unable to perform OCR on this document. Please try again later.", comment: "")
+                    } else {
+                        alert.informativeText = NSLocalizedString("OCR failed, please try again. Note: OCR Plan 2(Offline) is supported in macOS 10.15+.", comment: "")
+                        self.failedLabel.stringValue = NSLocalizedString("OCR failed, please try again. Note: OCR Plan 2(Offline) is supported in macOS 10.15+.", comment: "")
+                        self.failedBox.isHidden = false
+                    }
+                    alert.addButton(withTitle: NSLocalizedString("OK", comment: ""))
+                    let response = alert.runModal()
+                    if response == NSApplication.ModalResponse.alertFirstButtonReturn {
+                        
+                    }
+                }
+            } else {
+                var contextString = String(format: "%@", NSLocalizedString("Some problems occurred during the last operation:", comment: ""))
+                contextString += "\n"
+                
+                if self.errorOCRArrays?.count ?? 0 > 1 {
+                    contextString += NSLocalizedString("Pages", comment: "")
+                } else {
+                    contextString += NSLocalizedString("Page", comment: "")
+                }
+                if self.errorOCRArrays?.count ?? 0 > 0 {
+                    contextString += String(format: ": %@", self.fileNameWithNums(self.errorOCRArrays as! Array<NSNumber>))
+                }
+                
+                let alert = NSAlert()
+                alert.alertStyle = NSAlert.Style.critical
+                alert.messageText = NSLocalizedString("Completed", comment: "")
+                alert.informativeText = contextString
+                alert.addButton(withTitle: NSLocalizedString("OK", comment: ""))
+                let response = alert.runModal()
+                if response == NSApplication.ModalResponse.alertFirstButtonReturn {
+                    
+                }
+            }
+        }
+    }
+    
+    
+}

+ 493 - 0
PDF Office/PDF Master/Class/PDFTools/OCRNew/Controller/KMOCRPDFWindowController.xib

@@ -0,0 +1,493 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="22155" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
+    <dependencies>
+        <deployment identifier="macosx"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="22155"/>
+        <capability name="System colors introduced in macOS 10.14" minToolsVersion="10.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <customObject id="-2" userLabel="File's Owner" customClass="KMOCRPDFWindowController" customModule="PDF_Master" customModuleProvider="target">
+            <connections>
+                <outlet property="box1" destination="jMC-LE-Zxs" id="nh5-tV-ncG"/>
+                <outlet property="boxLabel1" destination="cZ7-HB-Jv8" id="Itx-SQ-dSU"/>
+                <outlet property="cancelButton" destination="D6y-c7-aMF" id="quK-va-wKT"/>
+                <outlet property="currentPageLabel" destination="1Pt-BN-QHC" id="Yht-Ys-zn5"/>
+                <outlet property="deleteButton" destination="TF7-NH-FRc" id="js8-KS-6N9"/>
+                <outlet property="emptyBox" destination="2Vw-iL-vO2" id="x2b-Xv-IL9"/>
+                <outlet property="emptyLabel" destination="4Cf-Mw-dtW" id="vER-3h-zaM"/>
+                <outlet property="failedBox" destination="rEZ-tQ-pPN" id="MTo-0N-7J1"/>
+                <outlet property="failedLabel" destination="aTR-5z-hhV" id="CRA-np-6pT"/>
+                <outlet property="languageButton" destination="ao2-UH-4ks" id="fMn-NQ-gTp"/>
+                <outlet property="languageLabel" destination="lSf-BX-nnw" id="eJC-0i-sGH"/>
+                <outlet property="ocrCopyButton" destination="PHr-ev-Pjc" id="czG-LT-4Bq"/>
+                <outlet property="ocrResultLabel" destination="vbX-fH-NDe" id="pJ2-lJ-5BG"/>
+                <outlet property="pageLabel" destination="CXf-vO-XkT" id="sTo-gc-zFm"/>
+                <outlet property="pageRangeBox" destination="Yut-Ff-6Wz" id="42r-vl-23P"/>
+                <outlet property="planComboBox" destination="sp6-NA-5hp" id="CdU-c8-DCP"/>
+                <outlet property="planLabel" destination="kG6-iz-ttR" id="Rgj-Fi-8qC"/>
+                <outlet property="prePdfBGView" destination="KlK-Pr-U01" id="9D0-nS-fQE"/>
+                <outlet property="previewLabel" destination="Ysf-e8-HyO" id="qaZ-7e-T2W"/>
+                <outlet property="progressControl" destination="SN7-ZH-OPS" id="q7d-nX-O2c"/>
+                <outlet property="progressIndicator" destination="Pmb-Zf-1Yy" id="dop-lM-rra"/>
+                <outlet property="saveButton" destination="iS7-aP-oXa" id="dnK-yD-onb"/>
+                <outlet property="savePDFButton" destination="Lfw-RX-daG" id="Jph-tK-oNR"/>
+                <outlet property="startButton" destination="wwH-or-0pV" id="fxr-zU-rhe"/>
+                <outlet property="totalPageLabel" destination="z6n-8y-3TH" id="rbY-CN-MRO"/>
+                <outlet property="txtTextView" destination="SKU-Tg-cHn" id="oIE-Ix-RtM"/>
+                <outlet property="window" destination="F0z-JX-Cv5" id="gIp-Ho-8D9"/>
+            </connections>
+        </customObject>
+        <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
+        <customObject id="-3" userLabel="Application" customClass="NSObject"/>
+        <window title="OCR" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="F0z-JX-Cv5">
+            <windowStyleMask key="styleMask" titled="YES" closable="YES"/>
+            <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
+            <rect key="contentRect" x="196" y="240" width="648" height="622"/>
+            <rect key="screenRect" x="0.0" y="0.0" width="2560" height="1415"/>
+            <view key="contentView" wantsLayer="YES" id="se5-gp-TjO">
+                <rect key="frame" x="0.0" y="0.0" width="648" height="622"/>
+                <autoresizingMask key="autoresizingMask"/>
+                <subviews>
+                    <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="wwH-or-0pV">
+                        <rect key="frame" x="287" y="272" width="74" height="32"/>
+                        <buttonCell key="cell" type="push" title="OCR" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="VI4-CK-Iwa">
+                            <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+                            <font key="font" metaFont="system"/>
+                            <string key="keyEquivalent" base64-UTF8="YES">
+DQ
+</string>
+                        </buttonCell>
+                        <constraints>
+                            <constraint firstAttribute="width" constant="60" id="ABz-Ik-T1v"/>
+                        </constraints>
+                        <connections>
+                            <action selector="startButtonAction:" target="-2" id="Cbd-lQ-aIN"/>
+                        </connections>
+                    </button>
+                    <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="D6y-c7-aMF">
+                        <rect key="frame" x="425" y="11" width="76" height="32"/>
+                        <buttonCell key="cell" type="push" title="Cancel" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="St2-Gz-e7S">
+                            <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+                            <font key="font" metaFont="system"/>
+                            <string key="keyEquivalent" base64-UTF8="YES">
+Gw
+</string>
+                        </buttonCell>
+                        <connections>
+                            <action selector="cancelButtonAction:" target="-2" id="oAn-RV-WSM"/>
+                        </connections>
+                    </button>
+                    <box boxType="custom" title="Box" titlePosition="noTitle" translatesAutoresizingMaskIntoConstraints="NO" id="GA7-kq-Dba">
+                        <rect key="frame" x="368" y="134" width="240" height="310"/>
+                        <view key="contentView" id="kQQ-QV-cpE">
+                            <rect key="frame" x="1" y="1" width="238" height="308"/>
+                            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                            <subviews>
+                                <scrollView borderType="none" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" translatesAutoresizingMaskIntoConstraints="NO" id="4J0-cr-FZG">
+                                    <rect key="frame" x="1" y="1" width="236" height="306"/>
+                                    <clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="61X-70-dQH">
+                                        <rect key="frame" x="0.0" y="0.0" width="221" height="306"/>
+                                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                                        <subviews>
+                                            <textView editable="NO" importsGraphics="NO" richText="NO" verticallyResizable="YES" allowsUndo="YES" spellingCorrection="YES" smartInsertDelete="YES" id="SKU-Tg-cHn">
+                                                <rect key="frame" x="0.0" y="0.0" width="221" height="306"/>
+                                                <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                                                <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
+                                                <size key="minSize" width="221" height="306"/>
+                                                <size key="maxSize" width="991" height="10000000"/>
+                                                <color key="insertionPointColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
+                                            </textView>
+                                        </subviews>
+                                    </clipView>
+                                    <scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="Fji-XC-UnY">
+                                        <rect key="frame" x="-100" y="-100" width="305" height="15"/>
+                                        <autoresizingMask key="autoresizingMask"/>
+                                    </scroller>
+                                    <scroller key="verticalScroller" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="fti-GX-vJf">
+                                        <rect key="frame" x="221" y="0.0" width="15" height="306"/>
+                                        <autoresizingMask key="autoresizingMask"/>
+                                    </scroller>
+                                </scrollView>
+                                <progressIndicator hidden="YES" wantsLayer="YES" horizontalHuggingPriority="750" verticalHuggingPriority="750" maxValue="1" bezeled="NO" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="Pmb-Zf-1Yy">
+                                    <rect key="frame" x="103" y="138" width="32" height="32"/>
+                                </progressIndicator>
+                                <progressIndicator wantsLayer="YES" maxValue="100" indeterminate="YES" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="SN7-ZH-OPS">
+                                    <rect key="frame" x="103" y="138" width="32" height="32"/>
+                                </progressIndicator>
+                                <box hidden="YES" boxType="custom" borderType="none" cornerRadius="4" title="Box" translatesAutoresizingMaskIntoConstraints="NO" id="2Vw-iL-vO2">
+                                    <rect key="frame" x="-1" y="138" width="240" height="32"/>
+                                    <view key="contentView" id="Vw5-qr-qx0">
+                                        <rect key="frame" x="0.0" y="0.0" width="240" height="32"/>
+                                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                                        <subviews>
+                                            <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="4Cf-Mw-dtW">
+                                                <rect key="frame" x="-2" y="0.0" width="244" height="32"/>
+                                                <textFieldCell key="cell" alignment="center" title="Recognize text from Image-based or Scanned PDF with OCR" id="Guh-NA-4d7">
+                                                    <font key="font" metaFont="system"/>
+                                                    <color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
+                                                    <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
+                                                </textFieldCell>
+                                            </textField>
+                                        </subviews>
+                                        <constraints>
+                                            <constraint firstAttribute="trailing" secondItem="4Cf-Mw-dtW" secondAttribute="trailing" id="1wQ-0q-Sxh"/>
+                                            <constraint firstItem="4Cf-Mw-dtW" firstAttribute="leading" secondItem="Vw5-qr-qx0" secondAttribute="leading" id="4zD-QV-ME8"/>
+                                            <constraint firstItem="4Cf-Mw-dtW" firstAttribute="top" secondItem="Vw5-qr-qx0" secondAttribute="top" id="7Iv-1L-oRH"/>
+                                            <constraint firstAttribute="bottom" secondItem="4Cf-Mw-dtW" secondAttribute="bottom" id="A4t-tU-Vyv"/>
+                                        </constraints>
+                                    </view>
+                                    <constraints>
+                                        <constraint firstAttribute="width" constant="240" id="6yO-8e-5VV"/>
+                                    </constraints>
+                                </box>
+                                <box boxType="custom" borderWidth="0.0" title="Box" titlePosition="noTitle" translatesAutoresizingMaskIntoConstraints="NO" id="rEZ-tQ-pPN">
+                                    <rect key="frame" x="0.0" y="86" width="238" height="136"/>
+                                    <view key="contentView" id="CbH-wM-kUR">
+                                        <rect key="frame" x="0.0" y="0.0" width="238" height="136"/>
+                                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                                        <subviews>
+                                            <imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Ktw-U9-EAd">
+                                                <rect key="frame" x="95" y="78" width="48" height="48"/>
+                                                <constraints>
+                                                    <constraint firstAttribute="height" constant="48" id="6yA-wF-tQ9"/>
+                                                    <constraint firstAttribute="width" constant="48" id="PMh-iI-B2A"/>
+                                                </constraints>
+                                                <imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="KMImageNameUXIconOCRWarning" id="8E0-jB-dUj"/>
+                                            </imageView>
+                                            <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="aTR-5z-hhV">
+                                                <rect key="frame" x="-2" y="10" width="242" height="48"/>
+                                                <textFieldCell key="cell" lineBreakMode="charWrapping" alignment="center" id="13A-yZ-SU0">
+                                                    <font key="font" metaFont="system"/>
+                                                    <string key="title">OCR failed. 
+Please try to change the OCR Plan to “Plan 2 (Offline)”</string>
+                                                    <color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
+                                                    <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
+                                                </textFieldCell>
+                                            </textField>
+                                        </subviews>
+                                        <constraints>
+                                            <constraint firstItem="aTR-5z-hhV" firstAttribute="top" secondItem="Ktw-U9-EAd" secondAttribute="bottom" constant="20" id="1TK-tD-VbD"/>
+                                            <constraint firstItem="Ktw-U9-EAd" firstAttribute="centerX" secondItem="CbH-wM-kUR" secondAttribute="centerX" id="GkX-En-F21"/>
+                                            <constraint firstItem="aTR-5z-hhV" firstAttribute="leading" secondItem="CbH-wM-kUR" secondAttribute="leading" id="SmR-BJ-0p5"/>
+                                            <constraint firstAttribute="trailing" secondItem="aTR-5z-hhV" secondAttribute="trailing" id="Zwp-Td-mif"/>
+                                            <constraint firstItem="Ktw-U9-EAd" firstAttribute="top" secondItem="CbH-wM-kUR" secondAttribute="top" constant="10" id="peF-fx-YDL"/>
+                                            <constraint firstAttribute="bottom" secondItem="aTR-5z-hhV" secondAttribute="bottom" constant="10" id="zze-rz-Grf"/>
+                                        </constraints>
+                                    </view>
+                                </box>
+                            </subviews>
+                            <constraints>
+                                <constraint firstItem="rEZ-tQ-pPN" firstAttribute="centerY" secondItem="kQQ-QV-cpE" secondAttribute="centerY" id="1ss-aJ-2J4"/>
+                                <constraint firstItem="2Vw-iL-vO2" firstAttribute="centerY" secondItem="4J0-cr-FZG" secondAttribute="centerY" id="7MF-L1-h42"/>
+                                <constraint firstAttribute="bottom" secondItem="4J0-cr-FZG" secondAttribute="bottom" constant="1" id="8xv-3B-Z7K"/>
+                                <constraint firstItem="4J0-cr-FZG" firstAttribute="centerX" secondItem="kQQ-QV-cpE" secondAttribute="centerX" id="MQU-eo-OaB"/>
+                                <constraint firstItem="SN7-ZH-OPS" firstAttribute="centerX" secondItem="4J0-cr-FZG" secondAttribute="centerX" id="S3Q-Dl-ObE"/>
+                                <constraint firstItem="Pmb-Zf-1Yy" firstAttribute="centerY" secondItem="4J0-cr-FZG" secondAttribute="centerY" id="TMg-SL-Nwo"/>
+                                <constraint firstItem="SN7-ZH-OPS" firstAttribute="centerY" secondItem="4J0-cr-FZG" secondAttribute="centerY" id="UfO-Ae-4f7"/>
+                                <constraint firstItem="4J0-cr-FZG" firstAttribute="leading" secondItem="kQQ-QV-cpE" secondAttribute="leading" constant="1" id="Wcz-XO-8ty"/>
+                                <constraint firstItem="rEZ-tQ-pPN" firstAttribute="centerX" secondItem="kQQ-QV-cpE" secondAttribute="centerX" id="bdg-WS-wRM"/>
+                                <constraint firstItem="rEZ-tQ-pPN" firstAttribute="width" secondItem="kQQ-QV-cpE" secondAttribute="width" id="qcE-g8-0sb"/>
+                                <constraint firstItem="4J0-cr-FZG" firstAttribute="top" secondItem="kQQ-QV-cpE" secondAttribute="top" constant="1" id="tcM-m0-fue"/>
+                                <constraint firstItem="Pmb-Zf-1Yy" firstAttribute="centerX" secondItem="4J0-cr-FZG" secondAttribute="centerX" id="vsc-li-ZBl"/>
+                                <constraint firstAttribute="trailing" secondItem="4J0-cr-FZG" secondAttribute="trailing" constant="1" id="x0T-bQ-iBd"/>
+                                <constraint firstItem="2Vw-iL-vO2" firstAttribute="centerX" secondItem="4J0-cr-FZG" secondAttribute="centerX" id="xTQ-5K-cFH"/>
+                            </constraints>
+                        </view>
+                        <constraints>
+                            <constraint firstAttribute="width" constant="240" id="AGe-iA-ypf"/>
+                            <constraint firstAttribute="height" constant="310" id="fko-SF-8tn"/>
+                        </constraints>
+                        <color key="borderColor" name="separatorColor" catalog="System" colorSpace="catalog"/>
+                    </box>
+                    <box title="Box" titlePosition="noTitle" translatesAutoresizingMaskIntoConstraints="NO" id="jMC-LE-Zxs">
+                        <rect key="frame" x="21" y="481" width="606" height="107"/>
+                        <view key="contentView" id="O6J-fM-PR9">
+                            <rect key="frame" x="4" y="5" width="598" height="99"/>
+                            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                            <subviews>
+                                <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="CXf-vO-XkT">
+                                    <rect key="frame" x="14" y="71" width="145" height="16"/>
+                                    <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Page Range" id="aUf-bp-SVY">
+                                        <font key="font" metaFont="system"/>
+                                        <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
+                                        <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+                                    </textFieldCell>
+                                </textField>
+                                <comboBox focusRingType="none" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Yut-Ff-6Wz">
+                                    <rect key="frame" x="163" y="67" width="392" height="23"/>
+                                    <comboBoxCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" drawsBackground="YES" completes="NO" numberOfVisibleItems="5" id="HtU-OY-BxR">
+                                        <font key="font" metaFont="system"/>
+                                        <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
+                                        <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
+                                    </comboBoxCell>
+                                    <connections>
+                                        <action selector="boxItemClicked_PageRange:" target="-2" id="Udv-ME-j7K"/>
+                                    </connections>
+                                </comboBox>
+                                <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="lSf-BX-nnw">
+                                    <rect key="frame" x="14" y="12" width="145" height="16"/>
+                                    <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Select OCR Language:" id="NeZ-KH-YIZ">
+                                        <font key="font" metaFont="system"/>
+                                        <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
+                                        <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+                                    </textFieldCell>
+                                </textField>
+                                <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ao2-UH-4ks">
+                                    <rect key="frame" x="157" y="3" width="402" height="31"/>
+                                    <buttonCell key="cell" type="push" bezelStyle="rounded" image="NSDescendingSortIndicator" imagePosition="right" alignment="left" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="4NX-Qb-sjd">
+                                        <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+                                        <font key="font" metaFont="system"/>
+                                    </buttonCell>
+                                    <constraints>
+                                        <constraint firstAttribute="width" constant="388" id="Nsy-6I-LEc"/>
+                                        <constraint firstAttribute="height" constant="19" id="g7F-eW-oca"/>
+                                    </constraints>
+                                    <connections>
+                                        <action selector="languageButtonAction:" target="-2" id="Btz-aH-X3D"/>
+                                    </connections>
+                                </button>
+                                <button horizontalHuggingPriority="750" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="YXa-BA-OOh">
+                                    <rect key="frame" x="559" y="6" width="25" height="25"/>
+                                    <buttonCell key="cell" type="help" bezelStyle="helpButton" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="aGG-Vv-0dY">
+                                        <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+                                        <font key="font" metaFont="system"/>
+                                    </buttonCell>
+                                    <connections>
+                                        <action selector="helpButtonAction:" target="-2" id="meG-Lo-zGh"/>
+                                    </connections>
+                                </button>
+                                <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="kG6-iz-ttR">
+                                    <rect key="frame" x="14" y="41" width="145" height="16"/>
+                                    <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="OCR Plan" id="npl-Ab-rPG">
+                                        <font key="font" metaFont="system"/>
+                                        <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
+                                        <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+                                    </textFieldCell>
+                                </textField>
+                                <popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="sp6-NA-5hp">
+                                    <rect key="frame" x="161" y="35" width="395" height="25"/>
+                                    <popUpButtonCell key="cell" type="push" title="Item 3" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="cNL-S3-gJw" id="ZzG-hZ-YtK">
+                                        <behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
+                                        <font key="font" metaFont="menu"/>
+                                        <menu key="menu" id="OnY-X5-N8e">
+                                            <items>
+                                                <menuItem title="Item 1" id="qEm-O8-rTN"/>
+                                                <menuItem title="Item 2" id="rRq-l8-JxM"/>
+                                                <menuItem title="Item 3" state="on" id="cNL-S3-gJw"/>
+                                            </items>
+                                        </menu>
+                                    </popUpButtonCell>
+                                    <connections>
+                                        <action selector="planSelectButtonAction:" target="-2" id="f7Z-cy-cY8"/>
+                                    </connections>
+                                </popUpButton>
+                            </subviews>
+                            <constraints>
+                                <constraint firstItem="ao2-UH-4ks" firstAttribute="leading" secondItem="Yut-Ff-6Wz" secondAttribute="leading" id="1mo-bY-ktp"/>
+                                <constraint firstItem="sp6-NA-5hp" firstAttribute="trailing" secondItem="Yut-Ff-6Wz" secondAttribute="trailing" id="AuM-Ke-bm3"/>
+                                <constraint firstItem="sp6-NA-5hp" firstAttribute="leading" secondItem="Yut-Ff-6Wz" secondAttribute="leading" id="Erv-20-kPj"/>
+                                <constraint firstAttribute="bottom" secondItem="YXa-BA-OOh" secondAttribute="bottom" constant="10" id="Fpf-Md-xWs"/>
+                                <constraint firstItem="kG6-iz-ttR" firstAttribute="trailing" secondItem="CXf-vO-XkT" secondAttribute="trailing" id="G4J-9k-WLY"/>
+                                <constraint firstAttribute="trailing" secondItem="YXa-BA-OOh" secondAttribute="trailing" constant="16" id="GJP-4U-QBh"/>
+                                <constraint firstItem="lSf-BX-nnw" firstAttribute="leading" secondItem="CXf-vO-XkT" secondAttribute="leading" id="JnA-M0-UNE"/>
+                                <constraint firstItem="Yut-Ff-6Wz" firstAttribute="leading" secondItem="CXf-vO-XkT" secondAttribute="trailing" constant="7" id="K2N-6W-T8T"/>
+                                <constraint firstItem="sp6-NA-5hp" firstAttribute="top" secondItem="Yut-Ff-6Wz" secondAttribute="bottom" constant="10" id="OiB-ah-sfP"/>
+                                <constraint firstItem="ao2-UH-4ks" firstAttribute="trailing" secondItem="Yut-Ff-6Wz" secondAttribute="trailing" id="RHH-hv-Xh4"/>
+                                <constraint firstItem="kG6-iz-ttR" firstAttribute="leading" secondItem="CXf-vO-XkT" secondAttribute="leading" id="V8G-z6-6FX"/>
+                                <constraint firstItem="CXf-vO-XkT" firstAttribute="leading" secondItem="O6J-fM-PR9" secondAttribute="leading" constant="16" id="ceD-at-3AT"/>
+                                <constraint firstItem="CXf-vO-XkT" firstAttribute="top" secondItem="O6J-fM-PR9" secondAttribute="top" constant="12" id="dzX-1B-Cbm"/>
+                                <constraint firstItem="YXa-BA-OOh" firstAttribute="centerY" secondItem="ao2-UH-4ks" secondAttribute="centerY" id="eVG-oF-5pD"/>
+                                <constraint firstItem="ao2-UH-4ks" firstAttribute="top" secondItem="sp6-NA-5hp" secondAttribute="bottom" constant="10" id="kXH-cR-NBE"/>
+                                <constraint firstItem="YXa-BA-OOh" firstAttribute="centerY" secondItem="lSf-BX-nnw" secondAttribute="centerY" id="mQS-3b-Uey"/>
+                                <constraint firstItem="Yut-Ff-6Wz" firstAttribute="centerY" secondItem="CXf-vO-XkT" secondAttribute="centerY" id="qRc-mh-Kxa"/>
+                                <constraint firstItem="YXa-BA-OOh" firstAttribute="leading" secondItem="ao2-UH-4ks" secondAttribute="trailing" constant="10" id="uiz-4g-UrX"/>
+                                <constraint firstItem="ao2-UH-4ks" firstAttribute="leading" secondItem="lSf-BX-nnw" secondAttribute="trailing" constant="7" id="wwm-Tt-mb9"/>
+                                <constraint firstItem="kG6-iz-ttR" firstAttribute="centerY" secondItem="sp6-NA-5hp" secondAttribute="centerY" id="zVv-0U-ycP"/>
+                            </constraints>
+                        </view>
+                    </box>
+                    <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Ysf-e8-HyO">
+                        <rect key="frame" x="38" y="449" width="37" height="16"/>
+                        <textFieldCell key="cell" lineBreakMode="clipping" title="Label" id="T12-8I-gk0">
+                            <font key="font" metaFont="system"/>
+                            <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
+                            <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
+                        </textFieldCell>
+                    </textField>
+                    <customView translatesAutoresizingMaskIntoConstraints="NO" id="KlK-Pr-U01">
+                        <rect key="frame" x="40" y="134" width="240" height="310"/>
+                        <constraints>
+                            <constraint firstAttribute="width" constant="240" id="WTI-zx-zZo"/>
+                            <constraint firstAttribute="height" constant="310" id="mV6-MO-a3n"/>
+                        </constraints>
+                    </customView>
+                    <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="PWK-VM-IgH">
+                        <rect key="frame" x="67" y="80" width="26" height="26"/>
+                        <buttonCell key="cell" type="round" title="Button" bezelStyle="circular" image="NSGoLeftTemplate" imagePosition="only" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="9VB-Ib-GXt">
+                            <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+                            <font key="font" metaFont="system"/>
+                        </buttonCell>
+                        <connections>
+                            <action selector="previousButtonAction:" target="-2" id="jSd-Bj-Kjb"/>
+                        </connections>
+                    </button>
+                    <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="MbN-D4-nfu">
+                        <rect key="frame" x="228" y="80" width="26" height="26"/>
+                        <buttonCell key="cell" type="round" bezelStyle="circular" image="NSGoRightTemplate" imagePosition="only" alignment="center" borderStyle="border" inset="2" id="WTu-kN-EpF">
+                            <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+                            <font key="font" metaFont="system"/>
+                        </buttonCell>
+                        <connections>
+                            <action selector="nextButtonAction:" target="-2" id="o6y-3K-OoR"/>
+                        </connections>
+                    </button>
+                    <customView translatesAutoresizingMaskIntoConstraints="NO" id="Pat-bk-VMi">
+                        <rect key="frame" x="130" y="82" width="61" height="22"/>
+                        <subviews>
+                            <textField focusRingType="none" verticalHuggingPriority="750" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="1Pt-BN-QHC">
+                                <rect key="frame" x="0.0" y="0.0" width="40" height="22"/>
+                                <constraints>
+                                    <constraint firstAttribute="width" constant="40" id="dv1-d9-ury"/>
+                                    <constraint firstAttribute="height" constant="22" id="gCJ-pf-OIS"/>
+                                </constraints>
+                                <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" alignment="center" drawsBackground="YES" id="h6k-vZ-FfP">
+                                    <numberFormatter key="formatter" formatterBehavior="default10_4" usesGroupingSeparator="NO" groupingSize="0" minimumIntegerDigits="0" maximumIntegerDigits="42" id="b7J-mT-vc1">
+                                        <real key="minimum" value="1"/>
+                                    </numberFormatter>
+                                    <font key="font" metaFont="system"/>
+                                    <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
+                                    <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
+                                </textFieldCell>
+                                <connections>
+                                    <outlet property="delegate" destination="-2" id="Gfx-ih-Gxb"/>
+                                </connections>
+                            </textField>
+                            <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="z6n-8y-3TH">
+                                <rect key="frame" x="43" y="3" width="20" height="16"/>
+                                <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="/ 0" id="gY4-J5-ZrU">
+                                    <font key="font" metaFont="system"/>
+                                    <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
+                                    <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+                                </textFieldCell>
+                            </textField>
+                        </subviews>
+                        <constraints>
+                            <constraint firstItem="z6n-8y-3TH" firstAttribute="centerY" secondItem="1Pt-BN-QHC" secondAttribute="centerY" id="HUU-oe-9Ue"/>
+                            <constraint firstAttribute="trailing" secondItem="z6n-8y-3TH" secondAttribute="trailing" id="YZY-Dc-PSq"/>
+                            <constraint firstItem="z6n-8y-3TH" firstAttribute="leading" secondItem="1Pt-BN-QHC" secondAttribute="trailing" constant="5" id="ZiJ-co-aup"/>
+                            <constraint firstItem="1Pt-BN-QHC" firstAttribute="top" secondItem="Pat-bk-VMi" secondAttribute="top" id="gz7-Bp-xkG"/>
+                            <constraint firstItem="1Pt-BN-QHC" firstAttribute="leading" secondItem="Pat-bk-VMi" secondAttribute="leading" id="ktl-MN-cg5"/>
+                            <constraint firstAttribute="bottom" secondItem="1Pt-BN-QHC" secondAttribute="bottom" id="pRj-ir-scG"/>
+                        </constraints>
+                    </customView>
+                    <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="vbX-fH-NDe">
+                        <rect key="frame" x="366" y="447" width="80" height="20"/>
+                        <constraints>
+                            <constraint firstAttribute="height" constant="20" id="8I8-Cc-TEN"/>
+                        </constraints>
+                        <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="OCR Results" id="XQ5-IT-tqH">
+                            <font key="font" metaFont="system"/>
+                            <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
+                            <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+                        </textFieldCell>
+                    </textField>
+                    <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="TF7-NH-FRc">
+                        <rect key="frame" x="490" y="76" width="64" height="32"/>
+                        <buttonCell key="cell" type="push" title="clear" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyUpOrDown" inset="2" id="huA-aN-o9D">
+                            <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+                            <font key="font" metaFont="system"/>
+                        </buttonCell>
+                        <connections>
+                            <action selector="buttonItemClick_Delete:" target="-2" id="BLx-4a-9HW"/>
+                        </connections>
+                    </button>
+                    <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="iS7-aP-oXa">
+                        <rect key="frame" x="496" y="11" width="75" height="32"/>
+                        <buttonCell key="cell" type="push" title="Button" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="tHI-Ax-nOP">
+                            <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+                            <font key="font" metaFont="system"/>
+                        </buttonCell>
+                        <connections>
+                            <action selector="buttonClicked_SaveButton:" target="-2" id="zet-F3-yS4"/>
+                        </connections>
+                    </button>
+                    <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="PHr-ev-Pjc">
+                        <rect key="frame" x="549" y="76" width="66" height="32"/>
+                        <buttonCell key="cell" type="push" title="Copy" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyUpOrDown" inset="2" id="rtm-wM-sq8">
+                            <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+                            <font key="font" metaFont="system"/>
+                        </buttonCell>
+                        <connections>
+                            <action selector="buttonItemClick_CopyTxt:" target="-2" id="0Ed-bk-Qwp"/>
+                        </connections>
+                    </button>
+                    <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="cZ7-HB-Jv8">
+                        <rect key="frame" x="38" y="586" width="37" height="18"/>
+                        <constraints>
+                            <constraint firstAttribute="height" constant="18" id="l3I-hu-YKX"/>
+                        </constraints>
+                        <textFieldCell key="cell" lineBreakMode="clipping" title="Label" id="FS3-DN-FbP">
+                            <font key="font" metaFont="system"/>
+                            <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
+                            <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
+                        </textFieldCell>
+                    </textField>
+                    <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Lfw-RX-daG">
+                        <rect key="frame" x="566" y="11" width="75" height="32"/>
+                        <buttonCell key="cell" type="push" title="Button" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="0zs-ov-Umq">
+                            <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+                            <font key="font" metaFont="system"/>
+                        </buttonCell>
+                        <connections>
+                            <action selector="savePDFButtonAction:" target="-2" id="k5y-ij-2Nr"/>
+                        </connections>
+                    </button>
+                </subviews>
+                <constraints>
+                    <constraint firstItem="MbN-D4-nfu" firstAttribute="centerY" secondItem="Pat-bk-VMi" secondAttribute="centerY" id="06Y-Gs-BTe"/>
+                    <constraint firstItem="cZ7-HB-Jv8" firstAttribute="top" secondItem="se5-gp-TjO" secondAttribute="top" constant="18" id="49F-Pk-Mjc"/>
+                    <constraint firstItem="KlK-Pr-U01" firstAttribute="top" secondItem="Ysf-e8-HyO" secondAttribute="bottom" constant="5" id="5KY-Yw-LDU"/>
+                    <constraint firstItem="Pat-bk-VMi" firstAttribute="leading" secondItem="PWK-VM-IgH" secondAttribute="trailing" constant="40" id="5QR-f1-Obx"/>
+                    <constraint firstItem="vbX-fH-NDe" firstAttribute="centerY" secondItem="Ysf-e8-HyO" secondAttribute="centerY" id="9fJ-p9-Icg"/>
+                    <constraint firstItem="Lfw-RX-daG" firstAttribute="leading" secondItem="iS7-aP-oXa" secondAttribute="trailing" constant="9" id="B23-6G-SlK"/>
+                    <constraint firstItem="iS7-aP-oXa" firstAttribute="top" secondItem="PHr-ev-Pjc" secondAttribute="bottom" constant="45" id="BA1-GV-9In"/>
+                    <constraint firstAttribute="trailing" secondItem="Lfw-RX-daG" secondAttribute="trailing" constant="14" id="Cuf-Tx-rZk"/>
+                    <constraint firstItem="GA7-kq-Dba" firstAttribute="leading" secondItem="vbX-fH-NDe" secondAttribute="leading" id="FM9-od-yTH"/>
+                    <constraint firstAttribute="bottom" secondItem="iS7-aP-oXa" secondAttribute="bottom" constant="18" id="GC3-Qa-XIH"/>
+                    <constraint firstItem="Pat-bk-VMi" firstAttribute="centerY" secondItem="PWK-VM-IgH" secondAttribute="centerY" id="Gff-kk-oVm"/>
+                    <constraint firstItem="KlK-Pr-U01" firstAttribute="centerX" secondItem="Pat-bk-VMi" secondAttribute="centerX" id="Guh-eG-UNN"/>
+                    <constraint firstItem="jMC-LE-Zxs" firstAttribute="leading" secondItem="se5-gp-TjO" secondAttribute="leading" constant="24" id="L8e-s9-f1Z"/>
+                    <constraint firstItem="KlK-Pr-U01" firstAttribute="leading" secondItem="Ysf-e8-HyO" secondAttribute="leading" id="NU1-eg-I49"/>
+                    <constraint firstItem="MbN-D4-nfu" firstAttribute="leading" secondItem="Pat-bk-VMi" secondAttribute="trailing" constant="40" id="QMn-je-QtO"/>
+                    <constraint firstAttribute="trailing" secondItem="GA7-kq-Dba" secondAttribute="trailing" constant="40" id="SOt-iD-Nlm"/>
+                    <constraint firstItem="Ysf-e8-HyO" firstAttribute="top" secondItem="jMC-LE-Zxs" secondAttribute="bottom" constant="20" id="SdX-Rz-L1f"/>
+                    <constraint firstItem="TF7-NH-FRc" firstAttribute="centerY" secondItem="MbN-D4-nfu" secondAttribute="centerY" id="TNI-k2-Y9x"/>
+                    <constraint firstItem="wwH-or-0pV" firstAttribute="centerY" secondItem="GA7-kq-Dba" secondAttribute="centerY" id="U0j-1k-hap"/>
+                    <constraint firstItem="iS7-aP-oXa" firstAttribute="centerY" secondItem="D6y-c7-aMF" secondAttribute="centerY" id="UOT-95-LaA"/>
+                    <constraint firstAttribute="trailing" secondItem="jMC-LE-Zxs" secondAttribute="trailing" constant="24" id="V8S-8b-6xw"/>
+                    <constraint firstItem="PHr-ev-Pjc" firstAttribute="centerY" secondItem="MbN-D4-nfu" secondAttribute="centerY" id="WQt-vf-BFv"/>
+                    <constraint firstItem="KlK-Pr-U01" firstAttribute="leading" secondItem="se5-gp-TjO" secondAttribute="leading" constant="40" id="Y13-Kq-7Bl"/>
+                    <constraint firstItem="Lfw-RX-daG" firstAttribute="centerY" secondItem="iS7-aP-oXa" secondAttribute="centerY" id="ZD9-23-exW"/>
+                    <constraint firstItem="iS7-aP-oXa" firstAttribute="leading" secondItem="D6y-c7-aMF" secondAttribute="trailing" constant="9" id="cJ1-C7-288"/>
+                    <constraint firstItem="KlK-Pr-U01" firstAttribute="top" secondItem="GA7-kq-Dba" secondAttribute="top" id="gn0-mo-u5g"/>
+                    <constraint firstItem="PHr-ev-Pjc" firstAttribute="leading" secondItem="TF7-NH-FRc" secondAttribute="trailing" constant="9" id="j1v-H8-Qyn"/>
+                    <constraint firstItem="cZ7-HB-Jv8" firstAttribute="leading" secondItem="se5-gp-TjO" secondAttribute="leading" constant="40" id="llb-gY-gAS"/>
+                    <constraint firstItem="jMC-LE-Zxs" firstAttribute="top" secondItem="cZ7-HB-Jv8" secondAttribute="bottom" id="rOk-H5-sLv"/>
+                    <constraint firstItem="PHr-ev-Pjc" firstAttribute="trailing" secondItem="GA7-kq-Dba" secondAttribute="trailing" id="tQf-zw-VQU"/>
+                    <constraint firstItem="wwH-or-0pV" firstAttribute="centerX" secondItem="se5-gp-TjO" secondAttribute="centerX" id="vhB-Na-ut5"/>
+                </constraints>
+            </view>
+            <connections>
+                <outlet property="delegate" destination="-2" id="0bl-1N-AYu"/>
+            </connections>
+            <point key="canvasLocation" x="363" y="153"/>
+        </window>
+    </objects>
+    <resources>
+        <image name="KMImageNameUXIconOCRWarning" width="48" height="48"/>
+        <image name="NSDescendingSortIndicator" width="9" height="9"/>
+        <image name="NSGoLeftTemplate" width="12" height="17"/>
+        <image name="NSGoRightTemplate" width="12" height="17"/>
+    </resources>
+</document>

+ 352 - 0
PDF Office/PDF Master/Class/PDFTools/OCRNew/Model/KMGOCRManagerNew.swift

@@ -0,0 +1,352 @@
+//
+//  KMGOCRManagerNew.swift
+//  PDF Master
+//
+//  Created by liujiajie on 2023/11/15.
+//
+
+import Foundation
+import Cocoa
+import Vision
+
+
+let KMGOCRLanguageCodeKey   = "KMGOCRLanguageCodeKey"
+let KMGOCRLanguageStringKey = "KMGOCRLanguageStringKey"
+let KMImageScale = 4.0
+
+@objc enum KMOCRType: Int {
+    case Google = 0
+    case Apple
+}
+
+@objc(KMGOCRManagerNewDelegate)
+protocol KMGOCRManagerNewDelegate: AnyObject {
+    @objc optional func GOCRManagerDidStartOCR(_ manager: KMGOCRManagerNew)
+    @objc optional func GOCRManagerDidFinishOCR(_ manager: KMGOCRManagerNew)
+    @objc optional func GOCRManagerDidCancel(_ manager:KMGOCRManagerNew, atIndex:Int)
+    @objc optional func GOCRManagerDidStart(_ manager:KMGOCRManagerNew, atIndex:Int)
+    @objc optional func GOCRManagerDidFinish(_ manager:KMGOCRManagerNew, atIndex:Int, results: [Any])
+    @objc optional func GOCRManagerDidFail(_ manager:KMGOCRManagerNew, atIndex:Int, error: Error?)
+}
+
+class KMOCROperationQueue: OperationQueue{
+    static let sharedInstance: KMOCROperationQueue = {
+        let queue = KMOCROperationQueue()
+        return queue
+    }()
+    
+    func addOCROperation(op: Operation) {
+        self.addOperation(op)
+    }
+    func cancelAll() {
+        self.cancelAllOperations()
+    }
+    
+}
+
+@objcMembers class KMGOCRManagerNew: NSObject, KMGOCROperationDelegate{
+    var delegate: KMGOCRManagerNewDelegate?
+    var images: Array<Any>?
+    var OCRType: KMOCRType = .Apple
+    var selectedLanguages: Array<Any>?
+    var isOCR = false
+    
+    var languages: Array<String>?
+    var fileType: String = ""
+    var ocrPath: Array<Any>?
+    var filePath: URL?
+    var finishIndex: Int = 0
+    var appleRequest: VNRecognizeTextRequest?
+    var appleRecognitionMode: VNRequestTextRecognitionLevel?
+    
+    override init() {
+        super.init()
+    }
+    
+    static let defaultManager: KMGOCRManagerNew = {
+       let manager = KMGOCRManagerNew()
+        return manager
+    }()
+    /*class func languages() -> [[String: Any]] {
+        if KMGOCRManagerNew.defaultManager.OCRType == .Google {
+            return [[KMGOCRLanguageCodeKey: "af", KMGOCRLanguageStringKey: "Afrikaans"],
+                    [KMGOCRLanguageCodeKey: "sq", KMGOCRLanguageStringKey: "Albanian"],
+                    [KMGOCRLanguageCodeKey: "ar", KMGOCRLanguageStringKey: "Arabic"],
+                    [KMGOCRLanguageCodeKey: "hy", KMGOCRLanguageStringKey: "Armenian"],
+                    [KMGOCRLanguageCodeKey: "az", KMGOCRLanguageStringKey: "Azerbaijani"],
+                    [KMGOCRLanguageCodeKey: "eu", KMGOCRLanguageStringKey: "Basque"],
+                    [KMGOCRLanguageCodeKey: "be", KMGOCRLanguageStringKey: "Belarusian"],
+                    [KMGOCRLanguageCodeKey: "bn", KMGOCRLanguageStringKey: "Bengali"],
+                    [KMGOCRLanguageCodeKey: "bs", KMGOCRLanguageStringKey: "Bosnian"],
+                    [KMGOCRLanguageCodeKey: "bg", KMGOCRLanguageStringKey: "Bulgarian"],
+                    [KMGOCRLanguageCodeKey: "ca", KMGOCRLanguageStringKey: "Catalan"],
+                    [KMGOCRLanguageCodeKey: "ceb", KMGOCRLanguageStringKey: "Cebuano"],
+                    [KMGOCRLanguageCodeKey: "ny", KMGOCRLanguageStringKey: "Chichewa"],
+                    [KMGOCRLanguageCodeKey: "zh-CN", KMGOCRLanguageStringKey: "Chinese Simplified"],
+                    [KMGOCRLanguageCodeKey: "zh-TW", KMGOCRLanguageStringKey: "Chinese Traditional"],
+                    [KMGOCRLanguageCodeKey: "hr", KMGOCRLanguageStringKey: "Croatian"],
+                    [KMGOCRLanguageCodeKey: "cs", KMGOCRLanguageStringKey: "Czech"],
+                    [KMGOCRLanguageCodeKey: "da", KMGOCRLanguageStringKey: "Danish"],
+                    [KMGOCRLanguageCodeKey: "nl", KMGOCRLanguageStringKey: "Dutch"],
+                    [KMGOCRLanguageCodeKey: "en", KMGOCRLanguageStringKey: "English"],
+                    [KMGOCRLanguageCodeKey: "eo", KMGOCRLanguageStringKey: "Esperanto"],
+                    [KMGOCRLanguageCodeKey: "et", KMGOCRLanguageStringKey: "Estonian"],
+                    [KMGOCRLanguageCodeKey: "tl", KMGOCRLanguageStringKey: "Filipino"],
+                    [KMGOCRLanguageCodeKey: "fi", KMGOCRLanguageStringKey: "Finnish"],
+                    [KMGOCRLanguageCodeKey: "fr", KMGOCRLanguageStringKey: "French"],
+                    [KMGOCRLanguageCodeKey: "gl", KMGOCRLanguageStringKey: "Galician"],
+                    [KMGOCRLanguageCodeKey: "ka", KMGOCRLanguageStringKey: "Georgian"],
+                    [KMGOCRLanguageCodeKey: "de", KMGOCRLanguageStringKey: "German"],
+                    [KMGOCRLanguageCodeKey: "el", KMGOCRLanguageStringKey: "Greek"],
+                    [KMGOCRLanguageCodeKey: "gu", KMGOCRLanguageStringKey: "Gujarati"],
+                    [KMGOCRLanguageCodeKey: "ht", KMGOCRLanguageStringKey: "Haitian Creole"],
+                    [KMGOCRLanguageCodeKey: "ha", KMGOCRLanguageStringKey: "Hausa"],
+                    [KMGOCRLanguageCodeKey: "iw", KMGOCRLanguageStringKey: "Hebrew"],
+                    [KMGOCRLanguageCodeKey: "hi", KMGOCRLanguageStringKey: "Hindi"],
+                    [KMGOCRLanguageCodeKey: "hmn", KMGOCRLanguageStringKey: "Hmong"],
+                    [KMGOCRLanguageCodeKey: "hu", KMGOCRLanguageStringKey: "Hungarian"],
+                    [KMGOCRLanguageCodeKey: "is", KMGOCRLanguageStringKey: "Icelandic"],
+                    [KMGOCRLanguageCodeKey: "ig", KMGOCRLanguageStringKey: "Igbo"],
+                    [KMGOCRLanguageCodeKey: "id", KMGOCRLanguageStringKey: "Indonesian"],
+                    [KMGOCRLanguageCodeKey: "ga", KMGOCRLanguageStringKey: "Irish"],
+                    [KMGOCRLanguageCodeKey: "it", KMGOCRLanguageStringKey: "Italian"],
+                    [KMGOCRLanguageCodeKey: "ja", KMGOCRLanguageStringKey: "Japanese"],
+                    [KMGOCRLanguageCodeKey: "jw", KMGOCRLanguageStringKey: "Javanese"],
+                    [KMGOCRLanguageCodeKey: "kn", KMGOCRLanguageStringKey: "Kannada"],
+                    [KMGOCRLanguageCodeKey: "kk", KMGOCRLanguageStringKey: "Kazakh"],
+                    [KMGOCRLanguageCodeKey: "km", KMGOCRLanguageStringKey: "Khmer"],
+                    [KMGOCRLanguageCodeKey: "ko", KMGOCRLanguageStringKey: "Korean"],
+                    [KMGOCRLanguageCodeKey: "lo", KMGOCRLanguageStringKey: "Lao"],
+                    [KMGOCRLanguageCodeKey: "la", KMGOCRLanguageStringKey: "Latin"],
+                    [KMGOCRLanguageCodeKey: "lv", KMGOCRLanguageStringKey: "Latvian"],
+                    [KMGOCRLanguageCodeKey: "lt", KMGOCRLanguageStringKey: "Lithuanian"],
+                    [KMGOCRLanguageCodeKey: "mk", KMGOCRLanguageStringKey: "Macedonian"],
+                    [KMGOCRLanguageCodeKey: "mg", KMGOCRLanguageStringKey: "Malagasy"],
+                    [KMGOCRLanguageCodeKey: "ms", KMGOCRLanguageStringKey: "Malay"],
+                    [KMGOCRLanguageCodeKey: "ml", KMGOCRLanguageStringKey: "Malayalam"],
+                    [KMGOCRLanguageCodeKey: "mt", KMGOCRLanguageStringKey: "Maltese"],
+                    [KMGOCRLanguageCodeKey: "mi", KMGOCRLanguageStringKey: "Maori"],
+                    [KMGOCRLanguageCodeKey: "mr", KMGOCRLanguageStringKey: "Marathi"],
+                    [KMGOCRLanguageCodeKey: "mn", KMGOCRLanguageStringKey: "Mongolian"],
+                    [KMGOCRLanguageCodeKey: "my", KMGOCRLanguageStringKey: "Myanmar (Burmese)"],
+                    [KMGOCRLanguageCodeKey: "ne", KMGOCRLanguageStringKey: "Nepali"],
+                    [KMGOCRLanguageCodeKey: "no", KMGOCRLanguageStringKey: "Norwegian"],
+                    [KMGOCRLanguageCodeKey: "fa", KMGOCRLanguageStringKey: "Persian"],
+                    [KMGOCRLanguageCodeKey: "pl", KMGOCRLanguageStringKey: "Polish"],
+                    [KMGOCRLanguageCodeKey: "pt", KMGOCRLanguageStringKey: "Portuguese"],
+                    [KMGOCRLanguageCodeKey: "ma", KMGOCRLanguageStringKey: "Punjabi"],
+                    [KMGOCRLanguageCodeKey: "ro", KMGOCRLanguageStringKey: "Romanian"],
+                    [KMGOCRLanguageCodeKey: "ru", KMGOCRLanguageStringKey: "Russian"],
+                    [KMGOCRLanguageCodeKey: "sr", KMGOCRLanguageStringKey: "Serbian"],
+                    [KMGOCRLanguageCodeKey: "st", KMGOCRLanguageStringKey: "Sesotho"],
+                    [KMGOCRLanguageCodeKey: "si", KMGOCRLanguageStringKey: "Sinhala"],
+                    [KMGOCRLanguageCodeKey:"sk", KMGOCRLanguageStringKey:"Slovak"],
+                    [KMGOCRLanguageCodeKey:"sl", KMGOCRLanguageStringKey:"Slovenian"],
+                    [KMGOCRLanguageCodeKey:"so", KMGOCRLanguageStringKey:"Somali"],
+                    [KMGOCRLanguageCodeKey:"es", KMGOCRLanguageStringKey:"Spanish"],
+                    [KMGOCRLanguageCodeKey:"su", KMGOCRLanguageStringKey:"Sudanese"],
+                    [KMGOCRLanguageCodeKey:"sw", KMGOCRLanguageStringKey:"Swahili"],
+                    [KMGOCRLanguageCodeKey:"sv", KMGOCRLanguageStringKey:"Swedish"],
+                    [KMGOCRLanguageCodeKey:"tg", KMGOCRLanguageStringKey:"Tajik"],
+                    [KMGOCRLanguageCodeKey:"ta", KMGOCRLanguageStringKey:"Tamil"],
+                    [KMGOCRLanguageCodeKey:"te", KMGOCRLanguageStringKey:"Telugu"],
+                    [KMGOCRLanguageCodeKey:"th", KMGOCRLanguageStringKey:"Thai"],
+                    [KMGOCRLanguageCodeKey:"tr", KMGOCRLanguageStringKey:"Turkish"],
+                    [KMGOCRLanguageCodeKey:"uk", KMGOCRLanguageStringKey:"Ukrainian"],
+                    [KMGOCRLanguageCodeKey:"ur", KMGOCRLanguageStringKey:"Urdu"],
+                    [KMGOCRLanguageCodeKey:"uz", KMGOCRLanguageStringKey:"Uzbek"],
+                    [KMGOCRLanguageCodeKey:"vi", KMGOCRLanguageStringKey:"Vietnamese"],
+                    [KMGOCRLanguageCodeKey:"cy", KMGOCRLanguageStringKey:"Welsh"],
+                    [KMGOCRLanguageCodeKey:"yi", KMGOCRLanguageStringKey:"Yiddish"],
+                    [KMGOCRLanguageCodeKey:"yo", KMGOCRLanguageStringKey:"Yoruba"],
+                    [KMGOCRLanguageCodeKey:"zu", KMGOCRLanguageStringKey:"Zulu"]]
+        }
+        return [[KMGOCRLanguageCodeKey: "en-US", KMGOCRLanguageStringKey: "English"],
+                [KMGOCRLanguageCodeKey: "fr-FR", KMGOCRLanguageStringKey: "French"],
+                [KMGOCRLanguageCodeKey: "it-IT", KMGOCRLanguageStringKey: "Italian"],
+                [KMGOCRLanguageCodeKey: "de-DE", KMGOCRLanguageStringKey: "German"],
+                [KMGOCRLanguageCodeKey: "es-ES", KMGOCRLanguageStringKey: "Spanish"],
+                [KMGOCRLanguageCodeKey: "pt-BR", KMGOCRLanguageStringKey: "Portuguese"],
+                [KMGOCRLanguageCodeKey: "zh-Hant", KMGOCRLanguageStringKey: "Chinese Traditional"],
+                [KMGOCRLanguageCodeKey: "zh-Hans", KMGOCRLanguageStringKey: "Chinese Simplified"]
+               ]
+    }
+    
+    func recognitionImages(_ images: [Any], withLanguages languages: [Any]) {
+        recognitionImages(images, withLanguages: languages, fileType: nil, filePath: nil)
+    }
+    func recognitionImages(_ images: [Any], withLanguages languages: [Any], fileType: String?, filePath: URL?) {
+        self.ocrPath = []
+        self.finishIndex = 0
+        self.fileType = "PDF"
+        self.images = images
+        if filePath == nil {
+            self.filePath = URL(string: NSSearchPathForDirectoriesInDomains(.desktopDirectory, .userDomainMask, true)[0])
+        } else {
+            self.filePath = filePath
+        }
+        
+        if self.OCRType == .Google {
+            gocrRecognitionImages(images, withLanguages: languages)
+        } else {
+            if #available(macOS 10.15, *) {
+                if appleRequest != nil {
+                    appleRequest?.cancel()
+                    appleRequest = nil
+                }
+                recognitionAppleImage(at: self.finishIndex)
+            } else {
+                self.delegate?.GOCRManagerDidFail?(self, atIndex: self.finishIndex, error: nil)
+            }
+        }
+    }
+    func gocrRecognitionImages(_ images: [Any], withLanguages languages: [Any]) {
+        if images.isEmpty || images.count == 0 { return }
+        
+        self.delegate?.GOCRManagerDidStartOCR?(self)
+        
+        for i in 0..<images.count {
+            self.delegate?.GOCRManagerDidStart?(self, atIndex: i)
+            let queue = KMOCROperationQueue.sharedInstance
+            queue.maxConcurrentOperationCount = 1
+            var image = NSImage()
+            if images[i] is NSImage {
+                image = images[i] as! NSImage
+            } else {
+                let data = images[i] as! Data
+                image = NSImage(data: data) ?? NSImage()
+            }
+            let op = KMGOCROperation(recognitionImg: image, imgIndex: i)
+            op.selectedLanguages = NSMutableArray(array: languages) as? Array<Any>
+            op.operationDelegate = self
+            queue.addOperation(op)
+        }
+    }
+    func recognitionAppleImage(at index: Int) { 
+        guard index < self.images?.count ?? 0 else {
+            if #available(macOS 10.15, *) {
+                appleRequest?.cancel()
+            }
+            appleRequest = nil 
+            self.delegate?.GOCRManagerDidFinishOCR?(self)
+            return
+        }
+        finishIndex = index
+        delegate?.GOCRManagerDidStart?(self, atIndex: self.finishIndex)
+        
+        if let image = self.images?[finishIndex] as? NSImage {
+            recognitionAppleImage(self.images?[self.finishIndex] as! NSImage)
+        } else if let data = self.images?[finishIndex] as? Data {
+            let image = NSImage(data: data)
+            recognitionAppleImage(image!)
+        }
+    }
+    func recognitionAppleImage(_ image: NSImage) {
+        DispatchQueue.global().async { [weak self] in guard let self = self else { return }
+            self.appleRequest = VNRecognizeTextRequest { [weak self] request, error in
+                guard let self = self else { return }
+            var results: [KMGOCRResult]? = nil
+            if let reqResults = request.results as? [VNRecognizedTextObservation] {
+                results = self.responseDataRequest(request, dictionary: nil, imageSize: image.size)
+            }
+            
+            var resultArray = [[String: Any]]()
+            if let results = results {
+                for result in results {
+                    let dic: [String: Any] = ["x": result.textBounds.origin.x,
+                                              "y": result.textBounds.origin.y,
+                                              "width": result.textBounds.size.width,
+                                              "height": result.textBounds.size.height,
+                                              "text": result.text]
+                    resultArray.append(dic)
+                }
+            }
+            
+            DispatchQueue.main.async {
+                if let error = error {
+                    self.delegate?.GOCRManagerDidFail?(self, atIndex: self.finishIndex, error: error)
+                } else {
+                    self.delegate?.GOCRManagerDidFinish?(self, atIndex: self.finishIndex, results: results!)
+                }
+                self.recognitionAppleImage(at: self.finishIndex + 1)
+            }
+            }
+            self.appleRequest?.usesCPUOnly = true
+            self.appleRequest?.recognitionLevel = self.appleRecognitionMode ?? .accurate
+            
+            var array: Array<String> = self.languages ?? []
+            if array.contains("zh-Hant") {
+                array.removeAll(where: { $0 == "zh-Hant" })
+                array.insert("zh-Hant", at: 0)
+            }
+            if array.contains("zh-Hans") {
+                array.removeAll(where: { $0 == "zh-Hans" })
+                array.insert("zh-Hans", at: 0)
+            }
+            if array.isEmpty {
+                array = ["zh-Hans", "zh-Hant"]
+            }
+            self.appleRequest?.recognitionLanguages = array
+            
+            let options = [VNImageOption: Any]()
+            if let cgImage = self.nsImageToCGImageRef(image) {
+                let handler = VNImageRequestHandler(cgImage: cgImage, options: options)
+                try? handler.perform([self.appleRequest!])
+            }
+        }
+    }
+    
+    func nsImageToCGImageRef(_ image: NSImage) -> CGImage? {
+        guard let imageData = image.tiffRepresentation else { return nil }
+        if let imageSource = CGImageSourceCreateWithData(imageData as CFData, nil), let imageRef = CGImageSourceCreateImageAtIndex(imageSource, 0, nil) {
+            return imageRef
+        }
+        return nil
+    }
+    
+    func responseDataRequest(_ request: VNRequest, dictionary: NSDictionary?, imageSize: CGSize) -> [KMGOCRResult] {
+        var results: Array<KMGOCRResult> = Array()
+        let maximumCandidates = 1
+        var OCRStr = ""
+//        if let observations = request.results as? [VNRecognizedTextObservation] {
+//            for observation in observations {
+//                if let text = observation.topCandidates(maximumCandidates).first {
+//                    OCRStr.append("\(text.string)\n")
+//                    
+//                    var x: CGFloat = 0, y: CGFloat = 0, width: CGFloat = 0, height: CGFloat = 0
+//                    var error: Error?
+//                    let cnt = text.string.count
+//                    let range = 0 ..< cnt
+//
+//                    if let rectangleObservation = text.boundingBox(for: Range(location: 0, length: text.string.count), error: &error) {
+//                        x = rectangleObservation.topLeft.x * imageSize.width
+//                        y = (1 - rectangleObservation.topLeft.y) * imageSize.height
+//                        width = rectangleObservation.boundingBox.size.width * imageSize.width
+//                        height = rectangleObservation.boundingBox.size.height * imageSize.height
+//                    }
+//                    
+//                    let result = KMGOCRResult()
+//                    result.text = text.string
+//                    result.locale = ""
+//                    result.textBounds = CGRect(x: x, y: y, width: width, height: height)
+//                    results.append(result)
+//                }
+//            }
+//            // Following Google's logic, the first element of the array represents the text of the entire image
+//            if results.count > 0 {
+//                let result = KMGOCRResult()
+//                result.text = OCRStr
+//                if self.languages.count > 0 {
+//                    result.locale = self.languages[0]
+//                }
+//                result.textBounds = CGRect.zero
+//                results.insert(result, at: 0)
+//            }
+//        }
+        return results
+    }
+*/
+}
+
+
+

+ 250 - 0
PDF Office/PDF Master/Class/PDFTools/OCRNew/Model/KMGOCROperation.swift

@@ -0,0 +1,250 @@
+//
+//  KMGOCROperation.swift
+//  PDF Master
+//
+//  Created by liujiajie on 2023/11/15.
+//
+
+import Foundation
+
+@objc(KMGOCROperationDelegate)
+protocol KMGOCROperationDelegate: AnyObject {
+    @objc optional func GOCROperationCancel(_ op: KMGOCROperation, atIndex: Int)
+    @objc optional func GOCROperationStart(_ op: KMGOCROperation, atIndex: Int)
+    @objc optional func GOCROperationFinish(_ op: KMGOCROperation, atIndex: Int, results: Array<Any>)
+    @objc optional func GOCROperationFail(_ op: KMGOCROperation, atIndex: Int, err: Error?)
+}
+
+let KMGOC_API_URL = "https://vision.googleapis.com/v1/images:annotate"
+
+#if VERSION_DMG
+let KMGOC_API_KEY = "AIzaSyBhSRohpngAu8pSgFDXPytslNDHgGm7uDs"
+#else
+let KMGOC_API_KEY = "AIzaSyCJuqJ9YvtkFKMl1mW3Yq-av3mmI9ScbRY"
+#endif
+
+
+@objcMembers class KMGOCROperation: Operation{
+    @objc var operationDelegate: KMGOCROperationDelegate?
+    var selectedLanguages: Array<Any>?
+    override var isExecuting: Bool{
+        set{
+            self.willChangeValue(forKey: "isExecuting")
+            self.isExecuting = newValue
+            didChangeValue(forKey: "isExecuting")
+        }
+        get{
+            return super.isExecuting
+        }
+    }
+    override var isFinished: Bool{
+        set{
+            self.willChangeValue(forKey: "isFinished")
+            self.isFinished = newValue
+            didChangeValue(forKey: "isFinished")
+        }
+        get{
+            return super.isFinished
+        }
+    }
+    override var isCancelled: Bool{
+        set{
+            self.willChangeValue(forKey: "isCancelled")
+            self.isCancelled = newValue
+            didChangeValue(forKey: "isCancelled")
+        }
+        get{
+            return super.isCancelled
+        }
+    }
+
+    var fileName: String = ""
+    var orcImage: NSImage?
+    var task: URLSessionDataTask?
+    var imageIndex: Int = 0
+    
+    init(recognitionImg:NSImage, imgIndex:Int) {
+        super.init()
+        self.imageIndex = imgIndex
+        self.fileName = fileNameWithDate()
+        self.orcImage = recognitionImg
+        self.queuePriority = .normal
+        self.name = self.fileName
+        self.isExecuting = false
+        self.isFinished = false
+    }
+    func fileNameWithDate() -> String {
+        let formatter = DateFormatter()
+        formatter.dateFormat = "YYYY-MM-dd-hh-mm-ss-SSS"
+        let dateString = formatter.string(from: Date())
+        let fileName = "\(dateString) \(imageIndex)"
+        return fileName
+    }
+    
+    override func start() {
+        if p_checkCancelled() { return }
+        isExecuting = true
+        
+        Thread.detachNewThreadSelector(#selector(main), toTarget: self, with: nil)
+    }
+    @objc override func main() {
+        do {
+            if p_checkCancelled() { return }
+            recognitionImage(orcImage ?? NSImage())
+            while isExecuting {
+                if p_checkCancelled() {
+                    return
+                }
+            }
+        } catch let e {
+            Swift.debugPrint("Exception %@", e)
+        }
+    }
+    override func cancel() { 
+        super.cancel()
+        if task != nil {
+            task?.cancel()
+            task = nil
+        }
+        
+        self.operationDelegate?.GOCROperationCancel?(self, atIndex: self.imageIndex)
+        
+        if isExecuting {
+            isExecuting = false
+            isFinished = true
+        } else {
+            isFinished = false
+        }
+        isCancelled = true
+    }
+    func p_done() {
+        isExecuting = false
+        isFinished = true
+    }
+    func p_checkCancelled() -> Bool { 
+        if isCancelled {
+            isFinished = true
+            return true
+        }
+        return false
+    }
+    func recognitionImage(_ image: NSImage) {
+        self.operationDelegate?.GOCROperationStart?(self, atIndex: self.imageIndex)
+        
+        let binaryImageData = base64EncodeImage(image)
+        
+        if binaryImageData == nil {
+            self.operationDelegate?.GOCROperationFail?(self, atIndex: self.imageIndex, err: nil)
+            return
+        }
+        let urlString = "\(KMGOC_API_URL)?key=\(KMGOC_API_KEY)"
+        var request = URLRequest(url: URL(string: urlString)!)
+        request.httpMethod = "POST"
+        request.addValue("application/json", forHTTPHeaderField: "Content-Type")
+        
+        let imageDictionary = ["content": binaryImageData]
+        let featuresArray = [["type": "TEXT_DETECTION", "maxResults": 10]]
+        var paramsDictionary = ["requests": [["image": imageDictionary, "features": featuresArray]]] as [String: Any]
+        if selectedLanguages != nil && selectedLanguages?.count ?? 0 > 0 {
+            let imageContextDictionary = ["languageHints": selectedLanguages]
+            paramsDictionary = ["requests": [["image": imageDictionary, "features": featuresArray, "imageContext": imageContextDictionary]]] as [String: Any]
+        }
+        
+        let requestData = try? JSONSerialization.data(withJSONObject: paramsDictionary, options: [])
+        request.httpBody = requestData
+        
+        let URLSession = URLSession.shared
+        task = URLSession.dataTask(with: request) { (data, response, error) in
+            if (error as NSError?)?.code == NSURLErrorCancelled {
+                return
+            }
+            var results: [Any]? = nil
+            if error == nil {
+                let dictionary = try? JSONSerialization.jsonObject(with: data!, options: []) as? [String: Any]
+                results = self.responseDataResults(dictionary! as NSDictionary)
+            }
+            
+            DispatchQueue.main.async {
+                if error != nil || results == nil {
+                    self.operationDelegate?.GOCROperationFail?(self, atIndex: self.imageIndex, err: error)
+                } else {
+                    self.operationDelegate?.GOCROperationFinish?(self, atIndex: self.imageIndex, results: results ?? [])
+                }
+            }
+            if error != nil || results == nil {
+                self.cancel()
+            } else {
+                self.p_done()
+            }
+        }
+        task?.resume()
+    }
+    func base64EncodeImage(_ image: NSImage) -> String? {
+        guard let data = image.tiffRepresentation else { return nil }
+        let imageRep = NSBitmapImageRep(data: data)!
+        imageRep.size = image.size
+        let imageData = imageRep.representation(using: .png, properties: [:])
+        // Resize the image if it exceeds the 4MB API limit
+        if imageData?.count ?? 0 > 4194304 {
+            let compressedData = compressImageData(imageData!, toMaxFileSize: 4194304)
+            if let data = compressedData {
+                return data.base64EncodedString(options: .endLineWithCarriageReturn)
+            }
+        }
+        
+        if let data = imageData {
+            if #available(macOS 10.9, *) {
+                return data.base64EncodedString(options: .endLineWithCarriageReturn)
+            } else {
+                return data.base64EncodedString(options: [])
+            }
+        }
+        
+        return nil
+    }
+    func compressImageData(_ imageData: Data, toMaxFileSize maxFileSize: Int) -> Data? {
+        var compression: CGFloat = 0.9
+        let maxCompression: CGFloat = 0.1
+        var compressImageData = imageData
+        while compressImageData.count > maxFileSize && compression > maxCompression {
+            compression -= 0.1
+            let imageRep = NSBitmapImageRep(data: compressImageData)!
+            compressImageData = imageRep.representation(using: .jpeg, properties: [NSBitmapImageRep.PropertyKey.compressionFactor: NSNumber(value: Float(compression))])!
+        }
+        return compressImageData
+    }
+    func responseDataResults(_ dictionary: NSDictionary) -> [Any]? { 
+        guard let responses = dictionary["responses"] as? [Any],
+                let responseData = responses.first as? NSDictionary,
+              let errorObj = dictionary["error"] as? NSDictionary else { return nil }
+        var results: [Any]? = nil
+        if let textAnnotations = responseData["textAnnotations"] as? [Any] {
+            results = [Any]()
+            for annotation in textAnnotations {
+                var textBounds = CGRect.zero
+                let dic = (annotation as? NSDictionary)?["boundingPoly"]
+                if let vertices = (dic as? NSDictionary)?["vertices"] as? [Any] {
+                    var minX: CGFloat = 0, minY: CGFloat = 0, maxX: CGFloat = 0, maxY: CGFloat = 0
+                    for i in 0..<vertices.count {
+                        if let vertex = vertices[i] as? NSDictionary,
+                           let x = vertex["x"] as? CGFloat,
+                           let y = vertex["y"] as? CGFloat {
+                            minX = i == 0 ? x : min(x, minX)
+                            minY = i == 0 ? y : min(y, minY)
+                            maxX = i == 0 ? x : max(x, maxX)
+                            maxY = i == 0 ? y : max(y, maxY)
+                        }
+                    }
+                    textBounds = CGRect(x: minX, y: minY, width: maxX-minX, height: maxY-minY)
+                }
+                
+                let result = KMGOCRResult()
+                result.text = (annotation as? NSDictionary)?["description"] as? String ?? ""
+                result.locale = (annotation as? NSDictionary)?["locale"] as? String ?? ""
+                result.textBounds = textBounds
+                results?.append(result)
+            }
+        }
+        return results
+    }
+}

+ 351 - 0
PDF Office/PDF Master/Class/PDFTools/OCRNew/Model/KMOCROperation.swift

@@ -0,0 +1,351 @@
+//
+//  KMOCROperation.swift
+//  PDF Master
+//
+//  Created by liujiajie on 2023/11/15.
+//
+
+import Foundation
+
+
+let  KMOCR_API_URL = "https://api.convertio.co/convert"
+
+#if VERSION_DMG
+let KMOCR_API_KEY = "d1a0c1a4cc7fcac5eb08730570217f97"
+#else
+let KMOCR_API_KEY = "d1a0c1a4cc7fcac5eb08730570217f97"
+#endif
+
+let KMTIMER_MAXIMUM_CYCLE = 10
+
+@objc(KMOCROperationDelegate)
+protocol KMOCROperationDelegate: AnyObject {
+    @objc optional func OCROperationCancel(_ op: KMOCROperation, atIndex: Int)
+    @objc optional func OCROperationStart(_ op: KMOCROperation, atIndex: Int)
+    @objc optional func OCROperationFinish(_ op: KMOCROperation, atIndex: Int, results: Array<Any>)
+    @objc optional func OCROperationFail(_ op: KMOCROperation, atIndex: Int, err: Error?)
+}
+
+@objcMembers class KMOCROperation: Operation{
+    var operationDelegate: KMOCROperationDelegate?
+    var selectedLanguages: Array<String>?
+    var fileType: String = ""
+    var filePath: String = ""
+    var timerCycleCount = 0
+    var imageIndex = 0
+    var task: URLSessionDataTask?
+    var fileID: String?
+    var fileName: String = ""
+    var orcImage: NSImage?
+    var fileURL:URL?
+    override var isExecuting: Bool{
+        set{
+            self.willChangeValue(forKey: "isExecuting")
+            self.isExecuting = newValue
+            didChangeValue(forKey: "isExecuting")
+        }
+        get{
+            return super.isExecuting
+        }
+    }
+    override var isFinished: Bool{
+        set{
+            self.willChangeValue(forKey: "isFinished")
+            self.isFinished = newValue
+            didChangeValue(forKey: "isFinished")
+        }
+        get{
+            return super.isFinished
+        }
+    }
+    override var isCancelled: Bool{
+        set{
+            self.willChangeValue(forKey: "isCancelled")
+            self.isCancelled = newValue
+            didChangeValue(forKey: "isCancelled")
+        }
+        get{
+            return super.isCancelled
+        }
+    }
+    init(recognitionImg:NSImage, imgIndex:Int) {
+        super.init()
+        self.timerCycleCount = 0
+        self.imageIndex = imgIndex
+        self.orcImage = recognitionImg
+        self.fileName = fileNameWithDate()
+        self.fileID = nil
+        self.fileURL = nil
+        self.queuePriority = .normal
+        self.name = self.fileName
+        self.isExecuting = false
+        self.isFinished = false
+    }
+    func fileNameWithDate() -> String {
+        let formatter = DateFormatter()
+        formatter.dateFormat = "YYYY-MM-dd-hh-mm-ss-SSS"
+        let dateString = formatter.string(from: Date())
+        let fileName = "\(dateString) \(imageIndex)"
+        return fileName
+    }
+    override func start() {
+        if p_checkCancelled() { return }
+        isExecuting = true
+        
+        Thread.detachNewThreadSelector(#selector(main), toTarget: self, with: nil)
+    }
+    @objc override func main() {
+        do {
+            if p_checkCancelled() { return }
+            recognitionImage(image: orcImage ?? NSImage())
+            while isExecuting {
+                if p_checkCancelled() {
+                    return
+                }
+            }
+        } catch let e {
+            Swift.debugPrint("Exception %@", e)
+        }
+    }
+    override func cancel() {
+        super.cancel()
+        if task != nil {
+            task?.cancel()
+            task = nil
+        }
+        
+        self.operationDelegate?.OCROperationCancel?(self, atIndex: self.imageIndex)
+        
+        if isExecuting {
+            isExecuting = false
+            isFinished = true
+        } else {
+            isFinished = false
+        }
+        isCancelled = true
+    }
+    func p_done() {
+        isExecuting = false
+        isFinished = true
+    }
+    func p_checkCancelled() -> Bool {
+        if isCancelled {
+            isFinished = true
+            return true
+        }
+        return false
+    }
+    
+    func recognitionImage(image: NSImage) { 
+        guard let binaryImageData = base64EncodeImage(image) else { return }
+        let url = URL(string: KMOCR_API_URL)!
+        var request = URLRequest(url: url)
+        request.httpMethod = "POST"
+        request.addValue("application/json", forHTTPHeaderField: "Content-Type")
+        
+        let paramsDictionary: [String: Any] = [
+            "apikey": KMOCR_API_KEY,
+            "input": "base64",
+            "file": binaryImageData,
+            "outputformat": fileType,
+            "filename": "1.pdf",
+            "options": [
+                "ocr_enabled": true,
+                "ocr_settings": [
+                    "langs": selectedLanguages
+                ]
+            ]
+        ]
+        
+        let requestData = try? JSONSerialization.data(withJSONObject: paramsDictionary, options: [])
+        request.httpBody = requestData
+        
+        let URLSession = URLSession.shared
+        let convertTask = URLSession.dataTask(with: request) { [self] (data, response, error) in
+            guard let data = data else {
+                return
+            }
+            
+            if (error as NSError?)?.code == NSURLErrorCancelled {
+                return
+            }
+            
+            guard let dictionary = try? JSONSerialization.jsonObject(with: data, options: .mutableLeaves) as? [String: Any] else {
+                return
+            }
+            
+            guard let statusStr = dictionary["status"] as? String else {
+                return
+            }
+            
+            if statusStr == "ok" {
+                let dic = (dictionary["data"] as? NSDictionary)
+                self.fileID = dic?["id"] as? String
+                DispatchQueue.main.asyncAfter(deadline: .now() + 15.0) {
+                    self.requestQuery()
+                }
+            } else {
+                self.cancel()
+                operationDelegate?.OCROperationFail?(self, atIndex: self.imageIndex, err: error)
+            }
+        }
+        convertTask.resume()
+    }
+    func errorWithContent(content: String) -> NSError {
+        let domain = "com.MyCompany.MyApplication.ErrorDomain"
+        let userInfo = [NSLocalizedDescriptionKey: content]
+        let error = NSError(domain: domain, code: -101, userInfo: userInfo)
+        return error
+    }
+    func requestQuery() { 
+        let urlString = "\(KMOCR_API_URL)/\(fileID ?? "")/status"
+        guard let url = URL(string: urlString) else { return }
+        var request = URLRequest(url: url)
+        request.httpMethod = "GET"
+        
+        let URLSession = URLSession.shared
+        let task = URLSession.dataTask(with: request) { [self] (data, response, error) in
+            if (error as NSError?)?.code == NSURLErrorCancelled {
+                return
+            }
+            
+            guard let dictionary = try? JSONSerialization.jsonObject(with: data!, options: .mutableLeaves) as? [String: Any] else {
+                return
+            }
+            
+            guard let statusStr = dictionary["status"] as? String else {
+                return
+            }
+            
+            let dic = (dictionary["data"] as? NSDictionary)
+            self.fileID = dic?["id"] as? String
+            
+            if statusStr == "ok" {
+                if let task = self.task {
+                    task.cancel()
+                    self.task = nil
+                }
+                
+                if let fileData = dictionary["data"] as? [String: Any], let stepPercent = fileData["step_percent"] as? Int {
+                    if stepPercent == 100 {
+                        let dic = (fileData["output"] as? NSDictionary)
+                        if let fileURLString = dic?["url"] as? String, let fileURL = URL(string: fileURLString) {
+                            self.fileURL = fileURL
+                            self.getResultFileContent()
+                        }
+                    } else {
+                        if self.timerCycleCount >= KMTIMER_MAXIMUM_CYCLE {
+                            self.cancel()
+                            operationDelegate?.OCROperationFail?(self, atIndex: self.imageIndex, err: error)
+                        } else {
+                            self.timerCycleCount += 1
+                            DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
+                                self.requestQuery()
+                            }
+                        }
+                    }
+                }
+            } else {
+                self.cancel()
+                operationDelegate?.OCROperationFail?(self, atIndex: self.imageIndex, err: error)
+            }
+        }
+        task.resume()
+    }
+    
+    func getResultFileContent() { 
+        let urlString = "\(KMOCR_API_URL)/\(fileID ?? "")/dl"
+        guard let url = URL(string: urlString) else { return }
+        var request = URLRequest(url: url)
+        request.httpMethod = "GET"
+        
+        let URLSession = URLSession.shared
+        let fileContentTask = URLSession.dataTask(with: request) { (data, response, error) in
+            if (error as NSError?)?.code == NSURLErrorCancelled {
+                return
+            }
+            
+            var fileData: Data?
+            guard let dictionary = try? JSONSerialization.jsonObject(with: data!, options: .mutableLeaves) as? [String: Any] else {
+                return
+            }
+            
+            guard let statusStr = dictionary["status"] as? String else {
+                return
+            }
+            
+            if statusStr == "ok" {
+                fileData = self.responseDataResults((dictionary as NSDictionary) as! [String : Any])
+            } else {
+                // err = self.errorWithContent(dictionary["error"])
+            }
+            
+            DispatchQueue.main.async { [self] in
+                if let error = error, fileData == nil {
+                    self.cancel()
+                    if let err = error as NSError? {
+                        operationDelegate?.OCROperationFail?(self, atIndex: self.imageIndex, err: error)
+                    }
+                } else {
+                    if let fileData = fileData {
+                        let arr = [fileData]
+                        if let delegate = operationDelegate {
+                            delegate.OCROperationFinish?(self, atIndex: self.imageIndex, results: arr)
+                        }
+                        self.p_done()
+                    }
+                }
+            }
+        }
+        fileContentTask.resume()
+    }
+    
+    func base64EncodeImage(_ image: NSImage) -> String? {
+        guard let data = image.tiffRepresentation else { return nil }
+        let imageRep = NSBitmapImageRep(data: data)!
+        imageRep.size = image.size
+        let imageData = imageRep.representation(using: .png, properties: [:])
+        // Resize the image if it exceeds the 4MB API limit
+        if imageData?.count ?? 0 > 4194304 {
+            let compressedData = compressImageData(imageData!, toMaxFileSize: 4194304)
+            if let data = compressedData {
+                return data.base64EncodedString(options: .endLineWithCarriageReturn)
+            }
+        }
+        
+        if let data = imageData {
+            if #available(macOS 10.9, *) {
+                return data.base64EncodedString(options: .endLineWithCarriageReturn)
+            } else {
+                return data.base64EncodedString(options: [])
+            }
+        }
+        
+        return nil
+    }
+    
+    func compressImageData(_ imageData: Data, toMaxFileSize maxFileSize: Int) -> Data? {
+        var compression: CGFloat = 0.9
+        let maxCompression: CGFloat = 0.1
+        var compressImageData = imageData
+        while compressImageData.count > maxFileSize && compression > maxCompression {
+            compression -= 0.1
+            let imageRep = NSBitmapImageRep(data: compressImageData)!
+            compressImageData = imageRep.representation(using: .jpeg, properties: [NSBitmapImageRep.PropertyKey.compressionFactor: NSNumber(value: Float(compression))])!
+        }
+        return compressImageData
+    }
+    func responseDataResults(_ dictionary: [String: Any]) -> Data? { 
+        guard let responses = dictionary["data"] as? [String: Any] else { return nil }
+        guard let stringBase64 = responses["content"] as? String else {
+            return nil
+        }
+        
+        guard let data = Data(base64Encoded: stringBase64, options: .ignoreUnknownCharacters) else {
+            return nil
+        }
+        
+        try? data.write(to: URL(fileURLWithPath: filePath), options: .atomic)
+        return data
+    }
+}

+ 4 - 0
PDF Office/PDF Master/PDF_Master DMG-Bridging-Header.h

@@ -60,3 +60,7 @@
 
 #import "KMConvertURLToPDF.h"
 #import "NSImage_SKExtensions.h"
+
+#import "KMOCTool.h"
+#import "KMGOCRManager.h"
+

+ 4 - 0
PDF Office/PDF Master/PDF_Master Pro-Bridging-Header.h

@@ -43,3 +43,7 @@
 
 #import "KMConvertURLToPDF.h"
 #import "NSImage_SKExtensions.h"
+
+#import "KMOCTool.h"
+#import "KMGOCRManager.h"
+

+ 4 - 0
PDF Office/PDF Master/PDF_Master-Bridging-Header.h

@@ -57,3 +57,7 @@
 #import "KMConvertURLToPDF.h"
 
 #import "NSImage_SKExtensions.h"
+
+#import "KMOCTool.h"
+#import "KMGOCRManager.h"
+