// // CustomStampWindowController.swift // PDF Reader Pro // // Created by Niehaoyu on 2024/6/19. // import Cocoa class CustomStampWindowController: NSWindowController, CPDFViewDelegate, CPDFListViewDelegate, NSWindowDelegate { @IBOutlet var contendView: NSView! @IBOutlet var leftContendView: NSView! @IBOutlet var pdfContendView: NSView! @IBOutlet var rightContendView: NSView! @IBOutlet var topContendView: NSView! @IBOutlet var cancelButton: NSButton! @IBOutlet var addStampButton: NSButton! var topToolbar: CustomStampTopToolbar! var leftToolbar: CustomStampLeftToolbar! var pdfView: CPDFListView! var rightToolbar: CustomStampRightToolbar! var eventMonitor: Any? static var currentWindowController: CustomStampWindowController! var stampListWC: CSStampListWindowController! var clickHandle: ((_ windowVC: CustomStampWindowController, _ actionType: Int, _ resultImage: NSImage?)->Void)? @objc static func currentWC() -> CustomStampWindowController { if currentWindowController != nil { return currentWindowController } else { let configWC: CustomStampWindowController = CustomStampWindowController.init(windowNibName: "CustomStampWindowController") currentWindowController = configWC; return currentWindowController } } deinit { NotificationCenter.default.removeObserver(self) print("\(self.className) deinit") } override func windowDidLoad() { super.windowDidLoad() // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file. self.window?.delegate = self self.window?.title = NSLocalizedString("Create Custom Stamp", comment: "") self.addEventMonitor() self.configPDFContendView() self.configTopToolbar() self.configLeftToolbar() self.configRightToolbar() self.cancelButton.title = NSLocalizedString("Cancel", comment: "") self.addStampButton.title = NSLocalizedString("Add Stamp", comment: "") self.updateViewColor() self.window?.makeFirstResponder(self.pdfView) } func reloadData() { self.topToolbar.reloadData() self.leftToolbar.reloadData() self.rightToolbar.reloadData() self.updateRightToolBarInfo() } func configPDFContendView() { let pdfDocument = CPDFDocument() pdfDocument?.insertBlankPage(pageSize: NSSize(width: 250, height: 200), at: 0) self.pdfView = CPDFListView(frame: self.pdfContendView.bounds) self.pdfView.autoresizingMask = [.width, .height] self.pdfView.document = pdfDocument self.pdfView.delegate = self self.pdfView.pdfListViewDelegate = self self.pdfView.autoScales = false self.pdfView.scaleFactor = 1 self.pdfView.isCustomStampType = "1" self.pdfContendView.wantsLayer = true self.pdfContendView.layer?.cornerRadius = 4 self.pdfContendView.layer?.masksToBounds = true self.pdfContendView.layer?.borderWidth = 1 self.pdfContendView.addSubview(self.pdfView) NotificationCenter.default.addObserver(self, selector: #selector(CPDFListViewActiveAnnotationsChangeNotification), name: NSNotification.Name.init(rawValue: "CPDFListViewActiveAnnotationsChangeNotification"), object: nil) } func configTopToolbar() { self.topToolbar = CustomStampTopToolbar.createFromNib() self.topToolbar.frame = self.topContendView.bounds self.topToolbar.autoresizingMask = [.width, .height] self.topToolbar.pdfView = self.pdfView self.topContendView.addSubview(self.topToolbar) } func configLeftToolbar() { self.leftToolbar = CustomStampLeftToolbar.createFromNib() self.leftToolbar.frame = self.leftContendView.bounds self.leftToolbar.autoresizingMask = [.width, .height] self.leftToolbar.clickHandle = {[weak self] view , toolbarType, string in self?.pdfView.customStampAnnotationType = String(toolbarType.rawValue) if toolbarType == .move { self?.pdfView.annotationType = .unkown }else if toolbarType == .text || toolbarType == .dateText || toolbarType == .idText { self?.pdfView.toolMode = .noteToolMode self?.pdfView.annotationType = .freeText if string.isEmpty == false { self?.pdfView.customStampTextValue = string } } else if toolbarType == .image { self?.pdfView.toolMode = .noteToolMode self?.pdfView.annotationType = .stamp self?.chooseImage() } else if toolbarType == .stamp { self?.pdfView.toolMode = .noteToolMode self?.chooseStamp() } } self.leftContendView.addSubview(self.leftToolbar) } func configRightToolbar() { self.rightToolbar = CustomStampRightToolbar.createFromNib() self.rightToolbar.frame = self.rightContendView.bounds self.rightToolbar.autoresizingMask = [.width, .height] self.rightToolbar.pdfView = self.pdfView self.rightToolbar.delegate = self self.rightContendView.addSubview(self.rightToolbar) } func updateViewColor() { if KMAppearance.isDarkMode() { self.window?.backgroundColor = NSColor(red: 38/255, green: 40/255, blue: 43/255, alpha: 1) self.pdfContendView.layer?.borderColor = NSColor.white.withAlphaComponent(0.05).cgColor self.pdfView.backgroundColor = NSColor(red: 47/255, green: 49/255, blue: 51/255, alpha: 1) } else { self.window?.backgroundColor = NSColor.white self.pdfContendView.layer?.borderColor = NSColor.black.withAlphaComponent(0.06).cgColor self.pdfView.backgroundColor = NSColor(red: 245/255, green: 245/255, blue: 245/255, alpha: 1) } } private func addEventMonitor() { if (self.eventMonitor != nil) { self.removeEventMonitor() } DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.3) { KMPrint("已添加事件监听") self.eventMonitor = NSEvent.addLocalMonitorForEvents(matching: [.scrollWheel, .leftMouseDown]) { [weak self] event in if (event.type == .scrollWheel && event.modifierFlags.contains(.option)) { // Alt + 鼠标滚轮 self?.pdfView.magnifyWheel(event) return nil } return event } } } private func removeEventMonitor() { if (self.eventMonitor != nil) { KMPrint("已移除事件监听") NSEvent.removeMonitor(self.eventMonitor as Any) self.eventMonitor = nil } } func clear() { self.removeEventMonitor() CustomStampWindowController.currentWindowController = nil NotificationCenter.default.removeObserver(self) } //MARK: Private Method func chooseImage() { let accessoryCtr = KMImageAccessoryController() let openPanel = NSOpenPanel() openPanel.allowedFileTypes = KMImageAccessoryController.supportedImageTypes() openPanel.allowsMultipleSelection = false openPanel.accessoryView = accessoryCtr.view openPanel.canSelectHiddenExtension = true openPanel.beginSheetModal(for: self.window!) { [weak self] (result) in if result == .OK { guard let url = openPanel.url else { return } let filePath = url.path if filePath.pathExtension.lowercased() == "pdf" { if let pdf = PDFDocument(url: url), pdf.isEncrypted { NSSound.beep() return } } let stamp = CStampObject(imageStampFilePath: filePath) stamp.nameString = "" self?.pdfView.setAddStamp(stamp, keepToolModel: true) self?.updateRightToolBarInfo() } else { self?.leftToolbar.toolbarType = .move self?.leftToolbar.reloadData() } } } func chooseStamp() { self.stampListWC = CSStampListWindowController.currentWC() self.stampListWC.clickHandle = {[weak self] view , actionType, filePath in if actionType == 0 { self?.leftToolbar.toolbarType = .move self?.leftToolbar.reloadData() } else if actionType == 1 { self?.pdfView.annotationType = .stamp let stamp = CStampObject(imageStampFilePath: filePath!) stamp.nameString = "" self?.pdfView.setAddStamp(stamp, keepToolModel: true) self?.updateRightToolBarInfo() } self?.window?.endSheet(view.window!) } self.window!.beginSheet(self.stampListWC.window!, completionHandler: {[weak self] response in self?.stampListWC.reloadData() }) } func updateRightToolBarInfo() { if self.pdfView?.activeAnnotations.count == 0 { self.rightToolbar.currentAnnotation = nil } else { self.rightToolbar.currentAnnotation = self.pdfView.activeAnnotation } let page = self.pdfView?.document?.page(at: 0) if page?.annotations.isEmpty == false { self.addStampButton.isEnabled = true self.addStampButton.bezelColor = NSColor.controlAccentColor } else { self.addStampButton.isEnabled = false self.addStampButton.bezelColor = self.cancelButton.bezelColor } } @objc func cropCurrentPage() { var rect = NSIntegralRect(self.pdfView.currentSelectionRect()) var page: CPDFPage? if let data = self.pdfView.currentSelectionPage() { page = data } else { page = self.pdfView.currentPage() } if (NSIsEmptyRect(rect)) { rect = KMCropTools.getPageForegroundBox(page!) } let index: UInt = (page?.pageIndex()) ?? 0 self._cropPage(at: index, in: rect) } @objc private func _cropPage(at index: UInt, in rect: NSRect) { let oldRect = self.pdfView?.document?.page(at: index)?.bounds(for: .cropBox) ?? .zero let undoManager = self.pdfView.undoManager (undoManager?.prepare(withInvocationTarget: self) as? AnyObject)?._cropPage(at: index, in: oldRect) let page = self.pdfView.document.page(at: index) let newRect = NSIntersectionRect(rect, (page?.bounds(for: .mediaBox)) ?? .zero) page?.setBounds(newRect, for: .cropBox) /// 刷新预览视图 self.pdfView.layoutDocumentView() self.pdfView.displayBox = .cropBox } //MARK: IBAction @IBAction func cancelAction(_ sender: Any) { guard let callBack = self.clickHandle else { return } callBack(self, 0, nil) } @IBAction func addStampAction(_ sender: Any) { guard let page = self.pdfView.document.page(at: 0) else { return } for anno in page.annotations ?? [] { self.pdfView.setNeedsDisplay(anno) } self.cropCurrentPage() let image = page.thumbnail(of: CGSizeMake(page.size.width*4, page.size.height*4)) guard let callBack = self.clickHandle else { return } callBack(self, 1, image) } //MARK: CPDFListViewDelegate & Notification func pdfViewScaleDidChanged(_ pdfView: CPDFView!) { self.topToolbar?.pdfViewScaleChanged() } @objc func CPDFListViewActiveAnnotationsChangeNotification(notification: NSNotification) { if notification.object is CPDFListView { let listView : CPDFListView = notification.object as! CPDFListView if listView.isEqual(self.pdfView) { self.updateRightToolBarInfo() } } } func pdfListViewAddAnnotations(_ pdfListView: CPDFListView!, forAdd annotations: [CPDFAnnotation]!, in pdfPage: CPDFPage!) { if self.leftToolbar.toolbarType != .move { self.leftToolbar.toolbarType = .move self.leftToolbar.reloadData() } } override func showWindow(_ sender: Any?) { super.showWindow(sender) } override func close() { super.close() self.removeEventMonitor() CustomStampWindowController.currentWindowController = nil NotificationCenter.default.removeObserver(self) } func windowShouldClose(_ sender: NSWindow) -> Bool { self.removeEventMonitor() CustomStampWindowController.currentWindowController = nil NotificationCenter.default.removeObserver(self) return true } } extension CustomStampWindowController: CSRightToolbarDelegate { func csRightToolbar(_ toolbar: CustomStampRightToolbar, didAnnotationUpdate annotation: CPDFAnnotation?) { } func csRightToolbar(_ toolbar: CustomStampRightToolbar, didDeleteAnnotation annotation: CPDFAnnotation?) { self.pdfView.remove(annotation) self.reloadData() } func csRightToolbar(_ toolbar: CustomStampRightToolbar, didAnnotationClear annotation: CPDFAnnotation?) { let page = self.pdfView?.document?.page(at: 0) page?.removeAllAnnotations() self.pdfView?.setNeedsDisplayForVisiblePages() self.reloadData() } func csRightToolbar(didPDFClip toolbar: CustomStampRightToolbar) { guard let page = self.pdfView.document.page(at: 0) else { return } for anno in page.annotations ?? [] { if anno is CPDFFreeTextAnnotation { let newAnnotation = (anno as! CPDFFreeTextAnnotation) if self.pdfView.isEdit(withCurrentFreeText: newAnnotation) { self.pdfView.commitEditAnnotationFreeText(newAnnotation) self.pdfView.annotationType = .unkown } } self.pdfView.setNeedsDisplay(anno) } self.cropCurrentPage() } }