// // KMInfoWindowController.swift // PDF Reader Pro // // Created by tangchao on 2023/10/10. // import Cocoa /* #define SKInfoWindowFrameAutosaveName @"SKInfoWindow" #define LABEL_COLUMN_ID @"label" #define VALUE_COLUMN_ID @"value" */ private let LABEL_COLUMN_ID = "label" private let VALUE_COLUMN_ID = "value" private let KMInfoVersionKey = "Version" private let KMInfoPageCountKey = "PageCount" private let KMInfoPageSizeKey = "PageSize" private let KMInfoPageWidthKey = "PageWidth" private let KMInfoPageHeightKey = "PageHeight" private let KMInfoKeywordsStringKey = "Keywords" private let KMInfoEncryptedKey = "Encrypted" private let KMInfoAllowsPrintingKey = "AllowsPrinting" private let KMInfoAllowsCopyingKey = "AllowsCopying" private let KMInfoAllowsDocumentAssemblyKey = "AllowsDocumentAssembly" private let KMInfoAllowsContentAccessibilityKey = "AllowsContentAccessibility" private let KMInfoAllowsCommentingKey = "AllowsCommenting" private let KMInfoAllowsFormFieldEntryKey = "AllowsFormFieldEntry" private let KMInfoFileNameKey = "FileName" private let KMInfoFileSizeKey = "FileSize" private let KMInfoPhysicalSizeKey = "PhysicalSize" private let KMInfoLogicalSizeKey = "LogicalSize" private let KMInfoTagsKey = "Tags" private let KMInfoRatingKey = "Rating" private let kKMWindowDidBecomeMainNotificationName = Notification.Name(rawValue: "KMWindowDidBecomeMainNotificationName") typealias KMInfoWindowC = KMInfoWindowController class KMInfoWindowController: NSWindowController { public static let windowDidBecomeMainNotification = kKMWindowDidBecomeMainNotificationName @IBOutlet weak var summaryTableView: NSTableView! @IBOutlet weak var attributesTableView: NSTableView! @IBOutlet weak var tabView: NSTabView! var info: NSDictionary? var editDictionary: NSMutableDictionary = NSMutableDictionary() private var _summaryKeys: [String] = [] var summaryKeys: [String] { get { return self._summaryKeys } } private var _attributesKeys: [String] = [] var attributesKeys: [String] { get { return self._attributesKeys } } private var _labels: [String : String] = [:] var labels: [String : String] { get { return self._labels } } private weak var _currentDocument: NSDocument? deinit { NotificationCenter.default.removeObserver(self) Swift.debugPrint("KMInfoWindowController 已释放") } static let shared = KMInfoWindowController() convenience init() { self.init(windowNibName: "InfoWindow") self.info = nil; self._summaryKeys = [KMInfoFileNameKey, KMInfoFileSizeKey, KMInfoPageSizeKey, KMInfoPageCountKey, KMInfoVersionKey, "", KMInfoEncryptedKey, KMInfoAllowsPrintingKey, KMInfoAllowsCopyingKey, KMInfoAllowsDocumentAssemblyKey, KMInfoAllowsContentAccessibilityKey, KMInfoAllowsCommentingKey, KMInfoAllowsFormFieldEntryKey] self._attributesKeys = [PDFDocumentAttribute.titleAttribute.rawValue, PDFDocumentAttribute.authorAttribute.rawValue, PDFDocumentAttribute.subjectAttribute.rawValue, PDFDocumentAttribute.creatorAttribute.rawValue, PDFDocumentAttribute.producerAttribute.rawValue, PDFDocumentAttribute.creationDateAttribute.rawValue, PDFDocumentAttribute.modificationDateAttribute.rawValue, KMInfoKeywordsStringKey] self._labels = [KMInfoFileNameKey : KMLocalizedString("File name:", ""), KMInfoFileSizeKey : KMLocalizedString("File size:", ""), KMInfoPageSizeKey : KMLocalizedString("Page size:", ""), KMInfoPageCountKey : KMLocalizedString("Page count:", ""), KMInfoVersionKey : KMLocalizedString("PDF Version:", ""), KMInfoEncryptedKey : KMLocalizedString("Encrypted:", ""), KMInfoAllowsPrintingKey : KMLocalizedString("Printing:", ""), KMInfoAllowsCopyingKey : KMLocalizedString("Content Copying:", ""), KMInfoAllowsDocumentAssemblyKey : KMLocalizedString("Document Assembly:", ""), KMInfoAllowsContentAccessibilityKey : KMLocalizedString("Content Copying for Accessibility:", ""), KMInfoAllowsCommentingKey : KMLocalizedString("Commenting:", ""), KMInfoAllowsFormFieldEntryKey : KMLocalizedString("Filling of form fields:", ""), PDFDocumentAttribute.titleAttribute.rawValue : KMLocalizedString("Title:", ""), PDFDocumentAttribute.authorAttribute.rawValue : KMLocalizedString("Author:", ""), PDFDocumentAttribute.subjectAttribute.rawValue : KMLocalizedString("Subject:", ""), PDFDocumentAttribute.creatorAttribute.rawValue : KMLocalizedString("Content Creator:", ""), PDFDocumentAttribute.producerAttribute.rawValue : KMLocalizedString("PDF Producer:", ""), PDFDocumentAttribute.creationDateAttribute.rawValue : KMLocalizedString("Creation date:", ""), PDFDocumentAttribute.modificationDateAttribute.rawValue : KMLocalizedString("Modification date:", ""), KMInfoKeywordsStringKey : KMLocalizedString("Keywords:", "")] } override func loadWindow() { super.loadWindow() self.window?.localizeStrings(fromTable: self.windowNibName) } override func windowDidLoad() { super.windowDidLoad() self.window?.title = NSLocalizedString("Document Info", comment: "") self.updateForDocument(NSApp.mainWindow?.windowController?.document as? NSDocument) self.summaryTableView.selectionHighlightStyle = .none self.attributesTableView.selectionHighlightStyle = .none for item in self.tabView.tabViewItems { if item.isEqual(to: self.tabView.tabViewItems.first) { item.label = NSLocalizedString("Summary", comment: "") } else { item.label = NSLocalizedString("Attributes", comment: "") } } self.tabView.selectTabViewItem(at: 1) NotificationCenter.default.addObserver(self, selector: #selector(_handleViewFrameDidChangeNotification), name: NSView.frameDidChangeNotification, object: self.attributesTableView.enclosingScrollView) NotificationCenter.default.addObserver(self, selector: #selector(_handleWindowDidBecomeMainNotification), name: NSWindow.didBecomeMainNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(_handleWindowDidResignMainNotification), name: NSWindow.didResignMainNotification, object: nil) // // [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(handlePDFDocumentInfoDidChangeNotification:) // // name: SKPDFPageBoundsDidChangeNotification object: nil]; // // [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(handleDocumentFileURLDidChangeNotification:) // // name: SKDocumentFileURLDidChangeNotification object: nil]; NotificationCenter.default.addObserver(self, selector: #selector(_handlePDFDocumentInfoDidChangeNotification), name: NSNotification.Name.PDFDocumentDidUnlock, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(_handleWindowWillCloseNotification), name: NSWindow.willCloseNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(_km_handleWindowDidBecomeMainNotification), name: KMInfoWindowC.windowDidBecomeMainNotification, object: nil) } func updateForDocument(_ doc: NSDocument?) { self._currentDocument = doc let info = self._infoForDocument(doc) var dic = NSMutableDictionary(dictionary: info) for key in self.editDictionary.allKeys { dic.setObject(self.editDictionary[key], forKey: key as! NSCopying) } self.info = dic self.summaryTableView.reloadData() self.attributesTableView.reloadData() } public func info(for document: NSDocument) -> NSDictionary { return self._infoForDocument(document) } // MARK: - Private Methods private func _infoForDocument(_ doc: NSDocument?) -> NSDictionary { var dictionary: NSMutableDictionary = NSMutableDictionary() var logicalSize: Int64 = 0 var physicalSize: Int64 = 0 if let data = doc, data.isKind(of: KMMainDocument.self) { if let pdfDoc = (data as! KMMainDocument).mainViewController?.document { dictionary.addEntries(from: pdfDoc.documentAttributes()) dictionary.setValue(String(format: "%ld.%ld", pdfDoc.majorVersion, pdfDoc.minorVersion), forKey: KMInfoVersionKey) dictionary.setValue(NSNumber(value: pdfDoc.pageCount), forKey: KMInfoPageCountKey) if (pdfDoc.pageCount > 0) { let page: CPDFPage = pdfDoc.page(at:0)! let cropSize = page.bounds(for: .cropBox).size let mediaSize = page.bounds(for: .mediaBox).size dictionary.setValue(KMSizeString(cropSize, mediaSize), forKey: KMInfoPageSizeKey) dictionary.setValue(NSNumber(value: cropSize.width), forKey: KMInfoPageWidthKey) dictionary.setValue(NSNumber(value: cropSize.height), forKey: KMInfoPageHeightKey) } if let keyworks = dictionary.value(forKey: CPDFDocumentAttribute.keywordsAttribute.rawValue) { if (keyworks as AnyObject).isKind(of: NSArray.self) { dictionary.setValue(keyworks, forKey: KMInfoKeywordsStringKey) } } dictionary.setValue(NSNumber(booleanLiteral: pdfDoc.isEncrypted), forKey: KMInfoEncryptedKey) dictionary.setValue(NSNumber(booleanLiteral: pdfDoc.allowsPrinting), forKey: KMInfoAllowsPrintingKey) dictionary.setValue(NSNumber(booleanLiteral: pdfDoc.allowsCopying), forKey: KMInfoAllowsCopyingKey) // dictionary.setValue(NSNumber(booleanLiteral: pdfDoc.allowsContentAccessibility), forKey: KMInfoAllowsContentAccessibilityKey) dictionary.setValue(NSNumber(booleanLiteral: pdfDoc.allowsCommenting), forKey: KMInfoAllowsCommentingKey) dictionary.setValue(NSNumber(booleanLiteral: pdfDoc.allowsFormFieldEntry), forKey: KMInfoAllowsDocumentAssemblyKey) dictionary.setValue(NSNumber(booleanLiteral: pdfDoc.allowsFormFieldEntry), forKey: KMInfoAllowsFormFieldEntryKey) } } dictionary.setValue(doc?.fileURL?.path.lastPathComponent, forKey: KMInfoFileNameKey) // [dictionary setValue:SKFileSizeStringForFileURL([doc fileURL], &physicalSize, &logicalSize) forKey:SKInfoFileSizeKey]; dictionary.setValue(NSNumber(value: physicalSize), forKey: KMInfoPhysicalSizeKey) dictionary.setValue(NSNumber(value: logicalSize), forKey: KMInfoLogicalSizeKey) return dictionary } @objc private func _handleViewFrameDidChangeNotification(_ sender: Notification) { self.attributesTableView.noteHeightOfRows(withIndexesChanged: IndexSet(integer: self.attributesKeys.count-1)) } @objc private func _handleWindowDidBecomeMainNotification(_ sender: Notification) { self.editDictionary.removeAllObjects() self.updateForDocument((sender.object as? NSWindow)?.windowController?.document as? NSDocument) } @objc private func _handleWindowDidResignMainNotification(_ sender: Notification) { self.editDictionary.removeAllObjects() self.updateForDocument(nil) } @objc private func _handlePDFDocumentInfoDidChangeNotification(_ sender: Notification) { guard let doc = NSApp.mainWindow?.windowController?.document else { return } if (doc.isKind(of: KMMainDocument.self)) { guard let pdfDocument = (doc as! KMMainDocument).mainViewController?.document else { return } if (pdfDocument.isEqual(to: sender.object)) { self.editDictionary.removeAllObjects() self.updateForDocument((doc as! NSDocument)) } } } @objc private func _handleWindowWillCloseNotification(_ sender: Notification) { if let object = sender.object as? AnyObject, object.isEqual(self.window) { if (self.editDictionary.count > 0) { if let windowController = NSApp.mainWindow?.windowController, windowController.isKind(of: KMBrowserWindowController.self) { let document = (windowController as! KMBrowserWindowController).document as? KMMainDocument var dic: NSMutableDictionary = NSMutableDictionary(dictionary: document?.mainViewController?.document?.documentAttributes() ?? [:]) var isSave = false for key in self.editDictionary.allKeys { if let data = key as? NSString, data.isEqual(to: KMInfoKeywordsStringKey) { let string = self.editDictionary[key] if let data = string as? NSString, data.isEqual(to: dic[PDFDocumentAttribute.keywordsAttribute.rawValue]) { isSave = true dic.setObject(string as Any, forKey: CPDFDocumentAttribute.keywordsAttribute.rawValue as NSCopying) } } else { if let data = dic[key] as? NSString, data.isEqual(to: self.editDictionary[key]) == false { isSave = true dic.setObject(self.editDictionary[key] as Any, forKey: key as! NSCopying) } } } if (isSave) { document?.mainViewController?.document?.setDocumentAttributes(dic as? [CPDFDocumentAttribute : Any]) document?.save(nil) } } } self.editDictionary.removeAllObjects() } } @objc private func _km_handleWindowDidBecomeMainNotification(_ sender: Notification) { self.editDictionary.removeAllObjects() self.updateForDocument(sender.object as? NSDocument) } /* - (void)handlePDFDocumentInfoDidChangeNotification:(NSNotification *)notification { [self.editDictionary removeAllObjects]; NSDocument *doc = [[[NSApp mainWindow] windowController] document]; if ([doc isKindOfClass:[KMMainDocument class]]) { CPDFDocument *pdfDocument = ((KMMainDocument *)doc).mainViewController.document; if ([pdfDocument isEqual:notification.object]) { [self updateForDocument:doc]; } } } - (void)handleDocumentFileURLDidChangeNotification:(NSNotification *)notification { [self.editDictionary removeAllObjects]; NSDocument *doc = [[[NSApp mainWindow] windowController] document]; if ([doc isEqual:[notification object]]) [self updateForDocument:doc]; } */ override func showWindow(_ sender: Any?) { super.showWindow(sender) self.editDictionary.removeAllObjects() self.attributesTableView.reloadData() } /* #define BYTE_FACTOR 1024 #define BYTE_FACTOR_F 1024.0f #define BYTE_SHIFT 10 static NSString *SKFileSizeStringForFileURL(NSURL *fileURL, unsigned long long *physicalSizePtr, unsigned long long *logicalSizePtr) { if (fileURL == nil) return @""; FSRef fileRef; FSCatalogInfo catalogInfo; unsigned long long size, logicalSize = 0; BOOL gotSize = NO, isDir = NO; NSMutableString *string = [NSMutableString string]; Boolean gotRef = CFURLGetFSRef((CFURLRef)fileURL, &fileRef); if (gotRef && noErr == FSGetCatalogInfo(&fileRef, kFSCatInfoDataSizes | kFSCatInfoRsrcSizes | kFSCatInfoNodeFlags, &catalogInfo, NULL, NULL, NULL)) { size = catalogInfo.dataPhysicalSize + catalogInfo.rsrcPhysicalSize; logicalSize = catalogInfo.dataLogicalSize + catalogInfo.rsrcLogicalSize; isDir = (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) != 0; gotSize = YES; } if (gotSize == NO) { // this seems to give the logical size NSDictionary *fileAttrs = [[NSFileManager defaultManager] attributesOfItemAtPath:[fileURL path] error:NULL]; logicalSize = size = [[fileAttrs objectForKey:NSFileSize] unsignedLongLongValue]; isDir = [[fileAttrs fileType] isEqualToString:NSFileTypeDirectory]; } if (isDir) { NSString *path = [fileURL path]; unsigned long long componentSize; unsigned long long logicalComponentSize; for (NSString *file in [[NSFileManager defaultManager] subpathsOfDirectoryAtPath:path error:NULL]) { SKFileSizeStringForFileURL([NSURL fileURLWithPath:[path stringByAppendingPathComponent:file]], &componentSize, &logicalComponentSize); size += componentSize; logicalSize += logicalComponentSize; } } if (physicalSizePtr) *physicalSizePtr = size; if (logicalSizePtr) *logicalSizePtr = logicalSize; if (size < BYTE_FACTOR) { [string appendFormat:@"%qu %@", size, NSLocalizedString(@"bytes", @"size unit")]; } else { #define numUnits 6 NSString *units[numUnits] = {NSLocalizedString(@"kB", @"size unit"), NSLocalizedString(@"MB", @"size unit"), NSLocalizedString(@"GB", @"size unit"), NSLocalizedString(@"TB", @"size unit"), NSLocalizedString(@"PB", @"size unit"), NSLocalizedString(@"EB", @"size unit")}; NSUInteger i; for (i = 0; i < numUnits; i++, size >>= BYTE_SHIFT) { if ((size >> BYTE_SHIFT) < BYTE_FACTOR || i == numUnits - 1) { [string appendFormat:@"%.1f %@", size / BYTE_FACTOR_F, units[i]]; break; } } } NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init]; [formatter setFormatterBehavior:NSNumberFormatterBehavior10_4]; [formatter setNumberStyle:NSNumberFormatterDecimalStyle]; [string appendFormat:@" (%@ %@)", [formatter stringFromNumber:[NSNumber numberWithUnsignedLongLong:logicalSize]], NSLocalizedString(@"bytes", @"size unit")]; return string; } - (NSArray *)keys { return [attributesKeys arrayByAddingObjectsFromArray:summaryKeys]; } */ } extension KMInfoWindowController: NSTableViewDelegate, NSTableViewDataSource { func numberOfRows(in tableView: NSTableView) -> Int { if (tableView.isEqual(to: self.summaryTableView)) { return self.summaryKeys.count } else if (tableView.isEqual(to: self.attributesTableView)) { return self.attributesKeys.count } return 0 } func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { var shortDateFormatter = DateFormatter() shortDateFormatter.dateStyle = .short shortDateFormatter.timeStyle = .none var keys: [String]? if (tableView.isEqual(to: self.summaryTableView)) { keys = self.summaryKeys } else if (tableView.isEqual(to: self.attributesTableView)) { keys = self.attributesKeys } let key = keys![row] let tcID = tableColumn?.identifier.rawValue var value: Any? if (key.isEmpty) { value = "" } else { if let data = tcID, data == LABEL_COLUMN_ID { value = self.labels[key] != nil ? self.labels[key] : key.appending(":") } else if let data = tcID, data == VALUE_COLUMN_ID { value = self.info?.object(forKey: key) if (value == nil) { value = "-" } else if let data = value as? NSDate { value = shortDateFormatter.string(from: data as Date) } else if let data = value as? NSNumber { if (key == KMInfoEncryptedKey) { value = data.boolValue ? KMLocalizedString("Yes", "") : KMLocalizedString("No", "") } else { value = key == KMInfoPageCountKey ? data.stringValue : (data.boolValue ? KMLocalizedString("Allowed", "") : KMLocalizedString("Not Allowed", "")) } } } } let cellView = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: tcID ?? ""), owner: self) as? NSTableCellView cellView?.textField?.stringValue = (value as? String) ?? "" if (tableView.isEqual(to: self.attributesTableView) && tableColumn?.identifier.rawValue == VALUE_COLUMN_ID) { if (row == 0 || row == 1 || row == 2 || row == 3 || row == 7) { cellView?.textField?.bezelStyle = .squareBezel cellView?.textField?.isEditable = true cellView?.textField?.isSelectable = true cellView?.textField?.isBordered = true cellView?.textField?.isBezeled = true cellView?.textField?.drawsBackground = true cellView?.textField?.delegate = self if (row == 1) { let cell = tableView.view(atColumn: 1, row: 0, makeIfNecessary: true) as? NSTableCellView cell?.textField?.nextKeyView = cellView!.textField } else if (row == 2) { let cell = tableView.view(atColumn: 1, row: 1, makeIfNecessary: true) as? NSTableCellView cell?.textField?.nextKeyView = cellView!.textField } else if (row == 3) { let cell = tableView.view(atColumn: 1, row: 2, makeIfNecessary: true) as? NSTableCellView cell?.textField?.nextKeyView = cellView!.textField } else if (row == 7) { let cell = tableView.view(atColumn: 1, row: 3, makeIfNecessary: true) as? NSTableCellView cell?.textField?.nextKeyView = cellView!.textField } } else { cellView?.textField?.isEditable = false cellView?.textField?.isSelectable = false cellView?.textField?.isBordered = false cellView?.textField?.isBezeled = false cellView?.textField?.drawsBackground = false cellView?.textField?.delegate = nil; } cellView?.textField?.tag = row } return cellView } func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat { var rowHeight = tableView.rowHeight if (tableView.isEqual(to: self.attributesTableView) && row == tableView.numberOfRows-1) { // } // if ([tv isEqual:attributesTableView] && row == [tv numberOfRows] - 1) rowHeight = fmax(rowHeight, NSHeight(tableView.enclosingScrollView?.bounds ?? NSZeroRect) - tableView.numberOfRows.cgFloat * (rowHeight + tableView.intercellSpacing.height) + rowHeight) } return rowHeight } /* - (id)tableView:(NSTableView *)tv objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row { static NSDateFormatter *shortDateFormatter = nil; if(shortDateFormatter == nil) { shortDateFormatter = [[NSDateFormatter alloc] init]; [shortDateFormatter setDateStyle:NSDateFormatterShortStyle]; [shortDateFormatter setTimeStyle:NSDateFormatterNoStyle]; } NSArray *keys = nil; if ([tv isEqual:summaryTableView]) keys = summaryKeys; else if ([tv isEqual:attributesTableView]) keys = attributesKeys; NSString *key = [keys objectAtIndex:row]; NSString *tcID = [tableColumn identifier]; id value = nil; if ([key length]) { if ([tcID isEqualToString:LABEL_COLUMN_ID]) { value = [labels objectForKey:key] ?: [key stringByAppendingString:@":"]; } else if ([tcID isEqualToString:VALUE_COLUMN_ID]) { value = [info objectForKey:key]; if (value == nil) value = @"-"; else if ([value isKindOfClass:[NSDate class]]) value = [shortDateFormatter stringFromDate:value]; else if ([value isKindOfClass:[NSNumber class]]){ if ([key isEqualToString:SKInfoEncryptedKey]) { value = [value boolValue] ? NSLocalizedString(@"Yes", @"") : NSLocalizedString(@"No", @""); } else { value = ([key isEqualToString:SKInfoPageCountKey] ? [value stringValue] : ([value boolValue] ? NSLocalizedString(@"Allowed", @"") : NSLocalizedString(@"Not Allowed", @""))); } } } } return value; } - (void)tableView:(NSTableView *)tv willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row { if ([tv isEqual:attributesTableView] && [[tableColumn identifier] isEqualToString:LABEL_COLUMN_ID]) [cell setLineBreakMode:row == [tv numberOfRows] - 1 ? NSLineBreakByWordWrapping : NSLineBreakByTruncatingTail]; } - (CGFloat)tableView:(NSTableView *)tv heightOfRow:(NSInteger)row { CGFloat rowHeight = [tv rowHeight]; if ([tv isEqual:attributesTableView] && row == [tv numberOfRows] - 1) rowHeight = fmax(rowHeight, NSHeight([[tv enclosingScrollView] bounds]) - [tv numberOfRows] * (rowHeight + [tv intercellSpacing].height) + rowHeight); return rowHeight; } - (BOOL)tableView:(NSTableView *)tv shouldSelectRow:(NSInteger)row { return YES; } - (void)tabView:(NSTabView *)tabView willSelectTabViewItem:(nullable NSTabViewItem *)tabViewItem { if ([tabView indexOfTabViewItem:tabViewItem] == 1) { if (self.currentDocument) { NSMutableDictionary *dic = [NSMutableDictionary dictionaryWithDictionary:[self infoForDocument:self.currentDocument]]; for (NSString *key in self.editDictionary.allKeys) { [dic setObject:self.editDictionary[key] forKey:key]; } [self setInfo:dic]; } [self.attributesTableView reloadData]; } else { [self.summaryTableView reloadData]; } } */ } extension KMInfoWindowController: NSTextFieldDelegate { func controlTextDidChange(_ obj: Notification) { if let textField = obj.object as? NSTextField { if (textField.tag == 0) { self.editDictionary.setObject(textField.stringValue, forKey: PDFDocumentAttribute.titleAttribute.rawValue as NSCopying) } else if (textField.tag == 1) { self.editDictionary.setObject(textField.stringValue, forKey: PDFDocumentAttribute.authorAttribute.rawValue as NSCopying) } else if (textField.tag == 2) { self.editDictionary.setObject(textField.stringValue, forKey: PDFDocumentAttribute.subjectAttribute.rawValue as NSCopying) } else if (textField.tag == 3) { self.editDictionary.setObject(textField.stringValue, forKey: PDFDocumentAttribute.creatorAttribute.rawValue as NSCopying) } else if (textField.tag == 7) { self.editDictionary.setObject(textField.stringValue, forKey: KMInfoKeywordsStringKey as NSCopying) } } } } private let CM_PER_POINT = 0.035277778 private let INCH_PER_POINT = 0.013888889 private func KMSizeString(_ size: NSSize, _ altSize: NSSize) -> String { var useMetric = false if let data = ((NSLocale.current as NSLocale).object(forKey: .usesMetricSystem) as? NSNumber) { useMetric = data.boolValue } let factor = useMetric ? CM_PER_POINT : INCH_PER_POINT let units = useMetric ? KMLocalizedString("cm", "size unit") : KMLocalizedString("in", "size unit") if (NSEqualSizes(size, altSize)) { return String(format: "%.2f x %.2f %@", size.width * factor, size.height * factor, units) } else { return String(format: "%.2f x %.2f %@ (%.2f x %.2f %@)", size.width * factor, size.height * factor, units, altSize.width * factor, altSize.height * factor, units) } }