// // KMBatchOperateImageToPDFViewController.swift // PDF Reader Pro // // Created by liujiajie on 2023/12/5. // import Cocoa class KMBatchOperateImageToPDFViewController: KMBatchOperateBaseViewController, KMImageToPDFMethodDelegate, NSPopoverDelegate{ @IBOutlet var outputTypeLabel: NSTextField! @IBOutlet var createNewPDFBtn: NSButton! @IBOutlet var btnMerge: NSButton! @IBOutlet var appendPDFBtn: NSButton! @IBOutlet var appendTextField: NSTextField! @IBOutlet var appendOtherPDFBtn: NSButton! @IBOutlet var appendBackView: NSView! @IBOutlet var ocrLabel: NSTextField! @IBOutlet var ocrSelectBtn: NSButton! @IBOutlet var languaeBox: NSBox! @IBOutlet var languageButton: NSButton! @IBOutlet var saveAsButton: NSButton! @IBOutlet var planButton: NSButton! @IBOutlet var selectLanguageLabel: NSTextField! @IBOutlet var planBox: NSBox! @IBOutlet var actionButton: NSButton! var password: String = "" lazy var method: KMImageToPDFMethod = { let method = KMImageToPDFMethod() method.imageTopdfDelegate = self return method }() override var interfaceStatus: KMBatchOperateInterfaceStatus?{ set{ super.interfaceStatus = newValue if newValue == .PrepareProcess { DispatchQueue.main.asyncAfter(deadline: .now() + 0.4) { let files = NSMutableArray() for url in self.successFilePathURLArray! { if FileManager.default.fileExists(atPath: url.path) { files.add(url) } } if files.count > 0 { let workspace = NSWorkspace.shared workspace.activateFileViewerSelecting(files as! [URL]) } } self._updateActionButtonEnable(true) } else { self._updateActionButtonEnable(false) } } get{ return super.interfaceStatus } } private func _updateActionButtonEnable(_ enabled: Bool) { if enabled { self.actionButton.tag = 1 self.actionButton.title = NSLocalizedString("Save as PDF", comment: "") self.actionButton.setTitleColor(KMAppearance.Layout.w0Color()) self.actionButton.isEnabled = true self.actionButton.layer?.backgroundColor = KMAppearance.Interactive.m0Color().cgColor } else { self.actionButton.tag = 0 self.actionButton.layer?.backgroundColor = KMAppearance.Interactive.m0Color().cgColor self.actionButton.setTitleColor(KMAppearance.Layout.w0Color()) self.actionButton.isEnabled = false } } deinit { NotificationCenter.default.removeObserver(self) DistributedNotificationCenter.default().removeObserver(self) } override func viewDidLoad() { super.viewDidLoad() self.localizedLanguage() self.configuUI() if let cnt = self.files?.count, cnt > 0 { self._updateActionButtonEnable(true) } else { self._updateActionButtonEnable(false) } 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) // NotificationCenter.default.addObserver(self, selector: #selector(themeChanged(notification:)), name: NSNotification.Name("AppleInterfaceThemeChangedNotification"), object: nil) DistributedNotificationCenter.default().addObserver(self, selector: #selector(themeChanged(notification:)), name: NSNotification.Name("AppleInterfaceThemeChangedNotification"), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(batchFilesCountNotification(notification:)), name: NSNotification.Name("KMBatchFilesCountNotification"), object: nil) } func localizedLanguage() { self.outputTypeLabel.stringValue = KMLocalizedString("Output",nil) self.btnMerge.title = KMLocalizedString("Merge All", nil) self.createNewPDFBtn.title = KMLocalizedString("New PDF Document", nil) self.appendPDFBtn.title = KMLocalizedString("Append To Existing File", nil) self.appendTextField.placeholderString = KMLocalizedString("Select a File", nil) self.selectLanguageLabel.stringValue = KMLocalizedString("Select OCR Language:",nil) self.ocrSelectBtn.title = KMLocalizedString("OCR Plan",nil) self.updateLanguageButton((KMGOCRManager.default().selectedLanguages?.value(forKeyPath: KMGOCRLanguageStringKey) as? [String])) self.actionButton.title = KMLocalizedString("Save as PDF", nil) self.saveAsButton.title = KMLocalizedString("Save as TXT", nil) self.OCRSelectedPlanChangeAction() } func configuUI() { self.view.wantsLayer = true appendOtherPDFBtn.wantsLayer = true appendBackView.wantsLayer = true appendBackView.layer?.borderWidth = 0.5 self.actionButton.wantsLayer = true self.actionButton.layer?.backgroundColor = KMAppearance.Interactive.m0Color().cgColor self.appendOtherPDFBtn.layer?.backgroundColor = KMAppearance.Interactive.s0Color().withAlphaComponent(0.4).cgColor self.actionButton.setTitleColor(KMAppearance.Layout.w0Color()) self.actionButton.layer?.cornerRadius = 1.0 self.createNewPDFBtn.setTitleColor(KMAppearance.Layout.h0Color()) self.btnMerge.setTitleColor(KMAppearance.Layout.h0Color()) self.appendPDFBtn.setTitleColor(KMAppearance.Layout.h0Color()) self.ocrSelectBtn.setTitleColor(KMAppearance.Layout.h0Color()) self.saveAsButton.setTitleColor(KMAppearance.Layout.h0Color()) self.selectLanguageLabel.textColor = KMAppearance.Layout.h0Color() self.languageButton.isEnabled = false self.planButton.isEnabled = false self.saveAsButton.isEnabled = false appendTextField.backgroundColor = KMAppearance.Layout.l1Color() planButton.wantsLayer = true appendTextField.wantsLayer = true planButton.wantsLayer = true appendTextField.layer?.cornerRadius = 1.0 languageButton.layer?.backgroundColor = NSColor.clear.cgColor languaeBox.borderColor = KMAppearance.Interactive.s0Color() planBox.borderColor = KMAppearance.Interactive.s0Color() languaeBox.fillColor = KMAppearance.Layout.l1Color() planBox.fillColor = KMAppearance.Layout.l1Color() self.updateViewColor() } func updateViewColor() { if KMAppearance.isDarkMode() { self.view.layer?.backgroundColor = NSColor(red: 0.055, green: 0.067, blue: 0.078, alpha: 1).cgColor appendBackView.layer?.borderColor = NSColor(red: 86/255.0, green: 88/255.0, blue: 90/255.0, alpha: 1).cgColor appendBackView.layer?.backgroundColor = NSColor(red: 57/255.0, green: 60/255.0, blue: 62/255.0, alpha: 1).cgColor } else { self.view.layer?.backgroundColor = NSColor(red: 0.922, green: 0.925, blue: 0.941, alpha: 1).cgColor appendBackView.layer?.borderColor = NSColor(red: 218/255.0, green: 219/255.0, blue: 222/255.0, alpha: 1).cgColor appendBackView.layer?.backgroundColor = NSColor.white.cgColor; } } func updateLanguageButton(_ languages: [String]?) { if languages?.count ?? 0 < 1 { self.languageButton.title = " " + KMLocalizedString("Auto Detection", nil) return } var languageName: String = "" if languages?.count ?? 0 > 0 { for i in 0..<(languages?.count ?? 0) { let language = languages?[i] if i == 0 { languageName = language ?? "" } else { languageName = languageName.appendingFormat(",%@", language ?? "") } } } else { languageName = "" } self.languageButton.title = " " + languageName } func converArrType(arr: Array, keyString: String) -> [String] { let newArr = NSMutableArray() for item in arr { newArr.add(item.filePath) } return newArr as! [String] } func isConnectionAvailable() -> Bool { // var isExistenceNetwork = true // let reach = Reachability(hostname: "www.apple.com") // let status: NetworkStatus = NetworkStatus(rawValue: (reach?.currentReachabilityStatus())!.rawValue) // switch status.rawValue { // case 0: // isExistenceNetwork = false // case 1: // isExistenceNetwork = true // case 2: // isExistenceNetwork = true // default: // break // } if Reachability.forInternetConnection().currentReachabilityStatus().rawValue == 0 { return false } return true } func beginImageToPDF() { if self.files?.count ?? 0 < 1 { return } let photoArray = converArrType(arr: self.files!, keyString: "") var path: String = "" var isMerge = false var isCreatNewPDF = false var isOCR = false if self.ocrSelectBtn.state == .on { isOCR = true } var isSaveAs = false if self.saveAsButton.state == .on { isSaveAs = true } let plan = UserDefaults.standard.integer(forKey: "KMOCRCurrentPlanKey") if isOCR && !self.isConnectionAvailable() && plan == 0 { let alert = NSAlert() alert.alertStyle = .critical alert.messageText = NSLocalizedString("Connection Error", comment: "") alert.informativeText = NSLocalizedString("Please make sure your internet connection is available.", comment: "") alert.addButton(withTitle: NSLocalizedString("OK", comment: "")) if alert.responds(to: #selector(alert.beginSheetModal(for:completionHandler:))) { alert.beginSheetModal(for: self.view.window!, completionHandler: nil) } else { alert.runModal() } return } if self.createNewPDFBtn.state == .on { if (self.choosePath.count < 1) { let alert = NSAlert() alert.alertStyle = .critical alert.messageText = String(format: NSLocalizedString("Output Folder cannot be empty.", comment: "")) alert.runModal() return } path = self.choosePath if self.btnMerge.state == .on { isMerge = true } isCreatNewPDF = true } else { let appenString = self.appendTextField.stringValue if appenString.isEmpty { let alert = NSAlert() alert.alertStyle = .critical alert.messageText = String(format: NSLocalizedString("Select a File", comment: "")) alert.runModal() return } path = self.appendTextField.stringValue isMerge = true isCreatNewPDF = false } self.languageButton.isEnabled = false self.planButton.isEnabled = false self.method.password = self.password self.interfaceStatus = .Processing self.method.saveAsTestPath = self.choosePath self.method.exportPDFFile(fileArray: photoArray, savePath: path, isOCR: isOCR, isCreatPDF: isCreatNewPDF, isMerge: isMerge, isSaveAsText: isSaveAs) { [weak self] savePath, errorArr, errorOCRArray in self?.languageButton.isEnabled = true self?.planButton.isEnabled = true self?.interfaceStatus = .PrepareProcess if errorArr.count > 0 { let dict: [String: Any] = ["isMerge": false, "isSuccess": false] NotificationCenter.default.post(name: NSNotification.Name(rawValue: "KMBatchOperateImageToPDFSuccessNotification"), object: self, userInfo: dict) let alert = NSAlert() alert.messageText = NSLocalizedString("Conversion Failed", comment: "") alert.addButton(withTitle: NSLocalizedString("OK", comment: "")) alert.informativeText = "\(errorArr)" alert.alertStyle = .informational alert.runModal() } else { if errorOCRArray.count > 0 { var contextString = NSLocalizedString("Some problems occurred during the last operation:", comment: "") for filePath in errorOCRArray { contextString += "\n" + (filePath as AnyObject).lastPathComponent } let alert = NSAlert() alert.messageText = NSLocalizedString("Converted Successfully", comment: "") alert.addButton(withTitle: NSLocalizedString("OK", comment: "")) alert.informativeText = contextString alert.alertStyle = .informational let response = alert.runModal() if response == .OK { self?.viewFileAtFinder(fileName: savePath) } } else { self?.viewFileAtFinder(fileName: savePath) } } } } func viewFileAtFinder(fileName: String) { let dict = ["isMerge": true, "isSuccess": true] NotificationCenter.default.post(name: Notification.Name("KMBatchOperateImageToPDFSuccessNotification"), object: self, userInfo: dict) let workspace = NSWorkspace.shared let url = URL(fileURLWithPath: fileName) workspace.activateFileViewerSelecting([url]) } //MARK: Notification @objc func OCRSelectedLanguagesChangeNotification(notification: Notification) { let selectedLanguages = notification.object/* as? [KMBatchOperateFile]*/ self.updateLanguageButton(selectedLanguages as? [String]) } @objc func OCRSelectedPlanChangeNotification(notification: Notification) { self.OCRSelectedPlanChangeAction() } @objc func themeChanged(notification: Notification) { DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { self.updateViewColor() } } func OCRSelectedPlanChangeAction() { let plan = UserDefaults.standard.integer(forKey: "KMOCRCurrentPlanKey") if plan == 0 { self.planButton.title = " " + KMLocalizedString("Plan 1 (Online)", nil) } else { self.planButton.title = " " + KMLocalizedString("Plan 2 (Offline)", nil) } KMGOCRManager.default().selectedLanguages = NSMutableArray() self.updateLanguageButton(KMGOCRManager.default().selectedLanguages?.value(forKeyPath: KMGOCRLanguageStringKey) as? [String]) } @objc func batchFilesCountNotification(notification: Notification) { let files: Array? = notification.object as? [KMBatchOperateFile] self.files? = files ?? [] let cnt = self.files?.count ?? 0 if cnt > 0 { self._updateActionButtonEnable(true) } else { self._updateActionButtonEnable(false) } } @IBAction func buttonClicked_CreateNewPDF(_ sender: NSButton) { if (sender.state == .on) { self.btnMerge.isEnabled = true self.appendPDFBtn.state = .off self.appendOtherPDFBtn.isEnabled = false } } @IBAction func buttonClicked_AppendOtherPDF(_ sender: NSButton) { if (sender.state == .on) { self.createNewPDFBtn.state = .off self.btnMerge.isEnabled = false self.appendOtherPDFBtn.isEnabled = true } } @IBAction func buttonClicked_OCRSelect(_ sender: NSButton) { //MARK: 判断是否付费用户 if KMMemberInfo.shared.isMemberAllFunction == false { KMMemberInfo.shared.advancedFunctionUsage() self.ocrSelectBtn.state = .off return } if (sender.state == .on) { self.languageButton.isEnabled = true self.planButton.isEnabled = true self.saveAsButton.isEnabled = true } else { self.languageButton.isEnabled = false self.planButton.isEnabled = false self.saveAsButton.isEnabled = false } } @IBAction func buttonClicked_ChooseLanguage(_ sender: NSButton) { let plan = UserDefaults.standard.integer(forKey: "KMOCRCurrentPlanKey") if plan == 0 { KMGOCRManager.default().ocrType = .google } else { KMGOCRManager.default().ocrType = .apple } let popover = NSPopover() popover.delegate = self popover.contentViewController = KMLanguageViewController(nibName: "KMLanguageViewController", bundle: Bundle.main) popover.animates = true popover.behavior = .transient popover.show(relativeTo: sender.bounds, of: sender as NSView, preferredEdge: .minX) } @IBAction func buttonClicked_ChoosePlan(_ sender: NSButton) { let popover = NSPopover() popover.delegate = self popover.contentViewController = KMPlanViewController(nibName: "KMPlanViewController", bundle: Bundle.main) popover.animates = true popover.behavior = .transient popover.show(relativeTo: sender.bounds, of: sender as NSView, preferredEdge: .minX) } @IBAction func buttonClicked_ImageToPDF(_ sender: NSButton) { //MARK: 判断是否付费用户,展示iap界面 if self.files?.count ?? 0 < 1 { return } if KMMemberInfo.shared.isMemberAllFunction == false && self.files?.count ?? 0 > 1 { KMMemberInfo.shared.advancedFunctionUsage() return } if sender.tag == 1 { self.choosePath = "" var hasTask = false for i in 0..<(self.files?.count ?? 0) { let file = self.files?[i] file?.currentOperateInfo?.resetState() if file?.fileType == .Image { hasTask = true } } if !hasTask { NSSound.beep() return } var isOCR = false if self.ocrSelectBtn.state == .on { isOCR = true } var isSaveAs = false if self.saveAsButton.state == .on { isSaveAs = true } let plan = UserDefaults.standard.integer(forKey: "KMOCRCurrentPlanKey") if isOCR && !isConnectionAvailable() && plan == 0 { let alert = NSAlert() alert.alertStyle = .critical alert.messageText = NSLocalizedString("Connection Error", comment: "") alert.informativeText = NSLocalizedString("Please make sure your internet connection is available.", comment: "") alert.addButton(withTitle: NSLocalizedString("OK", comment: "")) if let window = self.view.window { alert.beginSheetModal(for: window, completionHandler: nil) } else { alert.runModal() } return } if self.createNewPDFBtn.state == .off { let appenString = self.appendTextField.stringValue if appenString.isEmpty { let alert = NSAlert() alert.alertStyle = .critical alert.messageText = NSLocalizedString("Select a File", comment: "") alert.runModal() return } } let openPanel = NSOpenPanel() openPanel.canChooseFiles = false openPanel.canChooseDirectories = true openPanel.canCreateDirectories = true openPanel.beginSheetModal(for: self.view.window!) { (result) in if result == .OK { for fileURL in openPanel.urls { self.choosePath = fileURL.path self.beginImageToPDF() } } } } else { // Do something else } } } @IBAction func buttonItemClicked_AppendOtherPDF(_ sender: NSButton) { let openPanel = NSOpenPanel() openPanel.allowedFileTypes = ["pdf"] openPanel.canChooseDirectories = false openPanel.allowsMultipleSelection = false openPanel.beginSheetModal(for: self.view.window!) { (result) in if result == .OK { guard let url = openPanel.url else { return } guard let document = PDFDocument(url: url) else { let alert = NSAlert() alert.alertStyle = .critical alert.messageText = NSLocalizedString("An error occurred while opening this document. The file is damaged and could not be repaired.", comment: "") alert.runModal() return } if !document.allowsCopying || !document.allowsPrinting { let alert = NSAlert() alert.alertStyle = .critical alert.messageText = NSLocalizedString("This is a secured document. Editing is not permitted.", comment: "") alert.runModal() return } if document.isLocked { KMBaseWindowController.checkPassword(url: url, type: .owner) { [weak self] success, resultPassword in if success { self?.password = resultPassword self?.appendTextField.stringValue = url.path } } } else { self.appendTextField.stringValue = url.path } } } } @IBAction func buttonClicked_Help(_ sender: NSButton) { let helpController = NSViewController() let textView = NSTextView(frame: NSRect(x: 0, y: 0, width: 300.0, height: 100.0)) textView.backgroundColor = NSColor.clear textView.isEditable = false textView.layer?.cornerRadius = 6 let tStrAuto = NSLocalizedString("Choose automatic language detection for better OCR results.", comment: "") let tStrVPN = NSLocalizedString("The OCR service works via an internet connection. We would suggest you to perform OCR using a VPN connection while the service is limited.", comment: "") let plan = UserDefaults.standard.integer(forKey: "KMOCRCurrentPlanKey") if plan == 0 { textView.string = "\(tStrAuto)\n\n\(tStrVPN)" } else { textView.frame = NSRect(x: 0, y: 0, width: 300.0, height: 40.0) textView.string = tStrAuto } helpController.view = textView let popover = NSPopover() popover.delegate = self popover.contentViewController = helpController popover.animates = true popover.behavior = .transient popover.show(relativeTo: sender.bounds, of: sender as NSView, preferredEdge: .minY) } //MARK: KMImageToPDFMethodDelegate func imageToPDFMethod(_ method: KMImageToPDFMethod, progress: Float) { } }