// // KMPDFDigitalSignViewController.swift // PDF Reader Pro Edition // // Created by Niehaoyu on 2023/10/19. // import Cocoa class KMPDFDigitalSignViewController: NSViewController, CPDFViewDelegate { @IBOutlet weak var contendView: NSView! @IBOutlet weak var headerView: NSView! @IBOutlet weak var tipLabel: NSTextField! @IBOutlet weak var exitBox: NSBox! @IBOutlet weak var exitButton: KMCustomButton! @IBOutlet weak var saveBox: NSBox! @IBOutlet weak var saveButton: KMCustomButton! @IBOutlet weak var stateBGView: NSView! @IBOutlet weak var stateLbl: NSTextField! @IBOutlet weak var stateButton: NSButton! @IBOutlet weak var pdfContendView: NSView! @IBOutlet weak var contentTopConst: NSLayoutConstraint! var editWidgeAnnotation: CPDFSignatureWidgetAnnotation! var stateVC: CDSignatureCertificateStateViewController! @objc var scaleFactor: CGFloat = 0 @objc var url: URL! @objc var password = String() @objc var currentPageIndex: Int = 0 var signatures = NSArray() var type:CPromptSignaturesState = .failure @objc var titleChangeBlock: ((_ title: String, _ pageIndex: NSInteger)->Void)? @objc var buttonActionBlock: ((_ actionType: DSignatureActionType, _ isChanged: Bool)->Void)? var pdfView: CPDFDigtalView! override func viewDidAppear() { super.viewDidAppear() let contextString = NSLocalizedString("Please box an area for the signature to be added, then you can add a new digital signature.", comment: "") _ = CustomAlertView.alertView(message: contextString, fromView: self.view, withStyle: .black) } override func viewDidLoad() { super.viewDidLoad() // Do view setup here. self.contendView.wantsLayer = true self.contendView.layer?.backgroundColor = KMAppearance.Layout.bgColor().cgColor self.headerView.wantsLayer = true self.headerView.layer?.backgroundColor = KMAppearance.Interactive.a0Color().cgColor self.tipLabel.stringValue = NSLocalizedString("You are Under Digital Sign Mode", comment: "") self.exitBox.borderColor = KMAppearance.Layout.w70Color() self.exitBox.borderWidth = 1.0 self.exitButton.title = NSLocalizedString("Exit", comment: "") self.saveBox.fillColor = NSColor.white self.saveButton.title = NSLocalizedString("Exit", comment: "") self.saveButton.setTitleColor(KMAppearance.Interactive.a0Color()) self.exitBox.isHidden = true self.pdfView = CPDFDigtalView.init(frame: self.pdfContendView.bounds) self.pdfView.autoresizingMask = [.width, .height] let document = CPDFDocument.init(url: self.url) if self.password.count > 0 { document?.unlock(withPassword: self.password) } self.pdfView.delegate = self self.pdfView.document = document self.pdfView.autoScales = true self.pdfView.pdfListViewDelegate = self self.pdfContendView.addSubview(self.pdfView) self.stateBGView.wantsLayer = true self.stateBGView.layer?.backgroundColor = KMAppearance.Else.textHighlightColor().cgColor self.stateLbl.textColor = NSColor.labelColor self.reloadState() NotificationCenter.default.addObserver(self, selector: #selector(signatureStateChangeNoti), name: NSNotification.Name(rawValue: "CSignatureTrustCerDidChangeNotification"), object: nil) } //MARK: Public Method func reloadState() { if self.pdfView.signatures.count > 0 { self.stateBGView.isHidden = false self.contentTopConst.constant = 44 } else { self.stateBGView.isHidden = true self.contentTopConst.constant = 0 } self.signatures = self.pdfView.signatures! as NSArray var isSignVerified = false var isCertTrusted = false for item in self.signatures { let signature: CPDFSignature = item as! CPDFSignature if signature.signers != nil { let signer = signature.signers.first if signer?.isCertTrusted == false { isCertTrusted = false break } else { isCertTrusted = true } } } for item in self.signatures { let signature: CPDFSignature = item as! CPDFSignature if signature.signers != nil { let signer = signature.signers.first if signer?.isSignVerified == false { isSignVerified = false break } else { isSignVerified = true } } } self.stateBGView.isHidden = false if (isSignVerified && isCertTrusted) { self.type = .Success; } else if(isSignVerified && !isCertTrusted) { self.type = .Unknown; } else { self.type = .failure; } if (.Success == self.type) { self.stateButton.image = NSImage(named: "ImageNameSigntureVerifySuccess") self.stateLbl.stringValue = NSLocalizedString("Signature is valid.", comment: "") } else if(.Unknown == self.type) { self.stateButton.image = NSImage(named: "ImageNameSigntureTrustedFailure") if(self.signatures.count > 1) { self.stateLbl.stringValue = NSLocalizedString("At least one signature is invalid.", comment: "") } else { self.stateLbl.stringValue = NSLocalizedString("Signature is invalid", comment: "") } } else { self.stateButton.image = NSImage(named: "ImageNameSigntureVerifyFailure") if(self.signatures.count > 1) { self.stateLbl.stringValue = NSLocalizedString("At least one signature is invalid.", comment: "") } else { self.stateLbl.stringValue = NSLocalizedString("Signature is invalid", comment: "") } } } func writeSignatureToWidget(_ widget: CPDFSignatureWidgetAnnotation, _ path: String, _ password: String, _ config: CPDFSignatureConfig, _ isLock: Bool) ->() { let fileName = self.pdfView.document.documentURL?.lastPathComponent let fileNameWithoutExtension = URL(fileURLWithPath: fileName!).deletingPathExtension().lastPathComponent let outputSavePanel = NSSavePanel() outputSavePanel.directoryURL = self.pdfView.document.documentURL.deletingLastPathComponent() outputSavePanel.title = NSLocalizedString("", comment: "Save as PDF") outputSavePanel.allowedFileTypes = ["pdf"] outputSavePanel.nameFieldStringValue = fileNameWithoutExtension + "_" + NSLocalizedString("Signed", comment: "") let result = outputSavePanel.runModal() if result == .OK { let contentArr = NSMutableArray() var locationStr = "" var reasonStr = NSLocalizedString("none", comment: "") for item in config.contents { if item.key == NSLocalizedString("Reason", comment: "") { if item.value == NSLocalizedString("", comment: "") { item.value = " " + NSLocalizedString("none", comment: "") } reasonStr = item.value } else if item.key == NSLocalizedString("Location", comment: "") { if item.value == NSLocalizedString("", comment: "") { item.value = " " } locationStr = item.value } contentArr.add(item) } config.contents = contentArr as? [CPDFSignatureConfigItem] widget.signAppearanceConfig(config) let success = self.pdfView.document.writeSignature(to: outputSavePanel.url, withWidget: widget, pkcs12Cert: path, password: password, location: locationStr, reason: reasonStr, permissions: .forbidChange) widget.removeSignature() if success { DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.3) { NSDocumentController.shared.openDocument(withContentsOf: outputSavePanel.url!, display: true) { document, documentWasAlreadyOpen, error in if error != nil { NSApp.presentError(error!) return } } } } else { let alert = NSAlert.init() alert.messageText = NSLocalizedString("Save failed!", comment: "") alert.addButton(withTitle: NSLocalizedString("OK", comment: "")) alert.runModal() } widget.page.removeAnnotation(widget) self.pdfView.setNeedsDisplayAnnotationViewFor(widget.page) } else { widget.page.removeAnnotation(widget) self.pdfView.setNeedsDisplayAnnotationViewFor(widget.page) } } //MARK: Setter //MARK: IBAction @IBAction func exitButtonAction(_ sender: NSButton) { } @IBAction func saveButtonAction(_ sender: NSButton) { // var documentArray = NSDocumentController.shared.documents var didFileEdit = false // for i in 0...documentArray.count-1 { // let document = documentArray[i] // if document.fileURL!.path == self.pdfView.document.documentURL.path { // didFileEdit = document.isDocumentEdited // // break // } // } guard let callBack = self.buttonActionBlock else { return } callBack(.cancel, didFileEdit) } //MARK: CPDFViewDelegate func pdfViewDocumentDidLoaded(_ pdfView: CPDFView!) { self.pdfView.go(toPageIndex: self.currentPageIndex, animated: true) } func setCurrentPageIndex(_ currentPageIndex: Int) { self.pdfView.go(toPageIndex: currentPageIndex, animated: false) } func pdfViewCurrentPageDidChanged(_ pdfView: CPDFView!) { let fileName = pdfView.document.documentURL.deletingPathExtension().lastPathComponent let title = String(format: "%@ (page %ld / %ld)", fileName, pdfView.currentPageIndex+1, pdfView.document.pageCount) self.currentPageIndex = pdfView.currentPageIndex guard let callBack = self.titleChangeBlock else { return } callBack(title, self.currentPageIndex) } func popUpSignatureWidgetState(_ signature: CPDFSignature, _ pdfListView: CPDFDigtalView) ->(){ if self.stateVC == nil { self.stateVC = CDSignatureCertificateStateViewController.init() } self.stateVC.signature = signature self.stateVC.pdfListView = pdfListView self.stateVC.actionBlock = { [weak self, weak stateVC] stateVCSelf, actionType in guard let self = self, let stateVC = stateVC else { return } if actionType == .cancel { stateVC.dismiss(stateVCSelf) } else if actionType == .confirm { if let signer = signature.signers.first, let data = signer.certificates { let signatureDetail = DSignatureDetailsViewController.init() signatureDetail.certificates = data signatureDetail.signature = signature signatureDetail.pdfListView = pdfListView stateVCSelf.presentAsSheet(signatureDetail) } else { NSSound.beep() } } } if let stateVC = self.stateVC { self.presentAsSheet(stateVC) stateVC.reloadData() } } func signElectronicSignature(_ signatureWidgetAnnotation: CPDFSignatureWidgetAnnotation) -> () { // PDFCustomSignatureWindowController *signatureWindowController = [[PDFCustomSignatureWindowController alloc] init]; // __block typeof(self) blockSelf = self; // [signatureWindowController beginSheetModalForWindow:[NSApp mainWindow] completionHandler:^(PDFSignature *signature){ // if (signature) { // [signatureWidgetAnnotation signWithImage:signature.pathsImage]; // [blockSelf.PDFListView setNeedsDisplayAnnotationViewForPage:signatureWidgetAnnotation.page]; // } // }]; // [signatureWindowController release]; } //MARK: Notification @objc fileprivate func signatureStateChangeNoti() { let signatures = self.pdfView.document.signatures()! let tempArr = NSMutableArray() for signature in signatures { if signature.signers != nil { if signature.signers.count > 0 { signature.verifySignature(with: self.pdfView.document) tempArr.add(signature) } } } self.pdfView.signatures = tempArr as? [Any] self.reloadState() if self.stateVC != nil { let signatureWidget = self.editWidgeAnnotation! var signature = signatureWidget.signature() if signature == nil { return } self.stateVC.signature = signature self.stateVC.reloadData() } } } extension KMPDFDigitalSignViewController: CPDFDigtalViewDelegate { func pdfListViewAddAnnotation(_ pdfListView: CPDFDigtalView!, forAdd annotation: CPDFAnnotation!, in pdfPage: CPDFPage!) { let widget = CPDFSignatureWidgetAnnotation.init(PDFListViewNoteWith: self.pdfView.document) widget.bounds = NSMakeRect(-1000, -1000, 545, 178); annotation.page.addAnnotation(widget) let configWindowVC = DSignatureConfigWindowController.init(windowNibName: "DSignatureConfigWindowController") configWindowVC.viewType = .fileList; configWindowVC.appearanceWidget = widget; configWindowVC.isCreatDS = false configWindowVC.complentionHandle = {[weak self] isSign, dic, config, isLock in widget.page.removeAnnotation(widget) if isSign { if (dic.object(forKey: SAVEFILEPATH_KEY) != nil) { let p12Path = dic.object(forKey: SAVEFILEPATH_KEY) as! String let password = dic.object(forKey: PASSWORD_KEY) if p12Path.count > 0 { self?.writeSignatureToWidget(annotation as! CPDFSignatureWidgetAnnotation, p12Path, password as! String, config, isLock) } } } } configWindowVC.actionBlock = { controller, type in if (type == .cancel) { NSApplication.shared.stopModal() controller.window?.orderOut(nil) controller.window?.close() annotation.page.removeAnnotation(annotation) widget.page.removeAnnotation(widget) self.pdfView.setNeedsDisplayAnnotationViewFor(annotation.page) } else if (type == .confirm) { NSApplication.shared.stopModal() controller.window?.orderOut(nil) controller.window?.close() } } configWindowVC.window?.center() NSApplication.shared.runModal(for: configWindowVC.window!) } func pdfListViewEditAnnotation(_ pdfListView: CPDFDigtalView!, for anotation: CPDFAnnotation!) { if anotation.className == CPDFSignatureWidgetAnnotation.className() { let signatureWidget = anotation as! CPDFSignatureWidgetAnnotation var signature = signatureWidget.signature() if signature == nil { return } if signature?.signers != nil { self.editWidgeAnnotation = signatureWidget self.popUpSignatureWidgetState(signature!, self.pdfView) } else if signatureWidget.isSigned() { // self.signElectronicSignature(signatureWidget) } else { } //// __block WindowController *weakself = self; // CPDFSignatureWidgetAnnotation *signatureWidget = (CPDFSignatureWidgetAnnotation *)annotation; // CPDFSignature *signature = [signatureWidget signature]; // if (signature.signers.count > 0) { // [self popUpSignatureWidgetState:signature]; // } else if (signatureWidget.isSigned) { // [self signElectronicSignature:signatureWidget]; // } else { // CDSignatureSignTypeWindowController *signType = [[CDSignatureSignTypeWindowController alloc] init]; // signType.callback = ^(CDSignType type) { // dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ // if(type == CDSignType_ElectronicSignature) { // [weakself signElectronicSignature:signatureWidget]; // } else { // [weakself signDigitalSignature:signatureWidget]; // } // }); // }; // [signType startModal:nil]; // [signType release]; // } } } func pdfListViewDeleteAnnotation(_ pdfListView: CPDFDigtalView!, for anotation: CPDFAnnotation!) { let fileName = self.pdfView.document.documentURL?.lastPathComponent let fileNameWithoutExtension = URL(fileURLWithPath: fileName!).deletingPathExtension().lastPathComponent let outputSavePanel = NSSavePanel() outputSavePanel.directoryURL = self.pdfView.document.documentURL.deletingLastPathComponent() outputSavePanel.title = NSLocalizedString("", comment: "Save as PDF") outputSavePanel.allowedFileTypes = ["pdf"] outputSavePanel.nameFieldStringValue = fileNameWithoutExtension + "_" + NSLocalizedString("Signed", comment: "") let result = outputSavePanel.runModal() if result == .OK { let success = self.pdfView.document.write(to: outputSavePanel.url) if success { DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.3) { NSDocumentController.shared.openDocument(withContentsOf: outputSavePanel.url!, display: true) { document, documentWasAlreadyOpen, error in if error != nil { NSApp.presentError(error!) return } } } } else { let alert = NSAlert.init() alert.messageText = NSLocalizedString("Save failed!", comment: "") alert.addButton(withTitle: NSLocalizedString("OK", comment: "")) alert.runModal() } } } }