KMImageToPDFManager.swift 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. //
  2. // KMImageToPDFManager.swift
  3. // PDF Master
  4. //
  5. // Created by lizhe on 2022/12/6.
  6. //
  7. import Cocoa
  8. import ComPDFKit
  9. typealias KMImageToPDFManagerCompletion = (_ success: Bool, _ savePath: String?, _ errors: Array<String>?, _ OCRerrors: Array<String>?) -> Void
  10. typealias KMImageToPDFManagerProgress = (_ status: KMImageToPDFStatus) -> Void
  11. enum KMImageToPDFStatus: String {
  12. case unknow
  13. case prepareConversion
  14. case conversioning
  15. }
  16. class KMImageToPDFManager: NSObject {
  17. //回调
  18. var completion: KMImageToPDFManagerCompletion? = nil
  19. var progress: KMImageToPDFManagerProgress?
  20. /**
  21. 参数
  22. */
  23. var status: KMImageToPDFStatus = .unknow //当前manager状态
  24. var model: KMImageToPDFChooseModel? //内部数据
  25. var conversionIndex: Int? //转换下标
  26. //懒加载数组
  27. lazy var errors: Array<String> = []
  28. lazy var OCRerrors: Array<String> = []
  29. //单例
  30. public static let manager = KMImageToPDFManager()
  31. //MARK: public
  32. /**
  33. 导出PDF
  34. */
  35. func exportPDF(model: KMImageToPDFChooseModel, completion: KMImageToPDFManagerCompletion?, progress: @escaping KMImageToPDFManagerProgress) {
  36. if status == .unknow {
  37. self.progress = progress
  38. //变量
  39. self.model = model
  40. self.completion = completion
  41. //移除缓存数据
  42. self.errors.removeAll()
  43. self.OCRerrors.removeAll()
  44. //基本参数
  45. // let exportFilePath = self.model?.exportFilePath
  46. // let password = self.model?.password
  47. let options = self.model?.options!
  48. let exportPDFType = self.model?.exportPDFType
  49. if (options!.contains(.OCR)) {
  50. //TODO: OCR部分待SDK提供
  51. print("OCR 暂未实现")
  52. } else if (options!.contains(.PDF)) {
  53. if (exportPDFType == .new) {
  54. self.creatPDF(model: self.model!, completion: completion)
  55. } else if (exportPDFType == .merge) {
  56. self.mergePDF(model: self.model!, completion: completion)
  57. } else if (exportPDFType == .insert) {
  58. self.mergePDF(model: self.model!, completion: completion)
  59. }
  60. } else {
  61. if completion != nil {
  62. completion!(false, model.exportFilePath, model.filePaths(), nil)
  63. }
  64. }
  65. } else {
  66. if completion != nil {
  67. completion!(false, model.exportFilePath, model.filePaths(), nil)
  68. }
  69. }
  70. }
  71. func cancel () {
  72. }
  73. //MARK: private
  74. /**
  75. 创建PDF
  76. */
  77. private func creatPDF(model: KMImageToPDFChooseModel, completion: KMImageToPDFManagerCompletion?) {
  78. if status == .unknow {
  79. status = .conversioning
  80. let savePath = model.exportFilePath
  81. for imageFilePath in model.imageFilePaths! {
  82. let success = creatPDFConversion(filePath: imageFilePath.filePath, savePath: savePath!)
  83. print(success)
  84. if success == false {
  85. imageFilePath.state = .error
  86. self.errors.append(imageFilePath.filePath)
  87. } else {
  88. imageFilePath.state = .success
  89. }
  90. if (self.progress != nil) {
  91. self.progress!(self.status)
  92. }
  93. }
  94. status = .unknow
  95. if (self.progress != nil) {
  96. self.progress!(self.status)
  97. }
  98. if completion != nil {
  99. completion!(true, model.exportFilePath, self.errors, nil)
  100. }
  101. } else {
  102. if completion != nil {
  103. completion!(false, model.exportFilePath, model.filePaths(), nil)
  104. }
  105. }
  106. }
  107. private func creatPDFConversion(filePath: String, savePath: String) -> Bool {
  108. let imageName = NSString(string: NSString(string: filePath).lastPathComponent).deletingPathExtension
  109. let path = self.fetchDifferentFilePath(filePath: savePath + "/" + imageName + ".pdf")
  110. let document = CPDFDocument.init()
  111. var success = false
  112. // //系统
  113. // let document = PDFDocument.init()
  114. // let pdfPage = PDFPage.init(image: NSImage(contentsOfFile: filePath)!)
  115. // document.insert(pdfPage!, at: 0)
  116. // success = document.write(toFile: path)
  117. //FIXME: 无法插入图片
  118. let image = NSImage(contentsOfFile: filePath)
  119. let insertPageSuccess = document?.insertPage(image!.size, withImage: filePath, at: document!.pageCount)
  120. if insertPageSuccess != nil {
  121. print("插入成功")
  122. //信号量控制异步
  123. let semaphore = DispatchSemaphore(value: 0)
  124. DispatchQueue.global().async {
  125. success = ((document?.write(toFile: path)) != nil)
  126. semaphore.signal()
  127. }
  128. semaphore.wait()
  129. } else {
  130. print("插入失败")
  131. }
  132. return success
  133. }
  134. /**
  135. 合并PDF
  136. */
  137. private func mergePDF(model: KMImageToPDFChooseModel, completion: KMImageToPDFManagerCompletion?) {
  138. if status == .unknow {
  139. status = .conversioning
  140. //基本参数
  141. let exportFilePath = self.model?.exportFilePath
  142. let insetFilePath = self.model?.insertFilePath
  143. let password = self.model?.password
  144. let exportPDFType = self.model?.exportPDFType
  145. let imageFilePaths = self.model?.imageFilePaths
  146. var conversionIndex: Int = 0
  147. var document: CPDFDocument!
  148. /**
  149. * 获取文件路径
  150. * 如果是插入无需调整
  151. * 如果是新增 需排除文件同名
  152. */
  153. let filePath = (exportPDFType == .insert) ? insetFilePath : self.fetchDifferentFilePath(filePath: (exportFilePath?.appending("/Untitled.pdf"))!)
  154. //如果是插入现有PDF,需输入密码
  155. if exportPDFType == .insert {
  156. document = CPDFDocument.init(url: URL(fileURLWithPath: filePath!))!
  157. document.unlock(withPassword: password!)
  158. } else if exportPDFType == .merge {
  159. document = CPDFDocument.init()
  160. }
  161. //遍历图片文件
  162. for imageData in imageFilePaths! {
  163. imageData.state = .loading
  164. var isDirectory: ObjCBool = false
  165. if (FileManager.default.fileExists(atPath: imageData.filePath, isDirectory: &isDirectory) &&
  166. !isDirectory.boolValue) {
  167. let image = NSImage(contentsOfFile: imageData.filePath)
  168. let success = document?.insertPage(image!.size, withImage: imageData.filePath, at: UInt(conversionIndex))
  169. if success! {
  170. imageData.state = .success
  171. } else {
  172. imageData.state = .error
  173. }
  174. }
  175. conversionIndex += 1
  176. if (self.progress != nil) {
  177. self.progress!(self.status)
  178. }
  179. }
  180. //异步处理
  181. DispatchQueue.global().async { [unowned self] in
  182. var success = false
  183. if document!.pageCount >= 1 {
  184. if (document!.isEncrypted) {
  185. var attributes: [CPDFDocumentWriteOption : String] = [:]
  186. attributes[CPDFDocumentWriteOption.ownerPasswordOption] = password
  187. attributes[CPDFDocumentWriteOption.userPasswordOption] = password
  188. success = document.write(to: URL(string: filePath!), withOptions: attributes)
  189. } else {
  190. success = document.write(toFile: filePath!)
  191. }
  192. }
  193. if !success {
  194. self.errors.append(NSString(string: filePath!).lastPathComponent)
  195. }
  196. self.status = .unknow
  197. // 回到主线程异步
  198. DispatchQueue.main.async {
  199. if (self.progress != nil) {
  200. self.progress!(self.status)
  201. }
  202. if completion != nil {
  203. completion!(true, filePath, self.errors, self.OCRerrors)
  204. }
  205. }
  206. }
  207. } else {
  208. if completion != nil {
  209. completion!(false, model.exportFilePath, model.filePaths(), nil)
  210. }
  211. }
  212. }
  213. //获取不同文件路径
  214. func fetchDifferentFilePath(filePath: String) -> String {
  215. var resultFilePath = filePath
  216. var index: Int = 0
  217. while (FileManager.default.fileExists(atPath: resultFilePath)) {
  218. index += 1
  219. let path = NSString(string: filePath).deletingPathExtension + "(" + String(index) + ")"
  220. resultFilePath = NSString(string: path).appendingPathExtension(NSString(string: filePath).pathExtension)!
  221. }
  222. return resultFilePath;
  223. }
  224. }