// // KMBatchAddHeaderFooterOperation.swift // PDF Reader Pro // // Created by liujiajie on 2023/11/7. // import Foundation let supportDirectory = NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).last let mainBundleIdentifier = Bundle.main.bundleIdentifier ?? "" let kTempSavePath = supportDirectory?.stringByAppendingPathComponent(mainBundleIdentifier) class KMBatchAddHeaderFooterOperation: KMBatchOperation{ var headerFooter: KMHeaderFooterObject? var pdfDocument: CPDFDocument? var password: String? init(file: KMBatchOperateFile, headerFooter: KMHeaderFooterObject) { super.init(file: file) self.headerFooter = headerFooter } func currentParameter() -> KMBatchBaseParameter { if headerFooter!.isBates{ return operateFile!.addBatesInfo } return operateFile!.addHeaderFooterInfo } override func start() { self.pdfDocument = CPDFDocument(url: URL(fileURLWithPath: self.operateFile?.filePath ?? "")) self.password = self.operateFile?.password if let data = self.pdfDocument?.isLocked, data { self.pdfDocument?.unlock(withPassword: self.operateFile?.password) } if !self.isCancelled { self.delegate?.fileBeginOperate?(self.operateFile!, info: self.currentParameter()) willChangeValue(forKey: "isExecuting") self.hasExcuting = true didChangeValue(forKey: "isExecuting") if !FileManager.default.fileExists(atPath: self.operateFile!.filePath) { self.delegate?.fileOperateFailed?(self.operateFile!, error: self.errorWithMsg(KMLocalizedString("File Not Exist", nil)), info: self.operateFile!.removeBatesInfo) self.willChangeValue(forKey: "isFinished") self.hasFinished = true self.didChangeValue(forKey: "isFinished") return } if self.pdfDocument == nil { self.delegate?.fileOperateFailed?(self.operateFile!, error: self.errorWithMsg(KMLocalizedString("An error occurred while opening this document. The file is damaged and could not be repaired.", nil)), info: self.operateFile!.removeWatermarkInfo) self.willChangeValue(forKey: "isFinished") self.hasFinished = true self.didChangeValue(forKey: "isFinished") return } if !self.pdfDocument!.allowsPrinting || !self.pdfDocument!.allowsCopying { self.delegate?.fileOperateFailed?(self.operateFile!, error: self.errorWithMsg(KMLocalizedString("This is a secured document. Editing is not permitted.", nil)), info: self.operateFile!.removeWatermarkInfo) self.willChangeValue(forKey: "isFinished") self.hasFinished = true self.didChangeValue(forKey: "isFinished") return } self.saveAsPDFToPath((self.operateFile?.currentOperateInfo?.fetchDestinationFilepath())!) }else { willChangeValue(forKey: "isFinished") willChangeValue(forKey: "isExecuting") hasExcuting = false hasFinished = true didChangeValue(forKey: "isExecuting") didChangeValue(forKey: "isFinished") } } override func cancel() { // super.cancel() if isExecuting { operateFile!.removeWatermarkInfo.status = .Waiting if FileManager.default.fileExists(atPath: self.currentParameter().outPutPath!) { try? FileManager.default.removeItem(atPath: self.currentParameter().outPutPath!) } self.delegate?.fileOperateCanceled?(self.operateFile!, info: self.currentParameter()) willChangeValue(forKey: "isFinished") hasFinished = true didChangeValue(forKey: "isFinished") } else { willChangeValue(forKey: "isCancelled") hasCanceled = true didChangeValue(forKey: "isCancelled") } } func saveAsPDFToPath(_ path: String) { var filePath = self.pdfDocument?.documentURL?.path let password = self.password if filePath == nil { let str = String(format: "%@.pdf", KMLocalizedString("Untitled", nil)) let writeSuccess = self.pdfDocument!.write(to: URL(fileURLWithPath: (kTempSavePath?.stringByAppendingPathComponent(str))!)) if writeSuccess { let newDocument: CPDFDocument = CPDFDocument(url: URL(fileURLWithPath: (kTempSavePath?.stringByAppendingPathComponent(str))!)) filePath = newDocument.documentURL?.path } else { NSSound.beep() return } } guard let document = CPDFDocument(url: URL(fileURLWithPath: filePath!)) else { return } if password?.count ?? 0 > 0 { document.unlock(withPassword: password) } let font = NSFont.boldSystemFont(ofSize: self.headerFooter?.getTextFontSize() ?? 16) let style = NSMutableParagraphStyle() style.alignment = .center style.lineBreakMode = .byCharWrapping var dictionary = [NSAttributedString.Key: Any]() dictionary[.paragraphStyle] = style dictionary[.font] = font let size = "text".boundingRect(with: CGSize(width: CGFloat(MAXFLOAT), height: CGFloat(MAXFLOAT)), options: [.usesLineFragmentOrigin, .usesFontLeading], attributes: dictionary).size if self.headerFooter!.isBates { var bates: CPDFHeaderFooter = document.bates() bates.margin = NSEdgeInsets(top: max(CGFloat(self.headerFooter!.topMargin) - size.height, 0), left: CGFloat(self.headerFooter!.leftMargin), bottom: max(CGFloat(self.headerFooter!.bottomMargin) - size.height, 0), right: CGFloat(self.headerFooter!.rightMargin)) var arr = [NSNumber]() for i in 0..<(self.operateFile?.addBatesInfo.pagesArray?.count ?? 0) { var tmp = self.operateFile?.addBatesInfo.pagesArray?[i].intValue tmp! -= 1 let number = NSNumber(value: tmp ?? 0) arr.append(number) } if arr.count < 1 { let error = NSError(domain: "LocalError", code: 0, userInfo: [NSLocalizedDescriptionKey: KMLocalizedString("Invalid page range or the page number is out of range. Please try again.", nil)]) self.delegate?.fileOperateFailed?(self.operateFile!, error: error, info: self.operateFile!.addBatesInfo) willChangeValue(forKey: "isFinished") self.hasFinished = true didChangeValue(forKey: "isFinished") return } let pagesString = arr.map{ "\($0)" }.joined(separator: ",") if pagesString.count > 0 { bates.pageString = pagesString } else { let pageString = String(format: "0-%ld", document.pageCount-1) bates.pageString = pageString } let topLeftString = self.headerFooter?.topLeftString let topCenterString = self.headerFooter?.topCenterString let topRightString = self.headerFooter?.topRightString let bottomLeftString = self.headerFooter?.bottomLeftString let bottomCenterString = self.headerFooter?.bottomCenterString let bottomRightString = self.headerFooter?.bottomRightString let items = [topLeftString, topCenterString, topRightString, bottomLeftString, bottomCenterString, bottomRightString] for i in 0.. 0 { headerFooterNew?.pageString = pagesString } else { let pageString = String(format: "0-%ld", document.pageCount-1) headerFooterNew?.pageString = pageString } let num: Int = Int(self.headerFooter?.startString ?? "0") ?? 0 let pageCount = Int(document.pageCount) + num - 1 let topLeftString = convertPageFormat(oldString: self.headerFooter!.topLeftString, startPage: self.headerFooter!.startString,pageCount: "\(pageCount)") let topCenterString = convertPageFormat(oldString: self.headerFooter!.topCenterString, startPage: self.headerFooter!.startString,pageCount: "\(pageCount)") let topRightString = convertPageFormat(oldString: self.headerFooter!.topRightString, startPage: self.headerFooter!.startString,pageCount: "\(pageCount)") let bottomLeftString = convertPageFormat(oldString: self.headerFooter!.bottomLeftString, startPage: self.headerFooter!.startString, pageCount: "\(pageCount)") let bottomCenterString = convertPageFormat(oldString: self.headerFooter!.bottomCenterString, startPage: self.headerFooter!.startString,pageCount: "\(pageCount)") let bottomRightString = convertPageFormat(oldString: self.headerFooter!.bottomRightString, startPage: self.headerFooter!.startString,pageCount: "\(pageCount)") let items = [topLeftString, topCenterString, topRightString, bottomLeftString, bottomCenterString, bottomRightString] for i in 0.. NSError { return errorWithMsg(NSLocalizedString("Failed", comment: "")) } } func convertPageFormat(oldString: String, startPage: String, pageCount: String) -> String { let pageFormatArray = ["1", "1 of n", "1/n", "Page 1", "Page 1 of n"] var newString = oldString for pageFormat in pageFormatArray { let format = "<<(pageFormat)>>" if newString.contains(format) { var tString: String? = nil if pageFormat == "1" { tString = "<<\(startPage)>>" } else if pageFormat == "1 of n" { tString = "\(startPage) of \(pageCount)" } else if pageFormat == "1/n" { tString = "\(startPage)/\(pageCount)" } else if pageFormat == "Page 1" { tString = "Page <<\(startPage)>>" } else if pageFormat == "Page 1 of n" { tString = "Page <<\(startPage)>> of \(pageCount)" } newString = newString.replacingOccurrences(of: format, with: tString ?? "") } } newString = convertDateFormat(oldString: newString) return newString } func convertDateFormat(oldString: String) -> String { var newString = oldString for dateFormat in KMHeaderFooterManager.defaultManager.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 }