|
@@ -0,0 +1,354 @@
|
|
|
+//
|
|
|
+// KMNPopOperationWindowController.swift
|
|
|
+// PDF Reader Pro
|
|
|
+//
|
|
|
+// Created by 丁林圭 on 2024/12/12.
|
|
|
+//
|
|
|
+
|
|
|
+import Cocoa
|
|
|
+
|
|
|
+import KMComponentLibrary
|
|
|
+
|
|
|
+@objc public enum OperationType: Int, CaseIterable{
|
|
|
+ case crop = 0
|
|
|
+ case ocr
|
|
|
+ case operationNone
|
|
|
+}
|
|
|
+
|
|
|
+typealias OperaCropCallback = () -> ()
|
|
|
+
|
|
|
+class KMNPopOperationWindowController: KMNBaseWindowController {
|
|
|
+
|
|
|
+ @IBOutlet weak var contentBox: NSBox!
|
|
|
+ @IBOutlet weak var operationBox: NSBox!
|
|
|
+ @IBOutlet weak var operationWidthConstraint: NSLayoutConstraint!
|
|
|
+
|
|
|
+ @IBOutlet var cropView: NSView!
|
|
|
+ @IBOutlet var closeButton: ComponentButton!
|
|
|
+ @IBOutlet var printButton: ComponentButton!
|
|
|
+ @IBOutlet var extractSelect: ComponentCSelector!
|
|
|
+ @IBOutlet var resetButton: ComponentButton!
|
|
|
+ @IBOutlet var addButton: ComponentButton!
|
|
|
+
|
|
|
+ @IBOutlet var ocrView: NSView!
|
|
|
+ @IBOutlet var cancelButton: ComponentButton!
|
|
|
+ @IBOutlet var ocrButton: ComponentButton!
|
|
|
+ @IBOutlet weak var cancelWidthConstraint: NSLayoutConstraint!
|
|
|
+ @IBOutlet weak var ocrWidthConstraint: NSLayoutConstraint!
|
|
|
+
|
|
|
+ private var extractGroupView: ComponentGroup?
|
|
|
+
|
|
|
+ static let shared: KMNPopOperationWindowController = {
|
|
|
+ let windowC = KMNPopOperationWindowController(windowNibName: "KMNPopOperationWindowController")
|
|
|
+ return windowC
|
|
|
+ }()
|
|
|
+
|
|
|
+ var cropCurrentCallback: OperaCropCallback?
|
|
|
+
|
|
|
+ var updatePDFViewCallback: UpdatePDFViewCallback?
|
|
|
+
|
|
|
+ weak var listView:CPDFListView?
|
|
|
+
|
|
|
+ public var popType:OperationType = .operationNone {
|
|
|
+ didSet {
|
|
|
+
|
|
|
+ switch popType {
|
|
|
+ case .ocr :
|
|
|
+ ocrView.layoutSubtreeIfNeeded()
|
|
|
+ operationWidthConstraint.constant = ocrView.bounds.width
|
|
|
+ self.window?.display() //需刷新约束才会有值,不然会变化
|
|
|
+ operationBox.contentView = ocrView
|
|
|
+ break
|
|
|
+ case .crop:
|
|
|
+ cropView.layoutSubtreeIfNeeded()
|
|
|
+
|
|
|
+ operationWidthConstraint.constant = cropView.bounds.width
|
|
|
+ self.window?.display() //需刷新约束才会有值,不然会变化
|
|
|
+ operationBox.contentView = cropView
|
|
|
+ break
|
|
|
+ case .operationNone:
|
|
|
+ break
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ override func windowDidLoad() {
|
|
|
+ super.windowDidLoad()
|
|
|
+ }
|
|
|
+
|
|
|
+ override func initContentView() {
|
|
|
+ super.initContentView()
|
|
|
+
|
|
|
+ extractSelect.properties = ComponentCSelectorProperty(iconImage:NSImage(named: "KMNImageNameListViewCropExport"))
|
|
|
+ extractSelect.setTarget(self, action: #selector(extractButtonClicked(_ :)))
|
|
|
+
|
|
|
+ closeButton.properties = ComponentButtonProperty(type: .text_gray,
|
|
|
+ size: .s,
|
|
|
+ state: .normal,
|
|
|
+ onlyIcon: true,
|
|
|
+ icon:NSImage(named: "KMNImageNameListViewCropClose"),
|
|
|
+ keepPressState: false)
|
|
|
+ closeButton.setTarget(self, action: #selector(closeButtonClicked(_ :)))
|
|
|
+
|
|
|
+ printButton.properties = ComponentButtonProperty(type: .text_gray,
|
|
|
+ size: .s,
|
|
|
+ state: .normal,
|
|
|
+ onlyIcon: true,
|
|
|
+ icon:NSImage(named: "KMNImageNameListViewCropPrint"),
|
|
|
+ keepPressState: false)
|
|
|
+ printButton.setTarget(self, action: #selector(printButtonClicked(_ :)))
|
|
|
+
|
|
|
+ addButton.properties = ComponentButtonProperty(type: .text_gray,
|
|
|
+ size: .s,
|
|
|
+ state: .normal,
|
|
|
+ onlyIcon: true,
|
|
|
+ icon:NSImage(named: "KMNImageNameListViewCropAdd"),keepPressState: false)
|
|
|
+ addButton.setTarget(self, action: #selector(addButtonClicked(_ :)))
|
|
|
+
|
|
|
+ resetButton.properties = ComponentButtonProperty(type: .text_gray,
|
|
|
+ size: .s,
|
|
|
+ state: .normal,
|
|
|
+ onlyIcon: true,
|
|
|
+ icon:NSImage(named: "KMNImageNameListViewCropRest"),
|
|
|
+ keepPressState: false)
|
|
|
+ resetButton.setTarget(self, action: #selector(restButtonClicked(_ :)))
|
|
|
+ }
|
|
|
+
|
|
|
+ override func updateUIThemeColor() {
|
|
|
+ super.updateUIThemeColor()
|
|
|
+
|
|
|
+ contentBox.fillColor = ComponentLibrary.shared.getComponentColorFromKey("colorBg/popup")
|
|
|
+ contentBox.borderColor = ComponentLibrary.shared.getComponentColorFromKey("colorBorder/3-default")
|
|
|
+ }
|
|
|
+
|
|
|
+ override func updateUILanguage() {
|
|
|
+ super.updateUILanguage()
|
|
|
+
|
|
|
+ cancelButton.properties = ComponentButtonProperty(type: .default_tertiary,
|
|
|
+ size: .s,
|
|
|
+ state: .normal,
|
|
|
+ buttonText: KMLocalizedString("Cancel"))
|
|
|
+ cancelButton.setTarget(self, action: #selector(cancelButtonClicked(_ :)))
|
|
|
+
|
|
|
+ ocrButton.properties = ComponentButtonProperty(type: .primary,
|
|
|
+ size: .s,
|
|
|
+ state: .normal,
|
|
|
+ buttonText: KMLocalizedString("OCR"))
|
|
|
+ ocrButton.setTarget(self, action: #selector(ocrButtonClicked(_ :)))
|
|
|
+
|
|
|
+ ocrWidthConstraint.constant = ocrButton.properties.propertyInfo.viewWidth
|
|
|
+ cancelWidthConstraint.constant = cancelButton.properties.propertyInfo.viewWidth
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ func removeGroupView() {
|
|
|
+ if extractGroupView != nil {
|
|
|
+ extractGroupView?.removeFromSuperview()
|
|
|
+ }
|
|
|
+ extractSelect.properties.state = .normal
|
|
|
+ extractSelect.reloadData()
|
|
|
+ }
|
|
|
+
|
|
|
+ //MARK: - Public
|
|
|
+ public func updateFrame(listView:CPDFListView?,page:CPDFPage?) {
|
|
|
+ let windowFram = listView?.window?.frame ?? CGRectZero
|
|
|
+
|
|
|
+ let pageRect = listView?.currentSelectionRect() ?? NSRect.zero
|
|
|
+
|
|
|
+ if page != nil {
|
|
|
+ let positioningRect = listView?.convert(pageRect, from: page!) ?? NSRect.zero
|
|
|
+ if (CGRectIntersectsRect(positioningRect, listView?.frame ?? CGRectZero)) {
|
|
|
+ let view: NSView? = nil
|
|
|
+ let position = listView?.convert(positioningRect, to: view) ?? NSRect.zero
|
|
|
+ var positionNew = position.origin
|
|
|
+
|
|
|
+ positionNew.x += windowFram.origin.x + position.width
|
|
|
+ positionNew.y += windowFram.origin.y
|
|
|
+
|
|
|
+ var positionRect = self.window?.frame ?? CGRectZero
|
|
|
+ positionRect.origin.x = positionNew.x - positionRect.width
|
|
|
+ positionRect.origin.y = positionNew.y - popOffSet - positionRect.height
|
|
|
+
|
|
|
+ var listViewWindRect = listView?.convert(listView?.frame ?? CGRect.zero, to: view) ?? CGRect.zero
|
|
|
+ listViewWindRect.origin.x += windowFram.origin.x
|
|
|
+ listViewWindRect.origin.y += windowFram.origin.y
|
|
|
+
|
|
|
+ if CGRectGetMinY(positionRect) < CGRectGetMinY(listViewWindRect) {
|
|
|
+ positionRect.origin.y = positionNew.y + popOffSet + position.height
|
|
|
+ }
|
|
|
+
|
|
|
+ if CGRectGetMinX(positionRect) < CGRectGetMinX(listViewWindRect) {
|
|
|
+ positionRect.origin.x = CGRectGetMinX(listViewWindRect)
|
|
|
+ }
|
|
|
+
|
|
|
+ if CGRectGetMaxX(positionRect) > CGRectGetMaxX(listViewWindRect) {
|
|
|
+ positionRect.origin.x = CGRectGetMaxX(listViewWindRect) - positionRect.width
|
|
|
+ }
|
|
|
+ self.window?.setFrame(positionRect, display: true)
|
|
|
+
|
|
|
+ updateUILanguage()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ public func closeWindow(listView:CPDFListView?) {
|
|
|
+ if self.window?.isVisible == true {
|
|
|
+ listView?.window?.removeChildWindow(self.window ?? NSWindow())
|
|
|
+ self.window?.close()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ //MARK: - Action
|
|
|
+ @objc func cancelButtonClicked(_ sender: NSView) {
|
|
|
+ }
|
|
|
+
|
|
|
+ @objc func ocrButtonClicked(_ sender: NSView) {
|
|
|
+ }
|
|
|
+
|
|
|
+ @objc func addButtonClicked(_ sender: NSView) {
|
|
|
+ let rect = NSIntegralRect(listView?.currentSelectionRect() ?? CGRect.zero)
|
|
|
+ let orgPage : CPDFPage = listView?.currentSelectionPage() ?? CPDFPage()
|
|
|
+
|
|
|
+ if let page : CPDFPage = orgPage.copy() as? CPDFPage {
|
|
|
+ page.setBounds(rect, for: .cropBox)
|
|
|
+ let image = page.thumbnail(of: rect.size) ?? NSImage()
|
|
|
+ let data = image.tiffRepresentation
|
|
|
+ let imageItem = NSPasteboardItem()
|
|
|
+ imageItem.setData(data ?? Data(), forType: .tiff)
|
|
|
+ let pboard = NSPasteboard.general
|
|
|
+ pboard.clearContents()
|
|
|
+
|
|
|
+ pboard.writeObjects([imageItem])
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @objc func printButtonClicked(_ sender: NSView) {
|
|
|
+ let rect = listView?.currentSelectionRect() ?? CGRect.zero
|
|
|
+ let page = listView?.currentPage()
|
|
|
+ if page != nil {
|
|
|
+ let copyPage : CPDFPage = page!.copy() as! CPDFPage
|
|
|
+ copyPage.setBounds(rect, for: .cropBox)
|
|
|
+ let image : NSImage = copyPage.thumbnail(of:(copyPage.bounds(for: .cropBox)).size)
|
|
|
+ KMPrintWindowController.printImage(image: image)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @objc func restButtonClicked(_ sender: NSView) {
|
|
|
+ cropCurrentCallback?()
|
|
|
+ }
|
|
|
+
|
|
|
+ @objc func extractButtonClicked(_ sender: NSView) {
|
|
|
+ if extractGroupView?.superview != nil {
|
|
|
+ removeGroupView()
|
|
|
+ } else {
|
|
|
+ var viewHeight: CGFloat = 8
|
|
|
+ var menuItemArr: [ComponentMenuitemProperty] = []
|
|
|
+ for i in ["jpg", "png", "pdf"] {
|
|
|
+ let properties_Menuitem: ComponentMenuitemProperty = ComponentMenuitemProperty(multipleSelect: false,
|
|
|
+ itemSelected: false,
|
|
|
+ isDisabled: false,
|
|
|
+ keyEquivalent: nil,
|
|
|
+ text: KMLocalizedString(i))
|
|
|
+ menuItemArr.append(properties_Menuitem)
|
|
|
+ viewHeight += 36
|
|
|
+ }
|
|
|
+
|
|
|
+ if extractGroupView == nil {
|
|
|
+ extractGroupView = ComponentGroup.createFromNib(in: ComponentLibrary.shared.componentBundle())
|
|
|
+ }
|
|
|
+ extractGroupView?.groupDelegate = self
|
|
|
+ extractGroupView?.frame = CGRectMake(310, 0, 72, viewHeight)
|
|
|
+ extractGroupView?.updateGroupInfo(menuItemArr)
|
|
|
+
|
|
|
+ let point = CGPoint(x: sender.frame.origin.x + sender.frame.size.width/2, y: CGRectGetMinY(sender.frame) - viewHeight)
|
|
|
+ extractGroupView?.showWithPoint(point, relativeTo: self.window?.contentView)
|
|
|
+
|
|
|
+ extractSelect.properties.state = .pressed
|
|
|
+ extractSelect.reloadData()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @objc func closeButtonClicked(_ sender: NSView) {
|
|
|
+ listView?.selectionRect = NSZeroRect
|
|
|
+ listView?.selectionPageIndex = UInt(NSNotFound)
|
|
|
+ updatePDFViewCallback?()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+extension KMNPopOperationWindowController: ComponentGroupDelegate {
|
|
|
+ func componentGroupDidDismiss(group: ComponentGroup?) {
|
|
|
+ removeGroupView()
|
|
|
+
|
|
|
+ extractSelect.properties.state = .normal
|
|
|
+ extractSelect.reloadData()
|
|
|
+ }
|
|
|
+
|
|
|
+ func componentGroupDidSelect(group: ComponentGroup?, menuItemProperty: ComponentMenuitemProperty?) {
|
|
|
+ if group == extractGroupView {
|
|
|
+ let rect = NSIntegralRect(listView?.currentSelectionRect() ?? CGRect.zero)
|
|
|
+ let orgPage : CPDFPage = listView?.currentSelectionPage() ?? CPDFPage()
|
|
|
+
|
|
|
+ if let page : CPDFPage = orgPage.copy() as? CPDFPage {
|
|
|
+ page.setBounds(rect, for: .cropBox)
|
|
|
+ let image = page.thumbnail(of: rect.size) ?? NSImage()
|
|
|
+ let data = image.tiffRepresentation
|
|
|
+ guard let data = data else { return }
|
|
|
+ let imageRep : NSBitmapImageRep = NSBitmapImageRep(data: data) ?? NSBitmapImageRep()
|
|
|
+ imageRep.size = rect.size
|
|
|
+
|
|
|
+ let savePanel = NSSavePanel()
|
|
|
+ if let selItem = menuItemProperty {
|
|
|
+ let index = group?.menuItemArr.firstIndex(of: selItem)
|
|
|
+ if index == 0 {
|
|
|
+ savePanel.allowedFileTypes = ["jpg"]
|
|
|
+
|
|
|
+ let imageData : Data = imageRep.representation(using: NSBitmapImageRep.FileType.jpeg, properties: [:])!
|
|
|
+ savePanel.beginSheetModal(for: listView?.window ?? NSWindow()) { response in
|
|
|
+ if (response != .OK) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if NSData(data: imageData).write(to: savePanel.url!, atomically: true) {
|
|
|
+ NSWorkspace.shared.selectFile(savePanel.url?.path, inFileViewerRootedAtPath: "");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else if index == 1 {
|
|
|
+ savePanel.allowedFileTypes = ["png"]
|
|
|
+
|
|
|
+ let imageData : Data = imageRep.representation(using: NSBitmapImageRep.FileType.png, properties: [:])!
|
|
|
+ savePanel.beginSheetModal(for: listView?.window ?? NSWindow()) { response in
|
|
|
+ if (response != .OK) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if NSData(data: imageData).write(to: savePanel.url!, atomically: true) {
|
|
|
+ NSWorkspace.shared.selectFile(savePanel.url?.path, inFileViewerRootedAtPath: "");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ } else if index == 2 {
|
|
|
+ savePanel.allowedFileTypes = ["pdf"]
|
|
|
+
|
|
|
+ let pdfdocument = CPDFDocument()
|
|
|
+ let signatureImagePath = NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).first?.stringByAppendingPathComponent("signatureImage.png")
|
|
|
+ let imageData : Data = imageRep.representation(using: NSBitmapImageRep.FileType.jpeg, properties: [:])!
|
|
|
+ if NSData(data: imageData).write(to: URL(fileURLWithPath: signatureImagePath!), atomically: true) {
|
|
|
+ pdfdocument?.insertPage(image.size, withImage: signatureImagePath, at: 0)
|
|
|
+
|
|
|
+ savePanel.beginSheetModal(for: listView?.window ?? NSWindow()) { response in
|
|
|
+ if (response != .OK) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if pdfdocument!.write(to: savePanel.url!) {
|
|
|
+ NSWorkspace.shared.selectFile(savePanel.url?.path, inFileViewerRootedAtPath: "");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|