KMBatchImageToPDFView.swift 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. //
  2. // KMBatchImageToPDFView.swift
  3. // PDF Reader Pro
  4. //
  5. // Created by lizhe on 2025/1/15.
  6. //
  7. import Cocoa
  8. import KMComponentLibrary
  9. class KMBatchImageToPDFView: KMBatchSettingItemView {
  10. @IBOutlet weak var titleLabel: NSTextField!
  11. @IBOutlet weak var titleContentView: NSView!
  12. @IBOutlet weak var subTitleLabel: NSTextField!
  13. @IBOutlet weak var newPDFDocumentButton: ComponentRadio!
  14. @IBOutlet weak var mergeAllButton: ComponentCheckBox!
  15. @IBOutlet weak var appendToFileButton: ComponentRadio!
  16. @IBOutlet weak var selectFileInputButton: ComponentInput!
  17. @IBOutlet weak var selectFileInputAddonButton: ComponentInputAddon!
  18. @IBOutlet weak var textRecognitionSettingLabel: NSTextField!
  19. @IBOutlet weak var recognizeTextButton: ComponentCheckBox!
  20. @IBOutlet weak var languageSelectButton: ComponentSelect!
  21. @IBOutlet weak var lineView: ComponentDivider!
  22. @IBOutlet weak var OCRPlan1Button: ComponentRadio!
  23. @IBOutlet weak var OCRPlan1ButtonWidthConstraint: NSLayoutConstraint!
  24. @IBOutlet weak var OCRPlan2Button: ComponentRadio!
  25. @IBOutlet weak var OCRPlan2ButtonWidthConstraint: NSLayoutConstraint!
  26. @IBOutlet weak var extractTextButton: ComponentCheckBox!
  27. @IBOutlet weak var applyButton: ComponentButton!
  28. var model: KMBatchImageToPDFModel = KMBatchImageToPDFModel()
  29. var selectIndex = 0
  30. var password: String = ""
  31. override func draw(_ dirtyRect: NSRect) {
  32. super.draw(dirtyRect)
  33. // Drawing code here.
  34. }
  35. override func updateUILanguage() {
  36. self.titleLabel.stringValue = KMLocalizedString("Image To PDF")
  37. self.subTitleLabel.stringValue = KMLocalizedString("Output")
  38. self.newPDFDocumentButton.properties.text = KMLocalizedString("New PDF Document")
  39. self.newPDFDocumentButton.reloadData()
  40. self.mergeAllButton.properties.text = KMLocalizedString("Merge All")
  41. self.mergeAllButton.reloadData()
  42. self.appendToFileButton.properties.text = KMLocalizedString("Append To Existing File")
  43. self.appendToFileButton.reloadData()
  44. self.textRecognitionSettingLabel.stringValue = KMLocalizedString("Text Recognition Settings")
  45. self.recognizeTextButton.properties.text = KMLocalizedString("Recognize text if needed")
  46. self.recognizeTextButton.reloadData()
  47. self.OCRPlan1Button.properties.text = KMLocalizedString("Plan 1 (Online)")
  48. self.OCRPlan1Button.toolTip = KMLocalizedString("The OCR service works via an internet connection. We would suggest you to perform OCR using a VPN connection while the service is limited.")
  49. self.OCRPlan1Button.reloadData()
  50. self.OCRPlan1ButtonWidthConstraint.constant = self.OCRPlan1Button.properties.propertyInfo.viewWidth
  51. self.OCRPlan2Button.properties.text = KMLocalizedString("Plan 2 (Offline)")
  52. self.OCRPlan2Button.toolTip = KMLocalizedString("The OCR service works via an internet connection. We would suggest you to perform OCR using a VPN connection while the service is limited.")
  53. self.OCRPlan2Button.reloadData()
  54. self.OCRPlan2ButtonWidthConstraint.constant = self.OCRPlan2Button.properties.propertyInfo.viewWidth
  55. self.extractTextButton.properties.text = KMLocalizedString("Extract text content")
  56. self.extractTextButton.reloadData()
  57. self.applyButton.properties.buttonText = KMLocalizedString("Apply")
  58. }
  59. override func updateUIThemeColor() {
  60. self.titleContentView.border(ComponentLibrary.shared.getComponentColorFromKey("colorBorder/divider"), 0.5, 0)
  61. titleLabel.font = ComponentLibrary.shared.getFontFromKey("mac/body-m-medium")
  62. titleLabel.textColor = ComponentLibrary.shared.getComponentColorFromKey("colorText/2")
  63. subTitleLabel.font = ComponentLibrary.shared.getFontFromKey("mac/body-s-medium")
  64. subTitleLabel.textColor = ComponentLibrary.shared.getComponentColorFromKey("colorText/2")
  65. textRecognitionSettingLabel.font = ComponentLibrary.shared.getFontFromKey("mac/body-s-medium")
  66. textRecognitionSettingLabel.textColor = ComponentLibrary.shared.getComponentColorFromKey("colorText/2")
  67. }
  68. override func setup() {
  69. self.newPDFDocumentButton.properties = ComponentCheckBoxProperty(size: .s, state: .normal, isDisabled: false, showhelp: false, checkboxType: .normal)
  70. self.newPDFDocumentButton.setTarget(self, action: #selector(newPDFDocumentButtonAction))
  71. self.mergeAllButton.properties = ComponentCheckBoxProperty(size: .s, state: .normal, isDisabled: false, showhelp: false,checkboxType: .normal)
  72. self.mergeAllButton.setTarget(self, action: #selector(mergeAllButtonAction))
  73. self.appendToFileButton.properties = ComponentCheckBoxProperty(size: .s, state: .normal, isDisabled: false, showhelp: false, checkboxType: .normal)
  74. self.appendToFileButton.setTarget(self, action: #selector(appendToFileButtonAction))
  75. self.recognizeTextButton.properties = ComponentCheckBoxProperty(size: .s, state: .normal, isDisabled: false, showhelp: false, checkboxType: .normal)
  76. self.recognizeTextButton.setTarget(self, action: #selector(OCRButtonAction))
  77. let inputWithAddonProperty = ComponentInputProperty(size: .s,
  78. state: .normal,
  79. isError: false,
  80. showPrefix: false,
  81. showSuffix: false,
  82. showClear: false,
  83. isDisabled: true,
  84. placeholder: KMLocalizedString("Select File"),
  85. text: "",
  86. creatable: false)
  87. selectFileInputButton.properties = inputWithAddonProperty
  88. selectFileInputButton.properties.propertyInfo.cornerRadius_topRight = 0
  89. selectFileInputButton.properties.propertyInfo.cornerRadius_bottomRight = 0
  90. selectFileInputButton.properties.isDisabled = true
  91. selectFileInputButton.reloadData()
  92. selectFileInputAddonButton.properties = ComponentInputAddonProperty(size: .s,
  93. state: .normal,
  94. addOnBefore: false,
  95. onlyRead: false,
  96. addonType: .imageWithColor,
  97. iconImage: NSImage(named: "KMFolderIcon"))
  98. selectFileInputAddonButton.properties.isDisabled = true
  99. selectFileInputAddonButton.reloadData()
  100. selectFileInputAddonButton.setTarget(self, action: #selector(addFilesButtonAction))
  101. self.languageSelectButton.properties = ComponentSelectProperties(size: .s, state: .normal, isDisabled: false, isError: false, leftIcon: false, placeholder: nil, errorText: nil, creatable: false, text: KMLocalizedString("", comment: ""))
  102. self.languageSelectButton.delegate = self
  103. self.OCRPlan1Button.properties = ComponentCheckBoxProperty(size: .s, state: .normal, isDisabled: false, showhelp: true, text: KMLocalizedString("Plan 1 (Online)"), checkboxType: .normal)
  104. OCRPlan1Button.setTarget(self, action: #selector(OCRPlan1ButtonAction))
  105. self.OCRPlan1Button.reloadData()
  106. self.OCRPlan1ButtonWidthConstraint.constant = self.OCRPlan1Button.properties.propertyInfo.viewWidth
  107. self.OCRPlan2Button.properties = ComponentCheckBoxProperty(size: .s, state: .normal, isDisabled: false, showhelp: true, text: KMLocalizedString("Plan 2 (Offline)"), checkboxType: .normal)
  108. OCRPlan2Button.setTarget(self, action: #selector(OCRPlan2ButtonAction))
  109. self.OCRPlan2Button.reloadData()
  110. self.OCRPlan2ButtonWidthConstraint.constant = self.OCRPlan2Button.properties.propertyInfo.viewWidth
  111. self.extractTextButton.properties = ComponentCheckBoxProperty(size: .s, state: .normal, isDisabled: false, showhelp: false, checkboxType: .normal)
  112. extractTextButton.setTarget(self, action: #selector(extractTextButtonAction))
  113. applyButton.properties = ComponentButtonProperty(type: .primary, size: .m, showRightIcon: true, keepPressState: false)
  114. applyButton.setTarget(self, action: #selector(saveButtonClicked(_:)))
  115. self.lineView.properties = ComponentDividerProperty()
  116. super.setup()
  117. }
  118. override func reloadData() {
  119. self.updateLanguages()
  120. self.newPDFDocumentButton.properties.checkboxType = self.model.isNewPDF ? .selected : .normal
  121. self.newPDFDocumentButton.reloadData()
  122. self.mergeAllButton.properties.isDisabled = !self.model.isNewPDF
  123. self.mergeAllButton.reloadData()
  124. self.appendToFileButton.properties.checkboxType = self.model.isAppendToExistingFile ? .selected : .normal
  125. self.appendToFileButton.reloadData()
  126. self.selectFileInputButton.properties.isDisabled = !self.model.isAppendToExistingFile
  127. self.selectFileInputButton.reloadData()
  128. self.selectFileInputAddonButton.properties.isDisabled = !self.model.isAppendToExistingFile
  129. self.selectFileInputAddonButton.reloadData()
  130. self.recognizeTextButton.properties.checkboxType = self.model.isOCR ? .selected : .normal
  131. self.recognizeTextButton.reloadData()
  132. self.languageSelectButton.properties.isDisabled = !self.model.isOCR
  133. self.languageSelectButton.reloadData()
  134. if self.model.ocrType == .google {
  135. self.OCRPlan1Button.properties.checkboxType = .selected
  136. self.OCRPlan2Button.properties.checkboxType = .normal
  137. } else if self.model.ocrType == .apple {
  138. self.OCRPlan1Button.properties.checkboxType = .normal
  139. self.OCRPlan2Button.properties.checkboxType = .selected
  140. }
  141. self.OCRPlan1Button.properties.isDisabled = !self.model.isOCR
  142. self.OCRPlan1Button.reloadData()
  143. self.OCRPlan2Button.properties.isDisabled = !self.model.isOCR
  144. self.OCRPlan2Button.reloadData()
  145. self.extractTextButton.properties.isDisabled = !self.model.isOCR
  146. self.extractTextButton.reloadData()
  147. languageSelectButton.selectItemAtIndex(self.selectIndex)
  148. if self.filesData.count == 0 {
  149. self.applyButton.properties.isDisabled = true
  150. } else {
  151. self.applyButton.properties.isDisabled = false
  152. }
  153. self.applyButton.reloadData()
  154. }
  155. func updateLanguages() {
  156. var menuItemArr: [ComponentMenuitemProperty] = []
  157. var languages: [String] = KMOCRManager.manager.getLanguages(type: model.ocrType)
  158. languages.insert(KMLocalizedString("Auto Detection"), at: 0)
  159. for language in languages {
  160. let itemProperty: ComponentMenuitemProperty = ComponentMenuitemProperty(multipleSelect: false,
  161. itemSelected: false,
  162. isDisabled: false,
  163. keyEquivalent: nil,
  164. text: language, identifier: "1")
  165. menuItemArr.append(itemProperty)
  166. }
  167. languageSelectButton.updateMenuItemsArr(menuItemArr)
  168. let position = languages.firstIndex(of: model.language) ?? 0
  169. self.selectIndex = position
  170. }
  171. override func bacthProcessingNotification() {
  172. self.newPDFDocumentButton.properties.isDisabled = self.isDisable
  173. self.newPDFDocumentButton.reloadData()
  174. self.mergeAllButton.properties.isDisabled = self.isDisable
  175. self.mergeAllButton.reloadData()
  176. self.appendToFileButton.properties.isDisabled = self.isDisable
  177. self.appendToFileButton.reloadData()
  178. self.selectFileInputButton.properties.isDisabled = self.isDisable
  179. self.selectFileInputButton.reloadData()
  180. self.selectFileInputAddonButton.properties.isDisabled = self.isDisable
  181. self.selectFileInputAddonButton.reloadData()
  182. self.recognizeTextButton.properties.isDisabled = self.isDisable
  183. self.recognizeTextButton.reloadData()
  184. self.languageSelectButton.properties.isDisabled = self.isDisable
  185. self.languageSelectButton.reloadData()
  186. self.OCRPlan1Button.properties.isDisabled = self.isDisable
  187. self.OCRPlan1Button.reloadData()
  188. self.OCRPlan2Button.properties.isDisabled = self.isDisable
  189. self.OCRPlan2Button.reloadData()
  190. self.applyButton.properties.isDisabled = self.isDisable
  191. self.applyButton.reloadData()
  192. self.extractTextButton.properties.isDisabled = self.isDisable
  193. self.extractTextButton.reloadData()
  194. }
  195. }
  196. extension KMBatchImageToPDFView: ComponentSelectDelegate {
  197. func componentSelectDidSelect(view: ComponentSelect?, menuItemProperty: ComponentMenuitemProperty?) {
  198. if (view == languageSelectButton) {
  199. var languages: [String] = KMOCRManager.manager.getLanguages(type: model.ocrType)
  200. let position = languages.firstIndex(of: menuItemProperty?.text ?? "") ?? 0
  201. self.selectIndex = position + 1
  202. let values: [String: String] = KMGOCRManager.languages()[position] as? [String : String] ?? [:]
  203. let key = values[KMGOCRLanguageCodeKey]
  204. model.language = key ?? ""
  205. if let unwrappedKey = key, let intValue = Int(unwrappedKey) {
  206. model.languageType = COCRLanguage(rawValue: intValue) ?? .english
  207. } else {
  208. print("转换失败")
  209. }
  210. }
  211. }
  212. }
  213. //MARK: Action
  214. extension KMBatchImageToPDFView {
  215. @objc func saveButtonClicked(_ sender: ComponentButton) {
  216. if KMMemberInfo.shared.isLogin == false {
  217. KMLoginWindowsController.shared.showWindow(nil)
  218. return
  219. }
  220. self.model.selectFilePath = self.selectFileInputButton.properties.text
  221. if !self.model.isNewPDF {
  222. let selectFilePath = self.model.selectFilePath
  223. if selectFilePath.count == 0 {
  224. let alert = NSAlert()
  225. alert.alertStyle = .critical
  226. alert.messageText = KMLocalizedString("文件未选择")
  227. alert.runModal()
  228. return
  229. }
  230. }
  231. guard let callBack = self.batchExport else { return }
  232. callBack(self, model)
  233. }
  234. func addFilesButtonAction() {
  235. let openPanel = NSOpenPanel()
  236. openPanel.allowedFileTypes = ["pdf"]
  237. openPanel.canChooseDirectories = false
  238. openPanel.allowsMultipleSelection = false
  239. openPanel.beginSheetModal(for: self.window!) { (result) in
  240. if result == .OK {
  241. guard let url = openPanel.url else { return }
  242. if !url.path.isPDFValid() {
  243. let alert = NSAlert()
  244. alert.alertStyle = .critical
  245. alert.messageText = KMLocalizedString("An error occurred while opening this document. The file is damaged and could not be repaired.", comment: "")
  246. alert.runModal()
  247. return
  248. }
  249. guard let document = CPDFDocument(url: url) else {
  250. let alert = NSAlert()
  251. alert.alertStyle = .critical
  252. alert.messageText = KMLocalizedString("An error occurred while opening this document. The file is damaged and could not be repaired.", comment: "")
  253. alert.runModal()
  254. return
  255. }
  256. if !document.allowsCopying || !document.allowsPrinting {
  257. let alert = NSAlert()
  258. alert.alertStyle = .critical
  259. alert.messageText = KMLocalizedString("This is a secured document. Editing is not permitted.", comment: "")
  260. alert.runModal()
  261. return
  262. }
  263. if document.isLocked {
  264. NSWindowController.checkPassword(url: url, type: .owner) { [weak self] success, resultPassword in
  265. if success {
  266. self?.password = resultPassword
  267. self?.selectFileInputButton.properties.text = url.path
  268. self?.selectFileInputButton.reloadData()
  269. }
  270. }
  271. } else {
  272. self.selectFileInputButton.properties.text = url.path
  273. self.selectFileInputButton.reloadData()
  274. }
  275. }
  276. }
  277. }
  278. func newPDFDocumentButtonAction() {
  279. self.model.isNewPDF = !self.model.isNewPDF
  280. self.model.isAppendToExistingFile = !self.model.isNewPDF
  281. self.reloadData()
  282. }
  283. func mergeAllButtonAction() {
  284. self.model.isMergeAll = !self.model.isMergeAll
  285. self.reloadData()
  286. }
  287. func appendToFileButtonAction() {
  288. self.model.isAppendToExistingFile = !self.model.isAppendToExistingFile
  289. self.model.isNewPDF = !self.model.isAppendToExistingFile
  290. self.reloadData()
  291. }
  292. func OCRButtonAction() {
  293. //#if VERSION_DMG
  294. // KMResourceDownloadManager.manager.needDownloadOCRResource(complete: { [weak self] result in
  295. // if result {
  296. // self?.reloadData()
  297. // KMResourceDownloadManager.manager.downLoadOCRResource(window: self?.window ?? NSWindow.currentWindow())
  298. // } else {
  299. // self?.model.isOCR = !(self?.model.isOCR ?? false)
  300. // self?.reloadData()
  301. // }
  302. // })
  303. //#else
  304. self.model.isOCR = !self.model.isOCR
  305. self.reloadData()
  306. //#endif
  307. }
  308. func OCRPlan1ButtonAction() {
  309. self.model.ocrType = .google
  310. self.reloadData()
  311. }
  312. func OCRPlan2ButtonAction() {
  313. self.model.ocrType = .apple
  314. self.reloadData()
  315. }
  316. func extractTextButtonAction() {
  317. self.model.isExtractText = !self.model.isExtractText
  318. self.reloadData()
  319. }
  320. }