KMInfoWindowController.swift 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. //
  2. // KMInfoWindowController.swift
  3. // PDF Reader Pro
  4. //
  5. // Created by tangchao on 2023/10/10.
  6. //
  7. import Cocoa
  8. /*
  9. #define SKInfoWindowFrameAutosaveName @"SKInfoWindow"
  10. */
  11. private let LABEL_COLUMN_ID = "label"
  12. private let VALUE_COLUMN_ID = "value"
  13. private let KMInfoVersionKey = "Version"
  14. private let KMInfoPageCountKey = "PageCount"
  15. private let KMInfoPageSizeKey = "PageSize"
  16. private let KMInfoPageWidthKey = "PageWidth"
  17. private let KMInfoPageHeightKey = "PageHeight"
  18. private let KMInfoKeywordsStringKey = "Keywords"
  19. private let KMInfoEncryptedKey = "Encrypted"
  20. private let KMInfoAllowsPrintingKey = "AllowsPrinting"
  21. private let KMInfoAllowsCopyingKey = "AllowsCopying"
  22. private let KMInfoAllowsDocumentAssemblyKey = "AllowsDocumentAssembly"
  23. private let KMInfoAllowsContentAccessibilityKey = "AllowsContentAccessibility"
  24. private let KMInfoAllowsCommentingKey = "AllowsCommenting"
  25. private let KMInfoAllowsFormFieldEntryKey = "AllowsFormFieldEntry"
  26. private let KMInfoFileNameKey = "FileName"
  27. private let KMInfoFileSizeKey = "FileSize"
  28. private let KMInfoPhysicalSizeKey = "PhysicalSize"
  29. private let KMInfoLogicalSizeKey = "LogicalSize"
  30. private let KMInfoTagsKey = "Tags"
  31. private let KMInfoRatingKey = "Rating"
  32. private let kKMWindowDidBecomeMainNotificationName = Notification.Name(rawValue: "KMWindowDidBecomeMainNotificationName")
  33. typealias KMInfoWindowC = KMInfoWindowController
  34. class KMInfoWindowController: NSWindowController {
  35. public static let windowDidBecomeMainNotification = kKMWindowDidBecomeMainNotificationName
  36. @IBOutlet weak var summaryTableView: NSTableView!
  37. @IBOutlet weak var attributesTableView: NSTableView!
  38. @IBOutlet weak var tabView: NSTabView!
  39. var info: NSDictionary?
  40. var editDictionary: NSMutableDictionary = NSMutableDictionary()
  41. private var _summaryKeys: [String] = []
  42. var summaryKeys: [String] {
  43. get {
  44. return self._summaryKeys
  45. }
  46. }
  47. private var _attributesKeys: [String] = []
  48. var attributesKeys: [String] {
  49. get {
  50. return self._attributesKeys
  51. }
  52. }
  53. private var _labels: [String : String] = [:]
  54. var labels: [String : String] {
  55. get {
  56. return self._labels
  57. }
  58. }
  59. private weak var _currentDocument: NSDocument?
  60. deinit {
  61. NotificationCenter.default.removeObserver(self)
  62. Swift.debugPrint("KMInfoWindowController 已释放")
  63. }
  64. static let shared = KMInfoWindowController()
  65. convenience init() {
  66. self.init(windowNibName: "InfoWindow")
  67. self.info = nil;
  68. self._summaryKeys = [KMInfoFileNameKey,
  69. KMInfoFileSizeKey,
  70. KMInfoPageSizeKey,
  71. KMInfoPageCountKey,
  72. KMInfoVersionKey,
  73. "",
  74. KMInfoEncryptedKey,
  75. KMInfoAllowsPrintingKey,
  76. KMInfoAllowsCopyingKey,
  77. KMInfoAllowsDocumentAssemblyKey,
  78. KMInfoAllowsContentAccessibilityKey,
  79. KMInfoAllowsCommentingKey,
  80. KMInfoAllowsFormFieldEntryKey]
  81. self._attributesKeys = [PDFDocumentAttribute.titleAttribute.rawValue,
  82. PDFDocumentAttribute.authorAttribute.rawValue,
  83. PDFDocumentAttribute.subjectAttribute.rawValue,
  84. PDFDocumentAttribute.creatorAttribute.rawValue,
  85. PDFDocumentAttribute.producerAttribute.rawValue,
  86. PDFDocumentAttribute.creationDateAttribute.rawValue,
  87. PDFDocumentAttribute.modificationDateAttribute.rawValue,
  88. KMInfoKeywordsStringKey]
  89. self._labels = [KMInfoFileNameKey : KMLocalizedString("File name:"),
  90. KMInfoFileSizeKey : KMLocalizedString("File size:"),
  91. KMInfoPageSizeKey : KMLocalizedString("Page size:"),
  92. KMInfoPageCountKey : KMLocalizedString("Page count:"),
  93. KMInfoVersionKey : KMLocalizedString("PDF Version:"),
  94. KMInfoEncryptedKey : KMLocalizedString("Encrypted:"),
  95. KMInfoAllowsPrintingKey : KMLocalizedString("Printing:"),
  96. KMInfoAllowsCopyingKey : KMLocalizedString("Content Copying:"),
  97. KMInfoAllowsDocumentAssemblyKey : KMLocalizedString("Document Assembly:"),
  98. KMInfoAllowsContentAccessibilityKey : KMLocalizedString("Content Copying for Accessibility:"),
  99. KMInfoAllowsCommentingKey : KMLocalizedString("Commenting:"),
  100. KMInfoAllowsFormFieldEntryKey : KMLocalizedString("Filling of form fields:"),
  101. PDFDocumentAttribute.titleAttribute.rawValue : KMLocalizedString("Title:"),
  102. PDFDocumentAttribute.authorAttribute.rawValue : KMLocalizedString("Author:"),
  103. PDFDocumentAttribute.subjectAttribute.rawValue : KMLocalizedString("Subject:"),
  104. PDFDocumentAttribute.creatorAttribute.rawValue : KMLocalizedString("Content Creator:"),
  105. PDFDocumentAttribute.producerAttribute.rawValue : KMLocalizedString("PDF Producer:"),
  106. PDFDocumentAttribute.creationDateAttribute.rawValue : KMLocalizedString("Creation date:"),
  107. PDFDocumentAttribute.modificationDateAttribute.rawValue : KMLocalizedString("Modification date:"),
  108. KMInfoKeywordsStringKey : KMLocalizedString("Keywords:")]
  109. }
  110. override func loadWindow() {
  111. super.loadWindow()
  112. }
  113. override func windowDidLoad() {
  114. super.windowDidLoad()
  115. self.window?.title = NSLocalizedString("Document Info", comment: "")
  116. self.updateForDocument(NSApp.mainWindow?.windowController?.document as? NSDocument)
  117. self.summaryTableView.selectionHighlightStyle = .none
  118. self.attributesTableView.selectionHighlightStyle = .none
  119. for item in self.tabView.tabViewItems {
  120. if item.isEqual(to: self.tabView.tabViewItems.first) {
  121. item.label = NSLocalizedString("Summary", comment: "")
  122. } else {
  123. item.label = NSLocalizedString("Attributes", comment: "")
  124. }
  125. }
  126. self.tabView.selectTabViewItem(at: 1)
  127. NotificationCenter.default.addObserver(self, selector: #selector(_handleViewFrameDidChangeNotification), name: NSView.frameDidChangeNotification, object: self.attributesTableView.enclosingScrollView)
  128. NotificationCenter.default.addObserver(self, selector: #selector(_handleWindowDidBecomeMainNotification), name: NSWindow.didBecomeMainNotification, object: nil)
  129. NotificationCenter.default.addObserver(self, selector: #selector(_handleWindowDidResignMainNotification), name: NSWindow.didResignMainNotification, object: nil)
  130. NotificationCenter.default.addObserver(self, selector: #selector(_handlePDFDocumentInfoDidChangeNotification), name: NSNotification.Name.PDFDocumentDidUnlock, object: nil)
  131. NotificationCenter.default.addObserver(self, selector: #selector(_handleWindowWillCloseNotification), name: NSWindow.willCloseNotification, object: nil)
  132. NotificationCenter.default.addObserver(self, selector: #selector(_km_handleWindowDidBecomeMainNotification), name: KMInfoWindowC.windowDidBecomeMainNotification, object: nil)
  133. }
  134. func updateForDocument(_ doc: NSDocument?) {
  135. self._currentDocument = doc
  136. let info = self._infoForDocument(doc)
  137. var dic = NSMutableDictionary(dictionary: info)
  138. for key in self.editDictionary.allKeys {
  139. dic.setObject(self.editDictionary[key], forKey: key as! NSCopying)
  140. }
  141. self.info = dic
  142. self.summaryTableView.reloadData()
  143. self.attributesTableView.reloadData()
  144. }
  145. public func info(for document: NSDocument) -> NSDictionary {
  146. return self._infoForDocument(document)
  147. }
  148. // MARK: - Private Methods
  149. private func _infoForDocument(_ doc: NSDocument?) -> NSDictionary {
  150. var dictionary: NSMutableDictionary = NSMutableDictionary()
  151. var logicalSize: Int64 = 0
  152. var physicalSize: Int64 = 0
  153. if let data = doc, data.isKind(of: KMMainDocument.self) {
  154. if let pdfDoc = (data as! KMMainDocument).mainViewController?.document {
  155. dictionary.addEntries(from: pdfDoc.documentAttributes())
  156. dictionary.setValue(String(format: "%ld.%ld", pdfDoc.majorVersion, pdfDoc.minorVersion), forKey: KMInfoVersionKey)
  157. dictionary.setValue(NSNumber(value: pdfDoc.pageCount), forKey: KMInfoPageCountKey)
  158. if (pdfDoc.pageCount > 0) {
  159. let page: CPDFPage = pdfDoc.page(at:0)!
  160. let cropSize = page.bounds(for: .cropBox).size
  161. let mediaSize = page.bounds(for: .mediaBox).size
  162. dictionary.setValue(KMSizeString(cropSize, mediaSize), forKey: KMInfoPageSizeKey)
  163. dictionary.setValue(NSNumber(value: cropSize.width), forKey: KMInfoPageWidthKey)
  164. dictionary.setValue(NSNumber(value: cropSize.height), forKey: KMInfoPageHeightKey)
  165. }
  166. let fileSize = pdfDoc.documentURL.path.getFileSize()
  167. if fileSize > 0 {
  168. dictionary.setValue(NSNumber(value: fileSize), forKey: KMInfoFileSizeKey)
  169. }
  170. if let keyworks = dictionary.value(forKey: CPDFDocumentAttribute.keywordsAttribute.rawValue) {
  171. if (keyworks as AnyObject).isKind(of: NSArray.self) {
  172. dictionary.setValue(keyworks, forKey: KMInfoKeywordsStringKey)
  173. }
  174. }
  175. dictionary.setValue(NSNumber(booleanLiteral: pdfDoc.isEncrypted), forKey: KMInfoEncryptedKey)
  176. dictionary.setValue(NSNumber(booleanLiteral: pdfDoc.allowsPrinting), forKey: KMInfoAllowsPrintingKey)
  177. dictionary.setValue(NSNumber(booleanLiteral: pdfDoc.allowsCopying), forKey: KMInfoAllowsCopyingKey)
  178. dictionary.setValue(NSNumber(booleanLiteral: pdfDoc.allowsCommenting), forKey: KMInfoAllowsCommentingKey)
  179. dictionary.setValue(NSNumber(booleanLiteral: pdfDoc.allowsFormFieldEntry), forKey: KMInfoAllowsDocumentAssemblyKey)
  180. dictionary.setValue(NSNumber(booleanLiteral: pdfDoc.allowsFormFieldEntry), forKey: KMInfoAllowsFormFieldEntryKey)
  181. }
  182. }
  183. dictionary.setValue(doc?.fileURL?.path.lastPathComponent, forKey: KMInfoFileNameKey)
  184. dictionary.setValue(NSNumber(value: physicalSize), forKey: KMInfoPhysicalSizeKey)
  185. dictionary.setValue(NSNumber(value: logicalSize), forKey: KMInfoLogicalSizeKey)
  186. return dictionary
  187. }
  188. @objc private func _handleViewFrameDidChangeNotification(_ sender: Notification) {
  189. self.attributesTableView.noteHeightOfRows(withIndexesChanged: IndexSet(integer: self.attributesKeys.count-1))
  190. }
  191. @objc private func _handleWindowDidBecomeMainNotification(_ sender: Notification) {
  192. self.editDictionary.removeAllObjects()
  193. self.updateForDocument((sender.object as? NSWindow)?.windowController?.document as? NSDocument)
  194. }
  195. @objc private func _handleWindowDidResignMainNotification(_ sender: Notification) {
  196. self.editDictionary.removeAllObjects()
  197. self.updateForDocument(nil)
  198. }
  199. @objc private func _handlePDFDocumentInfoDidChangeNotification(_ sender: Notification) {
  200. guard let doc = NSApp.mainWindow?.windowController?.document else {
  201. return
  202. }
  203. if (doc.isKind(of: KMMainDocument.self)) {
  204. guard let pdfDocument = (doc as! KMMainDocument).mainViewController?.document else {
  205. return
  206. }
  207. if (pdfDocument.isEqual(to: sender.object)) {
  208. self.editDictionary.removeAllObjects()
  209. self.updateForDocument((doc as! NSDocument))
  210. }
  211. }
  212. }
  213. @objc private func _handleWindowWillCloseNotification(_ sender: Notification) {
  214. if let object = sender.object as? AnyObject, object.isEqual(self.window) {
  215. if (self.editDictionary.count > 0) {
  216. if let windowController = NSApp.mainWindow?.windowController, windowController.isKind(of: KMBrowserWindowController.self) {
  217. let document = (windowController as! KMBrowserWindowController).document as? KMMainDocument
  218. var dic: NSMutableDictionary = NSMutableDictionary(dictionary: document?.mainViewController?.document?.documentAttributes() ?? [:])
  219. var isSave = false
  220. for key in self.editDictionary.allKeys {
  221. if let data = key as? NSString, data.isEqual(to: KMInfoKeywordsStringKey) {
  222. let string = self.editDictionary[key]
  223. if let data = string as? NSString, data.isEqual(to: dic[PDFDocumentAttribute.keywordsAttribute.rawValue]) {
  224. isSave = true
  225. dic.setObject(string as Any, forKey: CPDFDocumentAttribute.keywordsAttribute.rawValue as NSCopying)
  226. }
  227. } else {
  228. if let data = dic[key] as? NSString, data.isEqual(to: self.editDictionary[key]) == false {
  229. isSave = true
  230. dic.setObject(self.editDictionary[key] as Any, forKey: key as! NSCopying)
  231. }
  232. }
  233. }
  234. if (isSave) {
  235. document?.mainViewController?.document?.setDocumentAttributes(dic as? [CPDFDocumentAttribute : Any])
  236. document?.save(nil)
  237. }
  238. }
  239. }
  240. self.editDictionary.removeAllObjects()
  241. }
  242. }
  243. @objc private func _km_handleWindowDidBecomeMainNotification(_ sender: Notification) {
  244. self.editDictionary.removeAllObjects()
  245. self.updateForDocument(sender.object as? NSDocument)
  246. }
  247. override func showWindow(_ sender: Any?) {
  248. super.showWindow(sender)
  249. self.editDictionary.removeAllObjects()
  250. self.attributesTableView.reloadData()
  251. }
  252. }
  253. extension KMInfoWindowController: NSTableViewDelegate, NSTableViewDataSource {
  254. func numberOfRows(in tableView: NSTableView) -> Int {
  255. if (tableView.isEqual(to: self.summaryTableView)) {
  256. return self.summaryKeys.count
  257. } else if (tableView.isEqual(to: self.attributesTableView)) {
  258. return self.attributesKeys.count
  259. }
  260. return 0
  261. }
  262. func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
  263. var shortDateFormatter = DateFormatter()
  264. shortDateFormatter.dateStyle = .short
  265. shortDateFormatter.timeStyle = .none
  266. var keys: [String]?
  267. if (tableView.isEqual(to: self.summaryTableView)) {
  268. keys = self.summaryKeys
  269. } else if (tableView.isEqual(to: self.attributesTableView)) {
  270. keys = self.attributesKeys
  271. }
  272. let key = keys![row]
  273. let tcID = tableColumn?.identifier.rawValue
  274. var value: Any?
  275. if (key.isEmpty) {
  276. value = ""
  277. } else {
  278. if let data = tcID, data == LABEL_COLUMN_ID {
  279. value = self.labels[key] != nil ? self.labels[key] : key.appending(":")
  280. } else if let data = tcID, data == VALUE_COLUMN_ID {
  281. value = self.info?.object(forKey: key)
  282. if (value == nil) {
  283. value = "-"
  284. } else if let data = value as? NSDate {
  285. value = shortDateFormatter.string(from: data as Date)
  286. } else if let data = value as? NSNumber {
  287. if (key == KMInfoEncryptedKey) {
  288. value = data.boolValue ? KMLocalizedString("Yes") : KMLocalizedString("No")
  289. } else if (key == KMInfoFileSizeKey) {
  290. value = String.formatFileSize(data.int64Value)
  291. } else {
  292. value = key == KMInfoPageCountKey ? data.stringValue : (data.boolValue ? KMLocalizedString("Allowed") : KMLocalizedString("Not Allowed"))
  293. }
  294. }
  295. }
  296. }
  297. let cellView = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: tcID ?? ""), owner: self) as? NSTableCellView
  298. cellView?.textField?.stringValue = (value as? String) ?? ""
  299. if (tableView.isEqual(to: self.attributesTableView) && tableColumn?.identifier.rawValue == VALUE_COLUMN_ID) {
  300. if (row == 0 || row == 1 || row == 2 || row == 3 || row == 7) {
  301. cellView?.textField?.bezelStyle = .squareBezel
  302. cellView?.textField?.isEditable = true
  303. cellView?.textField?.isSelectable = true
  304. cellView?.textField?.isBordered = true
  305. cellView?.textField?.isBezeled = true
  306. cellView?.textField?.drawsBackground = true
  307. cellView?.textField?.delegate = self
  308. if (row == 1) {
  309. let cell = tableView.view(atColumn: 1, row: 0, makeIfNecessary: true) as? NSTableCellView
  310. cell?.textField?.nextKeyView = cellView!.textField
  311. } else if (row == 2) {
  312. let cell = tableView.view(atColumn: 1, row: 1, makeIfNecessary: true) as? NSTableCellView
  313. cell?.textField?.nextKeyView = cellView!.textField
  314. } else if (row == 3) {
  315. let cell = tableView.view(atColumn: 1, row: 2, makeIfNecessary: true) as? NSTableCellView
  316. cell?.textField?.nextKeyView = cellView!.textField
  317. } else if (row == 7) {
  318. let cell = tableView.view(atColumn: 1, row: 3, makeIfNecessary: true) as? NSTableCellView
  319. cell?.textField?.nextKeyView = cellView!.textField
  320. }
  321. } else {
  322. cellView?.textField?.isEditable = false
  323. cellView?.textField?.isSelectable = false
  324. cellView?.textField?.isBordered = false
  325. cellView?.textField?.isBezeled = false
  326. cellView?.textField?.drawsBackground = false
  327. cellView?.textField?.delegate = nil;
  328. }
  329. cellView?.textField?.tag = row
  330. }
  331. return cellView
  332. }
  333. func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat {
  334. var rowHeight = tableView.rowHeight
  335. if (tableView.isEqual(to: self.attributesTableView) && row == tableView.numberOfRows-1) {
  336. rowHeight = fmax(rowHeight, NSHeight(tableView.enclosingScrollView?.bounds ?? NSZeroRect) - tableView.numberOfRows.cgFloat * (rowHeight + tableView.intercellSpacing.height) + rowHeight)
  337. }
  338. return rowHeight
  339. }
  340. }
  341. extension KMInfoWindowController: NSTextFieldDelegate {
  342. func controlTextDidChange(_ obj: Notification) {
  343. if let textField = obj.object as? NSTextField {
  344. if (textField.tag == 0) {
  345. self.editDictionary.setObject(textField.stringValue, forKey: PDFDocumentAttribute.titleAttribute.rawValue as NSCopying)
  346. } else if (textField.tag == 1) {
  347. self.editDictionary.setObject(textField.stringValue, forKey: PDFDocumentAttribute.authorAttribute.rawValue as NSCopying)
  348. } else if (textField.tag == 2) {
  349. self.editDictionary.setObject(textField.stringValue, forKey: PDFDocumentAttribute.subjectAttribute.rawValue as NSCopying)
  350. } else if (textField.tag == 3) {
  351. self.editDictionary.setObject(textField.stringValue, forKey: PDFDocumentAttribute.creatorAttribute.rawValue as NSCopying)
  352. } else if (textField.tag == 7) {
  353. self.editDictionary.setObject(textField.stringValue, forKey: KMInfoKeywordsStringKey as NSCopying)
  354. }
  355. }
  356. }
  357. }
  358. private let CM_PER_POINT = 0.035277778
  359. private let INCH_PER_POINT = 0.013888889
  360. private func KMSizeString(_ size: NSSize, _ altSize: NSSize) -> String {
  361. var useMetric = false
  362. if let data = ((NSLocale.current as NSLocale).object(forKey: .usesMetricSystem) as? NSNumber) {
  363. useMetric = data.boolValue
  364. }
  365. let factor = useMetric ? CM_PER_POINT : INCH_PER_POINT
  366. let units = useMetric ? KMLocalizedString("cm") : KMLocalizedString("in")
  367. if (NSEqualSizes(size, altSize)) {
  368. return String(format: "%.2f x %.2f %@", size.width * factor, size.height * factor, units)
  369. } else {
  370. return String(format: "%.2f x %.2f %@ (%.2f x %.2f %@)", size.width * factor, size.height * factor, units, altSize.width * factor, altSize.height * factor, units)
  371. }
  372. }