CustomStampWindowController.swift 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. //
  2. // CustomStampWindowController.swift
  3. // PDF Reader Pro
  4. //
  5. // Created by Niehaoyu on 2024/6/19.
  6. //
  7. import Cocoa
  8. class CustomStampWindowController: NSWindowController, CPDFViewDelegate, CPDFListViewDelegate, NSWindowDelegate {
  9. @IBOutlet var contendView: NSView!
  10. @IBOutlet var leftContendView: NSView!
  11. @IBOutlet var pdfContendView: NSView!
  12. @IBOutlet var rightContendView: NSView!
  13. @IBOutlet var topContendView: NSView!
  14. @IBOutlet var cancelButton: NSButton!
  15. @IBOutlet var addStampButton: NSButton!
  16. var topToolbar: CustomStampTopToolbar!
  17. var leftToolbar: CustomStampLeftToolbar!
  18. var pdfView: CPDFListView!
  19. var rightToolbar: CustomStampRightToolbar!
  20. var eventMonitor: Any?
  21. static var currentWindowController: CustomStampWindowController!
  22. var stampListWC: CSStampListWindowController!
  23. var clickHandle: ((_ windowVC: CustomStampWindowController, _ actionType: Int, _ resultImage: NSImage?)->Void)?
  24. @objc static func currentWC() -> CustomStampWindowController {
  25. if currentWindowController != nil {
  26. return currentWindowController
  27. } else {
  28. let configWC: CustomStampWindowController = CustomStampWindowController.init(windowNibName: "CustomStampWindowController")
  29. currentWindowController = configWC;
  30. return currentWindowController
  31. }
  32. }
  33. deinit {
  34. NotificationCenter.default.removeObserver(self)
  35. print("\(self.className) deinit")
  36. }
  37. override func windowDidLoad() {
  38. super.windowDidLoad()
  39. // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
  40. self.window?.delegate = self
  41. self.window?.title = NSLocalizedString("Create Custom Stamp", comment: "")
  42. self.addEventMonitor()
  43. self.configPDFContendView()
  44. self.configTopToolbar()
  45. self.configLeftToolbar()
  46. self.configRightToolbar()
  47. self.cancelButton.title = NSLocalizedString("Cancel", comment: "")
  48. self.addStampButton.title = NSLocalizedString("Add Stamp", comment: "")
  49. self.updateViewColor()
  50. self.window?.makeFirstResponder(self.pdfView)
  51. }
  52. func reloadData() {
  53. self.topToolbar.reloadData()
  54. self.leftToolbar.reloadData()
  55. self.rightToolbar.reloadData()
  56. self.updateRightToolBarInfo()
  57. }
  58. func configPDFContendView() {
  59. let pdfDocument = CPDFDocument()
  60. pdfDocument?.insertBlankPage(pageSize: NSSize(width: 250, height: 200), at: 0)
  61. self.pdfView = CPDFListView(frame: self.pdfContendView.bounds)
  62. self.pdfView.autoresizingMask = [.width, .height]
  63. self.pdfView.document = pdfDocument
  64. self.pdfView.delegate = self
  65. self.pdfView.pdfListViewDelegate = self
  66. self.pdfView.autoScales = false
  67. self.pdfView.scaleFactor = 1
  68. self.pdfView.isCustomStampType = "1"
  69. self.pdfContendView.wantsLayer = true
  70. self.pdfContendView.layer?.cornerRadius = 4
  71. self.pdfContendView.layer?.masksToBounds = true
  72. self.pdfContendView.layer?.borderWidth = 1
  73. self.pdfContendView.addSubview(self.pdfView)
  74. NotificationCenter.default.addObserver(self, selector: #selector(CPDFListViewActiveAnnotationsChangeNotification), name: NSNotification.Name.init(rawValue: "CPDFListViewActiveAnnotationsChangeNotification"), object: nil)
  75. }
  76. func configTopToolbar() {
  77. self.topToolbar = CustomStampTopToolbar.createFromNib()
  78. self.topToolbar.frame = self.topContendView.bounds
  79. self.topToolbar.autoresizingMask = [.width, .height]
  80. self.topToolbar.pdfView = self.pdfView
  81. self.topContendView.addSubview(self.topToolbar)
  82. }
  83. func configLeftToolbar() {
  84. self.leftToolbar = CustomStampLeftToolbar.createFromNib()
  85. self.leftToolbar.frame = self.leftContendView.bounds
  86. self.leftToolbar.autoresizingMask = [.width, .height]
  87. self.leftToolbar.clickHandle = {[weak self] view , toolbarType, string in
  88. self?.pdfView.customStampAnnotationType = String(toolbarType.rawValue)
  89. if toolbarType == .move {
  90. self?.pdfView.annotationType = .unkown
  91. }else if toolbarType == .text ||
  92. toolbarType == .dateText ||
  93. toolbarType == .idText {
  94. self?.pdfView.toolMode = .noteToolMode
  95. self?.pdfView.annotationType = .freeText
  96. if string.isEmpty == false {
  97. self?.pdfView.customStampTextValue = string
  98. }
  99. } else if toolbarType == .image {
  100. self?.pdfView.toolMode = .noteToolMode
  101. self?.pdfView.annotationType = .stamp
  102. self?.chooseImage()
  103. } else if toolbarType == .stamp {
  104. self?.pdfView.toolMode = .noteToolMode
  105. self?.chooseStamp()
  106. }
  107. }
  108. self.leftContendView.addSubview(self.leftToolbar)
  109. }
  110. func configRightToolbar() {
  111. self.rightToolbar = CustomStampRightToolbar.createFromNib()
  112. self.rightToolbar.frame = self.rightContendView.bounds
  113. self.rightToolbar.autoresizingMask = [.width, .height]
  114. self.rightToolbar.pdfView = self.pdfView
  115. self.rightToolbar.delegate = self
  116. self.rightContendView.addSubview(self.rightToolbar)
  117. }
  118. func updateViewColor() {
  119. if KMAppearance.isDarkMode() {
  120. self.window?.backgroundColor = NSColor(red: 38/255, green: 40/255, blue: 43/255, alpha: 1)
  121. self.pdfContendView.layer?.borderColor = NSColor.white.withAlphaComponent(0.05).cgColor
  122. self.pdfView.backgroundColor = NSColor(red: 47/255, green: 49/255, blue: 51/255, alpha: 1)
  123. } else {
  124. self.window?.backgroundColor = NSColor.white
  125. self.pdfContendView.layer?.borderColor = NSColor.black.withAlphaComponent(0.06).cgColor
  126. self.pdfView.backgroundColor = NSColor(red: 245/255, green: 245/255, blue: 245/255, alpha: 1)
  127. }
  128. }
  129. private func addEventMonitor() {
  130. if (self.eventMonitor != nil) {
  131. self.removeEventMonitor()
  132. }
  133. DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.3) {
  134. KMPrint("已添加事件监听")
  135. self.eventMonitor = NSEvent.addLocalMonitorForEvents(matching: [.scrollWheel, .leftMouseDown]) { [weak self] event in
  136. if (event.type == .scrollWheel && event.modifierFlags.contains(.option)) { // Alt + 鼠标滚轮
  137. self?.pdfView.magnifyWheel(event)
  138. return nil
  139. }
  140. return event
  141. }
  142. }
  143. }
  144. private func removeEventMonitor() {
  145. if (self.eventMonitor != nil) {
  146. KMPrint("已移除事件监听")
  147. NSEvent.removeMonitor(self.eventMonitor as Any)
  148. self.eventMonitor = nil
  149. }
  150. }
  151. func clear() {
  152. self.removeEventMonitor()
  153. CustomStampWindowController.currentWindowController = nil
  154. NotificationCenter.default.removeObserver(self)
  155. }
  156. //MARK: Private Method
  157. func chooseImage() {
  158. let accessoryCtr = KMImageAccessoryController()
  159. let openPanel = NSOpenPanel()
  160. openPanel.allowedFileTypes = KMImageAccessoryController.supportedImageTypes()
  161. openPanel.allowsMultipleSelection = false
  162. openPanel.accessoryView = accessoryCtr.view
  163. openPanel.canSelectHiddenExtension = true
  164. openPanel.beginSheetModal(for: self.window!) { [weak self] (result) in
  165. if result == .OK {
  166. guard let url = openPanel.url else {
  167. return
  168. }
  169. let filePath = url.path
  170. if filePath.pathExtension.lowercased() == "pdf" {
  171. if let pdf = PDFDocument(url: url), pdf.isEncrypted {
  172. NSSound.beep()
  173. return
  174. }
  175. }
  176. let stamp = CStampObject(imageStampFilePath: filePath)
  177. stamp.nameString = ""
  178. self?.pdfView.setAddStamp(stamp, keepToolModel: true)
  179. self?.updateRightToolBarInfo()
  180. } else {
  181. self?.leftToolbar.toolbarType = .move
  182. self?.leftToolbar.reloadData()
  183. }
  184. }
  185. }
  186. func chooseStamp() {
  187. self.stampListWC = CSStampListWindowController.currentWC()
  188. self.stampListWC.clickHandle = {[weak self] view , actionType, filePath in
  189. if actionType == 0 {
  190. self?.leftToolbar.toolbarType = .move
  191. self?.leftToolbar.reloadData()
  192. } else if actionType == 1 {
  193. self?.pdfView.annotationType = .stamp
  194. let stamp = CStampObject(imageStampFilePath: filePath!)
  195. stamp.nameString = ""
  196. self?.pdfView.setAddStamp(stamp, keepToolModel: true)
  197. self?.updateRightToolBarInfo()
  198. }
  199. self?.window?.endSheet(view.window!)
  200. }
  201. self.window!.beginSheet(self.stampListWC.window!, completionHandler: {[weak self] response in
  202. self?.stampListWC.reloadData()
  203. })
  204. }
  205. func updateRightToolBarInfo() {
  206. if self.pdfView?.activeAnnotations.count == 0 {
  207. self.rightToolbar.currentAnnotation = nil
  208. } else {
  209. self.rightToolbar.currentAnnotation = self.pdfView.activeAnnotation
  210. }
  211. let page = self.pdfView?.document?.page(at: 0)
  212. if page?.annotations.isEmpty == false {
  213. self.addStampButton.isEnabled = true
  214. self.addStampButton.bezelColor = NSColor.controlAccentColor
  215. } else {
  216. self.addStampButton.isEnabled = false
  217. self.addStampButton.bezelColor = self.cancelButton.bezelColor
  218. }
  219. }
  220. @objc func cropCurrentPage() {
  221. var rect = NSIntegralRect(self.pdfView.currentSelectionRect())
  222. var page: CPDFPage?
  223. if let data = self.pdfView.currentSelectionPage() {
  224. page = data
  225. } else {
  226. page = self.pdfView.currentPage()
  227. }
  228. if (NSIsEmptyRect(rect)) {
  229. rect = KMCropTools.getPageForegroundBox(page!)
  230. }
  231. let index: UInt = (page?.pageIndex()) ?? 0
  232. self._cropPage(at: index, in: rect)
  233. }
  234. @objc private func _cropPage(at index: UInt, in rect: NSRect) {
  235. let oldRect = self.pdfView?.document?.page(at: index)?.bounds(for: .cropBox) ?? .zero
  236. let undoManager = self.pdfView.undoManager
  237. (undoManager?.prepare(withInvocationTarget: self) as? AnyObject)?._cropPage(at: index, in: oldRect)
  238. let page = self.pdfView.document.page(at: index)
  239. let newRect = NSIntersectionRect(rect, (page?.bounds(for: .mediaBox)) ?? .zero)
  240. page?.setBounds(newRect, for: .cropBox)
  241. /// 刷新预览视图
  242. self.pdfView.layoutDocumentView()
  243. self.pdfView.displayBox = .cropBox
  244. }
  245. //MARK: IBAction
  246. @IBAction func cancelAction(_ sender: Any) {
  247. guard let callBack = self.clickHandle else {
  248. return
  249. }
  250. callBack(self, 0, nil)
  251. }
  252. @IBAction func addStampAction(_ sender: Any) {
  253. guard let page = self.pdfView.document.page(at: 0) else {
  254. return
  255. }
  256. for anno in page.annotations ?? [] {
  257. self.pdfView.setNeedsDisplay(anno)
  258. }
  259. self.cropCurrentPage()
  260. let image = page.thumbnail(of: CGSizeMake(page.size.width*4, page.size.height*4))
  261. guard let callBack = self.clickHandle else {
  262. return
  263. }
  264. callBack(self, 1, image)
  265. }
  266. //MARK: CPDFListViewDelegate & Notification
  267. func pdfViewScaleDidChanged(_ pdfView: CPDFView!) {
  268. self.topToolbar?.pdfViewScaleChanged()
  269. }
  270. @objc func CPDFListViewActiveAnnotationsChangeNotification(notification: NSNotification) {
  271. if notification.object is CPDFListView {
  272. let listView : CPDFListView = notification.object as! CPDFListView
  273. if listView.isEqual(self.pdfView) {
  274. self.updateRightToolBarInfo()
  275. }
  276. }
  277. }
  278. func pdfListViewAddAnnotations(_ pdfListView: CPDFListView!, forAdd annotations: [CPDFAnnotation]!, in pdfPage: CPDFPage!) {
  279. if self.leftToolbar.toolbarType != .move {
  280. self.leftToolbar.toolbarType = .move
  281. self.leftToolbar.reloadData()
  282. }
  283. }
  284. override func showWindow(_ sender: Any?) {
  285. super.showWindow(sender)
  286. }
  287. override func close() {
  288. super.close()
  289. self.removeEventMonitor()
  290. CustomStampWindowController.currentWindowController = nil
  291. NotificationCenter.default.removeObserver(self)
  292. }
  293. func windowShouldClose(_ sender: NSWindow) -> Bool {
  294. self.removeEventMonitor()
  295. CustomStampWindowController.currentWindowController = nil
  296. NotificationCenter.default.removeObserver(self)
  297. return true
  298. }
  299. }
  300. extension CustomStampWindowController: CSRightToolbarDelegate {
  301. func csRightToolbar(_ toolbar: CustomStampRightToolbar, didAnnotationUpdate annotation: CPDFAnnotation?) {
  302. }
  303. func csRightToolbar(_ toolbar: CustomStampRightToolbar, didDeleteAnnotation annotation: CPDFAnnotation?) {
  304. self.pdfView.remove(annotation)
  305. self.reloadData()
  306. }
  307. func csRightToolbar(_ toolbar: CustomStampRightToolbar, didAnnotationClear annotation: CPDFAnnotation?) {
  308. let page = self.pdfView?.document?.page(at: 0)
  309. page?.removeAllAnnotations()
  310. self.pdfView?.setNeedsDisplayForVisiblePages()
  311. self.reloadData()
  312. }
  313. func csRightToolbar(didPDFClip toolbar: CustomStampRightToolbar) {
  314. guard let page = self.pdfView.document.page(at: 0) else {
  315. return
  316. }
  317. for anno in page.annotations ?? [] {
  318. if anno is CPDFFreeTextAnnotation {
  319. let newAnnotation = (anno as! CPDFFreeTextAnnotation)
  320. if self.pdfView.isEdit(withCurrentFreeText: newAnnotation) {
  321. self.pdfView.commitEditAnnotationFreeText(newAnnotation)
  322. self.pdfView.annotationType = .unkown
  323. }
  324. }
  325. self.pdfView.setNeedsDisplay(anno)
  326. }
  327. self.cropCurrentPage()
  328. }
  329. }