KMBatchImageToPDFView.swift 18 KB

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