// // KMHeaderFooterView.swift // PDF Reader Pro // // Created by lizhe on 2023/11/24. // import Cocoa import Foundation let kHeaderFooterFileSavePath = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.applicationDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).last?.stringByAppendingPathComponent(Bundle.main.bundleIdentifier!).stringByAppendingPathComponent("HeaderFooterpdf.pdf") let dateFormatArray: [String] = [ "m/d", "m/d/yy", "m/d/yyyy", "mm/dd/yy", "mm/dd/yyyy", "d/m/yy", "d/m/yyyy", "dd/mm/yy", "dd/mm/yyyy", "mm/yy", "mm/yyyy", "m.d.yy", "m.d.yyyy", "mm.dd.yy", "mm.dd.yyyy", "mm.yy", "mm.yyyy", "d.m.yy", "d.m.yyyy", "dd.mm.yy", "dd.mm.yyyy", "yy-mm-dd", "yyyy-mm-dd" ] let pageFormatArray: [String] = [ "1", "1 of n", "1/n", "Page 1", "Page 1 of n" ] let fontSizeArray: [String] = [ "8", "9", "10", "11", "12", "14", "16", "18", "20", "22", "24", "26", "28", "36", "48", "72", "144", "288", ] func convertDateFormat(_ oldString: String) -> String { var newString = oldString for dateFormat in dateFormatArray { if newString.contains(dateFormat) { let formatString = dateFormat.replacingOccurrences(of: "m", with: "M") let replace = "<<\(dateFormat)>>" let date = Date() let dateFormatter = DateFormatter() dateFormatter.dateFormat = formatString let dateString = dateFormatter.string(from: date) newString = newString.replacingOccurrences(of: replace, with: dateString) } } return newString } typealias KMHeaderFooterViewDoneAction = ( _ model: KMHeaderFooterObject) -> () typealias KMHeaderFooterViewCancelAction = (_ view: KMHeaderFooterView) -> Void class KMHeaderFooterView: KMBaseXibView, NSTextViewDelegate, NSComboBoxDelegate { @IBOutlet weak var pdfView: KMWatermarkPDFView! @IBOutlet weak var currentPageIndexTextField: NSTextField! @IBOutlet weak var totalPageCountLabel: NSTextField! @IBOutlet weak var textBox: NSBox! @IBOutlet weak var textLabel: NSTextField! @IBOutlet weak var fontSizeLabel: NSTextField! @IBOutlet weak var fontSizeCombobox: NSComboBox! @IBOutlet weak var textColorLabel: NSTextField! @IBOutlet weak var textColorWell: NSColorWell! @IBOutlet weak var marginBox: NSBox! @IBOutlet weak var marginLabel: NSTextField! @IBOutlet weak var topMarginLabel: NSTextField! @IBOutlet weak var topMargintextField: NSTextField! @IBOutlet weak var topMarginStepper: NSStepper! @IBOutlet weak var bottomMarginLabel: NSTextField! @IBOutlet weak var bottomMarginTextField: NSTextField! @IBOutlet weak var bottomMarginStepper: NSStepper! @IBOutlet weak var leftMarginLabel: NSTextField! @IBOutlet weak var leftMarginTextField: NSTextField! @IBOutlet weak var leftMarginStepper: NSStepper! @IBOutlet weak var rightMarginLabel: NSTextField! @IBOutlet weak var rightMarginTextField: NSTextField! @IBOutlet weak var rightMarginStepper: NSStepper! @IBOutlet weak var formatBox: NSBox! @IBOutlet weak var formatLabel: NSTextField! @IBOutlet weak var dateFormateLabel: NSTextField! @IBOutlet weak var dateFormateCombobox: NSComboBox! @IBOutlet weak var pageLabel: NSTextField! @IBOutlet weak var pageCombobox: NSComboBox! @IBOutlet weak var beginPageLabel: NSTextField! @IBOutlet weak var beginPageCombobox: NSComboBox! @IBOutlet weak var topLeftLabel: NSTextField! @IBOutlet weak var topLeftTextView: NSTextView! @IBOutlet weak var topCenterLabel: NSTextField! @IBOutlet weak var topCenterTextView: NSTextView! @IBOutlet weak var topRightLabel: NSTextField! @IBOutlet weak var topRightTextView: NSTextView! @IBOutlet weak var bottomLeftLabel: NSTextField! @IBOutlet weak var bottomLeftTextView: NSTextView! @IBOutlet weak var bottomCenterLabel: NSTextField! @IBOutlet weak var BottomCenterTextView: NSTextView! @IBOutlet weak var bottomRightLabel: NSTextField! @IBOutlet weak var bottomRightTextView: NSTextView! @IBOutlet weak var pageRangeLabel: NSTextField! @IBOutlet weak var pageRangeCombobox: NSComboBox! @IBOutlet weak var saveToTemplateButton: NSButton! @IBOutlet weak var templateNameLabel: NSTextField! @IBOutlet weak var templateNameTextField: NSTextField! @IBOutlet weak var topLeftButton: NSButton! @IBOutlet weak var topCentButton: NSButton! @IBOutlet weak var topRightButton: NSButton! @IBOutlet weak var bottomLeftButton: NSButton! @IBOutlet weak var bottomCenterButton: NSButton! @IBOutlet weak var bottomRightButton: NSButton! @IBOutlet weak var batchButton: NSButton! @IBOutlet weak var cancelButton: NSButton! @IBOutlet weak var applyButton: NSButton! @IBOutlet weak var batesBox: NSBox! @IBOutlet weak var prefixLabel: NSTextField! @IBOutlet weak var prefixTextField: NSTextField! @IBOutlet weak var suffixLabel: NSTextField! @IBOutlet weak var suffixTextField: NSTextField! @IBOutlet weak var batesNumberLabel: NSTextField! @IBOutlet weak var batesNumberTextField: NSTextField! @IBOutlet weak var batesNumberStepper: NSStepper! @IBOutlet weak var batesStartPageLabel: NSTextField! @IBOutlet weak var batesStartpageCombobox: NSComboBox! @IBOutlet weak var constraint: NSLayoutConstraint! @IBOutlet weak var loadingView: KMLoadingView! var isHiddenBatchBtn: Bool = false var initialID: String = "" var filePath: String = Bundle.main.path(forResource: "Quick Start Guide", ofType: "pdf") ?? "" var password: String = "" var type: KMBatchModifyTemplateType = .Use // Replace with the actual type var pdfDocument: CPDFDocument? { didSet { self.pdfView.document = self.pdfDocument self.pdfView.headerFooter = self.headerFooterObj self.password = self.pdfDocument?.password ?? "" self._fileAttri = KMFileAttribute() self._fileAttri?.filePath = self.pdfDocument?.documentURL.path ?? "" self.reloadData() } } var headerFooterObj: KMHeaderFooterObject = KMHeaderFooterObject() var originalHeaderFooterObj: KMHeaderFooterObject = KMHeaderFooterObject() var dateFormatArray: [Any] { get { return KMHeaderFooterManager.defaultManager.dateFormatArray } } var onlyManagerTemplate: Bool = true var isBates: Bool = false { didSet { if (self.isBates) { headerFooterObj.id = KMHeaderFooterManager.defaultManager.fetchBatesAvailableName() } else { headerFooterObj.id = KMHeaderFooterManager.defaultManager.fetchHeaderFooterAvailableName() } headerFooterObj.isBates = self.isBates self.reloadData() } } private var _fileAttri: KMFileAttribute? var cancelAction: KMHeaderFooterViewCancelAction? var operateCallBack: KMHeaderFooterViewDoneAction? deinit { NotificationCenter.default.removeObserver(self) } convenience init?(baseFile filePath: String, headerFooter object: KMHeaderFooterObject, password: String, type: KMBatchModifyTemplateType) { self.init() self.filePath = filePath self.password = password self.headerFooterObj = object.copy() as! KMHeaderFooterObject self.originalHeaderFooterObj = object self.initialID = object.id self.type = type self.pdfDocument = CPDFDocument(url: URL(fileURLWithPath: self.filePath)) if self.pdfDocument!.isLocked { self.pdfDocument!.unlock(withPassword: password) } if self.pdfDocument!.isLocked { return nil } } override func setup() { let buttons = [ topLeftButton, topCentButton, topRightButton, bottomLeftButton, bottomCenterButton, bottomRightButton ] for button in buttons { button?.wantsLayer = true button?.layer?.borderWidth = 1.0 button?.layer?.cornerRadius = 1.0 } let textViewArr: [NSTextView] = [ self.topLeftTextView, self.topCenterTextView, self.topRightTextView, self.bottomLeftTextView, self.BottomCenterTextView, self.bottomRightTextView ] for textView in textViewArr { textView.wantsLayer = true textView.delegate = self // textView?.layer?.borderWidth = 1.0 // textView?.layer?.borderColor = NSColor.gridColor.cgColor } self.textColorWell.color = self.headerFooterObj.getTextColor() self.pdfView.autoScales = true self.pdfView.setDisplay(.singlePage) // self.pdfView.documentView?.enclosingScrollView?.hasVerticalScroller = false // self.pdfView.documentView?.enclosingScrollView?.hasHorizontalScroller = false self.topMarginLabel.allowsExpansionToolTips = true self.bottomMarginLabel.allowsExpansionToolTips = true self.leftMarginLabel.allowsExpansionToolTips = true self.rightMarginLabel.allowsExpansionToolTips = true self.formatLabel.allowsExpansionToolTips = true self.dateFormateLabel.allowsExpansionToolTips = true self.pageLabel.allowsExpansionToolTips = true self.beginPageLabel.allowsExpansionToolTips = true self.topLeftLabel.allowsExpansionToolTips = true self.topCenterLabel.allowsExpansionToolTips = true self.topRightLabel.allowsExpansionToolTips = true self.bottomLeftLabel.allowsExpansionToolTips = true self.bottomCenterLabel.allowsExpansionToolTips = true self.bottomRightLabel.allowsExpansionToolTips = true self.saveToTemplateButton.isEnabled = onlyManagerTemplate self.pageRangeCombobox.removeAllItems() self.pageRangeCombobox.addItems(withObjectValues: [ NSLocalizedString("All Pages", comment: ""), NSLocalizedString("Odd Pages Only", comment: ""), NSLocalizedString("Even Pages Only", comment: ""), NSLocalizedString("e.g. 1,3-5,10", comment: "") ]) self.pageRangeCombobox.placeholderString = NSLocalizedString("e.g. 1,3-5,10", comment: "") self.pageRangeCombobox.selectItem(at: 0) self.pageRangeCombobox.isEditable = false self.pageRangeCombobox.delegate = self pageRangeCombobox.wantsLayer = true pageRangeCombobox.layer?.cornerRadius = 3.0 self.batchButton.isHidden = true if self.type == .Use { self.batchButton.isHidden = false } self.fontSizeCombobox.removeAllItems() self.fontSizeCombobox.addItems(withObjectValues: fontSizeArray) // self.fontSizeCombobox.isEditable = false self.fontSizeCombobox.delegate = self } override func reloadData() { guard let pdfDocument = pdfDocument else { return } updateViewColor() self.totalPageCountLabel.stringValue = String(format: "/ %ld", pdfDocument.pageCount) self.leftMarginTextField.stringValue = self.headerFooterObj.leftMargin.description self.rightMarginTextField.stringValue = self.headerFooterObj.rightMargin.description self.topMargintextField.stringValue = self.headerFooterObj.topMargin.description self.bottomMarginTextField.stringValue = self.headerFooterObj.bottomMargin.description self.leftMarginStepper.integerValue = Int(self.headerFooterObj.leftMargin) self.rightMarginStepper.integerValue = Int(self.headerFooterObj.rightMargin) self.topMarginStepper.integerValue = Int(self.headerFooterObj.topMargin) self.bottomMarginStepper.integerValue = Int(self.headerFooterObj.bottomMargin) let string = Int(self.headerFooterObj.getTextFontSize()).description let index = self.fontSizeCombobox.indexOfItem(withObjectValue: string) if index != NSNotFound { self.fontSizeCombobox.selectItem(withObjectValue: string) } else { self.fontSizeCombobox.stringValue = string } self.textColorWell.color = self.headerFooterObj.getTextColor() self.dateFormateCombobox.removeAllItems() self.dateFormateCombobox.addItems(withObjectValues: self.dateFormatArray) self.dateFormateCombobox.selectItem(withObjectValue: self.headerFooterObj.dateFormatString) let containsItem = self.pageCombobox.objectValues.contains { item in return item as? String == self.headerFooterObj.pageFormatString } if containsItem { self.pageCombobox.selectItem(withObjectValue: self.headerFooterObj.pageFormatString) } else { self.pageCombobox.stringValue = "" } var pages = [Any]() for i in 0.. 0 { let array = sting.components(separatedBy: ",") var pagesString = "" for i in 0..<(array.count - 1) { let index = array[i] if i == array.count - 1 { pagesString = String(format: "%@%ld", pagesString, (Int(index ) ?? 0) + 1) } else { pagesString = String(format: "%@%ld,", pagesString, (Int(index) ?? 0 ) + 1) } } self.pageRangeCombobox.stringValue = pagesString } } self.topLeftTextView.string = self.headerFooterObj.topLeftString self.topCenterTextView.string = self.headerFooterObj.topCenterString self.topRightTextView.string = self.headerFooterObj.topRightString self.bottomLeftTextView.string = self.headerFooterObj.bottomLeftString self.BottomCenterTextView.string = self.headerFooterObj.bottomCenterString self.bottomRightTextView.string = self.headerFooterObj.bottomRightString self.templateNameTextField.stringValue = self.headerFooterObj.id if self.topLeftTextView.string.count <= 0 && self.topCenterTextView.string.count <= 0 && self.topRightTextView.string.count <= 0 && self.bottomLeftTextView.string.count <= 0 && self.BottomCenterTextView.string.count <= 0 && self.bottomRightTextView.string.count <= 0 { self.applyButton.isEnabled = false } else { self.applyButton.isEnabled = true } if self.isBates { self.constraint.constant = 49 self.formatBox.isHidden = true self.batesBox.isHidden = false } else { self.constraint.constant = 20 self.formatBox.isHidden = false self.batesBox.isHidden = true } self.batchButton.isHidden = isHiddenBatchBtn if !isHiddenBatchBtn { self.batchButton.isHidden = type != .Use } } func updateViewColor() { let borderColor: CGColor let backgroundColor: CGColor if KMAppearance.isDarkMode() { borderColor = NSColor.white.withAlphaComponent(0.3).cgColor backgroundColor = NSColor(red: 54 / 255.0, green: 54 / 255.0, blue: 54 / 255.0, alpha: 0.85).cgColor } else { borderColor = NSColor.white.withAlphaComponent(0.3).cgColor backgroundColor = NSColor(red: 238 / 255.0, green: 238 / 255.0, blue: 238 / 255.0, alpha: 0.85).cgColor } let buttons = [ topLeftButton, topCentButton, topRightButton, bottomLeftButton, bottomCenterButton, bottomRightButton ] for button in buttons { button?.layer?.borderColor = borderColor button?.layer?.backgroundColor = backgroundColor } } override func updateLanguage() { self.currentPageIndexTextField.stringValue = NSLocalizedString("1", comment: "") self.batesStartPageLabel.stringValue = String(format: "%@:", NSLocalizedString("Start Page Number", comment: "")) self.textLabel.stringValue = NSLocalizedString("Font Size", comment: "") self.fontSizeLabel.stringValue = String(format: "%@:", NSLocalizedString("Size", comment: "")) self.textColorLabel.stringValue = String(format: "%@:", NSLocalizedString("Color", comment: "")) self.marginLabel.stringValue = NSLocalizedString("Margin", comment: "") self.topMarginLabel.stringValue = String(format: "%@:", NSLocalizedString("Top", comment: "")) self.bottomMarginLabel.stringValue = String(format: "%@:", NSLocalizedString("Bottom", comment: "")) self.leftMarginLabel.stringValue = String(format: "%@:", NSLocalizedString("Left", comment: "")) self.rightMarginLabel.stringValue = String(format: "%@:", NSLocalizedString("Right", comment: "")) self.topMarginLabel.toolTip = String(format: "%@:", NSLocalizedString("Top", comment: "")) self.bottomMarginLabel.toolTip = String(format: "%@:", NSLocalizedString("Bottom", comment: "")) self.leftMarginLabel.toolTip = String(format: "%@:", NSLocalizedString("Left", comment: "")) self.rightMarginLabel.toolTip = String(format: "%@:", NSLocalizedString("Right", comment: "")) self.formatLabel.stringValue = NSLocalizedString("Page Number and Date Format", comment: "") self.dateFormateLabel.stringValue = String(format: "%@:", NSLocalizedString("Date", comment: "")) self.pageLabel.stringValue = String(format: "%@:", NSLocalizedString("Page", comment: "")) self.beginPageLabel.stringValue = String(format: "%@:", NSLocalizedString("Start Page Number", comment: "")) self.formatLabel.toolTip = NSLocalizedString("Page Number and Date Format", comment: "") self.dateFormateLabel.toolTip = String(format: "%@:", NSLocalizedString("Date", comment: "")) self.pageLabel.toolTip = String(format: "%@:", NSLocalizedString("Page", comment: "")) self.beginPageLabel.toolTip = String(format: "%@:", NSLocalizedString("Start Page Number", comment: "")) self.topLeftLabel.stringValue = NSLocalizedString("Left Header Text", comment: "") self.topCenterLabel.stringValue = NSLocalizedString("Center Header Text", comment: "") self.topRightLabel.stringValue = NSLocalizedString("Right Header Text", comment: "") self.bottomLeftLabel.stringValue = NSLocalizedString("Left Footer Text", comment: "") self.bottomCenterLabel.stringValue = NSLocalizedString("Center Footer Text", comment: "") self.bottomRightLabel.stringValue = NSLocalizedString("Right Footer Text", comment: "") self.topLeftLabel.toolTip = NSLocalizedString("Left Header Text", comment: "") self.topCenterLabel.toolTip = NSLocalizedString("Center Header Text", comment: "") self.topRightLabel.toolTip = NSLocalizedString("Right Header Text", comment: "") self.bottomLeftLabel.toolTip = NSLocalizedString("Left Footer Text", comment: "") self.bottomCenterLabel.toolTip = NSLocalizedString("Center Footer Text", comment: "") self.bottomRightLabel.toolTip = NSLocalizedString("Right Footer Text", comment: "") self.pageRangeLabel.stringValue = String(format: "%@:", NSLocalizedString("Page Range", comment: "")) self.saveToTemplateButton.title = NSLocalizedString("Add to Template", comment: "") self.templateNameLabel.stringValue = NSLocalizedString("Name:", comment: "") self.batchButton.title = NSLocalizedString("Batch", comment: "") self.cancelButton.title = NSLocalizedString("Cancel", comment: "") self.applyButton.title = NSLocalizedString("Apply", comment: "") self.batesBox.title = NSLocalizedString("Bates Settings", comment: "") self.prefixLabel.stringValue = String(format: "%@:", NSLocalizedString("Prefix", comment: "")) self.prefixLabel.toolTip = String(format: "%@:", NSLocalizedString("Prefix", comment: "")) self.prefixTextField.stringValue = self.headerFooterObj.batesPrefixString self.suffixLabel.stringValue = String(format: "%@:", NSLocalizedString("Suffix", comment: "")) self.suffixLabel.toolTip = String(format: "%@:", NSLocalizedString("Suffix", comment: "")) self.suffixTextField.stringValue = self.headerFooterObj.batesSuffixString self.batesNumberLabel.stringValue = String(format: "%@:", NSLocalizedString("Number of Digits", comment: "")) self.batesNumberStepper.integerValue = self.headerFooterObj.batesDigits self.batesNumberTextField.integerValue = self.headerFooterObj.batesDigits } override func addNotification() { NotificationCenter.default.addObserver(self, selector: #selector(pageChangeNotification), name: NSNotification.Name.CPDFViewPageChanged, object: self.pdfView) // NotificationCenter.default.addObserver(self, selector: #selector(themeChanged), name: Notification.Name("AppleInterfaceThemeChangedNotification"), object: nil) DistributedNotificationCenter.default().addObserver(self, selector: #selector(themeChanged), name: NSNotification.Name("AppleInterfaceThemeChangedNotification"), object: nil) } func checkPageRangeValidate(pageRangeString: String) -> Bool { var fileAttribute = self._fileAttri if fileAttribute == nil { fileAttribute = KMFileAttribute() self._fileAttri = fileAttribute fileAttribute?.filePath = self.pdfDocument?.documentURL?.path ?? "" } fileAttribute?.bAllPage = false var pageRange: KMPageRange = .all let pageRangeType: KMWatermarkeModelPageRangeType = headerFooterObj.pageRangeType if pageRangeType == .all { pageRange = .all } else if pageRangeType == .even { pageRange = .even } else if pageRangeType == .odd { pageRange = .odd } else if pageRangeType == .other { pageRange = .custom } fileAttribute?.pagesType = pageRange fileAttribute?.pagesString = self.pageRangeCombobox.stringValue if let cnt = fileAttribute?.fetchSelectPages().count, cnt == 0 { return false } return true } func popMenu(sender: NSButton) { if self.isBates { let menu = NSMenu() let title = NSLocalizedString("Insert Bates", comment: "") let item = menu.addItem(withTitle: title, action: #selector(menuItem_InsertBates), keyEquivalent: "") item.target = self item.representedObject = sender menu.popUp(positioning: menu.item(at: 0), at: NSPoint(x: 0, y: 15), in: sender) } else { let dateMenu = NSMenu() var title = NSLocalizedString("Insert Page Number", comment: "") var item = dateMenu.addItem(withTitle: title, action: #selector(menuItem_InsertPage), keyEquivalent: "") item.target = self item.representedObject = sender title = NSLocalizedString("Insert Date", comment: "") item = dateMenu.addItem(withTitle: title, action: #selector(menuItem_InsertDate), keyEquivalent: "") item.target = self item.representedObject = sender dateMenu.popUp(positioning: dateMenu.item(at: 0), at: NSPoint(x: 0, y: 15), in: sender) } } @objc func menuItem_InsertBates(_ sender: NSMenuItem) { guard let button = sender.representedObject as? NSButton else { return } self.window?.makeFirstResponder(self) self.headerFooterObj.batesDigits = self.batesNumberTextField.integerValue var tString = "<<#\((self.batesNumberTextField.integerValue))#\(self.batesStartpageCombobox.stringValue)" if !self.prefixTextField.stringValue.isEmpty { tString += "#\(self.prefixTextField.stringValue)" } else if self.prefixTextField.stringValue.isEmpty && !self.suffixTextField.stringValue.isEmpty { tString += "#" } if !self.suffixTextField.stringValue.isEmpty { tString += "#\(self.suffixTextField.stringValue)" } tString += ">>" if let textView = self.fetchTargetTextView(tag: button.tag) { textView.string = textView.string + tString self.updateHeaderFooterString(newString: textView.string, tag: button.tag) } self.applyButton.isEnabled = true self.updatePDFView() } @objc func menuItem_InsertPage(_ sender: NSMenuItem) { guard let button = sender.representedObject as? NSButton else { return } let tString = "<<\(self.pageCombobox.stringValue)>>" if let textView = self.fetchTargetTextView(tag: button.tag) { textView.string = textView.string + tString self.updateHeaderFooterString(newString: textView.string, tag: button.tag) } self.applyButton.isEnabled = true self.updatePDFView() } @objc func menuItem_InsertDate(_ sender: NSMenuItem) { guard let button = sender.representedObject as? NSButton else { return } let tString = "<<\(self.headerFooterObj.dateFormatString)>>" if let textView = self.fetchTargetTextView(tag: button.tag) { textView.string = textView.string + tString self.updateHeaderFooterString(newString: textView.string, tag: button.tag) } self.applyButton.isEnabled = true self.updatePDFView() } func fetchTargetTextView(tag: Int) -> NSTextView? { var textView: NSTextView? switch tag { case 0: textView = self.topLeftTextView case 1: textView = self.topCenterTextView case 2: textView = self.topRightTextView case 3: textView = self.bottomLeftTextView case 4: textView = self.BottomCenterTextView case 5: textView = self.bottomRightTextView default: break } return textView } func updateHeaderFooterString(newString: String, tag: Int) { switch tag { case 0: self.headerFooterObj.topLeftString = newString case 1: self.headerFooterObj.topCenterString = newString case 2: self.headerFooterObj.topRightString = newString case 3: self.headerFooterObj.bottomLeftString = newString case 4: self.headerFooterObj.bottomCenterString = newString case 5: self.headerFooterObj.bottomRightString = newString default: break } } func textDidChange(_ notification: Notification) { guard let textView = notification.object as? NSTextView else { return } switch textView { case topLeftTextView: headerFooterObj.topLeftString = topLeftTextView.string case topCenterTextView: headerFooterObj.topCenterString = topCenterTextView.string case topRightTextView: headerFooterObj.topRightString = topRightTextView.string case bottomLeftTextView: headerFooterObj.bottomLeftString = bottomLeftTextView.string case BottomCenterTextView: headerFooterObj.bottomCenterString = BottomCenterTextView.string case bottomRightTextView: headerFooterObj.bottomRightString = bottomRightTextView.string default: break } applyButton.isEnabled = !( topLeftTextView.string.isEmpty && topCenterTextView.string.isEmpty && topRightTextView.string.isEmpty && bottomLeftTextView.string.isEmpty && BottomCenterTextView.string.isEmpty && bottomRightTextView.string.isEmpty ) self.updatePDFView() } func saveAsPDFToPath(_ path: String, autoOpen: Bool) { self.loadingView.isHidden = false self.loadingView.startAnimation() DispatchQueue.global(qos: .default).async { [unowned self] in var filePath = self.pdfDocument?.documentURL?.path let password = self.password if filePath == nil { let writeSuccess = self.pdfDocument!.write(to: URL(fileURLWithPath: kNewDocumentTempSavePath(NSLocalizedString("Untitled", comment: "")))) if writeSuccess { let newDocument = CPDFDocument(url: URL(fileURLWithPath: kNewDocumentTempSavePath(NSLocalizedString("Untitled", comment: "")))) filePath = newDocument?.documentURL?.path } else { return } } guard let filePath = filePath else { return } let document = CPDFDocument(url: NSURL.fileURL(withPath: filePath)) document?.unlock(withPassword: password) let font = NSFont.boldSystemFont(ofSize: self.headerFooterObj.getTextFontSize()) let style = NSMutableParagraphStyle() style.alignment = .center style.lineBreakMode = .byCharWrapping let dictionary: [NSAttributedString.Key: Any] = [.paragraphStyle: style, .font: font] let size = ("text" as NSString).boundingRect(with: CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude), options: [.usesLineFragmentOrigin, .usesFontLeading], attributes: dictionary).size if self.isBates { if let bates = document?.bates() { bates.margin = NSEdgeInsets(top: max(CGFloat(self.headerFooterObj.topMargin) - size.height, 0), left: CGFloat(self.headerFooterObj.leftMargin), bottom: max(CGFloat(self.headerFooterObj.bottomMargin) - size.height, 0), right: CGFloat(self.headerFooterObj.rightMargin)) let pagesString = self.headerFooterObj.pageRangeString if pagesString.count != 0 { bates.pageString = pagesString } else { let pageString = "0-\(document!.pageCount - 1)" bates.pageString = pageString } let items = [self.headerFooterObj.topLeftString, self.headerFooterObj.topCenterString, self.headerFooterObj.topRightString, self.headerFooterObj.bottomLeftString, self.headerFooterObj.bottomCenterString, self.headerFooterObj.bottomRightString] for i in 0.. String { let searchPath = NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).last let append1 = searchPath?.stringByAppendingPathComponent(Bundle.main.bundleIdentifier!) let append2 = append1!.stringByAppendingPathComponent(String(format: "%@", fileName)) return append2 } static func saveAsPDFRemoveAllHeaderFooter(_ PDFDocument: CPDFDocument, password: String?, toPath path: String, completionHandler handler: ((Int) -> Void)?) { DispatchQueue.global(qos: .default).async { var filePath = PDFDocument.documentURL?.path let document = CPDFDocument(url: URL(fileURLWithPath: filePath ?? "")) if let password = password { document?.unlock(withPassword: password) } if let headerFooter = document?.headerFooter { headerFooter()?.clear() } let documentPath = NSTemporaryDirectory() let tempPath = (documentPath as NSString).appendingPathComponent((path as NSString).lastPathComponent) if FileManager.default.fileExists(atPath: tempPath) { try? FileManager.default.removeItem(atPath: tempPath) } let result = document?.write(to: URL(fileURLWithPath: tempPath)) ?? false if result { if FileManager.default.fileExists(atPath: path) { try? FileManager.default.removeItem(atPath: path) } try? FileManager.default.moveItem(atPath: tempPath, toPath: path) } else { try? FileManager.default.removeItem(atPath: tempPath) } DispatchQueue.main.async { if result { handler?(1) } else { handler?(0) } } } } static func saveAsPDFRemoveAllHeaderFooterBates(_ PDFDocument: CPDFDocument, password: String?, toPath path: String, completionHandler handler: ((Int) -> Void)?) { DispatchQueue.global(qos: .default).async { var filePath = PDFDocument.documentURL?.path let document = CPDFDocument(url: URL(fileURLWithPath: filePath ?? "")) if let password = password { document?.unlock(withPassword: password) } if let bates = document?.bates { bates()?.clear() } let documentPath = NSTemporaryDirectory() let tempPath = (documentPath as NSString).appendingPathComponent((path as NSString).lastPathComponent) if FileManager.default.fileExists(atPath: tempPath) { try? FileManager.default.removeItem(atPath: tempPath) } let result = document?.write(to: URL(fileURLWithPath: tempPath)) ?? false if result { if FileManager.default.fileExists(atPath: path) { try? FileManager.default.removeItem(atPath: path) } try? FileManager.default.moveItem(atPath: tempPath, toPath: path) } else { try? FileManager.default.removeItem(atPath: tempPath) } DispatchQueue.main.async { if result { handler?(1) } else { handler?(0) } } } } @objc func controlTextDidEndEditing(_ obj: Notification) { guard let comboBox = obj.object as? NSComboBox else { return } if comboBox == pageRangeCombobox { if pageRangeCombobox.indexOfSelectedItem == -1 { if !checkPageRangeValidate(pageRangeString: comboBox.stringValue) { let alert = NSAlert() alert.alertStyle = .critical alert.messageText = "\(pdfDocument?.documentURL?.lastPathComponent ?? NSLocalizedString("Untitled", comment: "")) \(NSLocalizedString("Invalid page range or the page number is out of range. Please try again.", comment: ""))" alert.runModal() headerFooterObj.pageRangeType = .all pageRangeCombobox.selectItem(at: 0) // pageRangeCombobox.isEditable = false pageRangeCombobox.isSelectable = false return } else { headerFooterObj.pageRangeString = comboBox.stringValue self.updatePDFView() } } } } @objc func comboBoxSelectionDidChange(_ notification: Notification) { guard let comboBox = notification.object as? NSComboBox else { return } if comboBox == pageRangeCombobox { pageRangeCombobox.isEditable = false switch comboBox.indexOfSelectedItem { case 0: headerFooterObj.pageRangeType = .all case 1: headerFooterObj.pageRangeType = .odd case 2: headerFooterObj.pageRangeType = .even default: headerFooterObj.pageRangeType = .other // pageRangeCombobox.stringValue = "" pageRangeCombobox.isEditable = true window?.makeFirstResponder(pageRangeCombobox) } } } @objc func pageChangeNotification(_ notification: Notification) { currentPageIndexTextField.stringValue = "\(pdfView.currentPage().pageIndex() + 1)" } @objc func themeChanged(_ notification: Notification) { DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { self.updateViewColor() } } func updatePDFView() { self.pdfView.needsDisplay = true } } extension KMHeaderFooterView { @IBAction func previousPage(_ sender: Any) { if pdfView.canGoToPreviousPage() { pdfView.goToPreviousPage(nil) } let index = pdfDocument?.index(for: pdfView.currentPage()) ?? 0 currentPageIndexTextField.stringValue = "\(index + 1)" } @IBAction func nextPage(_ sender: Any) { if pdfView.canGoToNextPage() { pdfView.goToNextPage(nil) } let index = pdfDocument?.index(for: pdfView.currentPage()) ?? 0 currentPageIndexTextField.stringValue = "\(index + 1)" } @IBAction func pageIndexTextFieldEndEdit(_ sender: NSTextField) { pdfView.go(to: pdfDocument?.page(at: UInt(sender.integerValue - 1))) } @IBAction func chooseColor(_ sender: NSColorWell) { if let color = sender.color.usingColorSpaceName(NSColorSpaceName.calibratedRGB) { headerFooterObj.textColor = KMWatermarkAdjectiveText.color(red: color.redComponent, green: color.greenComponent, blue: color.blueComponent, alpha: color.alphaComponent) self.updatePDFView() } } @IBAction func chooseFont(_ sender: NSComboBox) { headerFooterObj.textFont = KMWatermarkAdjectiveText.font(name: "Helvetica", size: sender.stringValue.stringToCGFloat()) self.updatePDFView() } @IBAction func topMarginTextFiedEndEdit(_ sender: NSTextField) { topMarginStepper.integerValue = sender.integerValue headerFooterObj.topMargin = sender.integerValue self.updatePDFView() } @IBAction func topMarginStepperAction(_ sender: NSStepper) { topMargintextField.integerValue = sender.integerValue headerFooterObj.topMargin = sender.integerValue self.updatePDFView() } @IBAction func bottomMarginTextFieldEndEdit(_ sender: NSTextField) { bottomMarginStepper.integerValue = sender.integerValue headerFooterObj.bottomMargin = sender.integerValue self.updatePDFView() } @IBAction func bottomMarginStepperAction(_ sender: NSStepper) { bottomMarginTextField.integerValue = sender.integerValue headerFooterObj.bottomMargin = sender.integerValue self.updatePDFView() } @IBAction func leftMarginTextFieldEndEdit(_ sender: NSTextField) { leftMarginStepper.integerValue = sender.integerValue headerFooterObj.leftMargin = sender.integerValue self.updatePDFView() } @IBAction func leftMarginStepperAction(_ sender: NSStepper) { leftMarginTextField.integerValue = sender.integerValue headerFooterObj.leftMargin = sender.integerValue self.updatePDFView() } @IBAction func rightMarginTextFieldEndEdit(_ sender: NSTextField) { rightMarginStepper.integerValue = sender.integerValue headerFooterObj.rightMargin = sender.integerValue self.updatePDFView() } @IBAction func rightMarginStepperAction(_ sender: NSStepper) { rightMarginTextField.integerValue = sender.integerValue headerFooterObj.rightMargin = sender.integerValue self.updatePDFView() } @IBAction func chooseDateFormate(_ sender: NSComboBox) { headerFooterObj.dateFormatString = sender.stringValue self.updatePDFView() } @IBAction func choosePageFormate(_ sender: NSComboBox) { headerFooterObj.pageFormatString = sender.stringValue self.updatePDFView() } @IBAction func chooseStartPage(_ sender: NSComboBox) { headerFooterObj.startString = sender.stringValue self.updatePDFView() } @IBAction func buttonClicked_AddHeaderFooter(_ sender: NSButton) { popMenu(sender: sender) } @IBAction func saveTemplateOrNot(_ sender: Any) { } @IBAction func templateNameTextFieldEndEdit(_ sender: NSTextField) { headerFooterObj.id = sender.stringValue } @IBAction func batch(_ sender: Any) { if saveToTemplateButton.state == .on { if templateNameTextField.stringValue.count < 1 { return } headerFooterObj.id = templateNameTextField.stringValue KMHeaderFooterManager.defaultManager.addHeaderFooter(headerFooterObj) } if let operateCallBack = operateCallBack { operateCallBack(headerFooterObj) } } @IBAction func cancel(_ sender: Any) { self.cancelAction?(self) } @IBAction func apply(_ sender: Any) { guard let pdfDocument = pdfDocument else { return } let needSave = saveToTemplateButton.state == .on var pages = [Int]() switch pageRangeCombobox.indexOfSelectedItem { case 0: pages = Array(0.. String { var newString = oldString for pageFormat in pageFormatArray { let formattedPage = "<<\(pageFormat)>>" if newString.contains(formattedPage) { var tString: String? switch formattedPage { case "<<1>>": tString = "<<\(startPage)>>" case "<<1 of n>>": tString = "<<\(startPage)>> of \(pageCount)" case "<<1/n>>": tString = "<<\(startPage)>>/\(pageCount)" case "<>": tString = "Page \(startPage)" case "<>": tString = "Page \(startPage) of \(pageCount)" default: break } if let tString = tString { newString = newString.replacingOccurrences(of: formattedPage, with: tString) } } } newString = convertDateFormat(newString) return newString } }