KMBatchAddHeaderFooterOperation.swift 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. //
  2. // KMBatchAddHeaderFooterOperation.swift
  3. // PDF Master
  4. //
  5. // Created by liujiajie on 2023/11/7.
  6. //
  7. import Foundation
  8. let supportDirectory = NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).last
  9. let mainBundleIdentifier = Bundle.main.bundleIdentifier ?? ""
  10. let kTempSavePath = supportDirectory?.stringByAppendingPathComponent(mainBundleIdentifier)
  11. class KMBatchAddHeaderFooterOperation: KMBatchOperation{
  12. var headerFooter: KMHeaderFooterObject?
  13. var pdfDocument: CPDFDocument?
  14. var password: String?
  15. init(file: KMBatchOperateFile, headerFooter: KMHeaderFooterObject) {
  16. super.init(file: file)
  17. self.headerFooter = headerFooter
  18. self.pdfDocument = CPDFDocument(url: URL(fileURLWithPath: file.filePath))
  19. self.password = file.password
  20. if ((self.pdfDocument?.isLocked) != nil) {
  21. self.pdfDocument?.unlock(withPassword: file.password)
  22. }
  23. }
  24. func currentParameter() -> KMBatchBaseParameter {
  25. if ((headerFooter?.isBates) != nil) {
  26. return operateFile!.addBatesInfo
  27. }
  28. return operateFile!.addHeaderFooterInfo
  29. }
  30. override func start() {
  31. if !self.isCancelled {
  32. self.delegate?.fileBeginOperate?(self.operateFile!, info: self.currentParameter())
  33. willChangeValue(forKey: "isExecuting")
  34. self.hasExcuting = true
  35. didChangeValue(forKey: "isExecuting")
  36. if !FileManager.default.fileExists(atPath: self.operateFile!.filePath) {
  37. self.delegate?.fileOperateFailed?(self.operateFile!, error: self.errorWithMsg(KMLocalizedString("File Not Exist", nil)), info: self.operateFile!.removeBatesInfo)
  38. self.willChangeValue(forKey: "isFinished")
  39. self.hasFinished = true
  40. self.didChangeValue(forKey: "isFinished")
  41. return
  42. }
  43. if self.pdfDocument == nil {
  44. 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)
  45. self.willChangeValue(forKey: "isFinished")
  46. self.hasFinished = true
  47. self.didChangeValue(forKey: "isFinished")
  48. return
  49. }
  50. if !self.pdfDocument!.allowsPrinting || !self.pdfDocument!.allowsCopying {
  51. self.delegate?.fileOperateFailed?(self.operateFile!, error: self.errorWithMsg(KMLocalizedString("This is a secured document. Editing is not permitted.", nil)), info: self.operateFile!.removeWatermarkInfo)
  52. self.willChangeValue(forKey: "isFinished")
  53. self.hasFinished = true
  54. self.didChangeValue(forKey: "isFinished")
  55. return
  56. }
  57. self.saveAsPDFToPath((self.operateFile?.currentOperateInfo?.fetchDestinationFilepath())!)
  58. }else {
  59. willChangeValue(forKey: "isFinished")
  60. willChangeValue(forKey: "isExecuting")
  61. hasExcuting = false
  62. hasFinished = true
  63. didChangeValue(forKey: "isExecuting")
  64. didChangeValue(forKey: "isFinished")
  65. }
  66. }
  67. override func cancel() {
  68. // super.cancel()
  69. if isExecuting {
  70. operateFile!.removeWatermarkInfo.status = .Waiting
  71. if FileManager.default.fileExists(atPath: self.currentParameter().outPutPath!) { try? FileManager.default.removeItem(atPath: self.currentParameter().outPutPath!)
  72. }
  73. self.delegate?.fileOperateCanceled?(self.operateFile!, info: self.currentParameter())
  74. willChangeValue(forKey: "isFinished")
  75. hasFinished = true
  76. didChangeValue(forKey: "isFinished")
  77. } else {
  78. willChangeValue(forKey: "isCancelled")
  79. hasCanceled = true
  80. didChangeValue(forKey: "isCancelled")
  81. }
  82. }
  83. func saveAsPDFToPath(_ path: String) {
  84. var filePath = self.pdfDocument?.documentURL?.path
  85. let password = self.password
  86. if filePath == nil {
  87. var str = String(format: "%@.pdf", KMLocalizedString("Untitled", nil))
  88. let writeSuccess = self.pdfDocument!.write(to: URL(fileURLWithPath: (kTempSavePath?.stringByAppendingPathComponent(str))!))
  89. if writeSuccess {
  90. let newDocument: CPDFDocument = CPDFDocument(url: URL(fileURLWithPath: (kTempSavePath?.stringByAppendingPathComponent(str))!))
  91. if #available(macOS 13.0, *) {
  92. filePath = newDocument.documentURL?.path()
  93. } else {
  94. // Fallback on earlier versions
  95. }
  96. } else {
  97. NSSound.beep()
  98. return
  99. }
  100. }
  101. guard let document = CPDFDocument(url: URL(fileURLWithPath: filePath!)) else {
  102. return
  103. }
  104. if let password = password {
  105. document.unlock(withPassword: password)
  106. }
  107. let font = NSFont.boldSystemFont(ofSize: self.headerFooter?.fontSize ?? 16.0)
  108. let style = NSMutableParagraphStyle()
  109. style.alignment = .center
  110. style.lineBreakMode = .byCharWrapping
  111. var dictionary = [NSAttributedString.Key: Any]()
  112. dictionary[.paragraphStyle] = style
  113. dictionary[.font] = font
  114. let size = "text".boundingRect(with: CGSize(width: CGFloat(MAXFLOAT), height: CGFloat(MAXFLOAT)), options: [.usesLineFragmentOrigin, .usesFontLeading], attributes: dictionary).size
  115. if ((self.headerFooter?.isBates) != nil) {
  116. var bates: CPDFHeaderFooter = document.bates()
  117. 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))
  118. var arr = [NSNumber]()
  119. for i in 0..<(self.operateFile?.addBatesInfo.pagesArray?.count ?? 0) {
  120. var tmp = self.operateFile?.addBatesInfo.pagesArray?[i].intValue
  121. tmp! -= 1
  122. let number = NSNumber(value: tmp ?? 0)
  123. arr.append(number)
  124. }
  125. if arr.count < 1 {
  126. 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)])
  127. self.delegate?.fileOperateFailed?(self.operateFile!, error: error, info: self.operateFile!.addBatesInfo)
  128. willChangeValue(forKey: "isFinished")
  129. self.hasFinished = true
  130. didChangeValue(forKey: "isFinished")
  131. return
  132. }
  133. let pagesString = arr.map{ "\($0)" }.joined(separator: ",")
  134. if pagesString.count > 0 {
  135. bates.pageString = pagesString
  136. } else {
  137. let pageString = String(format: "0-%ld", document.pageCount-1)
  138. bates.pageString = pageString
  139. }
  140. let topLeftString = self.headerFooter?.topLeftString
  141. let topCenterString = self.headerFooter?.topCenterString
  142. let topRightString = self.headerFooter?.topRightString
  143. let bottomLeftString = self.headerFooter?.bottomLeftString
  144. let bottomCenterString = self.headerFooter?.bottomCenterString
  145. let bottomRightString = self.headerFooter?.bottomRightString
  146. let items = [topLeftString, topCenterString, topRightString, bottomLeftString, bottomCenterString, bottomRightString]
  147. for i in 0..<items.count {
  148. let text = items[i]
  149. bates.setText(text, at: UInt(Int(i)))
  150. bates.setTextColor(self.headerFooter?.textColor, at: UInt(Int(i)))
  151. bates.setFontSize(self.headerFooter?.fontSize ?? 16.0, at: UInt(Int(i)))
  152. }
  153. bates.update()
  154. } else {
  155. let headerFooterNew = document.headerFooter()
  156. headerFooterNew?.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))
  157. var arr = [NSNumber]()
  158. for i in 0..<(self.operateFile?.addHeaderFooterInfo.pagesArray?.count ?? 0) {
  159. var tmp = self.operateFile?.addHeaderFooterInfo.pagesArray?[i].intValue
  160. tmp! -= 1
  161. let number = NSNumber(value: tmp ?? 0)
  162. arr.append(number)
  163. }
  164. if arr.count < 1 {
  165. 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)])
  166. self.delegate?.fileOperateFailed?(self.operateFile!, error: error, info: self.operateFile!.addHeaderFooterInfo)
  167. willChangeValue(forKey: "isFinished")
  168. self.hasFinished = true
  169. didChangeValue(forKey: "isFinished")
  170. return
  171. }
  172. let pagesString = arr.map{ "\($0)" }.joined(separator: ",")
  173. if pagesString.count > 0 {
  174. headerFooterNew?.pageString = pagesString
  175. } else {
  176. let pageString = String(format: "0-%ld", document.pageCount-1)
  177. headerFooterNew?.pageString = pageString
  178. }
  179. let num: Int = Int(self.headerFooter?.startString ?? "0") ?? 0
  180. let pageCount = Int(document.pageCount) + num - 1
  181. let topLeftString = convertPageFormat(oldString: self.headerFooter!.topLeftString, startPage: self.headerFooter!.startString,pageCount: "\(pageCount)")
  182. let topCenterString = convertPageFormat(oldString: self.headerFooter!.topCenterString, startPage: self.headerFooter!.startString,pageCount: "\(pageCount)")
  183. let topRightString = convertPageFormat(oldString: self.headerFooter!.topRightString, startPage: self.headerFooter!.startString,pageCount: "\(pageCount)")
  184. let bottomLeftString = convertPageFormat(oldString: self.headerFooter!.bottomLeftString, startPage: self.headerFooter!.startString, pageCount: "\(pageCount)")
  185. let bottomCenterString = convertPageFormat(oldString: self.headerFooter!.bottomCenterString, startPage: self.headerFooter!.startString,pageCount: "\(pageCount)")
  186. let bottomRightString = convertPageFormat(oldString: self.headerFooter!.bottomRightString, startPage: self.headerFooter!.startString,pageCount: "\(pageCount)")
  187. let items = [topLeftString, topCenterString, topRightString, bottomLeftString, bottomCenterString, bottomRightString]
  188. for i in 0..<items.count {
  189. let text = items[i]
  190. headerFooterNew?.setText(text, at: UInt(i))
  191. headerFooterNew?.setTextColor(self.headerFooter!.textColor, at: UInt(Int(i)))
  192. headerFooterNew?.setFontSize(self.headerFooter!.fontSize, at: UInt(Int(i)))
  193. }
  194. headerFooterNew?.update()
  195. }
  196. let documentPath = NSTemporaryDirectory()
  197. let tempPath = documentPath.appending(filePath!.lastPathComponent)
  198. if FileManager.default.fileExists(atPath: tempPath) {
  199. try? FileManager.default.removeItem(atPath: tempPath)
  200. }
  201. let result = document.write(to: URL(fileURLWithPath: tempPath))
  202. if result {
  203. if FileManager.default.fileExists(atPath: path) {
  204. try? FileManager.default.removeItem(atPath: path)
  205. }
  206. try? FileManager.default.moveItem(atPath: tempPath, toPath: path)
  207. } else {
  208. try? FileManager.default.removeItem(atPath: tempPath)
  209. }
  210. if result {
  211. self.delegate?.fileOperateSuccessed?(self.operateFile!, info: self.currentParameter())
  212. } else {
  213. self.delegate?.fileOperateFailed?(self.operateFile!, error: self.defaultError(), info: self.currentParameter())
  214. }
  215. willChangeValue(forKey: "isFinished")
  216. self.hasFinished = true
  217. didChangeValue(forKey: "isFinished")
  218. }
  219. func defaultError() -> NSError {
  220. return errorWithMsg(NSLocalizedString("Failed", comment: ""))
  221. }
  222. }
  223. func convertPageFormat(oldString: String, startPage: String, pageCount: String) -> String {
  224. let pageFormatArray = ["1", "1 of n", "1/n", "Page 1", "Page 1 of n"]
  225. var newString = oldString
  226. for pageFormat in pageFormatArray {
  227. let format = "<<(pageFormat)>>"
  228. if newString.contains(format) {
  229. var tString: String? = nil
  230. if pageFormat == "1" {
  231. tString = "<<\(startPage)>>"
  232. } else if pageFormat == "1 of n" {
  233. tString = "\(startPage) of \(pageCount)"
  234. } else if pageFormat == "1/n" {
  235. tString = "\(startPage)/\(pageCount)"
  236. } else if pageFormat == "Page 1" {
  237. tString = "Page <<\(startPage)>>"
  238. } else if pageFormat == "Page 1 of n" {
  239. tString = "Page <<\(startPage)>> of \(pageCount)"
  240. }
  241. newString = newString.replacingOccurrences(of: format, with: tString ?? "")
  242. }
  243. }
  244. newString = convertDateFormat(oldString: newString)
  245. return newString
  246. }
  247. func convertDateFormat(oldString: String) -> String {
  248. var newString = oldString
  249. for dateFormat in KMHeaderFooterManager.defaultManager.dateFormatArray {
  250. if newString.contains(dateFormat) {
  251. let formatString = dateFormat.replacingOccurrences(of: "m", with: "M")
  252. let replace = "<<\(dateFormat)>>"
  253. let date = Date()
  254. let dateFormatter = DateFormatter()
  255. dateFormatter.dateFormat = formatString
  256. let dateString = dateFormatter.string(from: date)
  257. newString = newString.replacingOccurrences(of: replace, with: dateString)
  258. }
  259. }
  260. return newString
  261. }