// // KMSignatureViewController.swift // PDF Reader Pro // // Created by lxy on 2022/11/17. // import Cocoa @objc protocol KMSignatureViewControllerDelegate { @objc optional func removeDocumentSignatues(signatures:[CPDFSignature]) } class KMSignatureViewController: NSViewController { @IBOutlet weak var sigOutlineView: KMOutlineView! @IBOutlet weak var deleteButton: NSButton! @IBOutlet weak var validationButton: NSButton! @IBOutlet weak var tipTextField: NSTextField! @IBOutlet weak var emptyLabel: NSTextField! @IBOutlet weak var emptyView: NSView! var listView : CPDFListView! var signatures : [CPDFSignature] = [] open weak var delete: KMSignatureViewControllerDelegate? override func viewDidLoad() { super.viewDidLoad() // Do view setup here. self.view.wantsLayer = true self.view.layer?.backgroundColor = NSColor.yellow.cgColor self.tipTextField.stringValue = NSLocalizedString("Digitally Sign", comment: ""); self.deleteButton.toolTip = NSLocalizedString("Delete", comment: ""); self.validationButton.toolTip = NSLocalizedString("Validate All Signatures", comment: ""); self.emptyLabel.stringValue = NSLocalizedString("No digital signature was added", comment: ""); let signatures : [CPDFSignature] = self.listView.document.signatures() ?? [] var mSignatures : [CPDFSignature] = [] for sign in signatures { if sign.signers.count > 0 { mSignatures.append(sign) } } self.signatures = mSignatures self.sigOutlineView.delegate = self self.sigOutlineView.dataSource = self let menu = NSMenu() menu.delegate = self self.sigOutlineView.menu = menu self.sigOutlineView.doubleAction = #selector(tableViewAction) } //MAAK: Accessors public func reloadData() { self.sigOutlineView.reloadData() } private func outlineForItem(item:Any) -> [Any] { var itemChildren : [Any] = [] if (item is String) && ((item as! String) == "") { if self.signatures.count > 0 { let lastSignates : CPDFSignature = self.signatures.last ?? CPDFSignature() if (lastSignates.permissions > 0) { var mutableArr : [Any] = self.signatures mutableArr.append(" \(NSLocalizedString("Already locked by", comment: ""))\(String(describing: lastSignates.fieldName))") itemChildren = mutableArr } else { itemChildren = self.signatures } } else { itemChildren = self.signatures } } else if (item is CPDFSignature) { itemChildren = (item as! CPDFSignature).numberOfChildren(document: self.listView.document) } else if (item is [String:Any]) { let key = (item as! [String:Any]).keys.first ?? "" itemChildren = (item as! [String:Any])[key] as! [Any] } return itemChildren } @IBAction func deleteButtonAction(_ sender: Any) { let alert = NSAlert() alert.alertStyle = .critical alert.messageText = NSLocalizedString("Are you sure you want to clear all signature fields in this document?", comment: "") alert.informativeText = NSLocalizedString("You cannot undo this operation.", comment: "") alert.addButton(withTitle: NSLocalizedString("Yes", comment: "")) alert.addButton(withTitle: NSLocalizedString("No", comment: "")) alert.beginSheetModal(for: self.view.window!, completionHandler: { result in if result != .OK { } }) } @IBAction func validationButtonAction(_ sender: Any) { } @IBAction func jumpItemAction(item: NSMenuItem) { let indexSet : NSIndexSet = item.representedObject as! NSIndexSet let outlineItem = self.sigOutlineView.item(atRow: indexSet.firstIndex) if outlineItem is CPDFSignature { let index: Int = Int((outlineItem as! CPDFSignature).pageIndex(with: listView.document)) if self.listView.currentPageIndex != index { self.listView.go(toPageIndex: index, animated: true) } } } @IBAction func removeItemAction(item:NSMenuItem) { let index : NSIndexSet = item.representedObject as! NSIndexSet let outlineItem = self.sigOutlineView.item(atRow: index.firstIndex) if outlineItem is CPDFSignature { let index: Int = Int((outlineItem as! CPDFSignature).pageIndex(with: listView.document)) if self.listView.currentPageIndex != index { self.delete?.removeDocumentSignatues?(signatures: [outlineItem as! CPDFSignature]) } } } @IBAction func validateItemAction(item:NSMenuItem) { let index : NSIndexSet = item.representedObject as! NSIndexSet let outlineItem = self.sigOutlineView.item(atRow: index.firstIndex) if outlineItem is CPDFSignature { } } @IBAction func showItemAction(item:NSMenuItem) { let index : NSIndexSet = item.representedObject as! NSIndexSet let outlineItem = self.sigOutlineView.item(atRow: index.firstIndex) if outlineItem is CPDFSignature { let index: Int = Int((outlineItem as! CPDFSignature).pageIndex(with: listView.document)) if self.listView.currentPageIndex != index { } } } @IBAction func tableViewAction(sender:Any) { let row = self.sigOutlineView.clickedRow if (row >= 0) && (row < self.signatures.count) { let outlineItem : CPDFSignature = self.sigOutlineView.item(atRow: row) as! CPDFSignature let index: Int = Int((outlineItem).pageIndex(with: listView.document)) if self.listView.currentPageIndex != index { if self.listView.currentPageIndex != index { self.listView.go(toPageIndex: index, animated: true) } } } } } extension KMSignatureViewController : NSOutlineViewDelegate,NSOutlineViewDataSource { func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int { var count = self.outlineForItem(item: item ?? "").count if case Optional.none = item { if count > 0 { self.emptyView.isHidden = true self.deleteButton.isEnabled = true self.validationButton.isEnabled = true } else { self.emptyView.isHidden = false self.deleteButton.isEnabled = false self.validationButton.isEnabled = false } } else if item is [String:Any] { count = 1 } return count } func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any { return self.outlineForItem(item: item ?? "")[index] } func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool { return self.outlineForItem(item: item).count > 0 } func outlineView(_ outlineView: NSOutlineView, shouldShowOutlineCellForItem item: Any) -> Bool { return true } func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? { let cell : KMSignatureCellView = outlineView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "KMSignatureCellView"), owner: self) as! KMSignatureCellView var contentString = "" if item is CPDFSignature { let signer : CPDFSigner = (item as! CPDFSignature).signers.first ?? CPDFSigner() contentString = "\(NSLocalizedString("Signature by", comment: ""))\(signer.timestampSignature.name ?? "")" if signer.isSignVerified && signer.isCertTrusted { cell.signatureImageView.image = NSImage(named: "KMImageNameSigntureVerifySuccess") } else if signer.isSignVerified && !signer.isCertTrusted { cell.signatureImageView.image = NSImage(named: "KMImageNameSigntureVerifySuccess") } else { cell.signatureImageView.image = NSImage(named: "KMImageNameSigntureVerifySuccess") } cell.signatureImageView.isHidden = false cell.leftOffset.constant = 36 cell.contenLabel.font = NSFont.systemFont(ofSize: 13) } else if item is String { cell.signatureImageView.isHidden = true cell.leftOffset.constant = 4 cell.contenLabel.font = NSFont.systemFont(ofSize: 12) contentString = item as! String } else if item is [String:Any] { cell.signatureImageView.isHidden = true cell.leftOffset.constant = 4 cell.contenLabel.font = NSFont.systemFont(ofSize: 12) contentString = (item as! [String:Any]).keys.first ?? "" } cell.contenLabel.stringValue = contentString return cell } func outlineView(_ outlineView: NSOutlineView, rowViewForItem item: Any) -> NSTableRowView? { let rowView = KMCustomTableRowView() return rowView } func outlineView(_ outlineView: NSOutlineView, heightOfRowByItem item: Any) -> CGFloat { var contentString = "" var font = NSFont.systemFont(ofSize: 12) if item is CPDFSignature { let signer = (item as! CPDFSignature).signers.first contentString = "\(NSLocalizedString("Signature by", comment: ""))\(String(describing: signer?.timestampSignature.name))" font = NSFont.systemFont(ofSize: 13) } else if (item is String){ contentString = item as! String } else if (item is [String : Any]) { contentString = (item as! [String:Any]).keys.first ?? "" } let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.lineBreakMode = .byWordWrapping var attribute = [NSAttributedString.Key.font : font, NSAttributedString.Key.paragraphStyle : paragraphStyle] let rect = NSString(string: contentString).boundingRect(with: NSSize(width: outlineView.frame.size.width-20, height:CGFloat(MAXFLOAT)),options: NSString.DrawingOptions(rawValue: 3), attributes: attribute).size if(rect.height < 40) { return 40 } else { return rect.height } } func outlineView(_ outlineView: NSOutlineView, shouldSelect tableColumn: NSTableColumn?) -> Bool { return true } } //MARK: NSMenuDelegate extension KMSignatureViewController : NSMenuDelegate { func menuNeedsUpdate(_ menu: NSMenu) { menu.removeAllItems() let clickedRow = self.sigOutlineView.clickedRow let item = self.sigOutlineView.item(atRow: clickedRow) if clickedRow > 0 && (item is CPDFSignature) { var item = NSMenuItem() item = menu.addItem(withTitle: NSLocalizedString("Jump To The Signature Field", comment: ""), action: #selector(jumpItemAction), target: self)! item.representedObject = NSIndexSet(index: clickedRow) menu.addItem(NSMenuItem.separator()) item = menu.addItem(withTitle: NSLocalizedString("Remove Signature", comment: ""), action: #selector(removeItemAction), target: self)! item.representedObject = NSIndexSet(index: clickedRow) item = menu.addItem(withTitle: NSLocalizedString("Remove Signature", comment: ""), action: #selector(validateItemAction), target: self)! item.representedObject = NSIndexSet(index: clickedRow) menu.addItem(NSMenuItem.separator()) item = menu.addItem(withTitle: NSLocalizedString("Show Signature Properties…", comment: ""), action: #selector(showItemAction), target: self)! item.representedObject = NSIndexSet(index: clickedRow) } } } //MARK: CPDFSignature extension CPDFSignature { func numberOfChildren(document:CPDFDocument) -> [Any] { var datas : [Any] = [] let signer = self.signers.first var validString = "" if signer?.isSignVerified != nil { validString = NSLocalizedString("Signature is valid", comment: "") } else { validString = NSLocalizedString("Signature is invalid", comment: "") } var certTrusted = "" if signer?.isCertTrusted != nil { certTrusted = NSLocalizedString("Signer's identity is valid", comment: "") } else { certTrusted = NSLocalizedString("Signer's identity is invalid", comment: "") } var timestampSigners = "" if signer?.timestampSignature.signers != nil { let tSign = signer?.timestampSignature.signers.first if tSign?.isSignVerified != nil && tSign?.isSignVerified != nil { timestampSigners = NSLocalizedString("The signature includes an embedded timestamp.", comment: "") } else { timestampSigners = NSLocalizedString("The signature includes an embedded timestamp but it could not be verified.", comment: "") } } var str3 = "\(validString)\n\n \(certTrusted)" if timestampSigners.lengthOfBytes(using: String.Encoding(rawValue: String.Encoding.utf16.rawValue)) > 0 { str3 = "\(str3)\n\n \(timestampSigners)" } datas.append(str3) var dic : [String:Any] = [:] let array = [NSLocalizedString("Certificate Details...", comment: ""),signer ?? CPDFSigner()] as [Any] dic[NSLocalizedString("Signature Details...", comment: "")] = array datas.append(dic) if signer?.timestampSignature.signers != nil && signer?.timestampSignature.signers.first?.authenDate != nil{ var dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss" let strDate = dateFormatter.string(from: signer?.timestampSignature.signers.first?.authenDate ?? Date()) datas.append("\(NSLocalizedString("Last Checked:", comment: ""))\(strDate)") } else { datas.append("\(NSLocalizedString("Last Checked:", comment: ""))\(NSLocalizedString("Never", comment: ""))") } let pageIndex = self.pageIndex(with: document) datas.append("\(NSLocalizedString("Field:", comment: ""))\(self.fieldName.description)\(NSLocalizedString("on Page", comment: ""))\(pageIndex+1)") return datas } }