KMMainDocument.swift 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987
  1. //
  2. // KMMainDocument.swift
  3. // PDF Reader Pro
  4. //
  5. // Created by wanjun on 2022/12/6.
  6. //
  7. import Cocoa
  8. @objc enum KMArchiveMask: Int {
  9. case diskImage = 1
  10. case email = 2
  11. }
  12. typealias KMMainDocumentCloudUploadHanddler = (@escaping(Bool, String)->()) -> ()
  13. @objcMembers class KMMainDocument: CTTabContents {
  14. var mainViewController: KMMainViewController?
  15. var homeWindowController: KMHomeWindowController?
  16. var homeViewController: KMHomeViewController?
  17. var isNewCreated: Bool = false
  18. var closedByUserGestureFlag: Bool = false // 标记 closedByUserGesture 这个状态需要延后存储(如果需要)
  19. var cloud: Bool = false
  20. var cloudUploadHanddler: KMMainDocumentCloudUploadHanddler?
  21. var isUnlockFromKeychain: Bool = false
  22. private var _saveAsing = false
  23. private var _saveToURL: URL?
  24. var saveToURL: URL? {
  25. get {
  26. return self._saveToURL
  27. }
  28. }
  29. weak var watermarkSaveDelegate: AnyObject?
  30. private var _trackEvents = IndexSet()
  31. override func save(to url: URL, ofType typeName: String, for saveOperation: NSDocument.SaveOperationType, delegate: Any?, didSave didSaveSelector: Selector?, contextInfo: UnsafeMutableRawPointer?) {
  32. if (self.isNewCreated) {
  33. // if let data = self.mainViewController, !data.isPDFDocumentEdited && !data.needSave && !self.isDocumentEdited {
  34. self._km_save(to: url, ofType: typeName, for: saveOperation, delegate: delegate, didSave: didSaveSelector, contextInfo: contextInfo)
  35. return
  36. // }
  37. }
  38. if (!self.needSaveWatermark()) {
  39. self._km_save(to: url, ofType: typeName, for: saveOperation, delegate: delegate, didSave: didSaveSelector, contextInfo: contextInfo)
  40. return
  41. }
  42. var openAccessoryView = self.watermarkSaveDelegate != nil
  43. if (openAccessoryView) {
  44. if let _browser = self.watermarkSaveDelegate as? KMBrowser, _browser.isCloseAllTabViewItem {
  45. openAccessoryView = false
  46. }
  47. }
  48. self._km_saveForWatermark(openAccessoryView: openAccessoryView) { [unowned self] in
  49. self.trackEvents()
  50. } callback: { [unowned self] needSave, params in
  51. if (needSave) {
  52. self._km_save(to: url, ofType: typeName, for: saveOperation, delegate: delegate, didSave: didSaveSelector, contextInfo: contextInfo)
  53. } else { // 水印保存
  54. if (self.watermarkSaveDelegate == nil) {
  55. if let data = params.first as? KMResult, data == .cancel {
  56. if let shouldClose = params.last as? Bool, shouldClose {
  57. DispatchQueue.main.async {
  58. self.mainViewController?.browserWindowController?.browser.windowDidBeginToClose()
  59. }
  60. }
  61. } else {
  62. DispatchQueue.main.async {
  63. self.mainViewController?.browserWindowController?.browser.windowDidBeginToClose()
  64. }
  65. }
  66. return
  67. }
  68. if let data = params.first as? KMResult, data == .cancel {
  69. if var shouldClose = params.last as? Bool {
  70. if let _browser = self.watermarkSaveDelegate as? KMBrowser, _browser.isCloseAllTabViewItem {
  71. shouldClose = true
  72. }
  73. (self.watermarkSaveDelegate as? KMBrowser)?.document(self, shouldClose: shouldClose, contextInfo: nil)
  74. }
  75. } else {
  76. (self.watermarkSaveDelegate as? KMBrowser)?.document(self, shouldClose: true, contextInfo: nil)
  77. }
  78. self.watermarkSaveDelegate = nil
  79. }
  80. }
  81. }
  82. override func makeWindowControllers() {
  83. // Returns the storyboard that contains your document window.
  84. if ((self.fileURL?.path) != nil) {
  85. if !self.fileURL!.path.isPDFValid() {
  86. let alert = NSAlert()
  87. alert.alertStyle = .critical
  88. alert.messageText = NSLocalizedString("An error occurred while opening this document. The file is damaged and could not be repaired.", comment: "")
  89. alert.runModal()
  90. return
  91. }
  92. }
  93. let mainWindow = NSApp.mainWindow
  94. var currentWindowController: KMBrowserWindowController?
  95. if mainWindow != nil {
  96. let windowController = mainWindow!.windowController
  97. if windowController is KMBrowserWindowController {
  98. currentWindowController = (windowController as! KMBrowserWindowController)
  99. } else {
  100. for window in NSApp.windows {
  101. let windowController = window.windowController
  102. if windowController is KMBrowserWindowController {
  103. currentWindowController = (windowController as! KMBrowserWindowController)
  104. break
  105. }
  106. }
  107. }
  108. } else {
  109. for window in NSApp.windows {
  110. let windowController = window.windowController
  111. if windowController is KMBrowserWindowController {
  112. currentWindowController = (windowController as! KMBrowserWindowController)
  113. break
  114. }
  115. }
  116. }
  117. if (currentWindowController == nil) && (self.fileURL != nil) {
  118. let browser = KMBrowser.init() as KMBrowser
  119. browser.addHomeTabContents()
  120. browser.windowController = KMBrowserWindowController.init(browser: browser)
  121. currentWindowController = browser.windowController as? KMBrowserWindowController
  122. }
  123. if currentWindowController?.browser == nil && (self.fileURL != nil) {
  124. let browser: KMBrowser = KMBrowser.init()
  125. browser.windowController = KMBrowserWindowController.init(browser: browser)
  126. browser.addHomeTabContents()
  127. currentWindowController = browser.windowController as? KMBrowserWindowController
  128. browser.windowController.showWindow(self)
  129. }
  130. mainViewController = KMMainViewController.init()
  131. mainViewController?.myDocument = self
  132. if ((self.fileURL?.path) != nil) {
  133. let pdfDocument = CPDFDocument.init(url: URL(fileURLWithPath: self.fileURL!.path))
  134. mainViewController?.document = pdfDocument
  135. }
  136. self.view = mainViewController?.view
  137. if currentWindowController != nil {
  138. if currentWindowController?.browser != nil {
  139. // currentWindowController?.browser.add(self, at: Int32()-1, inForeground: true)
  140. // self.addWindowController(currentWindowController!)
  141. // mainViewController.browserWindowController = currentWindowController
  142. let activeBrowser = (currentWindowController?.browser.activeTabContents())! as CTTabContents
  143. let activeIndex = Int((currentWindowController?.browser.activeTabIndex())!)
  144. self.addWindowController(currentWindowController!)
  145. self.mainViewController?.browserWindowController = currentWindowController
  146. let ishome = activeBrowser.isHome as Bool
  147. let isfirstTab = (activeIndex == 0)
  148. if ishome && !isfirstTab {
  149. // 替换 document
  150. currentWindowController?.browser.replaceTabContents(at: Int32(activeIndex), with: self)
  151. // 刷新标签
  152. currentWindowController?.browser.updateTabState(at: Int32(activeIndex))
  153. // 刷新 home icon
  154. if let tabStripController = currentWindowController?.tabStripController {
  155. if let view = tabStripController.view(at: UInt(activeIndex)) as? CTTabView {
  156. view.controller().isHome = self.isHome
  157. view.controller().isNewTab = self.isNewTab
  158. view.controller().updateUI()
  159. }
  160. }
  161. } else {
  162. currentWindowController?.browser.add(self, at: Int32()-1, inForeground: true)
  163. }
  164. }
  165. }
  166. }
  167. override func showWindows() {
  168. super.showWindows()
  169. self.setDataFromTmpData()
  170. }
  171. override func windowControllerDidLoadNib(_ aController: NSWindowController) {
  172. super.windowControllerDidLoadNib(aController)
  173. self.setDataFromTmpData()
  174. }
  175. override func save(to url: URL, ofType typeName: String, for saveOperation: NSDocument.SaveOperationType) async throws {
  176. do {
  177. try await super.save(to: url, ofType: typeName, for: saveOperation)
  178. } catch let outError {
  179. Swift.print(outError)
  180. }
  181. }
  182. override func write(to url: URL, ofType typeName: String, for saveOperation: NSDocument.SaveOperationType, originalContentsURL absoluteOriginalContentsURL: URL?) throws {
  183. try self._km_write(to: url, ofType: typeName, for: saveOperation, originalContentsURL: absoluteOriginalContentsURL)
  184. }
  185. override func canClose(withDelegate delegate: Any, shouldClose shouldCloseSelector: Selector?, contextInfo: UnsafeMutableRawPointer?) {
  186. let isPrompt = KMPreferenceManager.shared.closeFileIsPrompt()
  187. if (isPrompt) {
  188. super.canClose(withDelegate: delegate, shouldClose: shouldCloseSelector, contextInfo: contextInfo)
  189. return
  190. }
  191. if (self.isNewCreated) {
  192. self.save(nil)
  193. } else if (self.isDocumentEdited) {
  194. self.save(nil)
  195. } else if (mainViewController != nil) {
  196. if self.mainViewController!.isPDFDocumentEdited || self.mainViewController!.needSave {
  197. self.save(nil)
  198. }
  199. }
  200. super.canClose(withDelegate: delegate, shouldClose: shouldCloseSelector, contextInfo: contextInfo)
  201. }
  202. override func saveAs(_ sender: Any?) {
  203. if (!self.needSaveWatermark()) {
  204. self._km_saveAs(sender)
  205. return
  206. }
  207. self._km_saveForWatermark { [unowned self] needSave, _ in
  208. if (needSave) {
  209. self._km_saveAs(sender)
  210. }
  211. }
  212. }
  213. override func runModalSavePanel(for saveOperation: NSDocument.SaveOperationType, delegate: Any?, didSave didSaveSelector: Selector?, contextInfo: UnsafeMutableRawPointer?) {
  214. if (self.isNewCreated) {
  215. // if let data = self.mainViewController, !data.isPDFDocumentEdited && !data.needSave && !self.isDocumentEdited {
  216. self._km_runModalSavePanel(for: saveOperation, delegate: delegate, didSave: didSaveSelector, contextInfo: contextInfo)
  217. return
  218. // }
  219. }
  220. if (!self.needSaveWatermark()) {
  221. self._km_runModalSavePanel(for: saveOperation, delegate: delegate, didSave: didSaveSelector, contextInfo: contextInfo)
  222. return
  223. }
  224. self._km_saveForWatermark { [unowned self] needSave, _ in
  225. if (needSave) {
  226. self._km_runModalSavePanel(for: saveOperation, delegate: delegate, didSave: didSaveSelector, contextInfo: contextInfo)
  227. }
  228. }
  229. }
  230. override func save(_ sender: Any?) {
  231. if (!self.needSaveWatermark()) {
  232. self._km_save(sender)
  233. return
  234. }
  235. self._km_saveForWatermark { [unowned self] in
  236. self.trackEvents()
  237. } callback: { [unowned self] needSave, _ in
  238. if (needSave) {
  239. self._km_save(sender)
  240. }
  241. }
  242. }
  243. func systemInteractionMode() -> SKInteractionMode {
  244. let mainWindow = NSApp.mainWindow
  245. if mainWindow != nil {
  246. let windowController = mainWindow!.windowController
  247. if windowController?.window?.screen?.isEqual(NSScreen.screens[0]) ?? false{
  248. return mainViewController?.interactionMode ?? .SKNormalMode
  249. }
  250. }
  251. return .SKNormalMode
  252. }
  253. func saveForWatermark() {
  254. if (!self.needSaveWatermark()) {
  255. self._km_save(nil)
  256. return
  257. }
  258. self._km_saveForWatermark { [unowned self] in
  259. self.trackEvents()
  260. } callback: { [unowned self] needSave, params in
  261. if (needSave) {
  262. self._km_save(nil)
  263. } else { // 水印保存
  264. if (self.watermarkSaveDelegate == nil) {
  265. if let data = params.first as? KMResult, data == .cancel {
  266. if let shouldClose = params.last as? Bool, shouldClose {
  267. DispatchQueue.main.async {
  268. self.mainViewController?.browserWindowController?.browser.windowDidBeginToClose()
  269. }
  270. }
  271. } else {
  272. DispatchQueue.main.async {
  273. self.mainViewController?.browserWindowController?.browser.windowDidBeginToClose()
  274. }
  275. }
  276. return
  277. }
  278. if let data = params.first as? KMResult, data == .cancel {
  279. if var shouldClose = params.last as? Bool {
  280. if let _browser = self.watermarkSaveDelegate as? KMBrowser, _browser.isCloseAllTabViewItem {
  281. shouldClose = true
  282. }
  283. (self.watermarkSaveDelegate as? KMBrowser)?.document(self, shouldClose: shouldClose, contextInfo: nil)
  284. }
  285. } else {
  286. (self.watermarkSaveDelegate as? KMBrowser)?.document(self, shouldClose: true, contextInfo: nil)
  287. }
  288. self.watermarkSaveDelegate = nil
  289. }
  290. }
  291. }
  292. override func read(from absoluteURL: URL, ofType typeName: String) throws {
  293. do {
  294. try super.read(from: absoluteURL, ofType: typeName)
  295. updateChangeCount(.changeCleared)
  296. } catch let outError {
  297. Swift.print(outError)
  298. }
  299. }
  300. override func read(from data: Data, ofType typeName: String) throws {
  301. // Insert code here to read your document from the given data of the specified type, throwing an error in case of failure.
  302. // Alternatively, you could remove this method and override read(from:ofType:) instead. If you do, you should also override isEntireFileLoaded to return false if the contents are lazily loaded.
  303. let pdfDocument = CPDFDocument.init(data: data)
  304. if pdfDocument == nil {
  305. throw NSError(domain: NSOSStatusErrorDomain, code: unimpErr, userInfo: nil)
  306. }
  307. }
  308. // MARK: Autosaving
  309. override func close() {
  310. if self.isActive {
  311. if browser != nil {
  312. var activeIndex = 0
  313. let dex = browser.index(of: self)
  314. if dex == browser.tabCount() - 1 {
  315. activeIndex = Int(browser.tabCount()-2)
  316. } else {
  317. activeIndex = Int(dex + 1)
  318. }
  319. let activeContents = browser.tabContents(at: Int32(activeIndex))
  320. activeContents?.addWindowController(browser.windowController)
  321. }
  322. }
  323. super.close()
  324. }
  325. // MARK: init
  326. override init() {
  327. super.init()
  328. // Add your subclass-specific initialization here.
  329. NotificationCenter.default.addObserver(self, selector: #selector(pdfChangedNotification(_:)), name: NSNotification.Name.init(rawValue: "CPDFListViewAnnotationsAttributeHasChangeNotification"), object: nil)
  330. // NotificationCenter.default.addObserver(self, selector: #selector(pdfChangedNotification(_:)), name: NSNotification.Name.init(rawValue: "CPDFViewDocumentChangedNotification"), object: nil)
  331. // NotificationCenter.default.addObserver(self, selector: #selector(pdfChangedNotification(_:)), name: NSNotification.Name.init(rawValue: "CPDFViewPageChangedNotification"), object: nil)
  332. NotificationCenter.default.addObserver(self, selector: #selector(pdfChangedNotification(_:)), name: NSNotification.Name.init(rawValue: "CPDFListViewDidAddAnnotationNotification"), object: nil)
  333. NotificationCenter.default.addObserver(self, selector: #selector(pdfChangedNotification(_:)), name: NSNotification.Name.CPDFViewPageChanged, object: nil)
  334. }
  335. override init?(baseTabContents baseContents: CTTabContents?) {
  336. super.init(baseTabContents: baseContents)
  337. if isHome {
  338. homeViewController = KMHomeViewController.init()
  339. homeViewController?.myDocument = self
  340. self.view = homeViewController?.view
  341. }
  342. }
  343. // MARK: Handling User Actions
  344. override var title: String? {
  345. get {
  346. if isHome {
  347. if (self.isNewTab) {
  348. return NSLocalizedString("New Tab", comment: "")
  349. } else {
  350. return NSLocalizedString("Home", comment: "")
  351. }
  352. } else {
  353. return fileURL?.lastPathComponent
  354. }
  355. }
  356. set {
  357. super.title = newValue
  358. }
  359. }
  360. func needSaveWatermark() -> Bool {
  361. if let need = self.mainViewController?.saveWatermarkFlag {
  362. return need
  363. }
  364. return false
  365. }
  366. // MARK: Private Methods
  367. func pdfChangedNotification(_ notification: Notification) -> Void {
  368. if !isHome {
  369. let mainViewController = mainViewController
  370. var document: CPDFDocument!
  371. let dic = notification.object as? NSDictionary
  372. if dic?["object"] is CPDFAnnotation {
  373. let annotation : CPDFAnnotation = dic?["object"] as? CPDFAnnotation ?? CPDFAnnotation()
  374. document = annotation.page.document
  375. } else if dic?["object"] is CPDFListView {
  376. let pdflistView = notification.object as? CPDFListView
  377. document = pdflistView?.document
  378. }
  379. if mainViewController != nil {
  380. if document == mainViewController!.document {
  381. updateChangeCount(.changeDone)
  382. }
  383. }
  384. }
  385. }
  386. override func updateChangeCount(_ change: NSDocument.ChangeType) {
  387. let mainWindow = NSApp.mainWindow
  388. var currentWindowController: KMBrowserWindowController?
  389. if mainWindow != nil {
  390. let windowController = mainWindow!.windowController
  391. if windowController is KMBrowserWindowController {
  392. currentWindowController = (windowController as! KMBrowserWindowController)
  393. } else {
  394. for window in NSApp.windows {
  395. let windowController = window.windowController
  396. if windowController is KMBrowserWindowController {
  397. currentWindowController = (windowController as! KMBrowserWindowController)
  398. break
  399. }
  400. }
  401. }
  402. } else {
  403. for window in NSApp.windows {
  404. let windowController = window.windowController
  405. if windowController is KMBrowserWindowController {
  406. currentWindowController = (windowController as! KMBrowserWindowController)
  407. break
  408. }
  409. }
  410. }
  411. if currentWindowController != nil {
  412. if currentWindowController?.browser != nil {
  413. let activeBrowser = (currentWindowController?.browser.activeTabContents())! as CTTabContents
  414. let activeIndex = Int((currentWindowController?.browser.activeTabIndex())!)
  415. if self == activeBrowser {
  416. super.updateChangeCount(change)
  417. return
  418. }
  419. }
  420. }
  421. super.updateChangeCount(.changeCleared)
  422. }
  423. func uploadToCloud(_ callback: (@escaping(Bool, String)->())) {
  424. guard let handdler = self.cloudUploadHanddler else {
  425. return
  426. }
  427. handdler(callback)
  428. }
  429. func isPDFDocument() -> Bool {
  430. return true
  431. }
  432. func setDataFromTmpData() {
  433. guard let _document = self.mainViewController?.document else {
  434. return
  435. }
  436. // self.tryToUnlockDocument(document!)
  437. if (_document.permissionsStatus != .owner) {
  438. var password: NSString? = nil
  439. let fileId = self.fileId(for: _document)
  440. if (fileId.isEmpty) {
  441. return
  442. }
  443. self.getPassword(&password, fileId: fileId)
  444. if (password != nil) {
  445. self.isUnlockFromKeychain = true
  446. // document.unlock(withPassword: password! as String)
  447. self.mainViewController?.password = password as String?
  448. }
  449. }
  450. }
  451. func tryToUnlockDocument(_ document: CPDFDocument) {
  452. if (document.permissionsStatus != .owner) {
  453. var password: NSString? = nil
  454. let fileId = self.fileId(for: document)
  455. if (fileId.isEmpty) {
  456. return
  457. }
  458. self.getPassword(&password, fileId: fileId)
  459. if (password != nil) {
  460. self.isUnlockFromKeychain = true
  461. document.unlock(withPassword: password! as String)
  462. }
  463. }
  464. }
  465. func km_updateChangeCount(_ change: NSDocument.ChangeType) {
  466. super.updateChangeCount(change)
  467. }
  468. func trackEvents() {
  469. km_synchronized(self) {
  470. for i in self._trackEvents {
  471. if let type = KMSubscribeWaterMarkType(rawValue: i) {
  472. KMTools.trackEvent(type: type)
  473. }
  474. }
  475. }
  476. self.clearTrackEvents()
  477. }
  478. func recordTrackEvent(type: KMSubscribeWaterMarkType) {
  479. if (type == .none) {
  480. return
  481. }
  482. km_synchronized(self) {
  483. self._trackEvents.insert(type.rawValue)
  484. }
  485. }
  486. func clearTrackEvents() {
  487. km_synchronized(self) {
  488. self._trackEvents.removeAll()
  489. }
  490. }
  491. @IBAction func saveArchive(_ sender: Any?) {
  492. guard let item = sender as? NSMenuItem else {
  493. NSSound.beep()
  494. return
  495. }
  496. guard let fileURL = self.fileURL else {
  497. NSSound.beep()
  498. return
  499. }
  500. let check = try?fileURL.checkResourceIsReachable()
  501. if check == false || self.isDocumentEdited {
  502. let msg = KMLocalizedString("You must save this file first", "Alert text when trying to create archive for unsaved document")
  503. let inf = KMLocalizedString("The document has unsaved changes, or has not previously been saved to disk.", "Informative text in alert dialog")
  504. Task {
  505. _ = await KMAlertTool.runModel(message: msg, informative: inf)
  506. }
  507. return
  508. }
  509. // NSString *ext = ([sender tag] | SKArchiveDiskImageMask) ? @"dmg" : @"tgz";
  510. let idx = item.tag
  511. let ext = "dmg"
  512. let isEmail = true
  513. if isEmail {
  514. // if (([sender tag] | SKArchiveEmailMask)) {
  515. let tmpDirURL = FileManager.default.uniqueChewableItemsDirectoryURL()
  516. let tmpFileURL = tmpDirURL.appendingPathComponent(fileURL.lastPathComponentReplacingPathExtension(ext))
  517. self.newSaveArchive(to: tmpFileURL, email: true)
  518. } else {
  519. let sp = NSSavePanel()
  520. sp.allowedFileTypes = [ext]
  521. sp.canCreateDirectories = true
  522. // [sp setNameFieldStringValue:[fileURL lastPathComponentReplacingPathExtension:ext]];
  523. sp.beginSheetModal(for: self.windowForSheet!) { result in
  524. if result == .OK {
  525. self.newSaveArchive(to: sp.url!, email: false)
  526. }
  527. }
  528. }
  529. }
  530. // func saveArchiveToURL(to fileURL: URL, email: Bool) {
  531. // NSTask *task = [[[NSTask alloc] init] autorelease];
  532. // let task = Task()
  533. // if fileURL.pathExtension == "dmg" {
  534. // [task setLaunchPath:@""];
  535. // task.launchPath = "/usr/bin/hdiutil"
  536. // [task setArguments:[NSArray arrayWithObjects:@"create", @"-srcfolder", [[self fileURL] path], @"-format", @"UDZO", @"-volname", [[fileURL lastPathComponent] stringByDeletingPathExtension], [fileURL path], nil]];
  537. // } else {
  538. // [task setLaunchPath:@"/usr/bin/tar"];
  539. // [task setArguments:[NSArray arrayWithObjects:@"-czf", [fileURL path], [[self fileURL] lastPathComponent], nil]];
  540. // }
  541. // [task setCurrentDirectoryPath:[[[self fileURL] URLByDeletingLastPathComponent] path]];
  542. // [task setStandardOutput:[NSFileHandle fileHandleWithNullDevice]];
  543. // [task setStandardError:[NSFileHandle fileHandleWithNullDevice]];
  544. //
  545. // SKAttachmentEmailer *emailer = nil;
  546. // if (email)
  547. // emailer = [SKAttachmentEmailer attachmentEmailerWithFileURL:fileURL subject:[self displayName] waitingForTask:task];
  548. //
  549. // @try {
  550. // [task launch];
  551. // }
  552. // @catch (id exception) {
  553. // [emailer taskFailed];
  554. // }
  555. // }
  556. @IBAction func readNotes(_ sender: Any?) {
  557. KMPrint("readNotes")
  558. }
  559. @IBAction func convertNotes(_ sender: Any?) {
  560. KMPrint("convertNotes")
  561. }
  562. @IBAction func batchRemovePassWord(_ sender: Any?) {
  563. self.mainViewController?.removeOwnerPassword()
  564. }
  565. @IBAction func batchRemovPrivatySecurity(_ sender: Any?) {
  566. KMPrint("batchRemovPrivatySecurity")
  567. }
  568. @IBAction func printPDFDocument(_ sender: Any?) {
  569. KMPrint("printPDFDocument")
  570. KMPrintWindowController.showNewPrintWindowControll(inputDocument: self.mainViewController?.document, inputPageRange: KMPrintPageRange())
  571. }
  572. @IBAction func performFindPanelAction(_ sender: Any?) {
  573. KMPrint("performFindPanelAction")
  574. self.mainViewController?.toolbarController.showFindBar()
  575. }
  576. @IBAction func addBookmark(_ sender: Any?) {
  577. guard let item = sender as? NSMenuItem else {
  578. return
  579. }
  580. var bookmarkSheetController: KMBookmarkSheetController?
  581. if item.tag == 2 {
  582. KMPrint("Edit Bookmark")
  583. } else if item.tag == 1 {
  584. KMPrint("session Bookmark")
  585. bookmarkSheetController = KMBookmarkSheetController.showBookmarkSheetController(type: .session)
  586. } else if item.tag == 0 {
  587. KMPrint("add Bookmark")
  588. bookmarkSheetController = KMBookmarkSheetController.showBookmarkSheetController(type: .bookmark)
  589. }
  590. bookmarkSheetController?.cancelAction = { [unowned self] controller, type in
  591. }
  592. bookmarkSheetController?.doneAction = { [unowned self] controller, type, label in
  593. let folder = controller.selectedFolder
  594. var bookmark: KMBookmark?
  595. switch type {
  596. case .bookmark:
  597. let mainViewController = mainViewController
  598. if let page = mainViewController?.listView.currentPage() {
  599. let pageIndex = page.pageIndex
  600. bookmark = KMBookmark.bookmark(url: self.fileURL!, pageIndex: pageIndex(), label: label)
  601. }
  602. // case .setup:
  603. // if let setup = currentDocumentSetup {
  604. // bookmark = SKBookmark.bookmark(withSetup: setup, label: label)
  605. // }
  606. case .session:
  607. let setups = NSApp.orderedDocuments.compactMap { $0.value(forKey:"currentDocumentSetup") }
  608. bookmark = KMBookmark.bookmarkSession(setups: setups as NSArray, label: label)
  609. default:
  610. break
  611. }
  612. if let bookmark = bookmark {
  613. if let mutableChildren = folder?.mutableArrayValue(forKey: "children") as? NSMutableArray {
  614. mutableChildren.add(bookmark)
  615. }
  616. }
  617. }
  618. }
  619. @IBAction func showWindow(_ sender: Any?) {
  620. KMPrint("showWindow")
  621. }
  622. // MARK: - Private Methods
  623. private func _km_write(to url: URL, ofType typeName: String, for saveOperation: NSDocument.SaveOperationType, originalContentsURL absoluteOriginalContentsURL: URL?) throws {
  624. var success = true
  625. if !self.isHome {
  626. if mainViewController != nil {
  627. if mainViewController?.document != nil {
  628. self.mainViewController?.commitEditingIfNeed()
  629. // if mainViewController!.document!.isEncrypted {
  630. // success = mainViewController!.document!.write(to: url)
  631. // } else {
  632. if (mainViewController!.needSave) {
  633. if let options = self.mainViewController?.secureOptions, !options.isEmpty {
  634. self.mainViewController!.document?.setDocumentAttributes(self.mainViewController?.documentAttribute)
  635. success = self.mainViewController!.document!.write(to: url, withOptions: options)
  636. } else if let flag = self.mainViewController?.removeSecureFlag, flag {
  637. success = self.mainViewController!.document!.writeDecrypt(to: url)
  638. } else {
  639. success = mainViewController!.document!.write(to: url)
  640. }
  641. } else {
  642. success = mainViewController!.document!.write(to: url)
  643. }
  644. // }
  645. self.mainViewController?.needSave = false
  646. self.mainViewController?.clearSecureOptions()
  647. self.mainViewController?.clearRemoveSecureFlag()
  648. }
  649. }
  650. } else {
  651. success = false
  652. }
  653. if (success && self._saveAsing) {
  654. if let tabView = self.browser?.windowController?.tabStripController?.activeTabView() as? CTTabView {
  655. tabView.controller()?.title = url.lastPathComponent
  656. }
  657. self._saveAsing = false
  658. }
  659. if success && isNewCreated && NSDocument.SaveOperationType.saveAsOperation == saveOperation {
  660. isNewCreated = false
  661. }
  662. }
  663. private func _km_saveForWatermark(openAccessoryView: Bool = true, subscribeDidClick: (()->Void)? = nil, callback:@escaping (_ needSave: Bool, _ param: Any...)->Void) {
  664. Task { @MainActor in
  665. if await (KMLightMemberManager.manager.canPayFunction() == false) {
  666. let _ = KMSubscribeWaterMarkWindowController.show(window: NSApp.mainWindow!, isContinue:false, type: .save) {
  667. if let _callback = subscribeDidClick {
  668. _callback()
  669. }
  670. } completion: { isSubscribeSuccess, isWaterMarkExport, isClose in
  671. if (isClose) {
  672. callback(false, KMResult.cancel, false)
  673. return
  674. }
  675. if (isSubscribeSuccess) {
  676. callback(true)
  677. return
  678. }
  679. if (isWaterMarkExport) {
  680. guard let _document = self.mainViewController?.document else {
  681. callback(false, KMResult.failure)
  682. return
  683. }
  684. // 提交文本编辑的内容
  685. self.mainViewController?.commitEditingIfNeed()
  686. DispatchQueue.main.async {
  687. NSPanel.savePanel(NSApp.mainWindow!, openAccessoryView, panel:{ panel in
  688. if (!self.isNewCreated) {
  689. panel.directoryURL = _document.documentURL.deletingLastPathComponent()
  690. }
  691. panel.nameFieldStringValue = _document.documentURL.lastPathComponent
  692. }) { response, url, isOpen in
  693. if (response == .cancel) {
  694. callback(false, KMResult.cancel, true)
  695. return
  696. }
  697. guard let _url = KMTools.saveWatermarkDocument(document: _document, to: url!, secureOptions: self.mainViewController?.secureOptions, documentAttribute: self.mainViewController?.documentAttribute,removePWD: self.mainViewController!.removeSecureFlag) else {
  698. callback(false, KMResult.failure)
  699. return
  700. }
  701. callback(false, KMResult.success)
  702. if (isOpen) {
  703. NSDocumentController.shared.km_safe_openDocument(withContentsOf: _url, display: true) { _, _, _ in
  704. }
  705. } else {
  706. NSWorkspace.shared.activateFileViewerSelecting([_url])
  707. }
  708. }
  709. }
  710. return
  711. }
  712. callback(false, KMResult.cancel, false)
  713. }
  714. return
  715. }
  716. callback(true)
  717. }
  718. }
  719. private func _km_save(_ sender: Any?) {
  720. super.save(sender)
  721. }
  722. private func _km_save(to url: URL, ofType typeName: String, for saveOperation: NSDocument.SaveOperationType, delegate: Any?, didSave didSaveSelector: Selector?, contextInfo: UnsafeMutableRawPointer?) {
  723. self._saveToURL = url
  724. super.save(to: url, ofType: typeName, for: saveOperation, delegate: delegate, didSave: didSaveSelector, contextInfo: contextInfo)
  725. }
  726. private func _km_saveAs(_ sender: Any?) {
  727. super.saveAs(sender)
  728. self._saveAsing = true
  729. }
  730. private func _km_runModalSavePanel(for saveOperation: NSDocument.SaveOperationType, delegate: Any?, didSave didSaveSelector: Selector?, contextInfo: UnsafeMutableRawPointer?) {
  731. super.runModalSavePanel(for: saveOperation, delegate: delegate, didSave: didSaveSelector, contextInfo: contextInfo)
  732. }
  733. }
  734. extension KMMainDocument {
  735. override func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
  736. if (menuItem.action == #selector(save(_ :))) {
  737. if (self.isHome) {
  738. return false
  739. }
  740. if (self.isDocumentEdited) {
  741. return self.isDocumentEdited
  742. }
  743. guard let mainVC = self.mainViewController else {
  744. return false
  745. }
  746. return mainVC.isPDFDocumentEdited || mainVC.needSave
  747. } else if (menuItem.action == #selector(saveAs(_ :))) {
  748. return !self.isHome
  749. } else if menuItem.action == #selector(batchRemovePassWord) {
  750. if self.isHome {
  751. return false
  752. }
  753. guard let doc = self.mainViewController?.listView?.document else {
  754. return false
  755. }
  756. let allowsPrinting = doc.allowsPrinting
  757. let allowsCopying = doc.allowsCopying
  758. if allowsCopying && allowsPrinting {
  759. return false
  760. }
  761. return true
  762. } else if menuItem.action == #selector(saveArchive) {
  763. return !self.isHome
  764. }
  765. return super.validateMenuItem(menuItem)
  766. }
  767. }
  768. extension NSDocument {
  769. @objc class func isDamage(url: URL) -> Bool {
  770. // 文件路径是否存在
  771. if (FileManager.default.fileExists(atPath: url.path) == false) {
  772. return true
  773. }
  774. /// PDF 格式文件
  775. if (url.pathExtension.lowercased() == "pdf") {
  776. let document = PDFDocument(url: url)
  777. if (document == nil) {
  778. return true
  779. }
  780. if (document!.isLocked) { // 加锁文件不在这里判断
  781. return false
  782. }
  783. if (document!.pageCount <= 0) {
  784. return true
  785. }
  786. return false
  787. }
  788. // 支持的图片格式
  789. let imageExts = ["jpg","cur","bmp","jpeg","gif","png","tiff","tif","ico","icns","tga","psd","eps","hdr","jp2","jpc","pict","sgi","heic"]
  790. let isImage = imageExts.contains(url.pathExtension.lowercased())
  791. if (isImage == false) { // 其他格式目前返回没损坏,后续再补充(如果有需求)
  792. return false
  793. }
  794. // 图片格式
  795. let image = NSImage(contentsOf: url)
  796. let data = image?.tiffRepresentation
  797. if (data == nil) {
  798. return true
  799. }
  800. let imageRep = NSBitmapImageRep(data: data!)
  801. imageRep!.size = image!.size
  802. var imageData: NSData?
  803. if (url.pathExtension.lowercased() == "png") {
  804. imageData = imageRep?.representation(using: .png, properties: [:]) as NSData?
  805. } else {
  806. imageData = imageRep?.representation(using: .jpeg, properties: [:]) as NSData?
  807. }
  808. if (imageData == nil) {
  809. return true
  810. }
  811. let path = NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).last?.stringByAppendingPathComponent(Bundle.main.bundleIdentifier!)
  812. if (FileManager.default.fileExists(atPath: path!) == false) {
  813. try?FileManager.default.createDirectory(atPath: path!, withIntermediateDirectories: false)
  814. }
  815. var tagString: String = ""
  816. let dateFormatter = DateFormatter()
  817. dateFormatter.dateFormat = "yyMMddHHmmss"
  818. tagString.append(dateFormatter.string(from: Date()))
  819. tagString = tagString.appendingFormat("%04d", arc4random()%10000)
  820. let filePath = path?.appending("/\(tagString).png")
  821. if (imageData!.write(toFile: filePath!, atomically: true) == false) {
  822. return true
  823. }
  824. // 删除临时图片
  825. try?FileManager.default.removeItem(atPath: filePath!)
  826. return false
  827. }
  828. @objc class func isDamage(url: URL, needAlertIfDamage need: Bool) -> Bool {
  829. let result = self.isDamage(url: url)
  830. if (result == false) {
  831. return false
  832. }
  833. if (need == false) {
  834. return true
  835. }
  836. let alert = NSAlert()
  837. alert.messageText = NSLocalizedString("An error occurred while opening this document. The file is damaged and could not be repaired.", comment: "")
  838. alert.runModal()
  839. return true
  840. }
  841. }
  842. // MARK: -
  843. // MARK: 保存密码
  844. extension NSDocument {
  845. func savePasswordInKeychain(_ password: String, _ document: CPDFDocument) {
  846. if (document.isLocked || password.isEmpty) {
  847. return
  848. }
  849. let fileId = self.fileId(for: document)
  850. if (fileId.isEmpty) {
  851. return
  852. }
  853. // let status: SKPasswordStatus =
  854. let label = "PDF Reader Pro: \(self.displayName!)"
  855. SKKeychain.setPassword(password, item: nil, forService: self.passwordServiceName(), account: fileId, label: label, comment: self.fileURL?.path)
  856. }
  857. func getPassword(_ password: AutoreleasingUnsafeMutablePointer<NSString?>, fileId: String) {
  858. let status = SKKeychain.getPassword(password, item: nil, forService: self.passwordServiceName(), account: fileId)
  859. // if (status == .found) {
  860. // }
  861. }
  862. fileprivate func fileId(for document: CPDFDocument) -> String {
  863. return "\(document.documentURL.path.hash)"
  864. }
  865. private func passwordServiceName() -> String {
  866. return "PDF Reader Pro password"
  867. }
  868. }