KMPDFDigitalSignViewController.swift 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. //
  2. // KMPDFDigitalSignViewController.swift
  3. // PDF Reader Pro Edition
  4. //
  5. // Created by Niehaoyu on 2023/10/19.
  6. //
  7. import Cocoa
  8. class KMPDFDigitalSignViewController: NSViewController, CPDFViewDelegate {
  9. @IBOutlet weak var contendView: NSView!
  10. @IBOutlet weak var headerView: NSView!
  11. @IBOutlet weak var tipLabel: NSTextField!
  12. @IBOutlet weak var exitBox: NSBox!
  13. @IBOutlet weak var exitButton: KMCustomButton!
  14. @IBOutlet weak var saveBox: NSBox!
  15. @IBOutlet weak var saveButton: KMCustomButton!
  16. @IBOutlet weak var stateBGView: NSView!
  17. @IBOutlet weak var stateLbl: NSTextField!
  18. @IBOutlet weak var stateButton: NSButton!
  19. @IBOutlet weak var pdfContendView: NSView!
  20. @IBOutlet weak var contentTopConst: NSLayoutConstraint!
  21. var editWidgeAnnotation: CPDFSignatureWidgetAnnotation!
  22. var stateVC: CDSignatureCertificateStateViewController!
  23. @objc var scaleFactor: CGFloat = 0
  24. @objc var url: URL!
  25. @objc var password = String()
  26. @objc var currentPageIndex: Int = 0
  27. var signatures = NSArray()
  28. var type:CPromptSignaturesState = .failure
  29. @objc var titleChangeBlock: ((_ title: String, _ pageIndex: NSInteger)->Void)?
  30. @objc var buttonActionBlock: ((_ actionType: DSignatureActionType, _ isChanged: Bool)->Void)?
  31. var pdfView: CPDFDigtalView!
  32. override func viewDidAppear() {
  33. super.viewDidAppear()
  34. let contextString = NSLocalizedString("Please box an area for the signature to be added, then you can add a new digital signature.", comment: "")
  35. _ = CustomAlertView.alertView(message: contextString, fromView: self.view, withStyle: .black)
  36. }
  37. override func viewDidLoad() {
  38. super.viewDidLoad()
  39. // Do view setup here.
  40. self.contendView.wantsLayer = true
  41. self.contendView.layer?.backgroundColor = KMAppearance.Layout.bgColor().cgColor
  42. self.headerView.wantsLayer = true
  43. self.headerView.layer?.backgroundColor = KMAppearance.Interactive.a0Color().cgColor
  44. self.tipLabel.stringValue = NSLocalizedString("You are Under Digital Sign Mode", comment: "")
  45. self.exitBox.borderColor = KMAppearance.Layout.w70Color()
  46. self.exitBox.borderWidth = 1.0
  47. self.exitButton.title = NSLocalizedString("Exit", comment: "")
  48. self.saveBox.fillColor = NSColor.white
  49. self.saveButton.title = NSLocalizedString("Exit", comment: "")
  50. self.saveButton.setTitleColor(KMAppearance.Interactive.a0Color())
  51. self.exitBox.isHidden = true
  52. self.pdfView = CPDFDigtalView.init(frame: self.pdfContendView.bounds)
  53. self.pdfView.autoresizingMask = [.width, .height]
  54. let document = CPDFDocument.init(url: self.url)
  55. if self.password.count > 0 {
  56. document?.unlock(withPassword: self.password)
  57. }
  58. self.pdfView.delegate = self
  59. self.pdfView.document = document
  60. self.pdfView.autoScales = true
  61. self.pdfView.pdfListViewDelegate = self
  62. self.pdfContendView.addSubview(self.pdfView)
  63. self.stateBGView.wantsLayer = true
  64. self.stateBGView.layer?.backgroundColor = KMAppearance.Else.textHighlightColor().cgColor
  65. self.stateLbl.textColor = NSColor.labelColor
  66. self.reloadState()
  67. NotificationCenter.default.addObserver(self, selector: #selector(signatureStateChangeNoti), name: NSNotification.Name(rawValue: "CSignatureTrustCerDidChangeNotification"), object: nil)
  68. }
  69. //MARK: Public Method
  70. func reloadState() {
  71. if self.pdfView.signatures.count > 0 {
  72. self.stateBGView.isHidden = false
  73. self.contentTopConst.constant = 44
  74. } else {
  75. self.stateBGView.isHidden = true
  76. self.contentTopConst.constant = 0
  77. }
  78. self.signatures = self.pdfView.signatures! as NSArray
  79. var isSignVerified = false
  80. var isCertTrusted = false
  81. for item in self.signatures {
  82. let signature: CPDFSignature = item as! CPDFSignature
  83. if signature.signers != nil {
  84. let signer = signature.signers.first
  85. if signer?.isCertTrusted == false {
  86. isCertTrusted = false
  87. break
  88. } else {
  89. isCertTrusted = true
  90. }
  91. }
  92. }
  93. for item in self.signatures {
  94. let signature: CPDFSignature = item as! CPDFSignature
  95. if signature.signers != nil {
  96. let signer = signature.signers.first
  97. if signer?.isSignVerified == false {
  98. isSignVerified = false
  99. break
  100. } else {
  101. isSignVerified = true
  102. }
  103. }
  104. }
  105. self.stateBGView.isHidden = false
  106. if (isSignVerified && isCertTrusted) {
  107. self.type = .Success;
  108. } else if(isSignVerified && !isCertTrusted) {
  109. self.type = .Unknown;
  110. } else {
  111. self.type = .failure;
  112. }
  113. if (.Success == self.type) {
  114. self.stateButton.image = NSImage(named: "ImageNameSigntureVerifySuccess")
  115. self.stateLbl.stringValue = NSLocalizedString("Signature is valid.", comment: "")
  116. } else if(.Unknown == self.type) {
  117. self.stateButton.image = NSImage(named: "ImageNameSigntureTrustedFailure")
  118. if(self.signatures.count > 1) {
  119. self.stateLbl.stringValue = NSLocalizedString("At least one signature is invalid.", comment: "")
  120. } else {
  121. self.stateLbl.stringValue = NSLocalizedString("Signature is invalid", comment: "")
  122. }
  123. } else {
  124. self.stateButton.image = NSImage(named: "ImageNameSigntureVerifyFailure")
  125. if(self.signatures.count > 1) {
  126. self.stateLbl.stringValue = NSLocalizedString("At least one signature is invalid.", comment: "")
  127. } else {
  128. self.stateLbl.stringValue = NSLocalizedString("Signature is invalid", comment: "")
  129. }
  130. }
  131. }
  132. func writeSignatureToWidget(_ widget: CPDFSignatureWidgetAnnotation, _ path: String, _ password: String, _ config: CPDFSignatureConfig, _ isLock: Bool) ->() {
  133. let fileName = self.pdfView.document.documentURL?.lastPathComponent
  134. let fileNameWithoutExtension = URL(fileURLWithPath: fileName!).deletingPathExtension().lastPathComponent
  135. let outputSavePanel = NSSavePanel()
  136. outputSavePanel.directoryURL = self.pdfView.document.documentURL.deletingLastPathComponent()
  137. outputSavePanel.title = NSLocalizedString("", comment: "Save as PDF")
  138. outputSavePanel.allowedFileTypes = ["pdf"]
  139. outputSavePanel.nameFieldStringValue = fileNameWithoutExtension + "_" + NSLocalizedString("Signed", comment: "")
  140. let result = outputSavePanel.runModal()
  141. if result == .OK {
  142. let contentArr = NSMutableArray()
  143. var locationStr = ""
  144. var reasonStr = NSLocalizedString("none", comment: "")
  145. for item in config.contents {
  146. if item.key == NSLocalizedString("Reason", comment: "") {
  147. if item.value == NSLocalizedString("<your signing reason here>", comment: "") {
  148. item.value = " " + NSLocalizedString("none", comment: "")
  149. }
  150. reasonStr = item.value
  151. } else if item.key == NSLocalizedString("Location", comment: "") {
  152. if item.value == NSLocalizedString("<your signing location here>", comment: "") {
  153. item.value = " "
  154. }
  155. locationStr = item.value
  156. }
  157. contentArr.add(item)
  158. }
  159. config.contents = contentArr as? [CPDFSignatureConfigItem]
  160. widget.signAppearanceConfig(config)
  161. let success = self.pdfView.document.writeSignature(to: outputSavePanel.url, withWidget: widget, pkcs12Cert: path, password: password, location: locationStr, reason: reasonStr, permissions: .forbidChange)
  162. widget.removeSignature()
  163. if success {
  164. DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.3) {
  165. NSDocumentController.shared.openDocument(withContentsOf: outputSavePanel.url!, display: true) { document, documentWasAlreadyOpen, error in
  166. if error != nil {
  167. NSApp.presentError(error!)
  168. return
  169. }
  170. }
  171. }
  172. } else {
  173. let alert = NSAlert.init()
  174. alert.messageText = NSLocalizedString("Save failed!", comment: "")
  175. alert.addButton(withTitle: NSLocalizedString("OK", comment: ""))
  176. alert.runModal()
  177. }
  178. widget.page.removeAnnotation(widget)
  179. self.pdfView.setNeedsDisplayAnnotationViewFor(widget.page)
  180. } else {
  181. widget.page.removeAnnotation(widget)
  182. self.pdfView.setNeedsDisplayAnnotationViewFor(widget.page)
  183. }
  184. }
  185. //MARK: Setter
  186. //MARK: IBAction
  187. @IBAction func exitButtonAction(_ sender: NSButton) {
  188. }
  189. @IBAction func saveButtonAction(_ sender: NSButton) {
  190. // var documentArray = NSDocumentController.shared.documents
  191. var didFileEdit = false
  192. // for i in 0...documentArray.count-1 {
  193. // let document = documentArray[i]
  194. // if document.fileURL!.path == self.pdfView.document.documentURL.path {
  195. // didFileEdit = document.isDocumentEdited
  196. //
  197. // break
  198. // }
  199. // }
  200. guard let callBack = self.buttonActionBlock else {
  201. return
  202. }
  203. callBack(.cancel, didFileEdit)
  204. }
  205. //MARK: CPDFViewDelegate
  206. func pdfViewDocumentDidLoaded(_ pdfView: CPDFView!) {
  207. self.pdfView.go(toPageIndex: self.currentPageIndex, animated: true)
  208. }
  209. func pdfViewCurrentPageDidChanged(_ pdfView: CPDFView!) {
  210. let fileName = pdfView.document.documentURL.deletingPathExtension().lastPathComponent
  211. let title = String(format: "%@ (page %ld / %ld)", fileName, pdfView.currentPageIndex+1, pdfView.document.pageCount)
  212. self.currentPageIndex = pdfView.currentPageIndex
  213. guard let callBack = self.titleChangeBlock else {
  214. return
  215. }
  216. callBack(title, self.currentPageIndex)
  217. }
  218. func popUpSignatureWidgetState(_ signature: CPDFSignature, _ pdfListView: CPDFDigtalView) ->(){
  219. if self.stateVC == nil {
  220. self.stateVC = CDSignatureCertificateStateViewController.init()
  221. }
  222. self.stateVC.signature = signature
  223. self.stateVC.pdfListView = pdfListView
  224. self.stateVC.actionBlock = { [weak self, weak stateVC] stateVCSelf, actionType in
  225. guard let self = self, let stateVC = stateVC else { return }
  226. if actionType == .cancel {
  227. stateVC.dismiss(stateVCSelf)
  228. } else if actionType == .confirm {
  229. if let signer = signature.signers.first, let data = signer.certificates {
  230. let signatureDetail = DSignatureDetailsViewController.init()
  231. signatureDetail.certificates = data
  232. signatureDetail.signature = signature
  233. signatureDetail.pdfListView = pdfListView
  234. stateVCSelf.presentAsSheet(signatureDetail)
  235. } else {
  236. NSSound.beep()
  237. }
  238. }
  239. }
  240. if let stateVC = self.stateVC {
  241. self.presentAsSheet(stateVC)
  242. stateVC.reloadData()
  243. }
  244. }
  245. func signElectronicSignature(_ signatureWidgetAnnotation: CPDFSignatureWidgetAnnotation) -> () {
  246. // PDFCustomSignatureWindowController *signatureWindowController = [[PDFCustomSignatureWindowController alloc] init];
  247. // __block typeof(self) blockSelf = self;
  248. // [signatureWindowController beginSheetModalForWindow:[NSApp mainWindow] completionHandler:^(PDFSignature *signature){
  249. // if (signature) {
  250. // [signatureWidgetAnnotation signWithImage:signature.pathsImage];
  251. // [blockSelf.PDFListView setNeedsDisplayAnnotationViewForPage:signatureWidgetAnnotation.page];
  252. // }
  253. // }];
  254. // [signatureWindowController release];
  255. }
  256. //MARK: Notification
  257. @objc fileprivate func signatureStateChangeNoti() {
  258. let signatures = self.pdfView.document.signatures()!
  259. let tempArr = NSMutableArray()
  260. for signature in signatures {
  261. if signature.signers != nil {
  262. if signature.signers.count > 0 {
  263. signature.verifySignature(with: self.pdfView.document)
  264. tempArr.add(signature)
  265. }
  266. }
  267. }
  268. self.pdfView.signatures = tempArr as? [Any]
  269. self.reloadState()
  270. if self.stateVC != nil {
  271. let signatureWidget = self.editWidgeAnnotation!
  272. var signature = signatureWidget.signature()
  273. if signature == nil {
  274. return
  275. }
  276. self.stateVC.signature = signature
  277. self.stateVC.reloadData()
  278. }
  279. }
  280. }
  281. extension KMPDFDigitalSignViewController: CPDFDigtalViewDelegate {
  282. func pdfListViewAddAnnotation(_ pdfListView: CPDFDigtalView!, forAdd annotation: CPDFAnnotation!, in pdfPage: CPDFPage!) {
  283. let widget = CPDFSignatureWidgetAnnotation.init(PDFListViewNoteWith: self.pdfView.document)
  284. widget.bounds = CGRectMake(-1000, -1000, 545, 178);
  285. annotation.page.addAnnotation(widget)
  286. let configWindowVC = DSignatureConfigWindowController.init(windowNibName: "DSignatureConfigWindowController")
  287. configWindowVC.viewType = .fileList;
  288. configWindowVC.appearanceWidget = widget;
  289. configWindowVC.isCreatDS = false
  290. configWindowVC.complentionHandle = {[unowned self] isSign, dic, config, isLock in
  291. widget.page.removeAnnotation(widget)
  292. if isSign {
  293. if (dic.object(forKey: SAVEFILEPATH_KEY) != nil) {
  294. let p12Path = dic.object(forKey: SAVEFILEPATH_KEY) as! String
  295. let password = dic.object(forKey: PASSWORD_KEY)
  296. if p12Path.count > 0 {
  297. self.writeSignatureToWidget(annotation as! CPDFSignatureWidgetAnnotation, p12Path, password as! String, config, isLock)
  298. }
  299. }
  300. }
  301. }
  302. configWindowVC.actionBlock = { controller, type in
  303. if (type == .cancel) {
  304. NSApplication.shared.stopModal()
  305. controller.window?.orderOut(nil)
  306. controller.window?.close()
  307. annotation.page.removeAnnotation(annotation)
  308. widget.page.removeAnnotation(widget)
  309. self.pdfView.setNeedsDisplayAnnotationViewFor(annotation.page)
  310. } else if (type == .confirm) {
  311. NSApplication.shared.stopModal()
  312. controller.window?.orderOut(nil)
  313. controller.window?.close()
  314. }
  315. }
  316. configWindowVC.window?.center()
  317. NSApplication.shared.runModal(for: configWindowVC.window!)
  318. }
  319. func pdfListViewEditAnnotation(_ pdfListView: CPDFDigtalView!, for anotation: CPDFAnnotation!) {
  320. if anotation.className == CPDFSignatureWidgetAnnotation.className() {
  321. let signatureWidget = anotation as! CPDFSignatureWidgetAnnotation
  322. var signature = signatureWidget.signature()
  323. if signature == nil {
  324. return
  325. }
  326. if signature?.signers != nil {
  327. self.editWidgeAnnotation = signatureWidget
  328. self.popUpSignatureWidgetState(signature!, self.pdfView)
  329. } else if signatureWidget.isSigned() {
  330. // self.signElectronicSignature(signatureWidget)
  331. } else {
  332. }
  333. //// __block WindowController *weakself = self;
  334. // CPDFSignatureWidgetAnnotation *signatureWidget = (CPDFSignatureWidgetAnnotation *)annotation;
  335. // CPDFSignature *signature = [signatureWidget signature];
  336. // if (signature.signers.count > 0) {
  337. // [self popUpSignatureWidgetState:signature];
  338. // } else if (signatureWidget.isSigned) {
  339. // [self signElectronicSignature:signatureWidget];
  340. // } else {
  341. // CDSignatureSignTypeWindowController *signType = [[CDSignatureSignTypeWindowController alloc] init];
  342. // signType.callback = ^(CDSignType type) {
  343. // dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  344. // if(type == CDSignType_ElectronicSignature) {
  345. // [weakself signElectronicSignature:signatureWidget];
  346. // } else {
  347. // [weakself signDigitalSignature:signatureWidget];
  348. // }
  349. // });
  350. // };
  351. // [signType startModal:nil];
  352. // [signType release];
  353. // }
  354. }
  355. }
  356. func pdfListViewDeleteAnnotation(_ pdfListView: CPDFDigtalView!, for anotation: CPDFAnnotation!) {
  357. let fileName = self.pdfView.document.documentURL?.lastPathComponent
  358. let fileNameWithoutExtension = URL(fileURLWithPath: fileName!).deletingPathExtension().lastPathComponent
  359. let outputSavePanel = NSSavePanel()
  360. outputSavePanel.directoryURL = self.pdfView.document.documentURL.deletingLastPathComponent()
  361. outputSavePanel.title = NSLocalizedString("", comment: "Save as PDF")
  362. outputSavePanel.allowedFileTypes = ["pdf"]
  363. outputSavePanel.nameFieldStringValue = fileNameWithoutExtension + "_" + NSLocalizedString("Signed", comment: "")
  364. let result = outputSavePanel.runModal()
  365. if result == .OK {
  366. let success = self.pdfView.document.write(to: outputSavePanel.url)
  367. if success {
  368. DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.3) {
  369. NSDocumentController.shared.openDocument(withContentsOf: outputSavePanel.url!, display: true) { document, documentWasAlreadyOpen, error in
  370. if error != nil {
  371. NSApp.presentError(error!)
  372. return
  373. }
  374. }
  375. }
  376. } else {
  377. let alert = NSAlert.init()
  378. alert.messageText = NSLocalizedString("Save failed!", comment: "")
  379. alert.addButton(withTitle: NSLocalizedString("OK", comment: ""))
  380. alert.runModal()
  381. }
  382. }
  383. }
  384. }