KMInfoWindowController.swift 21 KB

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