KMBatchOperateImageToPDFViewController.swift 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  1. //
  2. // KMBatchOperateImageToPDFViewController.swift
  3. // PDF Master
  4. //
  5. // Created by liujiajie on 2023/12/5.
  6. //
  7. import Cocoa
  8. class KMBatchOperateImageToPDFViewController: KMBatchOperateBaseViewController, KMImageToPDFMethodDelegate, NSPopoverDelegate{
  9. @IBOutlet var outputTypeLabel: NSTextField!
  10. @IBOutlet var createNewPDFBtn: NSButton!
  11. @IBOutlet var btnMerge: NSButton!
  12. @IBOutlet var appendPDFBtn: NSButton!
  13. @IBOutlet var appendTextField: NSTextField!
  14. @IBOutlet var appendOtherPDFBtn: NSButton!
  15. @IBOutlet var appendBackView: NSView!
  16. @IBOutlet var ocrLabel: NSTextField!
  17. @IBOutlet var ocrSelectBtn: NSButton!
  18. @IBOutlet var languaeBox: NSBox!
  19. @IBOutlet var languageButton: NSButton!
  20. @IBOutlet var saveAsButton: NSButton!
  21. @IBOutlet var planButton: NSButton!
  22. @IBOutlet var selectLanguageLabel: NSTextField!
  23. @IBOutlet var planBox: NSBox!
  24. @IBOutlet var actionButton: NSButton!
  25. var password: String = ""
  26. lazy var method: KMImageToPDFMethod = {
  27. let method = KMImageToPDFMethod()
  28. method.imageTopdfDelegate = self
  29. return method
  30. }()
  31. override var interfaceStatus: KMBatchOperateInterfaceStatus?{
  32. set{
  33. super.interfaceStatus = newValue
  34. if newValue == .PrepareProcess {
  35. DispatchQueue.main.asyncAfter(deadline: .now() + 0.4) {
  36. var files = NSMutableArray()
  37. for url in self.successFilePathURLArray! {
  38. if FileManager.default.fileExists(atPath: url.path) {
  39. files.add(url)
  40. }
  41. }
  42. if files.count > 0 {
  43. let workspace = NSWorkspace.shared
  44. workspace.activateFileViewerSelecting(files as! [URL])
  45. }
  46. }
  47. self.actionButton.tag = 1
  48. self.actionButton.title = NSLocalizedString("Save as PDF", comment: "")
  49. self.actionButton.setTitleColor(KMAppearance.Layout.w0Color())
  50. self.actionButton.isEnabled = true
  51. self.actionButton.layer?.backgroundColor = KMAppearance.Interactive.m0Color().cgColor
  52. } else {
  53. self.actionButton.tag = 0
  54. self.actionButton.layer?.backgroundColor = KMAppearance.Interactive.m0Color().cgColor
  55. self.actionButton.setTitleColor(KMAppearance.Layout.w0Color())
  56. self.actionButton.isEnabled = false
  57. }
  58. }
  59. get{
  60. return super.interfaceStatus
  61. }
  62. }
  63. deinit {
  64. NotificationCenter.default.removeObserver(self)
  65. }
  66. override func viewDidLoad() {
  67. super.viewDidLoad()
  68. self.localizedLanguage()
  69. self.configuUI()
  70. NotificationCenter.default.addObserver(self, selector: #selector(OCRSelectedLanguagesChangeNotification(notification:)), name: NSNotification.Name("KMOCRSelectedLanguagesChangeNotification"), object: nil)
  71. NotificationCenter.default.addObserver(self, selector: #selector(OCRSelectedPlanChangeNotification(notification:)), name: NSNotification.Name("KMOCRSelectedPlanChangeNotification"), object: nil)
  72. NotificationCenter.default.addObserver(self, selector: #selector(themeChanged(notification:)), name: NSNotification.Name("AppleInterfaceThemeChangedNotification"), object: nil)
  73. }
  74. func localizedLanguage() {
  75. self.outputTypeLabel.stringValue = KMLocalizedString("Output",nil)
  76. self.btnMerge.title = KMLocalizedString("Merge All", nil)
  77. self.createNewPDFBtn.title = KMLocalizedString("New PDF Document", nil)
  78. self.appendPDFBtn.title = KMLocalizedString("Append To Existing File", nil)
  79. self.appendTextField.placeholderString = KMLocalizedString("Select a File", nil)
  80. self.selectLanguageLabel.stringValue = KMLocalizedString("Select OCR Language:",nil)
  81. self.ocrSelectBtn.title = KMLocalizedString("OCR Plan",nil)
  82. self.updateLanguageButton((KMGOCRManager.default().selectedLanguages?.value(forKeyPath: KMGOCRLanguageStringKey) as? [String]))
  83. self.actionButton.title = KMLocalizedString("Save as PDF", nil)
  84. self.saveAsButton.title = KMLocalizedString("Save as TXT", nil)
  85. self.OCRSelectedPlanChangeAction()
  86. }
  87. func configuUI() {
  88. self.view.wantsLayer = true
  89. appendOtherPDFBtn.wantsLayer = true
  90. appendBackView.wantsLayer = true
  91. appendBackView.layer?.borderWidth = 0.5
  92. self.actionButton.wantsLayer = true
  93. self.actionButton.layer?.backgroundColor = KMAppearance.Interactive.m0Color().cgColor
  94. self.appendOtherPDFBtn.layer?.backgroundColor = KMAppearance.Interactive.s0Color().withAlphaComponent(0.4).cgColor
  95. self.actionButton.setTitleColor(KMAppearance.Layout.w0Color())
  96. self.actionButton.layer?.cornerRadius = 1.0
  97. self.createNewPDFBtn.setTitleColor(KMAppearance.Layout.h0Color())
  98. self.btnMerge.setTitleColor(KMAppearance.Layout.h0Color())
  99. self.appendPDFBtn.setTitleColor(KMAppearance.Layout.h0Color())
  100. self.ocrSelectBtn.setTitleColor(KMAppearance.Layout.h0Color())
  101. self.saveAsButton.setTitleColor(KMAppearance.Layout.h0Color())
  102. self.selectLanguageLabel.textColor = KMAppearance.Layout.h0Color()
  103. self.languageButton.isEnabled = false
  104. self.planButton.isEnabled = false
  105. self.saveAsButton.isEnabled = false
  106. appendTextField.backgroundColor = KMAppearance.Layout.l1Color()
  107. planButton.wantsLayer = true
  108. appendTextField.wantsLayer = true
  109. planButton.wantsLayer = true
  110. appendTextField.layer?.cornerRadius = 1.0
  111. languageButton.layer?.backgroundColor = NSColor.clear.cgColor
  112. languaeBox.borderColor = KMAppearance.Interactive.s0Color()
  113. planBox.borderColor = KMAppearance.Interactive.s0Color()
  114. languaeBox.fillColor = KMAppearance.Layout.l1Color()
  115. planBox.fillColor = KMAppearance.Layout.l1Color()
  116. self.updateViewColor()
  117. }
  118. func updateViewColor() {
  119. if KMAppearance.isDarkMode() {
  120. self.view.layer?.backgroundColor = NSColor(red: 0.055, green: 0.067, blue: 0.078, alpha: 1).cgColor
  121. appendBackView.layer?.borderColor = NSColor(red: 86/255.0, green: 88/255.0, blue: 90/255.0, alpha: 1).cgColor
  122. appendBackView.layer?.backgroundColor = NSColor(red: 57/255.0, green: 60/255.0, blue: 62/255.0, alpha: 1).cgColor
  123. } else {
  124. self.view.layer?.backgroundColor = NSColor(red: 0.922, green: 0.925, blue: 0.941, alpha: 1).cgColor
  125. appendBackView.layer?.borderColor = NSColor(red: 218/255.0, green: 219/255.0, blue: 222/255.0, alpha: 1).cgColor
  126. appendBackView.layer?.backgroundColor = NSColor.white.cgColor;
  127. }
  128. }
  129. func updateLanguageButton(_ languages: [String]?) {
  130. if languages?.count ?? 0 < 1 {
  131. self.languageButton.title = KMLocalizedString("Auto Detection", nil)
  132. return
  133. }
  134. var languageName: String = ""
  135. if languages?.count ?? 0 > 0 {
  136. for i in 0..<(languages?.count ?? 0) {
  137. let language = languages?[i]
  138. if i == 0 {
  139. languageName = language ?? ""
  140. } else {
  141. languageName = languageName.appendingFormat(",%@", language ?? "")
  142. }
  143. }
  144. } else {
  145. languageName = ""
  146. }
  147. self.languageButton.title = languageName
  148. }
  149. func converArrType(arr: Array<KMBatchOperateFile>, keyString: String) -> [String] {
  150. let newArr = NSMutableArray()
  151. for item in arr {
  152. newArr.add(item.filePath)
  153. }
  154. return newArr as! [String]
  155. }
  156. func isConnectionAvailable() -> Bool {
  157. // var isExistenceNetwork = true
  158. // let reach = Reachability(hostname: "www.apple.com")
  159. // let status: NetworkStatus = NetworkStatus(rawValue: (reach?.currentReachabilityStatus())!.rawValue)
  160. // switch status.rawValue {
  161. // case 0:
  162. // isExistenceNetwork = false
  163. // case 1:
  164. // isExistenceNetwork = true
  165. // case 2:
  166. // isExistenceNetwork = true
  167. // default:
  168. // break
  169. // }
  170. if Reachability.forInternetConnection().currentReachabilityStatus().rawValue == 0 {
  171. return false
  172. }
  173. return true
  174. }
  175. func beginImageToPDF() {
  176. if self.files?.count ?? 0 < 1 {
  177. return
  178. }
  179. let photoArray = converArrType(arr: self.files!, keyString: "")
  180. var path: String = ""
  181. var isMerge = false
  182. var isCreatNewPDF = false
  183. var isOCR = false
  184. if self.ocrSelectBtn.state == .on {
  185. isOCR = true
  186. }
  187. var isSaveAs = false
  188. if self.saveAsButton.state == .on {
  189. isSaveAs = true
  190. }
  191. let plan = UserDefaults.standard.integer(forKey: "KMOCRCurrentPlanKey")
  192. if isOCR && !self.isConnectionAvailable() && plan == 0 {
  193. let alert = NSAlert()
  194. alert.alertStyle = .critical
  195. alert.messageText = NSLocalizedString("Connection Error", comment: "")
  196. alert.informativeText = NSLocalizedString("Please make sure your internet connection is available.", comment: "")
  197. alert.addButton(withTitle: NSLocalizedString("OK", comment: ""))
  198. if alert.responds(to: #selector(alert.beginSheetModal(for:completionHandler:))) {
  199. alert.beginSheetModal(for: self.view.window!, completionHandler: nil)
  200. } else {
  201. alert.runModal()
  202. }
  203. return
  204. }
  205. if self.createNewPDFBtn.state == .on {
  206. if (self.choosePath.count < 1) {
  207. let alert = NSAlert()
  208. alert.alertStyle = .critical
  209. alert.messageText = String(format: NSLocalizedString("Output Folder cannot be empty.", comment: ""))
  210. alert.runModal()
  211. return
  212. }
  213. path = self.choosePath
  214. if self.btnMerge.state == .on {
  215. isMerge = true
  216. }
  217. isCreatNewPDF = true
  218. } else {
  219. let appenString = self.appendTextField.stringValue
  220. if appenString.isEmpty {
  221. let alert = NSAlert()
  222. alert.alertStyle = .critical
  223. alert.messageText = String(format: NSLocalizedString("Select a File", comment: ""))
  224. alert.runModal()
  225. return
  226. }
  227. path = self.appendTextField.stringValue
  228. isMerge = true
  229. isCreatNewPDF = false
  230. }
  231. self.languageButton.isEnabled = false
  232. self.planButton.isEnabled = false
  233. self.method.password = self.password
  234. self.interfaceStatus = .Processing
  235. self.method.exportPDFFile(fileArray: photoArray, savePath: path, isOCR: isOCR, isCreatPDF: isCreatNewPDF, isMerge: isMerge, isSaveAsText: isSaveAs) { [weak self] savePath, errorArr, errorOCRArray in
  236. self?.languageButton.isEnabled = true
  237. self?.planButton.isEnabled = true
  238. self?.interfaceStatus = .PrepareProcess
  239. if errorArr.count > 0 {
  240. let dict: [String: Any] = ["isMerge": false, "isSuccess": false]
  241. NotificationCenter.default.post(name: NSNotification.Name(rawValue: "KMBatchOperateImageToPDFSuccessNotification"), object: self, userInfo: dict)
  242. let alert = NSAlert()
  243. alert.messageText = NSLocalizedString("Conversion Failed", comment: "")
  244. alert.addButton(withTitle: NSLocalizedString("OK", comment: ""))
  245. alert.informativeText = "\(errorArr)"
  246. alert.alertStyle = .informational
  247. alert.runModal()
  248. } else {
  249. if errorOCRArray.count > 0 {
  250. var contextString = NSLocalizedString("Some problems occurred during the last operation:", comment: "")
  251. for filePath in errorOCRArray {
  252. contextString += "\n" + (filePath as AnyObject).lastPathComponent
  253. }
  254. let alert = NSAlert()
  255. alert.messageText = NSLocalizedString("Converted Successfully", comment: "")
  256. alert.addButton(withTitle: NSLocalizedString("OK", comment: ""))
  257. alert.informativeText = contextString
  258. alert.alertStyle = .informational
  259. let response = alert.runModal()
  260. if response == .OK {
  261. self?.viewFileAtFinder(fileName: savePath)
  262. }
  263. } else {
  264. self?.viewFileAtFinder(fileName: savePath)
  265. }
  266. }
  267. }
  268. }
  269. func viewFileAtFinder(fileName: String) {
  270. let dict = ["isMerge": true, "isSuccess": true]
  271. NotificationCenter.default.post(name: Notification.Name("KMBatchOperateImageToPDFSuccessNotification"), object: self, userInfo: dict)
  272. let workspace = NSWorkspace.shared
  273. let url = URL(fileURLWithPath: fileName)
  274. workspace.activateFileViewerSelecting([url])
  275. }
  276. //MARK: Notification
  277. @objc func OCRSelectedLanguagesChangeNotification(notification: Notification) {
  278. let selectedLanguages = notification.object/* as? [KMBatchOperateFile]*/
  279. self.updateLanguageButton(selectedLanguages as? [String])
  280. }
  281. @objc func OCRSelectedPlanChangeNotification(notification: Notification) {
  282. self.OCRSelectedPlanChangeAction()
  283. }
  284. @objc func themeChanged(notification: Notification) {
  285. DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
  286. self.updateViewColor()
  287. }
  288. }
  289. func OCRSelectedPlanChangeAction() {
  290. let plan = UserDefaults.standard.integer(forKey: "KMOCRCurrentPlanKey")
  291. if plan == 0 {
  292. self.planButton.title = KMLocalizedString("Plan 1 (Online)", nil)
  293. } else {
  294. self.planButton.title = KMLocalizedString("Plan 2 (Offline)", nil)
  295. }
  296. KMGOCRManager.default().selectedLanguages = NSMutableArray()
  297. self.updateLanguageButton(KMGOCRManager.default().selectedLanguages?.value(forKeyPath: KMGOCRLanguageStringKey) as? [String])
  298. }
  299. @IBAction func buttonClicked_CreateNewPDF(_ sender: NSButton) {
  300. if (sender.state == .on) {
  301. self.btnMerge.isEnabled = true
  302. self.appendPDFBtn.state = .off
  303. self.appendOtherPDFBtn.isEnabled = false
  304. }
  305. }
  306. @IBAction func buttonClicked_AppendOtherPDF(_ sender: NSButton) {
  307. if (sender.state == .on) {
  308. self.createNewPDFBtn.state = .off
  309. self.btnMerge.isEnabled = false
  310. self.appendOtherPDFBtn.isEnabled = true
  311. }
  312. }
  313. @IBAction func buttonClicked_OCRSelect(_ sender: NSButton) {
  314. //MARK: 判断是否付费用户
  315. if (sender.state == .on) {
  316. self.languageButton.isEnabled = true
  317. self.planButton.isEnabled = true
  318. self.saveAsButton.isEnabled = true
  319. } else {
  320. self.languageButton.isEnabled = false
  321. self.planButton.isEnabled = false
  322. self.saveAsButton.isEnabled = false
  323. }
  324. }
  325. @IBAction func buttonClicked_ChooseLanguage(_ sender: NSButton) {
  326. let plan = UserDefaults.standard.integer(forKey: "KMOCRCurrentPlanKey")
  327. if plan == 0 {
  328. KMGOCRManager.default().ocrType = .google
  329. } else {
  330. KMGOCRManager.default().ocrType = .apple
  331. }
  332. let popover = NSPopover()
  333. popover.delegate = self
  334. popover.contentViewController = KMLanguageViewController(nibName: "KMLanguageViewController", bundle: Bundle.main)
  335. popover.animates = true
  336. popover.behavior = .transient
  337. popover.show(relativeTo: sender.bounds, of: sender as NSView, preferredEdge: .minX)
  338. }
  339. @IBAction func buttonClicked_ChoosePlan(_ sender: NSButton) {
  340. let popover = NSPopover()
  341. popover.delegate = self
  342. popover.contentViewController = KMPlanViewController(nibName: "KMPlanViewController", bundle: Bundle.main)
  343. popover.animates = true
  344. popover.behavior = .transient
  345. popover.show(relativeTo: sender.bounds, of: sender as NSView, preferredEdge: .minX)
  346. }
  347. @IBAction func buttonClicked_ImageToPDF(_ sender: NSButton) {
  348. //MARK: 判断是否付费用户,展示iap界面
  349. if sender.tag == 1 {
  350. self.choosePath = ""
  351. var hasTask = false
  352. for i in 0..<(self.files?.count ?? 0) {
  353. let file = self.files?[i]
  354. file?.currentOperateInfo?.resetState()
  355. if file?.fileType == .Image {
  356. hasTask = true
  357. }
  358. }
  359. if !hasTask {
  360. NSSound.beep()
  361. return
  362. }
  363. var isOCR = false
  364. if self.ocrSelectBtn.state == .on {
  365. isOCR = true
  366. }
  367. var isSaveAs = false
  368. if self.saveAsButton.state == .on {
  369. isSaveAs = true
  370. }
  371. let plan = UserDefaults.standard.integer(forKey: "KMOCRCurrentPlanKey")
  372. if isOCR && !isConnectionAvailable() && plan == 0 {
  373. let alert = NSAlert()
  374. alert.alertStyle = .critical
  375. alert.messageText = NSLocalizedString("Connection Error", comment: "")
  376. alert.informativeText = NSLocalizedString("Please make sure your internet connection is available.", comment: "")
  377. alert.addButton(withTitle: NSLocalizedString("OK", comment: ""))
  378. if let window = self.view.window {
  379. alert.beginSheetModal(for: window, completionHandler: nil)
  380. } else {
  381. alert.runModal()
  382. }
  383. return
  384. }
  385. if self.createNewPDFBtn.state == .off {
  386. let appenString = self.appendTextField.stringValue
  387. if appenString.isEmpty {
  388. let alert = NSAlert()
  389. alert.alertStyle = .critical
  390. alert.messageText = NSLocalizedString("Select a File", comment: "")
  391. alert.runModal()
  392. return
  393. }
  394. }
  395. let openPanel = NSOpenPanel()
  396. openPanel.canChooseFiles = false
  397. openPanel.canChooseDirectories = true
  398. openPanel.canCreateDirectories = true
  399. openPanel.beginSheetModal(for: self.view.window!) { (result) in
  400. if result == .OK {
  401. for fileURL in openPanel.urls {
  402. self.choosePath = fileURL.path
  403. self.beginImageToPDF()
  404. }
  405. }
  406. }
  407. } else { // Do something else }
  408. }
  409. }
  410. @IBAction func buttonItemClicked_AppendOtherPDF(_ sender: NSButton) {
  411. let openPanel = NSOpenPanel()
  412. openPanel.allowedFileTypes = ["pdf"]
  413. openPanel.canChooseDirectories = false
  414. openPanel.allowsMultipleSelection = false
  415. openPanel.beginSheetModal(for: self.view.window!) { (result) in
  416. if result == .OK {
  417. guard let url = openPanel.url else { return }
  418. guard let document = PDFDocument(url: url) else {
  419. let alert = NSAlert()
  420. alert.alertStyle = .critical
  421. alert.messageText = NSLocalizedString("An error occurred while opening this document. The file is damaged and could not be repaired.", comment: "")
  422. alert.runModal()
  423. return
  424. }
  425. if !document.allowsCopying || !document.allowsPrinting {
  426. let alert = NSAlert()
  427. alert.alertStyle = .critical
  428. alert.messageText = NSLocalizedString("This is a secured document. Editing is not permitted.", comment: "")
  429. alert.runModal()
  430. return
  431. }
  432. if document.isLocked {
  433. let com = PasswordWindowController(windowNibName: "PasswordWindowController")
  434. com.fileURL = url
  435. com.beginSheetModal(for: self.view.window!) { (password) in
  436. if password.count > 0 {
  437. self.password = password
  438. self.appendTextField.stringValue = url.path
  439. }
  440. }
  441. } else {
  442. self.appendTextField.stringValue = url.path
  443. }
  444. }
  445. }
  446. }
  447. @IBAction func buttonClicked_Help(_ sender: NSButton) {
  448. let helpController = NSViewController()
  449. let textView = NSTextView(frame: NSRect(x: 0, y: 0, width: 300.0, height: 100.0))
  450. textView.backgroundColor = NSColor.clear
  451. textView.isEditable = false
  452. textView.layer?.cornerRadius = 6
  453. let tStrAuto = NSLocalizedString("Choose automatic language detection for better OCR results.", comment: "")
  454. 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: "")
  455. let plan = UserDefaults.standard.integer(forKey: "KMOCRCurrentPlanKey")
  456. if plan == 0 {
  457. textView.string = "\(tStrAuto)\n\n\(tStrVPN)"
  458. } else {
  459. textView.frame = NSRect(x: 0, y: 0, width: 300.0, height: 40.0)
  460. textView.string = tStrAuto
  461. }
  462. helpController.view = textView
  463. let popover = NSPopover()
  464. popover.delegate = self
  465. popover.contentViewController = helpController
  466. popover.animates = true
  467. popover.behavior = .transient
  468. popover.show(relativeTo: sender.bounds, of: sender as NSView, preferredEdge: .minY)
  469. }
  470. //MARK: KMImageToPDFMethodDelegate
  471. func imageToPDFMethod(_ method: KMImageToPDFMethod, progress: Float) {
  472. }
  473. }