KMPDFRedactViewController.swift 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. //
  2. // KMPDFRedactViewController.swift
  3. // PDF Reader Pro
  4. //
  5. // Created by tangchao on 2023/12/18.
  6. //
  7. import Cocoa
  8. class KMPDFRedactViewController: NSViewController {
  9. var scaleFactor: CGFloat = 0 {
  10. didSet {
  11. self.redactPdfView.scaleFactor = scaleFactor
  12. }
  13. }
  14. var callback: ((Bool, Int, Bool, URL?)->Void)?
  15. var titleBack: ((String)->Void)?
  16. @IBOutlet var redactPdfView: KMRedactPDFView!
  17. @IBOutlet weak var redactTipLabel: NSTextField!
  18. @IBOutlet weak var propertiesLabel: NSTextField!
  19. @IBOutlet weak var redactTextLabel: NSTextField!
  20. @IBOutlet weak var exitBox: KMBox!
  21. @IBOutlet weak var applyBox: KMBox!
  22. @IBOutlet weak var exitButton: NSButton!
  23. @IBOutlet weak var applyButton: NSButton!
  24. @IBOutlet weak var toolView: NSView!
  25. @IBOutlet weak var redactTextBox: NSBox!
  26. @IBOutlet weak var propertiesBox: NSBox!
  27. @IBOutlet var whiteRedactBox: NSBox!
  28. @IBOutlet var whiteRedactLabel: NSTextField!
  29. @IBOutlet var whiteRedactBtn: KMCustomButton!
  30. @IBOutlet weak var redactTextButton: KMCustomButton!
  31. @IBOutlet weak var propertiesButton: KMCustomButton!
  32. @IBOutlet weak var whiteOutBox: NSBox!
  33. @IBOutlet weak var whiteOutLabel: NSTextField!
  34. @IBOutlet weak var whiteOutButton: KMCustomButton!
  35. private var _url: URL?
  36. private var url: URL? {
  37. get {
  38. return self._url
  39. }
  40. }
  41. private var _password: String?
  42. private var _indicator: NSProgressIndicator?
  43. private var isWhiteOut_ = false
  44. deinit {
  45. KMPrint("KMPDFRedactViewController deinit.")
  46. NotificationCenter.default.removeObserver(self)
  47. self.redactPdfView.delegate = nil
  48. }
  49. convenience init(url: URL, password: String?) {
  50. self.init()
  51. self._url = url
  52. self._password = password
  53. }
  54. override func loadView() {
  55. super.loadView()
  56. self.isWhiteOut_ = false
  57. self.propertiesBox.wantsLayer = true
  58. self.redactTextBox.wantsLayer = true
  59. self.whiteRedactBox.wantsLayer = true
  60. self.redactTextLabel.textColor = KMAppearance.Layout.w0Color()
  61. self.propertiesLabel.textColor = KMAppearance.Layout.w0Color()
  62. self.whiteRedactLabel.textColor = KMAppearance.Layout.w0Color()
  63. self.redactTextLabel.stringValue = KMLocalizedString("Redact Text", nil)
  64. self.propertiesLabel.stringValue = KMLocalizedString("Properties", nil)
  65. self.whiteRedactLabel.stringValue = KMLocalizedString("White Redact", nil)
  66. self.propertiesButton.mouseMoveCallback = { [weak self] mouseEntered in
  67. self?.propertiesBox.layer?.cornerRadius = 6
  68. if mouseEntered {
  69. self?.propertiesBox.fillColor = KMAppearance.Layout.w30Color()
  70. } else {
  71. self?.propertiesBox.fillColor = .clear
  72. }
  73. }
  74. // self.whiteOutBox.wantsLayer = true
  75. // self.whiteOutBox.fillColor = KMAppearance.Layout.w30Color()
  76. // self.whiteOutBox.layer?.cornerRadius = 6
  77. // self.whiteOutLabel.textColor = KMAppearance.Layout.w0Color()
  78. // self.whiteOutLabel.stringValue = NSLocalizedString("White Out PDF", comment: "")
  79. self.redactTextButton.target = self
  80. self.redactTextButton.action = #selector(redactButtonAction)
  81. // self.whiteOutButton.target = self
  82. // self.whiteOutButton.action = #selector(whiteOutButtonAction)
  83. // Do view setup here.
  84. self.redactTipLabel.stringValue = KMLocalizedString("You are under redact mode", nil)
  85. self.applyButton.title = KMLocalizedString("Apply", nil)
  86. self.exitButton.toolTip = KMLocalizedString("Exit", nil)
  87. self.exitBox.fillColor = .clear
  88. self.exitBox.borderColor = KMAppearance.Layout.w70Color()
  89. self.exitBox.borderWidth = 1.0
  90. self.exitButton.title = KMLocalizedString("Exit", nil)
  91. self.exitButton.setTitleColor(KMAppearance.Layout.w0Color())
  92. self.applyButton.setTitleColor(KMAppearance.Interactive.a0Color())
  93. self.applyButton.wantsLayer = true
  94. self.applyBox.downCallback = { [weak self] downEntered, mouseBox, event in
  95. if downEntered {
  96. self?.apply_button(nil)
  97. }
  98. }
  99. self.exitBox.downCallback = { [weak self] downEntered, mouseBox, event in
  100. if downEntered {
  101. self?.exit_button(nil)
  102. }
  103. }
  104. self.toolView.wantsLayer = true
  105. self.toolView.layer?.backgroundColor = KMAppearance.Interactive.a0Color().cgColor
  106. let document = CPDFDocument(url: self.url!)
  107. if let pwd = self._password, pwd.isEmpty == false {
  108. document?.unlock(withPassword: pwd)
  109. }
  110. self.redactPdfView.document = document
  111. // self.redactPdfView.scaleFactor = self.scaleFactor;
  112. self.redactPdfView.autoScales = true
  113. self.redactPdfView.delegate = self
  114. // self.redactPdfView.toolMode = .redactToolMode
  115. self.redactPdfView.operationType = .redact
  116. // [self.redactPdfView layoutDocumentView];
  117. let frame = NSMakeRect((self.redactPdfView.frame.size.width-32)/2.0, (self.redactPdfView.frame.size.height-32)/2.0, 32, 32)
  118. self._indicator = NSProgressIndicator(frame: frame)
  119. self._indicator?.autoresizingMask = [.minXMargin, .maxXMargin, .minYMargin, .maxYMargin]
  120. self._indicator?.style = .spinning
  121. self._indicator?.controlSize = .regular
  122. self._indicator?.isIndeterminate = true
  123. self._indicator?.startAnimation(nil)
  124. self.redactPdfView.addSubview(self._indicator!)
  125. self._indicator?.isHidden = true
  126. self.reloadUI()
  127. }
  128. override func viewDidLoad() {
  129. super.viewDidLoad()
  130. NotificationCenter.default.addObserver(self, selector: #selector(showCurrentRedactAnnotationProperties), name: KMRedactPDFView.showCurrentRedactAnnotationNotificationName, object: nil)
  131. NotificationCenter.default.addObserver(self, selector: #selector(apply), name: KMRedactPDFView.redactAnnotationApplyNotificationName, object: nil)
  132. NotificationCenter.default.addObserver(self, selector: #selector(across), name: KMRedactPDFView.redactAnnotationAcrossNotificationName, object: nil)
  133. }
  134. //MARK: Setter
  135. func setCurrentPageIndex(_ currentPageIndex: Int) {
  136. self.redactPdfView.go(toPageIndex: currentPageIndex, animated: false)
  137. }
  138. func reloadUI() {
  139. self.redactTextBox.fillColor = .clear
  140. self.redactTextBox.layer?.cornerRadius = 0
  141. self.whiteRedactBox.fillColor = .clear
  142. self.whiteRedactBox.layer?.cornerRadius = 0
  143. if self.redactPdfView.operationType == .redact {
  144. self.redactTextBox.fillColor = KMAppearance.Layout.w30Color()
  145. self.redactTextBox.layer?.cornerRadius = 6
  146. } else if self.redactPdfView.operationType == .redactWhite {
  147. self.whiteRedactBox.fillColor = KMAppearance.Layout.w30Color()
  148. self.whiteRedactBox.layer?.cornerRadius = 6
  149. }
  150. }
  151. // MARK: - Button Actions
  152. @IBAction func showProperties_button(_ sender: AnyObject?) {
  153. // FMTrackEventManager.defaultManager.trackEvent(event: "SubTbr_RedactText", withProperties: ["SubTbr_Btn": "Btn_SubTbr_RedactText_Properties"])
  154. let properties = KMRedactPropertiesWindowController()
  155. self.km_beginSheet(windowC: properties)
  156. properties.callback = { annotation in
  157. }
  158. }
  159. @IBAction func apply_button(_ sender: AnyObject?) {
  160. var isApply = false
  161. for i in 0 ..< self.redactPdfView.document.pageCount {
  162. let page = self.redactPdfView.document.page(at: i)
  163. for anno in page?.annotations ?? [] {
  164. if anno.isKind(of: CPDFRedactAnnotation.self) {
  165. isApply = true
  166. }
  167. }
  168. }
  169. if(self.redactPdfView.newAddAnnotation.count == 0 && !isApply) {
  170. return
  171. }
  172. 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)])
  173. if returnCode == .alertFirstButtonReturn {
  174. DispatchQueue.main.async {
  175. self.saveAsPath()
  176. }
  177. }
  178. }
  179. @IBAction func exit_button(_ sender: AnyObject?) {
  180. guard let block = self.callback else {
  181. return
  182. }
  183. block(false, self.redactPdfView.currentPageIndex, false, nil)
  184. }
  185. @IBAction func redactBtnClicked(_ sender: Any) {
  186. self.redactPdfView.operationType = .redact
  187. self.reloadUI()
  188. }
  189. @IBAction func whiteRedactBtnClicked(_ sender: Any) {
  190. self.redactPdfView.operationType = .redactWhite
  191. self.reloadUI()
  192. }
  193. func saveAsPath() {
  194. let saveAccessCtr = KMSavePanelAccessoryController()
  195. var fileName = self.redactPdfView.document.documentURL.deletingPathExtension().lastPathComponent
  196. fileName = String(format: "%@_%@.pdf", fileName, "Redact")
  197. let outputSavePanel = NSSavePanel()
  198. outputSavePanel.allowedFileTypes = ["pdf"]
  199. outputSavePanel.nameFieldStringValue = fileName
  200. outputSavePanel.accessoryView = saveAccessCtr.view
  201. outputSavePanel.beginSheetModal(for: NSApp.mainWindow!) { result in
  202. if result == .OK {
  203. self.redactPdfView.document.applyRedactions()
  204. self._indicator?.isHidden = false
  205. self._indicator?.startAnimation(nil)
  206. let savePDFPath = outputSavePanel.url!.path
  207. let isSuccess = self.redactPdfView.document.write(toFile: savePDFPath)
  208. if (isSuccess) {
  209. // [self.redactPdfView.newAddAnnotation removeAllObjects];
  210. if (saveAccessCtr.openAutomaticButton.state == .on) {
  211. NSDocumentController.shared.km_safe_openDocument(withContentsOf: outputSavePanel.url!, display: true) { _, _, _ in
  212. }
  213. } else {
  214. KMTools.viewFile(at: savePDFPath)
  215. }
  216. }
  217. self._indicator?.stopAnimation(nil)
  218. self._indicator?.isHidden = true
  219. guard let block = self.callback else {
  220. return
  221. }
  222. block(true, self.redactPdfView.currentPageIndex, false, outputSavePanel.url)
  223. }
  224. }
  225. }
  226. // MARK: - Button notice
  227. @objc func showCurrentRedactAnnotationProperties(_ notice: NSNotification) {
  228. guard let pdfview = notice.object as? KMRedactPDFView else {
  229. return
  230. }
  231. if(pdfview == self.redactPdfView) {
  232. let properties = KMRedactPropertiesWindowController()
  233. properties.readactAnnotation = self.redactPdfView.currentAnnotation
  234. // __block KMPDFRedactViewController *weak_self = self;
  235. self.km_beginSheet(windowC: properties)
  236. properties.callback = { [weak self] annotation in
  237. if let page = self?.redactPdfView.currentAnnotation?.page {
  238. if let anno = annotation {
  239. self?.redactPdfView.newAddAnnotation.append(anno)
  240. }
  241. self?.redactPdfView.setNeedsDisplayAnnotationViewFor(page)
  242. }
  243. }
  244. }
  245. }
  246. @objc func apply(_ notice: NSNotification) {
  247. guard let pdfview = notice.object as? KMRedactPDFView else {
  248. return
  249. }
  250. if(pdfview == self.redactPdfView) {
  251. self.apply_button(nil)
  252. }
  253. }
  254. @objc func across(_ notice: NSNotification) {
  255. guard let pdfview = notice.object as? KMRedactPDFView else {
  256. return
  257. }
  258. if(pdfview == self.redactPdfView) {
  259. let pagesWindowController = KMRedactSelectPagesWindowController(document: self.redactPdfView.document)
  260. self.km_beginSheet(windowC: pagesWindowController)
  261. pagesWindowController.callback = { [unowned self] pages in
  262. if pages.count > 0 {
  263. self.redactPdfView.acrossAddAnnotations(pages)
  264. }
  265. }
  266. }
  267. }
  268. @objc func redactButtonAction(sender : NSButton) {
  269. // FMTrackEventManager.defaultManager.trackEvent(event: "SubTbr_RedactText", withProperties: ["SubTbr_Btn": "Btn_SubTbr_RedactText_RedactPDF"])
  270. self.isWhiteOut_ = false
  271. self.redactPdfView.isWhiteOut = false
  272. self.redactPdfView.operationType = .redact
  273. self.updateButtonsState()
  274. }
  275. @objc func whiteOutButtonAction(sender: NSButton) {
  276. // FMTrackEventManager.defaultManager.trackEvent(event: "SubTbr_RedactText", withProperties: ["SubTbr_Btn": "Btn_SubTbr_RedactText_WhiteOutPDF"])
  277. self.isWhiteOut_ = true
  278. self.redactPdfView.isWhiteOut = true
  279. self.redactPdfView.operationType = .redactWhite
  280. self.updateButtonsState()
  281. }
  282. // MARK: - Private Methods
  283. private func updateButtonsState() {
  284. if self.isWhiteOut_ {
  285. // self.whiteOutBox.fillColor = KMAppearance.Layout.w30Color()
  286. self.redactTextBox.fillColor = .clear
  287. } else {
  288. self.redactTextBox.fillColor = KMAppearance.Layout.w30Color()
  289. // self.whiteOutBox.fillColor = .clear
  290. }
  291. self.reloadUI()
  292. }
  293. }
  294. extension KMPDFRedactViewController: CPDFViewDelegate {
  295. func pdfViewCurrentPageDidChanged(_ pdfView: CPDFView!) {
  296. let fileName = pdfView.document.documentURL.deletingLastPathComponent().lastPathComponent
  297. let title = String(format: KMLocalizedString("%@ (page %ld / %ld)", "Window title format"), fileName, pdfView.currentPageIndex+1, pdfView.document.pageCount)
  298. if(self.titleBack != nil) {
  299. self.titleBack!(title)
  300. }
  301. }
  302. }