// // KMPDFRedactViewController.swift // PDF Reader Pro // // Created by tangchao on 2023/12/18. // import Cocoa class KMPDFRedactViewController: NSViewController { var scaleFactor: CGFloat = 0 { didSet { self.redactPdfView.scaleFactor = scaleFactor } } var callback: ((Bool, Int, Bool, URL?)->Void)? var titleBack: ((String)->Void)? @IBOutlet var redactPdfView: KMRedactPDFView! @IBOutlet weak var redactTipLabel: NSTextField! @IBOutlet weak var propertiesLabel: NSTextField! @IBOutlet weak var redactTextLabel: NSTextField! @IBOutlet weak var exitBox: KMBox! @IBOutlet weak var applyBox: KMBox! @IBOutlet weak var exitButton: NSButton! @IBOutlet weak var applyButton: NSButton! @IBOutlet weak var toolView: NSView! @IBOutlet weak var redactTextBox: NSBox! @IBOutlet weak var propertiesBox: NSBox! @IBOutlet var whiteRedactBox: NSBox! @IBOutlet var whiteRedactLabel: NSTextField! @IBOutlet var whiteRedactBtn: KMCustomButton! @IBOutlet weak var redactTextButton: KMCustomButton! @IBOutlet weak var propertiesButton: KMCustomButton! @IBOutlet weak var whiteOutBox: NSBox! @IBOutlet weak var whiteOutLabel: NSTextField! @IBOutlet weak var whiteOutButton: KMCustomButton! private var _url: URL? private var url: URL? { get { return self._url } } private var _password: String? private var _indicator: NSProgressIndicator? private var isWhiteOut_ = false deinit { KMPrint("KMPDFRedactViewController deinit.") NotificationCenter.default.removeObserver(self) self.redactPdfView.delegate = nil } convenience init(url: URL, password: String?) { self.init() self._url = url self._password = password } override func loadView() { super.loadView() self.isWhiteOut_ = false self.propertiesBox.wantsLayer = true self.redactTextBox.wantsLayer = true self.whiteRedactBox.wantsLayer = true self.redactTextLabel.textColor = KMAppearance.Layout.w0Color() self.propertiesLabel.textColor = KMAppearance.Layout.w0Color() self.whiteRedactLabel.textColor = KMAppearance.Layout.w0Color() self.redactTextLabel.stringValue = KMLocalizedString("Redact Text", nil) self.propertiesLabel.stringValue = KMLocalizedString("Properties", nil) self.whiteRedactLabel.stringValue = KMLocalizedString("White Redact", nil) self.propertiesButton.mouseMoveCallback = { [weak self] mouseEntered in self?.propertiesBox.layer?.cornerRadius = 6 if mouseEntered { self?.propertiesBox.fillColor = KMAppearance.Layout.w30Color() } else { self?.propertiesBox.fillColor = .clear } } // self.whiteOutBox.wantsLayer = true // self.whiteOutBox.fillColor = KMAppearance.Layout.w30Color() // self.whiteOutBox.layer?.cornerRadius = 6 // self.whiteOutLabel.textColor = KMAppearance.Layout.w0Color() // self.whiteOutLabel.stringValue = NSLocalizedString("White Out PDF", comment: "") self.redactTextButton.target = self self.redactTextButton.action = #selector(redactButtonAction) // self.whiteOutButton.target = self // self.whiteOutButton.action = #selector(whiteOutButtonAction) // Do view setup here. self.redactTipLabel.stringValue = KMLocalizedString("You are under redact mode", nil) self.applyButton.title = KMLocalizedString("Apply", nil) self.exitButton.toolTip = KMLocalizedString("Exit", nil) self.exitBox.fillColor = .clear self.exitBox.borderColor = KMAppearance.Layout.w70Color() self.exitBox.borderWidth = 1.0 self.exitButton.title = KMLocalizedString("Exit", nil) self.exitButton.setTitleColor(KMAppearance.Layout.w0Color()) self.applyButton.setTitleColor(KMAppearance.Interactive.a0Color()) self.applyButton.wantsLayer = true self.applyBox.downCallback = { [weak self] downEntered, mouseBox, event in if downEntered { self?.apply_button(nil) } } self.exitBox.downCallback = { [weak self] downEntered, mouseBox, event in if downEntered { self?.exit_button(nil) } } self.toolView.wantsLayer = true self.toolView.layer?.backgroundColor = KMAppearance.Interactive.a0Color().cgColor let document = CPDFDocument(url: self.url!) if let pwd = self._password, pwd.isEmpty == false { document?.unlock(withPassword: pwd) } self.redactPdfView.document = document // self.redactPdfView.scaleFactor = self.scaleFactor; self.redactPdfView.autoScales = true self.redactPdfView.delegate = self // self.redactPdfView.toolMode = .redactToolMode self.redactPdfView.operationType = .redact // [self.redactPdfView layoutDocumentView]; let frame = NSMakeRect((self.redactPdfView.frame.size.width-32)/2.0, (self.redactPdfView.frame.size.height-32)/2.0, 32, 32) self._indicator = NSProgressIndicator(frame: frame) self._indicator?.autoresizingMask = [.minXMargin, .maxXMargin, .minYMargin, .maxYMargin] self._indicator?.style = .spinning self._indicator?.controlSize = .regular self._indicator?.isIndeterminate = true self._indicator?.startAnimation(nil) self.redactPdfView.addSubview(self._indicator!) self._indicator?.isHidden = true self.reloadUI() } override func viewDidLoad() { super.viewDidLoad() NotificationCenter.default.addObserver(self, selector: #selector(showCurrentRedactAnnotationProperties), name: KMRedactPDFView.showCurrentRedactAnnotationNotificationName, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(apply), name: KMRedactPDFView.redactAnnotationApplyNotificationName, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(across), name: KMRedactPDFView.redactAnnotationAcrossNotificationName, object: nil) } //MARK: Setter func setCurrentPageIndex(_ currentPageIndex: Int) { self.redactPdfView.go(toPageIndex: currentPageIndex, animated: false) } func reloadUI() { self.redactTextBox.fillColor = .clear self.redactTextBox.layer?.cornerRadius = 0 self.whiteRedactBox.fillColor = .clear self.whiteRedactBox.layer?.cornerRadius = 0 if self.redactPdfView.operationType == .redact { self.redactTextBox.fillColor = KMAppearance.Layout.w30Color() self.redactTextBox.layer?.cornerRadius = 6 } else if self.redactPdfView.operationType == .redactWhite { self.whiteRedactBox.fillColor = KMAppearance.Layout.w30Color() self.whiteRedactBox.layer?.cornerRadius = 6 } } // MARK: - Button Actions @IBAction func showProperties_button(_ sender: AnyObject?) { // FMTrackEventManager.defaultManager.trackEvent(event: "SubTbr_RedactText", withProperties: ["SubTbr_Btn": "Btn_SubTbr_RedactText_Properties"]) let properties = KMRedactPropertiesWindowController() self.km_beginSheet(windowC: properties) properties.callback = { annotation in } } @IBAction func apply_button(_ sender: AnyObject?) { var isApply = false for i in 0 ..< self.redactPdfView.document.pageCount { let page = self.redactPdfView.document.page(at: i) for anno in page?.annotations ?? [] { if anno.isKind(of: CPDFRedactAnnotation.self) { isApply = true } } } if(self.redactPdfView.newAddAnnotation.count == 0 && !isApply) { return } let returnCode = KMAlertTool.runModelForMainThread_r(message: "", informative: KMLocalizedString("This will permanently remove the redacted information from this document. Once you save this document, you won’t be able to retrieve the redacted information.", nil), buttons: [KMLocalizedString("Apply", nil), KMLocalizedString("Cancel", nil)]) if returnCode == .alertFirstButtonReturn { DispatchQueue.main.async { self.saveAsPath() } } } @IBAction func exit_button(_ sender: AnyObject?) { guard let block = self.callback else { return } block(false, self.redactPdfView.currentPageIndex, false, nil) } @IBAction func redactBtnClicked(_ sender: Any) { self.redactPdfView.operationType = .redact self.reloadUI() } @IBAction func whiteRedactBtnClicked(_ sender: Any) { self.redactPdfView.operationType = .redactWhite self.reloadUI() } func saveAsPath() { let saveAccessCtr = KMSavePanelAccessoryController() var fileName = self.redactPdfView.document.documentURL.deletingPathExtension().lastPathComponent fileName = String(format: "%@_%@.pdf", fileName, "Redact") let outputSavePanel = NSSavePanel() outputSavePanel.allowedFileTypes = ["pdf"] outputSavePanel.nameFieldStringValue = fileName outputSavePanel.accessoryView = saveAccessCtr.view outputSavePanel.beginSheetModal(for: NSApp.mainWindow!) { result in if result == .OK { self.redactPdfView.document.applyRedactions() self._indicator?.isHidden = false self._indicator?.startAnimation(nil) let savePDFPath = outputSavePanel.url!.path let isSuccess = self.redactPdfView.document.write(toFile: savePDFPath) if (isSuccess) { // [self.redactPdfView.newAddAnnotation removeAllObjects]; if (saveAccessCtr.openAutomaticButton.state == .on) { NSDocumentController.shared.km_safe_openDocument(withContentsOf: outputSavePanel.url!, display: true) { _, _, _ in } } else { KMTools.viewFile(at: savePDFPath) } } self._indicator?.stopAnimation(nil) self._indicator?.isHidden = true guard let block = self.callback else { return } block(true, self.redactPdfView.currentPageIndex, false, outputSavePanel.url) } } } // MARK: - Button notice @objc func showCurrentRedactAnnotationProperties(_ notice: NSNotification) { guard let pdfview = notice.object as? KMRedactPDFView else { return } if(pdfview == self.redactPdfView) { let properties = KMRedactPropertiesWindowController() properties.readactAnnotation = self.redactPdfView.currentAnnotation // __block KMPDFRedactViewController *weak_self = self; self.km_beginSheet(windowC: properties) properties.callback = { [weak self] annotation in if let page = self?.redactPdfView.currentAnnotation?.page { if let anno = annotation { self?.redactPdfView.newAddAnnotation.append(anno) } self?.redactPdfView.setNeedsDisplayAnnotationViewFor(page) } } } } @objc func apply(_ notice: NSNotification) { guard let pdfview = notice.object as? KMRedactPDFView else { return } if(pdfview == self.redactPdfView) { self.apply_button(nil) } } @objc func across(_ notice: NSNotification) { guard let pdfview = notice.object as? KMRedactPDFView else { return } if(pdfview == self.redactPdfView) { let pagesWindowController = KMRedactSelectPagesWindowController(document: self.redactPdfView.document) self.km_beginSheet(windowC: pagesWindowController) pagesWindowController.callback = { [unowned self] pages in if pages.count > 0 { self.redactPdfView.acrossAddAnnotations(pages) } } } } @objc func redactButtonAction(sender : NSButton) { // FMTrackEventManager.defaultManager.trackEvent(event: "SubTbr_RedactText", withProperties: ["SubTbr_Btn": "Btn_SubTbr_RedactText_RedactPDF"]) self.isWhiteOut_ = false self.redactPdfView.isWhiteOut = false self.redactPdfView.operationType = .redact self.updateButtonsState() } @objc func whiteOutButtonAction(sender: NSButton) { // FMTrackEventManager.defaultManager.trackEvent(event: "SubTbr_RedactText", withProperties: ["SubTbr_Btn": "Btn_SubTbr_RedactText_WhiteOutPDF"]) self.isWhiteOut_ = true self.redactPdfView.isWhiteOut = true self.redactPdfView.operationType = .redactWhite self.updateButtonsState() } // MARK: - Private Methods private func updateButtonsState() { if self.isWhiteOut_ { // self.whiteOutBox.fillColor = KMAppearance.Layout.w30Color() self.redactTextBox.fillColor = .clear } else { self.redactTextBox.fillColor = KMAppearance.Layout.w30Color() // self.whiteOutBox.fillColor = .clear } self.reloadUI() } } extension KMPDFRedactViewController: CPDFViewDelegate { func pdfViewCurrentPageDidChanged(_ pdfView: CPDFView!) { let fileName = pdfView.document.documentURL.deletingLastPathComponent().lastPathComponent let title = String(format: KMLocalizedString("%@ (page %ld / %ld)", "Window title format"), fileName, pdfView.currentPageIndex+1, pdfView.document.pageCount) if(self.titleBack != nil) { self.titleBack!(title) } } }