KMImageToPDFManager.swift 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  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. if image != nil {
  120. let insertPageSuccess = document?.insertPage(image!.size, withImage: filePath, at: document!.pageCount)
  121. if insertPageSuccess != nil {
  122. print("插入成功")
  123. //信号量控制异步
  124. let semaphore = DispatchSemaphore(value: 0)
  125. DispatchQueue.global().async {
  126. success = ((document?.write(toFile: path)) != nil)
  127. semaphore.signal()
  128. }
  129. semaphore.wait()
  130. } else {
  131. print("插入失败")
  132. }
  133. } else {
  134. print("插入失败")
  135. }
  136. return success
  137. }
  138. /**
  139. 合并PDF
  140. */
  141. private func mergePDF(model: KMImageToPDFChooseModel, completion: KMImageToPDFManagerCompletion?) {
  142. if status == .unknow {
  143. status = .conversioning
  144. //基本参数
  145. let exportFilePath = self.model?.exportFilePath
  146. let insetFilePath = self.model?.insertFilePath
  147. let password = self.model?.password
  148. let exportPDFType = self.model?.exportPDFType
  149. let imageFilePaths = self.model?.imageFilePaths
  150. var conversionIndex: Int = 0
  151. var document: CPDFDocument!
  152. /**
  153. * 获取文件路径
  154. * 如果是插入无需调整
  155. * 如果是新增 需排除文件同名
  156. */
  157. let filePath = (exportPDFType == .insert) ? insetFilePath : self.fetchDifferentFilePath(filePath: (exportFilePath?.appending("/Untitled.pdf"))!)
  158. //如果是插入现有PDF,需输入密码
  159. if exportPDFType == .insert {
  160. document = CPDFDocument.init(url: URL(fileURLWithPath: filePath!))!
  161. document.unlock(withPassword: password!)
  162. } else if exportPDFType == .merge {
  163. document = CPDFDocument.init()
  164. }
  165. //遍历图片文件
  166. for imageData in imageFilePaths! {
  167. imageData.state = .loading
  168. var isDirectory: ObjCBool = false
  169. if (FileManager.default.fileExists(atPath: imageData.filePath, isDirectory: &isDirectory) &&
  170. !isDirectory.boolValue) {
  171. let image = NSImage(contentsOfFile: imageData.filePath)
  172. if image != nil {
  173. let success = document?.insertPage(image!.size, withImage: imageData.filePath, at: UInt(conversionIndex))
  174. if success! {
  175. imageData.state = .success
  176. } else {
  177. imageData.state = .error
  178. if image?.size.width != 0 && image?.size.height != 0 {
  179. document.insertPage(image!.size, at: UInt(conversionIndex))
  180. } else {
  181. document.insertPage(CGSizeZero, at: UInt(conversionIndex))
  182. }
  183. }
  184. } else {
  185. imageData.state = .error
  186. document.insertPage(CGSizeZero, at: UInt(conversionIndex))
  187. }
  188. }
  189. conversionIndex += 1
  190. if (self.progress != nil) {
  191. self.progress!(self.status)
  192. }
  193. }
  194. //异步处理
  195. DispatchQueue.global().async { [unowned self] in
  196. var success = false
  197. if document!.pageCount >= 1 {
  198. if (document!.isEncrypted) {
  199. var attributes: [CPDFDocumentWriteOption : String] = [:]
  200. attributes[CPDFDocumentWriteOption.ownerPasswordOption] = password
  201. attributes[CPDFDocumentWriteOption.userPasswordOption] = password
  202. success = document.write(to: URL(string: filePath!), withOptions: attributes)
  203. } else {
  204. success = document.write(toFile: filePath!)
  205. }
  206. }
  207. if !success {
  208. self.errors.append(NSString(string: filePath!).lastPathComponent)
  209. }
  210. self.status = .unknow
  211. // 回到主线程异步
  212. DispatchQueue.main.async {
  213. if (self.progress != nil) {
  214. self.progress!(self.status)
  215. }
  216. if completion != nil {
  217. completion!(true, filePath, self.errors, self.OCRerrors)
  218. }
  219. }
  220. }
  221. } else {
  222. if completion != nil {
  223. completion!(false, model.exportFilePath, model.filePaths(), nil)
  224. }
  225. }
  226. }
  227. //获取不同文件路径
  228. func fetchDifferentFilePath(filePath: String) -> String {
  229. var resultFilePath = filePath
  230. var index: Int = 0
  231. while (FileManager.default.fileExists(atPath: resultFilePath)) {
  232. index += 1
  233. let path = NSString(string: filePath).deletingPathExtension + "(" + String(index) + ")"
  234. resultFilePath = NSString(string: path).appendingPathExtension(NSString(string: filePath).pathExtension)!
  235. }
  236. return resultFilePath;
  237. }
  238. }