KMPDFDigitalSignViewController.swift 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  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 setCurrentPageIndex(_ currentPageIndex: Int) {
  210. self.pdfView.go(toPageIndex: currentPageIndex, animated: false)
  211. }
  212. func pdfViewCurrentPageDidChanged(_ pdfView: CPDFView!) {
  213. let fileName = pdfView.document.documentURL.deletingPathExtension().lastPathComponent
  214. let title = String(format: "%@ (page %ld / %ld)", fileName, pdfView.currentPageIndex+1, pdfView.document.pageCount)
  215. self.currentPageIndex = pdfView.currentPageIndex
  216. guard let callBack = self.titleChangeBlock else {
  217. return
  218. }
  219. callBack(title, self.currentPageIndex)
  220. }
  221. func popUpSignatureWidgetState(_ signature: CPDFSignature, _ pdfListView: CPDFDigtalView) ->(){
  222. if self.stateVC == nil {
  223. self.stateVC = CDSignatureCertificateStateViewController.init()
  224. }
  225. self.stateVC.signature = signature
  226. self.stateVC.pdfListView = pdfListView
  227. self.stateVC.actionBlock = { [weak self, weak stateVC] stateVCSelf, actionType in
  228. guard let self = self, let stateVC = stateVC else { return }
  229. if actionType == .cancel {
  230. stateVC.dismiss(stateVCSelf)
  231. } else if actionType == .confirm {
  232. if let signer = signature.signers.first, let data = signer.certificates {
  233. let signatureDetail = DSignatureDetailsViewController.init()
  234. signatureDetail.certificates = data
  235. signatureDetail.signature = signature
  236. signatureDetail.pdfListView = pdfListView
  237. stateVCSelf.presentAsSheet(signatureDetail)
  238. } else {
  239. NSSound.beep()
  240. }
  241. }
  242. }
  243. if let stateVC = self.stateVC {
  244. self.presentAsSheet(stateVC)
  245. stateVC.reloadData()
  246. }
  247. }
  248. func signElectronicSignature(_ signatureWidgetAnnotation: CPDFSignatureWidgetAnnotation) -> () {
  249. // PDFCustomSignatureWindowController *signatureWindowController = [[PDFCustomSignatureWindowController alloc] init];
  250. // __block typeof(self) blockSelf = self;
  251. // [signatureWindowController beginSheetModalForWindow:[NSApp mainWindow] completionHandler:^(PDFSignature *signature){
  252. // if (signature) {
  253. // [signatureWidgetAnnotation signWithImage:signature.pathsImage];
  254. // [blockSelf.PDFListView setNeedsDisplayAnnotationViewForPage:signatureWidgetAnnotation.page];
  255. // }
  256. // }];
  257. // [signatureWindowController release];
  258. }
  259. //MARK: Notification
  260. @objc fileprivate func signatureStateChangeNoti() {
  261. let signatures = self.pdfView.document.signatures()!
  262. let tempArr = NSMutableArray()
  263. for signature in signatures {
  264. if signature.signers != nil {
  265. if signature.signers.count > 0 {
  266. signature.verifySignature(with: self.pdfView.document)
  267. tempArr.add(signature)
  268. }
  269. }
  270. }
  271. self.pdfView.signatures = tempArr as? [Any]
  272. self.reloadState()
  273. if self.stateVC != nil {
  274. let signatureWidget = self.editWidgeAnnotation!
  275. var signature = signatureWidget.signature()
  276. if signature == nil {
  277. return
  278. }
  279. self.stateVC.signature = signature
  280. self.stateVC.reloadData()
  281. }
  282. }
  283. }
  284. extension KMPDFDigitalSignViewController: CPDFDigtalViewDelegate {
  285. func pdfListViewAddAnnotation(_ pdfListView: CPDFDigtalView!, forAdd annotation: CPDFAnnotation!, in pdfPage: CPDFPage!) {
  286. let widget = CPDFSignatureWidgetAnnotation.init(PDFListViewNoteWith: self.pdfView.document)
  287. widget.bounds = NSMakeRect(-1000, -1000, 545, 178);
  288. annotation.page.addAnnotation(widget)
  289. let configWindowVC = DSignatureConfigWindowController.init(windowNibName: "DSignatureConfigWindowController")
  290. configWindowVC.viewType = .fileList;
  291. configWindowVC.appearanceWidget = widget;
  292. configWindowVC.isCreatDS = false
  293. configWindowVC.complentionHandle = {[weak self] isSign, dic, config, isLock in
  294. widget.page.removeAnnotation(widget)
  295. if isSign {
  296. if (dic.object(forKey: SAVEFILEPATH_KEY) != nil) {
  297. let p12Path = dic.object(forKey: SAVEFILEPATH_KEY) as! String
  298. let password = dic.object(forKey: PASSWORD_KEY)
  299. if p12Path.count > 0 {
  300. self?.writeSignatureToWidget(annotation as! CPDFSignatureWidgetAnnotation, p12Path, password as! String, config, isLock)
  301. }
  302. }
  303. }
  304. }
  305. configWindowVC.actionBlock = { controller, type in
  306. if (type == .cancel) {
  307. NSApplication.shared.stopModal()
  308. controller.window?.orderOut(nil)
  309. controller.window?.close()
  310. annotation.page.removeAnnotation(annotation)
  311. widget.page.removeAnnotation(widget)
  312. self.pdfView.setNeedsDisplayAnnotationViewFor(annotation.page)
  313. } else if (type == .confirm) {
  314. NSApplication.shared.stopModal()
  315. controller.window?.orderOut(nil)
  316. controller.window?.close()
  317. }
  318. }
  319. configWindowVC.window?.center()
  320. NSApplication.shared.runModal(for: configWindowVC.window!)
  321. }
  322. func pdfListViewEditAnnotation(_ pdfListView: CPDFDigtalView!, for anotation: CPDFAnnotation!) {
  323. if anotation.className == CPDFSignatureWidgetAnnotation.className() {
  324. let signatureWidget = anotation as! CPDFSignatureWidgetAnnotation
  325. var signature = signatureWidget.signature()
  326. if signature == nil {
  327. return
  328. }
  329. if signature?.signers != nil {
  330. self.editWidgeAnnotation = signatureWidget
  331. self.popUpSignatureWidgetState(signature!, self.pdfView)
  332. } else if signatureWidget.isSigned() {
  333. // self.signElectronicSignature(signatureWidget)
  334. } else {
  335. }
  336. //// __block WindowController *weakself = self;
  337. // CPDFSignatureWidgetAnnotation *signatureWidget = (CPDFSignatureWidgetAnnotation *)annotation;
  338. // CPDFSignature *signature = [signatureWidget signature];
  339. // if (signature.signers.count > 0) {
  340. // [self popUpSignatureWidgetState:signature];
  341. // } else if (signatureWidget.isSigned) {
  342. // [self signElectronicSignature:signatureWidget];
  343. // } else {
  344. // CDSignatureSignTypeWindowController *signType = [[CDSignatureSignTypeWindowController alloc] init];
  345. // signType.callback = ^(CDSignType type) {
  346. // dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  347. // if(type == CDSignType_ElectronicSignature) {
  348. // [weakself signElectronicSignature:signatureWidget];
  349. // } else {
  350. // [weakself signDigitalSignature:signatureWidget];
  351. // }
  352. // });
  353. // };
  354. // [signType startModal:nil];
  355. // [signType release];
  356. // }
  357. }
  358. }
  359. func pdfListViewDeleteAnnotation(_ pdfListView: CPDFDigtalView!, for anotation: CPDFAnnotation!) {
  360. let fileName = self.pdfView.document.documentURL?.lastPathComponent
  361. let fileNameWithoutExtension = URL(fileURLWithPath: fileName!).deletingPathExtension().lastPathComponent
  362. let outputSavePanel = NSSavePanel()
  363. outputSavePanel.directoryURL = self.pdfView.document.documentURL.deletingLastPathComponent()
  364. outputSavePanel.title = NSLocalizedString("", comment: "Save as PDF")
  365. outputSavePanel.allowedFileTypes = ["pdf"]
  366. outputSavePanel.nameFieldStringValue = fileNameWithoutExtension + "_" + NSLocalizedString("Signed", comment: "")
  367. let result = outputSavePanel.runModal()
  368. if result == .OK {
  369. let success = self.pdfView.document.write(to: outputSavePanel.url)
  370. if success {
  371. DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.3) {
  372. NSDocumentController.shared.openDocument(withContentsOf: outputSavePanel.url!, display: true) { document, documentWasAlreadyOpen, error in
  373. if error != nil {
  374. NSApp.presentError(error!)
  375. return
  376. }
  377. }
  378. }
  379. } else {
  380. let alert = NSAlert.init()
  381. alert.messageText = NSLocalizedString("Save failed!", comment: "")
  382. alert.addButton(withTitle: NSLocalizedString("OK", comment: ""))
  383. alert.runModal()
  384. }
  385. }
  386. }
  387. }