KMNPageEditViewController.swift 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519
  1. //
  2. // KMNPageEditViewController.swift
  3. // PDF Reader Pro
  4. //
  5. // Created by 丁林圭 on 2024/10/21.
  6. //
  7. import Cocoa
  8. import KMComponentLibrary
  9. @objc protocol KMNPageEditViewViewDelegate: AnyObject {
  10. @objc optional func changeBoundEditViewController(pageEditVC:KMNPageEditViewController)
  11. @objc optional func changeUndoManagerEditViewController(pageEditVC:KMNPageEditViewController)
  12. }
  13. class KMNPageEditViewController: KMNThumbnailBaseViewController {
  14. override func viewDidLoad() {
  15. super.viewDidLoad()
  16. self.backViewBox.fillColor = ComponentLibrary.shared.getComponentColorFromKey("colorBg/layout-pdfView")
  17. collectionView.menuClickedAction = { point in
  18. return self.clickMenu(point: point)
  19. }
  20. }
  21. private func clickMenu(point:NSPoint)->NSMenu {
  22. let copyPages: [CPDFPage] = KMNThumbnailManager.manager.copyPages
  23. let menu = NSMenu()
  24. // 根据 clickPoint 创建菜单项
  25. let copyMenuItem = NSMenuItem(title: KMLocalizedString("Copy"), action: #selector(copyMenuItemAciton), target: self)
  26. copyMenuItem.keyEquivalent = "c"
  27. let pastMenuItem = NSMenuItem(title: KMLocalizedString("Paste"), action: #selector(pastMenuItemAciton), target: self)
  28. pastMenuItem.keyEquivalent = "v"
  29. let pastNullMenuItem = NSMenuItem(title: KMLocalizedString("Paste"), action: nil, target: self)
  30. pastNullMenuItem.keyEquivalent = "v"
  31. let cutMenuItem = NSMenuItem(title: KMLocalizedString("Cut"), action: #selector(cutMenuItemAciton), target: self)
  32. cutMenuItem.keyEquivalent = "x"
  33. let deleteMenuItem = NSMenuItem(title: KMLocalizedString("Delete"), action: #selector(deleteMenuItemAciton), target: self)
  34. deleteMenuItem.keyEquivalent = String(Unicode.Scalar(NSBackspaceCharacter)!)
  35. let rotateLeftMenuItem = NSMenuItem(title: KMLocalizedString("90° CCW"), action: #selector(rotatePageLeftAction), target: self)
  36. rotateLeftMenuItem.keyEquivalent = "l" // 设置为字母 l
  37. rotateLeftMenuItem.keyEquivalentModifierMask = [.option, .command] // 设置为 Option + Command
  38. let rotateRightMenuItem = NSMenuItem(title: KMLocalizedString("90° CW"), action: #selector(rotatePageRightAction), target: self)
  39. rotateRightMenuItem.keyEquivalent = "r" // 设置为字母 r
  40. rotateRightMenuItem.keyEquivalentModifierMask = [.option, .command] // 设置为 Option + Command
  41. let insertFileMenuItem = NSMenuItem(title: KMLocalizedString("Insert File"), action: #selector(insertFromPDFAction), target: self)
  42. let insertBlankMenuItem = NSMenuItem(title: KMLocalizedString("Insert a Blank Page"), action: #selector(insertFromBlankAction), target: self)
  43. let replaceMenuItem = NSMenuItem(title: KMLocalizedString("Replace"), action: #selector(replacePDFAction), target: self)
  44. let extractMenuItem = NSMenuItem(title: KMLocalizedString("Export"), action: #selector(extractPDFAction), target: self)
  45. let shareMenuItem = NSMenuItem(title: KMLocalizedString("Share"), action: nil, target: self)
  46. shareMenuItem.submenu = NSSharingServicePicker.menu(forSharingItems: [showDocument?.documentURL ?? ""], subjectContext: "", withTarget: self, selector: NSSelectorFromString("sharePageItemAction:"), serviceDelegate: nil)
  47. let showFileSizeMenuItem = NSMenuItem(title: KMLocalizedString("Display Page Size"), action: #selector(displayPageSizeAction), target: self)
  48. showFileSizeMenuItem.state = isShowPageSize ? .on : .off
  49. let selectedIndexPaths = collectionView.selectionIndexPaths
  50. if(selectedIndexPaths.count > 0) {
  51. menu.addItem(copyMenuItem)
  52. menu.addItem(cutMenuItem)
  53. if(copyPages.count > 0) {
  54. menu.addItem(pastMenuItem)
  55. }
  56. menu.addItem(deleteMenuItem)
  57. menu.addItem(NSMenuItem.separator())
  58. menu.addItem(rotateRightMenuItem)
  59. menu.addItem(rotateLeftMenuItem)
  60. menu.addItem(NSMenuItem.separator())
  61. if(selectedIndexPaths.count == 1) {
  62. menu.addItem(insertFileMenuItem)
  63. menu.addItem(insertBlankMenuItem)
  64. menu.addItem(replaceMenuItem)
  65. }
  66. menu.addItem(extractMenuItem)
  67. menu.addItem(shareMenuItem)
  68. menu.addItem(NSMenuItem.separator())
  69. menu.addItem(showFileSizeMenuItem)
  70. } else {
  71. if(copyPages.count > 0) {
  72. menu.addItem(pastMenuItem)
  73. menu.addItem(NSMenuItem.separator())
  74. } else {
  75. menu.addItem(pastNullMenuItem)
  76. menu.addItem(NSMenuItem.separator())
  77. }
  78. menu.addItem(showFileSizeMenuItem)
  79. }
  80. return menu
  81. }
  82. @objc public func insertFromPDFAction() {
  83. if IAPProductsManager.default().isAvailableAllFunction() == false {
  84. KMPurchaseCompareWindowController.sharedInstance()?.showWindow(nil)
  85. return
  86. }
  87. let selectedIndexPaths = collectionView.selectionIndexPaths
  88. let minimumIndexPath = selectedIndexPaths.min(by: { $0 < $1 })
  89. let open = NSOpenPanel()
  90. let insertPDF = KMNPDFInsertPDFWindowController(self.showDocument, currentPageIndex: minimumIndexPath?.item ?? 0)
  91. insertPDF.callback = { [weak self] fileAttribute, insertIdx in
  92. let doc = fileAttribute.pdfDocument
  93. self?.thumbnailBaseViewDelegate?.insertPDFThumbnailViewControlle?(pageEditVC: self, pdfDocment: doc)
  94. var insertIndex = insertIdx
  95. var insertPages: [CPDFPage] = []
  96. var indexpaths = Set<IndexPath>()
  97. for number in fileAttribute.fetchSelectPages() {
  98. if let page = doc?.page(at: UInt(number-1)) {
  99. insertPages.append(page)
  100. indexpaths.insert(IndexPath(item: insertIndex, section: 0))
  101. insertIndex += 1
  102. }
  103. }
  104. self?.insertFormPDF(insertPages: insertPages, pageDex: insertIdx)
  105. }
  106. insertPDF.own_beginSheetModal(for: self.view.window, completionHandler: nil)
  107. }
  108. @objc public func insertFromBlankAction() {
  109. if IAPProductsManager.default().isAvailableAllFunction() == false {
  110. KMPurchaseCompareWindowController.sharedInstance()?.showWindow(nil)
  111. return
  112. }
  113. let selectedIndexPaths = collectionView.selectionIndexPaths
  114. let maxmumIndexPath = selectedIndexPaths.max(by: { $0 < $1 })
  115. let insertPDF = KMNPDFInsertBlankWindowController(self.showDocument, currentPageIndex: maxmumIndexPath?.item ?? 0)
  116. insertPDF.own_beginSheetModal(for: self.view.window, completionHandler: nil)
  117. }
  118. public func insertFromClipboardAction() {
  119. if IAPProductsManager.default().isAvailableAllFunction() == false {
  120. KMPurchaseCompareWindowController.sharedInstance()?.showWindow(nil)
  121. return
  122. }
  123. let selectedIndexPaths = collectionView.selectionIndexPaths
  124. let maxmumIndexPath = selectedIndexPaths.max(by: { $0 < $1 })
  125. let insertPDF = KMNPDFInsertClipboardWindowController(self.showDocument, currentPageIndex: maxmumIndexPath?.item ?? 0)
  126. insertPDF.own_beginSheetModal(for: self.view.window, completionHandler: nil)
  127. }
  128. public func insertFromScannerAction() {
  129. if IAPProductsManager.default().isAvailableAllFunction() == false {
  130. KMPurchaseCompareWindowController.sharedInstance()?.showWindow(nil)
  131. return
  132. }
  133. let selectedIndexPaths = collectionView.selectionIndexPaths
  134. let maxmumIndexPath = selectedIndexPaths.max(by: { $0 < $1 })
  135. let vc = KMDeviceBrowserWindowController.shared
  136. vc.type = .scanner
  137. vc.importScannerFileCallback = { [weak self] (url: NSURL) -> Void in
  138. if let imag = NSImage(contentsOfFile: url.path! ) {
  139. let index = (maxmumIndexPath?.item ?? 0) + 1
  140. let document = CPDFDocument()
  141. _ = document?.km_insertPage(imag.size, withImage: url.path! , at:UInt(index))
  142. self?.refreshDatas()
  143. }
  144. }
  145. vc.showWindow(nil)
  146. vc.window?.center()
  147. }
  148. public func canZoomInPageSize()->Bool {
  149. if pageThumbnailSize.width > defaultItemSize.width {
  150. return false
  151. }
  152. return true
  153. }
  154. public func canZoomOutPageSize()->Bool {
  155. if pageThumbnailSize.width < defaultItemSize.width {
  156. return false
  157. }
  158. return true
  159. }
  160. public func canUndo()->Bool {
  161. return true
  162. }
  163. public func undoPDFAction() {
  164. }
  165. public func redoPDFAction() {
  166. }
  167. public func canRodo()->Bool {
  168. return true
  169. }
  170. @objc public func extractPDFAction() {
  171. if IAPProductsManager.default().isAvailableAllFunction() == false {
  172. KMPurchaseCompareWindowController.sharedInstance()?.showWindow(nil)
  173. return
  174. }
  175. let selectedIndexPaths = collectionView.selectionIndexPaths
  176. if selectedIndexPaths.count < 1 {
  177. _ = KMNCustomAlertView.alertView(message: KMLocalizedString("Please select one or more pages first to organize."), type: .info, fromView: self.view, point:CGPointMake(self.view.frame.origin.x + self.view.frame.size.width/2, self.view.bounds.size.height - 30))
  178. return
  179. }
  180. let extractPDF = KMNExtractPDFWindowController(self.showDocument, selectionIndexPaths: collectionView.selectionIndexPaths)
  181. extractPDF.callback = { [weak self] oneDocumentPerPage, isDeletePage in
  182. extractPDF.own_closeEndSheet()
  183. if let _ = self?.showDocument {
  184. self?.extractPages(indexpaths: selectedIndexPaths, oneDocumentPerPage: oneDocumentPerPage, callback: { [weak self] result, params in
  185. if (result == .failure || result == .cancel) {
  186. return
  187. }
  188. if (isDeletePage) {
  189. self?.deletePages(indexpaths: selectedIndexPaths)
  190. }
  191. })
  192. }
  193. }
  194. extractPDF.own_beginSheetModal(for: self.view.window, completionHandler: nil)
  195. }
  196. @objc public func replacePDFAction() {
  197. if IAPProductsManager.default().isAvailableAllFunction() == false {
  198. KMPurchaseCompareWindowController.sharedInstance()?.showWindow(nil)
  199. return
  200. }
  201. let selectedIndexPaths = collectionView.selectionIndexPaths
  202. if selectedIndexPaths.count < 1 {
  203. _ = KMNCustomAlertView.alertView(message: KMLocalizedString("Please select one or more pages first to organize."), type: .info, fromView: self.view, point:CGPointMake(self.view.frame.origin.x + self.view.frame.size.width/2, self.view.bounds.size.height - 30))
  204. return
  205. }
  206. self.km_open_file_multi { [unowned self] index, params in
  207. if (self.fetchProgressBlockParamsIsPasswordFile(params: params)) { // 加密文档进度回调
  208. return
  209. }
  210. let tFileUrl = self.fetchProgressBlockParamsForFileUrl(params: params)
  211. let pdfExtensions = KMNConvertTool.supportPDFFileType()
  212. if let exn = tFileUrl?.pathExtension, pdfExtensions.contains(exn) {
  213. if (tFileUrl!.path.isPDFValid() == false) {
  214. let alert = NSAlert()
  215. alert.alertStyle = .critical
  216. alert.messageText = NSLocalizedString("An error occurred while opening this document. The file is damaged and could not be repaired.", comment: "")
  217. alert.runModal()
  218. }
  219. }
  220. } completionBlock: { [unowned self] documents in
  221. self.replacePages(of: selectedIndexPaths, with: documents)
  222. }
  223. }
  224. public func splitPDFAction() {
  225. if IAPProductsManager.default().isAvailableAllFunction() == false {
  226. KMPurchaseCompareWindowController.sharedInstance()?.showWindow(nil)
  227. return
  228. }
  229. let selectedIndexPaths = collectionView.selectionIndexPaths
  230. if collectionView.selectionIndexPaths.count < 1 {
  231. _ = KMNCustomAlertView.alertView(message: KMLocalizedString("Please select one or more pages first to organize."), type: .info, fromView: self.view, point:CGPointMake(self.view.bounds.origin.x + self.view.bounds.size.width/2, self.view.bounds.size.height - 30))
  232. return
  233. }
  234. let splitPDF = KMNSplitPDFWindowController(self.showDocument,selectionIndexPaths: selectedIndexPaths)
  235. splitPDF.own_beginSheetModal(for: self.view.window, completionHandler: nil)
  236. }
  237. public func reversePDFAction() {
  238. if IAPProductsManager.default().isAvailableAllFunction() == false {
  239. KMPurchaseCompareWindowController.sharedInstance()?.showWindow(nil)
  240. return
  241. }
  242. let selectedIndexPaths = collectionView.selectionIndexPaths
  243. if selectedIndexPaths.count < 2 {
  244. _ = KMNCustomAlertView.alertView(message: KMLocalizedString("No page selected. Please select at least two pages to organize."), type: .info, fromView: self.view, point:CGPointMake(self.view.frame.origin.x + self.view.frame.size.width/2, self.view.bounds.size.height - 30))
  245. return
  246. }
  247. reversePages(indexs: self.indexpathsToIndexs(indexpaths: selectedIndexPaths))
  248. }
  249. @objc public func rotatePageLeftAction() {
  250. if IAPProductsManager.default().isAvailableAllFunction() == false {
  251. KMPurchaseCompareWindowController.sharedInstance()?.showWindow(nil)
  252. return
  253. }
  254. if collectionView.selectionIndexPaths.count < 1 {
  255. _ = KMNCustomAlertView.alertView(message: KMLocalizedString("Please select one or more pages first to organize."), type: .info, fromView: self.view, point:CGPointMake(self.view.frame.origin.x + self.view.frame.size.width/2, self.view.bounds.size.height - 30))
  256. return
  257. }
  258. rotatePages(indexPaths: collectionView.selectionIndexPaths, rotateAngle: -90)
  259. }
  260. @objc public func rotatePageRightAction() {
  261. if IAPProductsManager.default().isAvailableAllFunction() == false {
  262. KMPurchaseCompareWindowController.sharedInstance()?.showWindow(nil)
  263. return
  264. }
  265. if collectionView.selectionIndexPaths.count < 1 {
  266. _ = KMNCustomAlertView.alertView(message: KMLocalizedString("Please select one or more pages first to organize."), type: .info, fromView: self.view, point:CGPointMake(self.view.frame.origin.x + self.view.frame.size.width/2, self.view.bounds.size.height - 30))
  267. return
  268. }
  269. rotatePages(indexPaths: collectionView.selectionIndexPaths, rotateAngle: 90)
  270. }
  271. public func deletePageAction() {
  272. if IAPProductsManager.default().isAvailableAllFunction() == false {
  273. KMPurchaseCompareWindowController.sharedInstance()?.showWindow(nil)
  274. return
  275. }
  276. let selectedIndexPaths = collectionView.selectionIndexPaths
  277. if selectedIndexPaths.count < 1 {
  278. _ = KMNCustomAlertView.alertView(message: KMLocalizedString("Please select one or more pages first to organize."), type: .info, fromView: self.view, point:CGPointMake(self.view.frame.origin.x + self.view.frame.size.width/2, self.view.bounds.size.height - 30))
  279. return
  280. } else if selectedIndexPaths.count == (showDocument?.pageCount ?? 0) {
  281. _ = KMNCustomAlertView.alertView(message: KMLocalizedString("Can not delete all pages."), type: .info, fromView: self.view, point:CGPointMake(self.view.frame.origin.x + self.view.frame.size.width/2, self.view.bounds.size.height - 30))
  282. return
  283. }
  284. deletePages(indexpaths: selectedIndexPaths)
  285. }
  286. public func zoomInPageAction() { //放大
  287. if (canZoomInPageSize() == false) {
  288. return
  289. }
  290. let indexpaths = collectionView.selectionIndexPaths
  291. if (pageThumbnailSize.width < defaultItemSize.width) {
  292. pageThumbnailSize = defaultItemSize
  293. } else if (pageThumbnailSize.width == defaultItemSize.width) {
  294. pageThumbnailSize = NSSize(width: defaultItemSize.width*1.5, height: defaultItemSize.height*1.5)
  295. }
  296. collectionView.reloadData()
  297. collectionView.selectionIndexPaths = indexpaths
  298. }
  299. public func zoomOutPageAction() { //缩小
  300. if (canZoomOutPageSize() == false) {
  301. return
  302. }
  303. let indexpaths = collectionView.selectionIndexPaths
  304. if (pageThumbnailSize.width > defaultItemSize.width) {
  305. pageThumbnailSize = defaultItemSize
  306. } else if (pageThumbnailSize.width == defaultItemSize.width) {
  307. pageThumbnailSize = NSSize(width: defaultItemSize.width*0.5, height: defaultItemSize.height*0.5)
  308. }
  309. collectionView.reloadData()
  310. collectionView.selectionIndexPaths = indexpaths
  311. }
  312. @objc public func copyMenuItemAciton() {
  313. if IAPProductsManager.default().isAvailableAllFunction() == false {
  314. let winC = KMPurchaseCompareWindowController.sharedInstance()
  315. winC?.showWindow(nil)
  316. return
  317. }
  318. KMNThumbnailManager.manager.copyPages = []
  319. let indexpaths = collectionView.selectionIndexPaths
  320. for indexpath in indexpaths.sorted() {
  321. guard let page = showDocument?.page(at: UInt(indexpath.item)).copy() as? CPDFPage else {
  322. continue
  323. }
  324. KMNThumbnailManager.manager.copyDocument.append(showDocument ?? CPDFDocument())
  325. KMNThumbnailManager.manager.copyPages.append(page)
  326. }
  327. }
  328. @objc public func pastMenuItemAciton() {
  329. if IAPProductsManager.default().isAvailableAllFunction() == false {
  330. let winC = KMPurchaseCompareWindowController.sharedInstance()
  331. winC?.showWindow(nil)
  332. return
  333. }
  334. let selectedIndexPaths = collectionView.selectionIndexPaths
  335. var pastIndex = 1
  336. if(selectedIndexPaths.count > 0) {
  337. let maxmumIndexPath = selectedIndexPaths.max(by: { $0 < $1 })
  338. pastIndex = maxmumIndexPath?.item ?? 1
  339. }
  340. var indexpaths: Set<IndexPath> = []
  341. let copyPages = KMNThumbnailManager.manager.copyPages
  342. let page = copyPages.first
  343. if page?.document == showDocument {
  344. for (i, page) in copyPages.enumerated() {
  345. let zCopyPage = page.copy() as? CPDFPage
  346. if(zCopyPage != nil) {
  347. showDocument?.insertPageObject(zCopyPage, at: UInt(pastIndex+i))
  348. indexpaths.insert(IndexPath(item: pastIndex+i, section: 0))
  349. }
  350. }
  351. } else {
  352. if let pasteDocument = page?.document {
  353. var indexs: IndexSet = IndexSet()
  354. for (i, _) in copyPages.enumerated() {
  355. indexs.insert(i)
  356. indexpaths.insert(IndexPath(item: pastIndex+i, section: 0))
  357. }
  358. showDocument?.importPages(indexs, from: pasteDocument, at: UInt(pastIndex))
  359. }
  360. }
  361. refreshDatas()
  362. collectionView.reloadData()
  363. collectionView.selectionIndexPaths = indexpaths
  364. }
  365. @objc public func cutMenuItemAciton() {
  366. if IAPProductsManager.default().isAvailableAllFunction() == false {
  367. let winC = KMPurchaseCompareWindowController.sharedInstance()
  368. winC?.showWindow(nil)
  369. return
  370. }
  371. let indexpaths = collectionView.selectionIndexPaths
  372. for indexpath in indexpaths.sorted() {
  373. guard let page = showDocument?.page(at: UInt(indexpath.item)).copy() as? CPDFPage else {
  374. continue
  375. }
  376. KMNThumbnailManager.manager.copyDocument.append(showDocument ?? CPDFDocument())
  377. KMNThumbnailManager.manager.copyPages.append(page)
  378. showDocument?.removePage(at: UInt(indexpath.item))
  379. refreshDatas()
  380. collectionView.reloadData()
  381. }
  382. }
  383. @objc public func deleteMenuItemAciton() {
  384. if IAPProductsManager.default().isAvailableAllFunction() == false {
  385. let winC = KMPurchaseCompareWindowController.sharedInstance()
  386. winC?.showWindow(nil)
  387. return
  388. }
  389. let indexpaths = collectionView.selectionIndexPaths
  390. for indexpath in indexpaths.sorted() {
  391. showDocument?.removePage(at: UInt(indexpath.item))
  392. refreshDatas()
  393. collectionView.reloadData()
  394. }
  395. }
  396. @objc func sharePageItemAction(menuItem:NSMenuItem) {
  397. if IAPProductsManager.default().isAvailableAllFunction() == false {
  398. let winC = KMPurchaseCompareWindowController.sharedInstance()
  399. winC?.showWindow(nil)
  400. return
  401. }
  402. let indexpaths = collectionView.selectionIndexPaths
  403. let doucument = self.showDocument
  404. let filename : String = doucument?.documentURL.lastPathComponent ?? ""
  405. let folderPath = (NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.applicationSupportDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).last?.stringByAppendingPathComponent(filename)) ?? ""
  406. try? FileManager.default.removeItem(atPath: folderPath)
  407. let pdfdocument = CPDFDocument()
  408. let ret = pdfdocument?.importPages(indexpathsToIndexs(indexpaths: indexpaths), from: doucument, at: 0) ?? false
  409. let url = URL(fileURLWithPath: folderPath)
  410. if ret {
  411. let success = pdfdocument?.write(toFile: folderPath)
  412. let represent = menuItem.representedObject as? NSSharingService
  413. represent?.perform(withItems: [url])
  414. }
  415. }
  416. @objc public func displayPageSizeAction() {
  417. let indexpaths = collectionView.selectionIndexPaths
  418. isShowPageSize = !isShowPageSize
  419. collectionView.selectionIndexPaths = indexpaths
  420. }
  421. }