// // KMHeaderFooterManager.swift // PDF Master // // Created by tangchao on 2022/12/27. // import Cocoa let kHeaderFooterInfoSaveKey = "kHeaderFooterInfoSaveKey" class KMHeaderFooterObject: NSObject, NSCopying, NSCoding{ var type: KMBatchHeaderFooterType = .HeaderFooter var leftMargin: Int = 30 var rightMargin: Int = 30 var bottomMargin: Int = 30 var topMargin: Int = 30 var topLeftString: String = "" var topCenterString: String = "" var topRightString: String = "" var bottomLeftString: String = "" var bottomCenterString: String = "" var bottomRightString: String = "" var startString: String = "1" var fontSize: CGFloat = 16.0 var textColor: NSColor = .black var pagesString: String = "" var isBates: Bool = false var batesPrefixString: String = "" var batesSuffixString: String = "" var batesDigits: Int = 0 var hasHeader: Bool = false var hasFooter: Bool = false var cellHeight: CGFloat = 0.0{ willSet{ } didSet { if cellHeight < 0.1 { var baseHeight: CGFloat = 33.0 + 24.0 if self.topLeftString.count > 0 { baseHeight += 20.0 } if topCenterString.count > 0 { baseHeight += 20.0 } if topRightString.count > 0 { baseHeight += 20.0 } if bottomLeftString.count > 0 { baseHeight += 20.0 } if bottomCenterString.count > 0 { baseHeight += 20.0 } if bottomRightString.count > 0 { baseHeight += 20.0 } baseHeight -= 4.0 cellHeight = baseHeight } } } var headerFooterID: String = "" var pageChoice: KMBatchOperatePageChoice = .All var dateFormatString: String = "m/d" var pageFormatString: String = "1" override init() { super.init() self.topLeftString = "" self.topCenterString = "" self.topRightString = "" self.bottomLeftString = "" self.bottomCenterString = "" self.bottomRightString = "" self.startString = "" self.pagesString = "" self.batesPrefixString = "" self.batesSuffixString = "" self.headerFooterID = "" self.fontSize = 16.0 self.dateFormatString = "m/d" self.startString = "1" self.pageFormatString = "1" self.textColor = .black self.leftMargin = 30 self.rightMargin = 30 self.topMargin = 30 self.bottomMargin = 30 } func copy(with zone: NSZone? = nil) -> Any { var obj: KMHeaderFooterObject = KMHeaderFooterObject() obj.type = self.type obj.leftMargin = self.leftMargin obj.rightMargin = self.rightMargin obj.bottomMargin = self.bottomMargin obj.topMargin = self.topMargin obj.topLeftString = self.topLeftString obj.topCenterString = self.topCenterString obj.topRightString = self.topRightString obj.bottomLeftString = self.bottomLeftString obj.bottomCenterString = self.bottomCenterString obj.bottomRightString = self.bottomRightString obj.startString = self.startString obj.fontSize = self.fontSize obj.textColor = self.textColor obj.pagesString = self.pagesString obj.isBates = self.isBates obj.batesPrefixString = self.batesPrefixString obj.batesSuffixString = self.batesSuffixString obj.batesDigits = self.batesDigits obj.headerFooterID = self.headerFooterID obj.hasHeader = self.hasHeader obj.hasFooter = self.hasFooter obj.pageChoice = self.pageChoice obj.pageFormatString = self.pageFormatString obj.dateFormatString = self.dateFormatString return obj; } func encode(with coder: NSCoder) { coder.encode(self.type.rawValue, forKey: "type") coder.encode(self.leftMargin, forKey: "leftMargin") coder.encode(self.rightMargin, forKey: "rightMargin") coder.encode(self.bottomMargin, forKey: "bottomMargin") coder.encode(self.topMargin, forKey: "topMargin") coder.encode(self.topLeftString, forKey: "topLeftString") coder.encode(self.topCenterString, forKey: "topCenterString") coder.encode(self.topRightString, forKey: "topRightString") coder.encode(self.bottomLeftString, forKey: "bottomLeftString") coder.encode(self.bottomCenterString, forKey: "bottomCenterString") coder.encode(self.bottomRightString, forKey: "bottomRightString") coder.encode(self.startString, forKey: "startString") coder.encode(self.fontSize, forKey: "fontSize") coder.encode(self.textColor, forKey: "textColor") coder.encode(self.pagesString, forKey: "pagesString") coder.encode(self.isBates, forKey: "isBates") coder.encode(self.batesPrefixString, forKey: "batesPrefixString") coder.encode(self.batesSuffixString, forKey: "batesSuffixString") coder.encode(self.batesDigits, forKey: "batesDigits") coder.encode(self.hasHeader, forKey: "hasHeader") coder.encode(self.hasFooter, forKey: "hasFooter") coder.encode(self.cellHeight, forKey: "cellHeight") coder.encode(self.headerFooterID, forKey: "headerFooterID") coder.encode(self.pageChoice, forKey: "pageChoice") coder.encode(self.dateFormatString, forKey: "dateFormatString") coder.encode(self.pageFormatString, forKey: "pageFormatString") } required init?(coder: NSCoder) { type = KMBatchHeaderFooterType(rawValue: coder.decodeInteger(forKey: "type")) ?? .HeaderFooter leftMargin = coder.decodeInteger(forKey: "leftMargin") rightMargin = coder.decodeInteger(forKey: "rightMargin") bottomMargin = coder.decodeInteger(forKey: "bottomMargin") topMargin = coder.decodeInteger(forKey: "topMargin") topLeftString = coder.decodeObject(forKey: "topLeftString") as! String topCenterString = coder.decodeObject(forKey: "topCenterString") as! String topRightString = coder.decodeObject(forKey: "topRightString") as! String bottomLeftString = coder.decodeObject(forKey: "bottomLeftString") as! String bottomCenterString = coder.decodeObject(forKey: "bottomCenterString") as! String bottomRightString = coder.decodeObject(forKey: "bottomCenterString") as! String startString = coder.decodeObject(forKey: "startString") as! String fontSize = CGFloat(coder.decodeFloat(forKey: "fontSize")) textColor = coder.decodeObject(forKey: "textColor") as! NSColor pagesString = coder.decodeObject(forKey: "pagesString") as! String isBates = coder.decodeBool(forKey: "isBates") batesPrefixString = coder.decodeObject(forKey: "batesPrefixString") as! String batesSuffixString = coder.decodeObject(forKey: "batesSuffixString") as! String batesDigits = coder.decodeInteger(forKey: "batesDigits") hasHeader = coder.decodeBool(forKey: "hasHeader") hasFooter = coder.decodeBool(forKey: "hasFooter") cellHeight = CGFloat(coder.decodeFloat(forKey: "cellHeight")) headerFooterID = coder.decodeObject(forKey: "headerFooterID") as! String pageChoice = KMBatchOperatePageChoice(rawValue: coder.decodeInteger(forKey: "pageChoice")) ?? .All dateFormatString = coder.decodeObject(forKey: "dateFormatString") as! String pageFormatString = coder.decodeObject(forKey: "pageFormatString") as! String } func copyData(dataObj: KMHeaderFooterObject) { type = dataObj.type leftMargin = dataObj.leftMargin rightMargin = dataObj.rightMargin bottomMargin = dataObj.bottomMargin topMargin = dataObj.topMargin topLeftString = dataObj.topLeftString topCenterString = dataObj.topCenterString topRightString = dataObj.topRightString bottomLeftString = dataObj.bottomLeftString bottomCenterString = dataObj.bottomCenterString bottomRightString = dataObj.bottomRightString startString = dataObj.startString fontSize = dataObj.fontSize textColor = dataObj.textColor pagesString = dataObj.pagesString isBates = dataObj.isBates batesPrefixString = dataObj.batesPrefixString batesSuffixString = dataObj.batesSuffixString batesDigits = dataObj.batesDigits hasHeader = dataObj.hasHeader hasFooter = dataObj.hasFooter cellHeight = dataObj.cellHeight headerFooterID = dataObj.headerFooterID pageChoice = dataObj.pageChoice dateFormatString = dataObj.dateFormatString pageFormatString = dataObj.pageFormatString } } class KMHeaderFooterManager: NSObject, NSCoding{ let kFolderPath = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.applicationSupportDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).last?.stringByAppendingPathComponent(Bundle.main.bundleIdentifier!).stringByAppendingPathComponent("headerfooter") let kPlistPath = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.applicationSupportDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).last?.stringByAppendingPathComponent(Bundle.main.bundleIdentifier!).stringByAppendingPathComponent("headerfooter").stringByAppendingPathComponent("headerfooter.plist") static let defaultManager: KMHeaderFooterManager = { var manager = KMHeaderFooterManager() if let storedData = UserDefaults.standard.value(forKey: kHeaderFooterInfoSaveKey) as? Data { manager = NSKeyedUnarchiver.unarchiveObject(with: storedData) as! KMHeaderFooterManager } else { manager = KMHeaderFooterManager() if manager.headFooterObjects == nil { manager.headFooterObjects = [] } } return manager }() func encode(with coder: NSCoder) { coder.encode(self.headFooterObjects, forKey: "headFooterObjects") } required init?(coder: NSCoder) { self.headFooterObjects = coder.decodeObject(forKey: "headFooterObjects") as? [KMHeaderFooterObject] } var datas: Array = [] var headFooterObjects: [KMHeaderFooterObject]? lazy var dateFormatArray: Array = { let arr = [ "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" ] return arr }() lazy var onlyBatesObjects: [KMHeaderFooterObject] = { var arr: [KMHeaderFooterObject] = Array() for i in 0..<(headFooterObjects?.count ?? 0) { let obj = headFooterObjects?[i] if KMDataVersionManager.updateBatesData() { KMDataVersionManager.refrshBatesData(bates: obj) store() } if obj!.isBates { arr.append(obj!) } } return arr }() lazy var onlyHeaderFooterObjects: [KMHeaderFooterObject] = { var arr: [KMHeaderFooterObject] = Array() for i in 0..<(headFooterObjects?.count ?? 0) { let obj = headFooterObjects?[i] if !obj!.isBates { arr.append(obj!) } } return arr }() override init() { super.init() if (FileManager.default.fileExists(atPath: kPlistPath!)) { let dataDict = NSDictionary(contentsOfFile: kPlistPath!) if (dataDict == nil) { return } for keyIndex in 0 ..< (dataDict?.allKeys.count)! { let key: String = dataDict?.allKeys[keyIndex] as! String let backgroundDict: NSDictionary = dataDict?.object(forKey: key) as! NSDictionary let model = parseDictionary(dict: backgroundDict) /// 赋值id model.id = key self.datas.append(model) } /// 根据id进行排序(升序) self.datas.sort(){$0.id > $1.id} } } func store() { let encodedObject = NSKeyedArchiver.archivedData(withRootObject: self) let defaults = UserDefaults.standard defaults.set(encodedObject, forKey: kHeaderFooterInfoSaveKey) defaults.synchronize() } func fetchBatesAvailableName() -> String { var availableIndex = 0 let nameArray = converArrType(arr: onlyBatesObjects, keyString: "headerFooterID") for i in 0..= availableIndex { availableIndex = index + 1 } } } return String(format: "Bates%ld", availableIndex) } func fetchHeaderFooterAvailableName() -> String { var availableIndex = 0 let nameArray = converArrType(arr: onlyHeaderFooterObjects, keyString: "headerFooterID") for i in 0..= availableIndex { availableIndex = index + 1 } } } return String(format: "HeaderFooter%ld", availableIndex) } func converArrType(arr: Array, keyString: String) -> [String] { var newArr = NSMutableArray() for item in arr { newArr.add(item.headerFooterID) } return newArr as! [String] } func addTemplate(_ model: KMHeaderFooterModel) -> Bool { if (!FileManager.default.fileExists(atPath: kFolderPath!)) { let create: ()? = try?FileManager.default.createDirectory(atPath: kFolderPath!, withIntermediateDirectories: false) if (create == nil) { return false } } if (!FileManager.default.fileExists(atPath: kPlistPath!)) { let create = try?FileManager.default.createFile(atPath: kPlistPath!, contents: nil) if (create == nil) { return false } } let dict = NSDictionary(contentsOfFile: kPlistPath!) var newDict:NSMutableDictionary! if (dict != nil) { newDict = NSMutableDictionary(dictionary: dict!) } else { newDict = NSMutableDictionary() } let modelDict = self.parseModel(model: model) let tag = tagString() newDict.addEntries(from: [tag : modelDict]) model.id = tag let result = newDict.write(toFile: kPlistPath!, atomically: true) if (result) { if (self.datas.count < 1) { self.datas.append(model) } else { self.datas.insert(model, at: 0) } } return result } func deleteTemplate(_ model: KMHeaderFooterModel) -> Bool { if (model.id.isEmpty) { return false } if (!FileManager.default.fileExists(atPath: kPlistPath!)) { return false } let key: String = model.id let dictionary = NSDictionary(contentsOfFile: kPlistPath!) var newDictionary: NSMutableDictionary! if (dictionary != nil) { newDictionary = NSMutableDictionary(dictionary: dictionary!) } else { newDictionary = NSMutableDictionary() } newDictionary.removeObject(forKey: key) let result = newDictionary.write(toFile: kPlistPath!, atomically: true) if (result) { if (self.datas.contains(model)) { self.datas.removeObject(model) } } return result } func deleteAllTemplate() -> Bool { if (!FileManager.default.fileExists(atPath: kPlistPath!)) { return false } let dictionary = NSDictionary(contentsOfFile: kPlistPath!) var newDictionary: NSMutableDictionary! if (dictionary != nil) { newDictionary = NSMutableDictionary(dictionary: dictionary!) } else { newDictionary = NSMutableDictionary() } newDictionary.removeAllObjects() let result = newDictionary.write(toFile: kPlistPath!, atomically: true) if (result) { self.datas.removeAll() } return result } func updateTemplate(_ model: KMHeaderFooterModel) -> Bool { if (!FileManager.default.fileExists(atPath: kFolderPath!)) { let create = try?FileManager.default.createDirectory(atPath: kFolderPath!, withIntermediateDirectories: false) if (create == nil) { return false } } if (!FileManager.default.fileExists(atPath: kPlistPath!)) { let create = try?FileManager.default.createFile(atPath: kPlistPath!, contents: nil) if (create == nil) { return false } } var flagModel: KMHeaderFooterModel! for model_ in self.datas { if (model_.id == model.id) { flagModel = model_ break } } if (flagModel == nil) { return false } let dict = NSDictionary(contentsOfFile: kPlistPath!) var newDict:NSMutableDictionary! if (dict != nil) { newDict = NSMutableDictionary(dictionary: dict!) } else { newDict = NSMutableDictionary() } let modelDict = self.parseModel(model: model) newDict.setObject(modelDict, forKey: flagModel.id as NSCopying) let result = newDict.write(toFile: kPlistPath!, atomically: true) if (result) { let index = self.datas.index(of: flagModel) self.datas[index!] = model } return result } func removeHeaderFooter(_ obj: KMHeaderFooterObject) { self.headFooterObjects?.removeObject(obj) self.store() } /** `Private Methods` */ private func parseModel(model: KMHeaderFooterModel) -> Dictionary { var dict: [String : Any] = [:] /// 字体相关 switch model.textFont { case .font(name: var name, size: var size): dict["fontName"] = name dict["fontSize"] = size default: break } switch model.textColor { case .color(red: var red, green: var green, blue: var blue, alpha: var alpha): dict["red"] = red dict["green"] = green dict["blue"] = blue dict["alpha"] = alpha default: break } /// 页边距 dict["leftMargin"] = model.leftMargin dict["rightMargin"] = model.rightMargin dict["bottomMargin"] = model.bottomMargin dict["topMargin"] = model.topMargin /// 内容 dict["topLeftString"] = model.topLeftString dict["topCenterString"] = model.topCenterString dict["topRightString"] = model.topRightString dict["bottomLeftString"] = model.bottomLeftString dict["bottomCenterString"] = model.bottomCenterString dict["bottomRightString"] = model.bottomRightString /// 日期 dict["dateFormatString"] = model.dateFormatString /// 页面 dict["pageRangeString"] = model.pageRangeString dict["startString"] = model.startString /// 页面范围 dict["pageRangeType"] = model.pageRangeType dict["pageRangeString"] = model.pageRangeString return dict } private func parseDictionary(dict: NSDictionary) -> KMHeaderFooterModel { let model = KMHeaderFooterModel() /// 字体相关 model.textFont = .font(name: dict["fontName"] as! String, size: dict["fontSize"] as! CGFloat) model.textColor = .color(red: dict["red"] as! CGFloat, green: dict["green"] as! CGFloat, blue: dict["blue"] as! CGFloat, alpha: dict["alpha"] as! CGFloat) /// 页边距 model.leftMargin = dict["leftMargin"] as! CGFloat model.rightMargin = dict["rightMargin"] as! CGFloat model.bottomMargin = dict["bottomMargin"] as! CGFloat model.topMargin = dict["topMargin"] as! CGFloat /// 内容 model.topLeftString = dict["topLeftString"] as! String model.topCenterString = dict["topCenterString"] as! String model.topRightString = dict["topRightString"] as! String model.bottomLeftString = dict["bottomLeftString"] as! String model.bottomCenterString = dict["bottomCenterString"] as! String model.bottomRightString = dict["bottomRightString"] as! String /// 日期 model.dateFormatString = dict["dateFormatString"] as! String /// 页面 model.pageRangeString = dict["pageRangeString"] as! String model.startString = dict["startString"] as! String /// 页面范围 model.pageRangeType = dict["pageRangeType"] as! Int model.pageRangeString = dict["pageRangeString"] as! String return model } private func tagString() -> String { var result: String = "" let dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyMMddHHmmss" result.append(dateFormatter.string(from: Date())) result = result.appendingFormat("%04d", arc4random()%10000) return result } }