KMImageToPDFManager.swift 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  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. KMPrint("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. KMPrint(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. let jpgPath = self.imageToJPG(filePath: filePath, savePath: savePath)
  118. //FIXME: 无法插入图片
  119. let image = NSImage(contentsOfFile: jpgPath)
  120. if image != nil {
  121. let insertPageSuccess = document?.insertPage(image!.size, withImage: jpgPath, at: document!.pageCount)
  122. if insertPageSuccess != nil {
  123. KMPrint("插入成功")
  124. //信号量控制异步
  125. let semaphore = DispatchSemaphore(value: 0)
  126. DispatchQueue.global().async {
  127. success = ((document?.write(toFile: path)) != nil)
  128. semaphore.signal()
  129. }
  130. semaphore.wait()
  131. } else {
  132. KMPrint("插入失败")
  133. }
  134. } else {
  135. KMPrint("插入失败")
  136. }
  137. return success
  138. }
  139. /**
  140. 合并PDF
  141. */
  142. private func mergePDF(model: KMImageToPDFChooseModel, completion: KMImageToPDFManagerCompletion?) {
  143. if status == .unknow {
  144. status = .conversioning
  145. //基本参数
  146. let exportFilePath = self.model?.exportFilePath
  147. let insetFilePath = self.model?.insertFilePath
  148. let password = self.model?.password
  149. let exportPDFType = self.model?.exportPDFType
  150. let imageFilePaths = self.model?.imageFilePaths
  151. var conversionIndex: Int = 0
  152. var document: CPDFDocument!
  153. /**
  154. * 获取文件路径
  155. * 如果是插入无需调整
  156. * 如果是新增 需排除文件同名
  157. */
  158. let filePath = (exportPDFType == .insert) ? insetFilePath : self.fetchDifferentFilePath(filePath: (exportFilePath?.appending("/Untitled.pdf"))!)
  159. //如果是插入现有PDF,需输入密码
  160. if exportPDFType == .insert {
  161. document = CPDFDocument.init(url: URL(fileURLWithPath: filePath!))!
  162. document.unlock(withPassword: password!)
  163. } else if exportPDFType == .merge {
  164. document = CPDFDocument.init()
  165. }
  166. //遍历图片文件
  167. for imageData in imageFilePaths! {
  168. imageData.state = .loading
  169. var isDirectory: ObjCBool = false
  170. if (FileManager.default.fileExists(atPath: imageData.filePath, isDirectory: &isDirectory) &&
  171. !isDirectory.boolValue) {
  172. let image = NSImage(contentsOfFile: imageData.filePath)
  173. if image != nil {
  174. let success = document?.insertPage(image!.size, withImage: imageData.filePath, at: UInt(conversionIndex))
  175. if success! {
  176. imageData.state = .success
  177. } else {
  178. imageData.state = .error
  179. if image?.size.width != 0 && image?.size.height != 0 {
  180. document.insertPage(image!.size, at: UInt(conversionIndex))
  181. } else {
  182. document.insertPage(CGSizeZero, at: UInt(conversionIndex))
  183. }
  184. }
  185. } else {
  186. imageData.state = .error
  187. document.insertPage(CGSizeZero, at: UInt(conversionIndex))
  188. }
  189. }
  190. conversionIndex += 1
  191. if (self.progress != nil) {
  192. self.progress!(self.status)
  193. }
  194. }
  195. //异步处理
  196. DispatchQueue.global().async { [unowned self] in
  197. var success = false
  198. if document!.pageCount >= 1 {
  199. if (document!.isEncrypted) {
  200. var attributes: [CPDFDocumentWriteOption : String] = [:]
  201. attributes[CPDFDocumentWriteOption.ownerPasswordOption] = password
  202. attributes[CPDFDocumentWriteOption.userPasswordOption] = password
  203. success = document.write(to: URL(string: filePath!), withOptions: attributes)
  204. } else {
  205. success = document.write(toFile: filePath!)
  206. }
  207. }
  208. if !success {
  209. self.errors.append(NSString(string: filePath!).lastPathComponent)
  210. }
  211. self.status = .unknow
  212. // 回到主线程异步
  213. DispatchQueue.main.async {
  214. if (self.progress != nil) {
  215. self.progress!(self.status)
  216. }
  217. if completion != nil {
  218. completion!(true, filePath, self.errors, self.OCRerrors)
  219. }
  220. }
  221. }
  222. } else {
  223. if completion != nil {
  224. completion!(false, model.exportFilePath, model.filePaths(), nil)
  225. }
  226. }
  227. }
  228. //获取不同文件路径
  229. func fetchDifferentFilePath(filePath: String) -> String {
  230. var resultFilePath = filePath
  231. var index: Int = 0
  232. while (FileManager.default.fileExists(atPath: resultFilePath)) {
  233. index += 1
  234. let path = NSString(string: filePath).deletingPathExtension + "(" + String(index) + ")"
  235. resultFilePath = NSString(string: path).appendingPathExtension(NSString(string: filePath).pathExtension)!
  236. }
  237. return resultFilePath;
  238. }
  239. // 图片转PNG
  240. func imageToJPG(filePath: String, savePath: String) -> String {
  241. if NSString(string: NSString(string: filePath).lastPathComponent).pathExtension == "png" ||
  242. NSString(string: NSString(string: filePath).lastPathComponent).pathExtension == "PNG" {
  243. let imageName = NSString(string: NSString(string: filePath).lastPathComponent).deletingPathExtension
  244. let jpgPath = self.fetchDifferentFilePath(filePath: savePath + "/" + imageName + ".jpg")
  245. if (!FileManager.default.fileExists(atPath: jpgPath as String)) {
  246. FileManager.default.createFile(atPath: jpgPath as String, contents: nil)
  247. }
  248. // 加载 PNG 图像
  249. guard let pngImage = NSImage(contentsOfFile: filePath) else {
  250. KMPrint("Failed to load PNG image")
  251. return filePath
  252. }
  253. // 创建 NSBitmapImageRep 对象,并将 PNG 图像绘制到其中
  254. let bitmap = NSBitmapImageRep(data: pngImage.tiffRepresentation!)
  255. let rect = NSRect(origin: .zero, size: bitmap!.size)
  256. bitmap?.draw(in: rect)
  257. // 将 PNG 图像数据转换为 JPG 图像数据
  258. guard let jpgData = bitmap?.representation(using: .jpeg, properties: [:]) else {
  259. KMPrint("Failed to convert PNG to JPG")
  260. return filePath
  261. }
  262. // 保存 JPG 图像数据到文件
  263. let fileURL = URL(fileURLWithPath: jpgPath)
  264. do {
  265. try jpgData.write(to: fileURL)
  266. KMPrint("JPG image saved successfully")
  267. return fileURL.path
  268. } catch {
  269. KMPrint("Failed to save JPG image: \(error.localizedDescription)")
  270. return filePath
  271. }
  272. }
  273. return filePath
  274. }
  275. }