KMExtractWindowController.swift 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. //
  2. // KMExtractWindowController.swift
  3. // PDF Reader Pro
  4. //
  5. // Created by kdanmobile on 2025/2/28.
  6. //
  7. import Cocoa
  8. import KMComponentLibrary
  9. import ComPDFKit
  10. class KMExtractWindowController: KMNBaseWindowController {
  11. @IBOutlet var contendBox: NSBox!
  12. @IBOutlet var titleLabel: NSTextField!
  13. @IBOutlet var previewBGView: NSView!
  14. @IBOutlet var pdfBGView: NSView!
  15. @IBOutlet var paginationView: ComponentPagination!
  16. @IBOutlet var pageRangeView: NSView!
  17. @IBOutlet var pageRangeLabel: NSTextField!
  18. @IBOutlet var allPageRadio: ComponentRadio!
  19. @IBOutlet var currentPageRadio: ComponentRadio!
  20. @IBOutlet var oddPageRadio: ComponentRadio!
  21. @IBOutlet var evenPageRadio: ComponentRadio!
  22. @IBOutlet var customPageRadio: ComponentRadio!
  23. @IBOutlet var customPageInput: ComponentInput!
  24. @IBOutlet var allPageBoxWidthConst: NSLayoutConstraint!
  25. @IBOutlet var curPageBoxWidthConst: NSLayoutConstraint!
  26. @IBOutlet var oddPageBoxWidthConst: NSLayoutConstraint!
  27. @IBOutlet var evenPageBoxWidthConst: NSLayoutConstraint!
  28. @IBOutlet var customPageBoxWidthConst: NSLayoutConstraint!
  29. @IBOutlet var settingView: NSView!
  30. @IBOutlet var settingLabel: NSTextField!
  31. @IBOutlet var eachPageCheckbox: ComponentCheckBox!
  32. @IBOutlet var eachPageBoxWidthConst: NSLayoutConstraint!
  33. @IBOutlet var cancelButton: ComponentButton!
  34. @IBOutlet var confirmButton: ComponentButton!
  35. @IBOutlet var cancelBtnWidthConst: NSLayoutConstraint!
  36. @IBOutlet var confirmBtnWidthConst: NSLayoutConstraint!
  37. var fileURL: URL?
  38. var pdfDocument: CPDFDocument?
  39. var pdfView: CPDFListView = CPDFListView.init()
  40. var typeIndex: Int = 1
  41. var resultIndexs: [Int] = []
  42. override func windowDidLoad() {
  43. super.windowDidLoad()
  44. // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
  45. pdfView.frame = pdfBGView.bounds
  46. pdfView.autoresizingMask = [.width, .height]
  47. pdfView.setDisplay(.singlePage)
  48. pdfView.toolMode = .CNoteToolMode
  49. pdfView.autoScales = true
  50. pdfView.delegate = self
  51. pdfView.pdfListViewDelegate = self
  52. pdfBGView.addSubview(pdfView)
  53. reloadData()
  54. }
  55. override func initContentView() {
  56. super.initContentView()
  57. guard let _ = self.contendBox else {
  58. return
  59. }
  60. paginationView.properties = ComponentPaginationProperty(doubleArrow_show: false)
  61. for box in [allPageRadio, currentPageRadio, oddPageRadio, evenPageRadio, customPageRadio] {
  62. box?.setTarget(self, action: #selector(checkBoxClicked(_:)))
  63. }
  64. cancelButton.setTarget(self, action: #selector(cancelButtonClicked(_ :)))
  65. cancelButton.keyEquivalent = KMKeyEquivalent.esc.string()
  66. confirmButton.setTarget(self, action: #selector(openButtonClicked(_ :)))
  67. confirmButton.keyEquivalent = KMKeyEquivalent.enter // Enter key
  68. }
  69. override func updateUIThemeColor() {
  70. super.updateUIThemeColor()
  71. self.reloadUI()
  72. }
  73. override func updateUILanguage() {
  74. super.updateUILanguage()
  75. self.reloadUI()
  76. }
  77. override func beginSheetFinish() {
  78. super.beginSheetFinish()
  79. typeIndex = 1
  80. self.initContentView()
  81. reloadData()
  82. loadPDFPreview()
  83. self.window?.makeFirstResponder(nil)
  84. }
  85. //
  86. func reloadUI() {
  87. guard let _ = self.contendBox else {
  88. return
  89. }
  90. previewBGView.wantsLayer = true
  91. previewBGView.layer?.cornerRadius = 8
  92. previewBGView.layer?.borderWidth = 1
  93. previewBGView.layer?.backgroundColor = ComponentLibrary.shared.getComponentColorFromKey("colorFill/4").cgColor
  94. previewBGView.layer?.borderColor = ComponentLibrary.shared.getComponentColorFromKey("colorBorder/4").cgColor
  95. titleLabel.stringValue = KMLocalizedString("Extract")
  96. titleLabel.textColor = ComponentLibrary.shared.getComponentColorFromKey("colorText/1")
  97. titleLabel.font = ComponentLibrary.shared.getFontFromKey("mac/body-m-medium")
  98. pageRangeLabel.stringValue = KMLocalizedString("Page Range")
  99. pageRangeLabel.textColor = ComponentLibrary.shared.getComponentColorFromKey("colorText/2")
  100. pageRangeLabel.font = ComponentLibrary.shared.getFontFromKey("mac/body-s-medium")
  101. settingLabel.stringValue = KMLocalizedString("Settings")
  102. settingLabel.textColor = ComponentLibrary.shared.getComponentColorFromKey("colorText/2")
  103. settingLabel.font = ComponentLibrary.shared.getFontFromKey("mac/body-s-medium")
  104. allPageRadio.properties = ComponentCheckBoxProperty(size: .s,
  105. state: .normal,
  106. isDisabled: false,
  107. showhelp: false,
  108. text: KMLocalizedString("All Pages"),
  109. checkboxType: .normal)
  110. currentPageRadio.properties = ComponentCheckBoxProperty(size: .s,
  111. state: .normal,
  112. isDisabled: false,
  113. showhelp: false,
  114. text: KMLocalizedString("Current Page"),
  115. checkboxType: .normal)
  116. oddPageRadio.properties = ComponentCheckBoxProperty(size: .s,
  117. state: .normal,
  118. isDisabled: false,
  119. showhelp: false,
  120. text: KMLocalizedString("Odd Pages Only"),
  121. checkboxType: .normal)
  122. evenPageRadio.properties = ComponentCheckBoxProperty(size: .s,
  123. state: .normal,
  124. isDisabled: false,
  125. showhelp: false,
  126. text: KMLocalizedString("Even Pages Only"),
  127. checkboxType: .normal)
  128. customPageRadio.properties = ComponentCheckBoxProperty(size: .s,
  129. state: .normal,
  130. isDisabled: false,
  131. showhelp: false,
  132. text: KMLocalizedString("Custom"),
  133. checkboxType: .normal)
  134. customPageInput.properties = ComponentInputProperty(size: .s,
  135. state:.normal,
  136. isDisabled: true,
  137. placeholder: KMLocalizedString("e.g. 1,3-5,10"),
  138. text: "",
  139. creatable: true,
  140. regexString: "0123456789,-")
  141. eachPageCheckbox.properties = ComponentCheckBoxProperty(size: .s,
  142. state: .normal,
  143. isDisabled: false,
  144. showhelp: false,
  145. text: KMLocalizedString("Each page in a separate file"),
  146. checkboxType: .normal)
  147. cancelButton.properties = ComponentButtonProperty(type: .default_tertiary,
  148. size: .s,
  149. state: .normal,
  150. buttonText: KMLocalizedString("Cancel"),
  151. keepPressState: false)
  152. confirmButton.properties = ComponentButtonProperty(type: .primary,
  153. size: .s,
  154. state: .normal,
  155. buttonText: KMLocalizedString("Extract"),
  156. keepPressState: false)
  157. allPageBoxWidthConst.constant = allPageRadio.properties.propertyInfo.viewWidth
  158. curPageBoxWidthConst.constant = currentPageRadio.properties.propertyInfo.viewWidth
  159. oddPageBoxWidthConst.constant = oddPageRadio.properties.propertyInfo.viewWidth
  160. evenPageBoxWidthConst.constant = evenPageRadio.properties.propertyInfo.viewWidth
  161. customPageBoxWidthConst.constant = customPageRadio.properties.propertyInfo.viewWidth
  162. eachPageBoxWidthConst.constant = eachPageCheckbox.properties.propertyInfo.viewWidth
  163. cancelBtnWidthConst.constant = cancelButton.properties.propertyInfo.viewWidth
  164. confirmBtnWidthConst.constant = confirmButton.properties.propertyInfo.viewWidth
  165. }
  166. func loadPDFPreview() {
  167. guard let filePath = fileURL else {
  168. return
  169. }
  170. NSWindowController.checkPassword(url: filePath, type: .owner, password: "") { [unowned self] success, resultPassword in
  171. if success {
  172. DispatchQueue.main.async {
  173. self.pdfDocument = CPDFDocument.init(url: filePath)
  174. if resultPassword.isEmpty == false {
  175. self.pdfDocument?.unlock(withPassword: resultPassword)
  176. }
  177. self.pdfView.document = self.pdfDocument
  178. self.paginationView.properties.currentIndex = 1
  179. self.paginationView.properties.totalCount = Int(self.pdfDocument?.pageCount ?? 1 )
  180. self.paginationView.reloadData()
  181. }
  182. }
  183. }
  184. }
  185. func reloadData() {
  186. allPageRadio.properties.checkboxType = .normal
  187. currentPageRadio.properties.checkboxType = .normal
  188. oddPageRadio.properties.checkboxType = .normal
  189. evenPageRadio.properties.checkboxType = .normal
  190. customPageRadio.properties.checkboxType = .normal
  191. customPageInput.properties.isDisabled = true
  192. if typeIndex == 0 {
  193. allPageRadio.properties.checkboxType = .selected
  194. } else if typeIndex == 1 {
  195. currentPageRadio.properties.checkboxType = .selected
  196. } else if typeIndex == 2 {
  197. oddPageRadio.properties.checkboxType = .selected
  198. } else if typeIndex == 3 {
  199. evenPageRadio.properties.checkboxType = .selected
  200. } else if typeIndex == 4 {
  201. customPageRadio.properties.checkboxType = .selected
  202. customPageInput.properties.isDisabled = false
  203. }
  204. allPageRadio.reloadData()
  205. currentPageRadio.reloadData()
  206. oddPageRadio.reloadData()
  207. evenPageRadio.reloadData()
  208. customPageRadio.reloadData()
  209. customPageInput.reloadData()
  210. }
  211. //MARK: - Action
  212. @objc func checkBoxClicked(_ sender: ComponentCheckBox) {
  213. if sender == self.allPageRadio {
  214. self.typeIndex = 0
  215. } else if sender == self.currentPageRadio {
  216. self.typeIndex = 1
  217. } else if sender == self.oddPageRadio {
  218. self.typeIndex = 2
  219. } else if sender == self.evenPageRadio {
  220. self.typeIndex = 3
  221. } else if sender == self.customPageRadio {
  222. self.typeIndex = 4
  223. }
  224. reloadData()
  225. }
  226. @objc func cancelButtonClicked(_ sender: NSView) {
  227. self.own_closeEndSheet()
  228. }
  229. @objc func openButtonClicked(_ sender: NSView) {
  230. guard let pdfDocument = self.pdfDocument else {
  231. return
  232. }
  233. var pageIndexs: [Int] = []
  234. if typeIndex == 0 {
  235. pageIndexs = KMPageRangeSelectView.getSelectedPageIndex(pdfDocument, isAllPages: true)
  236. } else if typeIndex == 1 {
  237. pageIndexs = [self.pdfView.currentPageIndex + 1]
  238. } else if typeIndex == 2 {
  239. pageIndexs = KMPageRangeSelectView.getSelectedPageIndex(pdfDocument, isOddPage: true)
  240. } else if typeIndex == 3 {
  241. pageIndexs = KMPageRangeSelectView.getSelectedPageIndex(pdfDocument, isEvenPage: true)
  242. } else if typeIndex == 4 {
  243. if customPageInput.properties.text.isEmpty == true {
  244. let alert = NSAlert()
  245. alert.alertStyle = .warning
  246. alert.messageText = KMLocalizedString("Invalid page range .", comment: "")
  247. alert.runModal()
  248. return
  249. }
  250. pageIndexs = KMPageRangeSelectView.getSelectedPageIndex(pdfDocument, isCustom: customPageInput.properties.text)
  251. }
  252. if pageIndexs.count < 1 {
  253. let alert = NSAlert()
  254. alert.alertStyle = .warning
  255. alert.messageText = KMLocalizedString("Invalid page range .", comment: "")
  256. alert.runModal()
  257. return
  258. }
  259. self.resultIndexs.removeAll()
  260. for i in pageIndexs {
  261. resultIndexs.append(i - 1)
  262. }
  263. var fileName = pdfDocument.documentURL.deletingPathExtension().lastPathComponent
  264. if self.eachPageCheckbox.properties.checkboxType == .normal {
  265. fileName.append(" pages ")
  266. fileName.append(KMPageRangeTools.newParseSelectedIndexs(selectedIndex: resultIndexs.sorted()))
  267. fileName.append(".pdf")
  268. let panel = NSSavePanel()
  269. panel.nameFieldStringValue = fileName
  270. panel.canCreateDirectories = true
  271. panel.beginSheetModal(for: self.window!) { response in
  272. if response == .OK {
  273. if let pdf = CPDFDocument.init() {
  274. var extractPages: Array<CPDFPage> = []
  275. for i in self.resultIndexs {
  276. extractPages.append(pdfDocument.page(at: UInt(i)))
  277. }
  278. let _ = pdf.extractAsOneDocument(withPages: extractPages, savePath: panel.url!.path)
  279. self.own_closeEndSheet()
  280. NSWorkspace.shared.activateFileViewerSelecting([panel.url!])
  281. }
  282. }
  283. }
  284. } else {
  285. let panel = NSOpenPanel()
  286. panel.canChooseFiles = false
  287. panel.canChooseDirectories = true
  288. panel.canCreateDirectories = true
  289. panel.beginSheetModal(for: self.window!) { response in
  290. if response == .OK {
  291. let folderName = String((pdfDocument.documentURL!.lastPathComponent.split(separator: ".")[0])) + "_extract"
  292. var filePath = URL(fileURLWithPath: panel.url!.path).appendingPathComponent(folderName).path
  293. var i = 1
  294. let testFilePath = filePath
  295. while FileManager.default.fileExists(atPath: filePath) {
  296. filePath = testFilePath + "\(i)"
  297. i += 1
  298. }
  299. try? FileManager.default.createDirectory(atPath: filePath, withIntermediateDirectories: false, attributes: nil)
  300. /// 提取的页面
  301. var extractPages: Array<CPDFPage> = []
  302. for i in self.resultIndexs {
  303. extractPages.append(pdfDocument.page(at: UInt(i)))
  304. }
  305. var successArray: [URL]?
  306. successArray = pdfDocument.extractPerPageDocument(withPages: extractPages, folerPath: filePath)
  307. self.own_closeEndSheet()
  308. NSWorkspace.shared.activateFileViewerSelecting([URL(fileURLWithPath: filePath)])
  309. }
  310. }
  311. }
  312. }
  313. }
  314. extension KMExtractWindowController: CPDFListViewDelegate, CPDFViewDelegate {
  315. func pdfViewCurrentPageDidChanged(_ pdfView: CPDFView!) {
  316. let index = pdfView.currentPageIndex
  317. paginationView.properties.currentIndex = index + 1
  318. paginationView.reloadData()
  319. }
  320. }