KMCompressManager.swift 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. //
  2. // KMCompressManager.swift
  3. // PDF Reader Pro
  4. //
  5. // Created by lizhe on 2024/12/17.
  6. //
  7. import Cocoa
  8. import ComPDFKit
  9. class KMCompressManager: NSObject {
  10. static let shared = KMCompressManager()
  11. func compress(documentURL: URL,
  12. password: String = "",
  13. limit: Bool = false,
  14. model: KMCompressSettingModel,
  15. view: NSView?,
  16. completionHandler: (@escaping (Bool, URL) -> Void)) {
  17. DispatchQueue.main.async {
  18. NSPanel.savePanel(NSWindow.currentWindow(), true) { panel in
  19. let url: URL = documentURL
  20. panel.nameFieldStringValue = ""+url.deletingPathExtension().lastPathComponent+"_Compressed"
  21. panel.allowedFileTypes = ["pdf"]
  22. } completion: { response, url, isOpen in
  23. if (response == .cancel) {
  24. return
  25. }
  26. view?.beginLoading()
  27. DispatchQueue.global().async {
  28. self.compress(documentURL: documentURL, fileURL: url!, password: password, limit: limit, model: model) { currentPage, totalPages in
  29. } cancelHandler: {
  30. return false
  31. } completionHandler: { [unowned self] isFinish in
  32. DispatchQueue.main.async {
  33. view?.endLoading()
  34. if (isFinish) {
  35. if isOpen { /// 开启文档
  36. NSDocumentController.shared.km_safe_openDocument(withContentsOf: url!, display: true) { _, _, _ in
  37. }
  38. } else {
  39. NSWorkspace.shared.activateFileViewerSelecting([url!])
  40. }
  41. }
  42. completionHandler(isFinish, url!)
  43. }
  44. }
  45. }
  46. }
  47. }
  48. }
  49. func compress(documentURL: URL,
  50. fileURL: URL,
  51. password: String = "",
  52. limit: Bool,
  53. model: KMCompressSettingModel,
  54. progressHandler: (@escaping (Float, Float) -> Void),
  55. cancelHandler: (@escaping () -> Bool),
  56. completionHandler: (@escaping (Bool) -> Void)) {
  57. DispatchQueue.global().async {
  58. let docuemt = CPDFDocument.init(url: documentURL)
  59. if (docuemt?.isLocked)! && password.count != 0 {
  60. docuemt?.unlock(withPassword: password)
  61. }
  62. if (limit) {
  63. if let _document = docuemt, let _ = KMTools.saveWatermarkDocumentForCompress(document: _document, to: fileURL, imageQuality: 20) {
  64. }
  65. } else if (docuemt != nil) {
  66. self.compress(document: docuemt!,
  67. fileURL: fileURL,
  68. model: model,
  69. tProgressHandler: progressHandler,
  70. tCancelHandler: cancelHandler,
  71. tCompletionHandler: completionHandler)
  72. }
  73. }
  74. }
  75. func compress(document: CPDFDocument,
  76. fileURL: URL,
  77. model: KMCompressSettingModel,
  78. tProgressHandler: (@escaping (Float, Float) -> Void),
  79. tCancelHandler: (@escaping () -> Bool),
  80. tCompletionHandler: (@escaping (Bool) -> Void)) {
  81. let url = fileURL
  82. // 创建 imageOption 对象
  83. var imageOption = CPDFOptimizeImageOption()
  84. imageOption.uperPpi = Int32(model.ppi);
  85. imageOption.targetPpi = Int32(model.maxPpi);
  86. imageOption.compAlg = .CPDFCOMP_ALG_JPEG2000
  87. imageOption.blockSize = 256
  88. if model.imageQualityType == .hight {
  89. imageOption.quality = 100;
  90. } else if model.imageQualityType == .medium {
  91. imageOption.quality = 60;
  92. } else if model.imageQualityType == .low {
  93. imageOption.quality = 20;
  94. }
  95. // 创建 optimizeFlag
  96. var optimizeFlag: Int = 0
  97. if model.fontUnembed {
  98. } else {
  99. optimizeFlag |= Int(CPDF_OPTIMIZE_FLAG.CPDFOPTIMIZE_FLAG_RMEMBFONT.rawValue)
  100. }
  101. model.objectOptions.contains(.formAndAction) ? optimizeFlag |= Int(CPDF_OPTIMIZE_FLAG.CPDFOPTIMIZE_FLAG_RMFORMCOMMITIMPORTRESETACTION.rawValue) : nil
  102. model.objectOptions.contains(.javaScript) ? optimizeFlag |= Int(CPDF_OPTIMIZE_FLAG.CPDFOPTIMIZE_FLAG_RMJSACTION.rawValue) : nil
  103. model.objectOptions.contains(.thumbnails) ? optimizeFlag |= Int(CPDF_OPTIMIZE_FLAG.CPDFOPTIMIZE_FLAG_RMPAGETHUMBNAIL.rawValue) : nil
  104. model.objectOptions.contains(.documentTags) ? optimizeFlag |= Int(CPDF_OPTIMIZE_FLAG.CPDFOPTIMIZE_FLAG_RMLABEL.rawValue) : nil
  105. model.objectOptions.contains(.bookmarks) ? optimizeFlag |= Int(CPDF_OPTIMIZE_FLAG.CPDFOPTIMIZE_FLAG_RMBK.rawValue) : nil
  106. if model.userDataOptions.contains(.commentsFormAndMultimedia) {
  107. optimizeFlag |= Int(CPDF_OPTIMIZE_FLAG.CPDFOPTIMIZE_FLAG_RMMULMEDIA.rawValue)
  108. optimizeFlag |= Int(CPDF_OPTIMIZE_FLAG.CPDFOPTIMIZE_FLAG_RMFORM.rawValue)
  109. // optimizeFlag |= Int(CPDF_OPTIMIZE_FLAG.CPDFOPTIMIZE_FLAG_RMANNOT.rawValue)
  110. }
  111. if model.userDataOptions.contains(.documentInfomationAndMetadata) {
  112. optimizeFlag |= Int(CPDF_OPTIMIZE_FLAG.CPDFOPTIMIZE_FLAG_RMDOCINFO.rawValue)
  113. optimizeFlag |= Int(CPDF_OPTIMIZE_FLAG.CPDFOPTIMIZE_FLAG_RMMEDTADATA.rawValue)
  114. }
  115. model.userDataOptions.contains(.allObject) ? optimizeFlag |= Int(CPDF_OPTIMIZE_FLAG.CPDFOPTIMIZE_FLAG_RMOBJDATA.rawValue) : nil
  116. model.userDataOptions.contains(.fileAttachments) ? optimizeFlag |= Int(CPDF_OPTIMIZE_FLAG.CPDFOPTIMIZE_FLAG_RMFILEATTACHMENT.rawValue) : nil
  117. if model.userDataOptions.contains(.hiddenLayerContentAndFlattenVisibleLayers) {
  118. optimizeFlag |= Int(CPDF_OPTIMIZE_FLAG.CPDFOPTIMIZE_FLAG_RMHIDERLAYER.rawValue)
  119. optimizeFlag |= Int(CPDF_OPTIMIZE_FLAG.CPDFOPTIMIZE_FLAG_MERGEVISIBLELAYER.rawValue)
  120. }
  121. model.otherDataOptions.contains(.invalidBookmarks) ? optimizeFlag |= Int(CPDF_OPTIMIZE_FLAG.CPDFOPTIMIZE_FLAG_RMINVABK.rawValue) : nil
  122. model.otherDataOptions.contains(.invalidLinks) ? optimizeFlag |= Int(CPDF_OPTIMIZE_FLAG.CPDFOPTIMIZE_FLAG_RMINVALINK.rawValue) : nil
  123. model.otherDataOptions.contains(.unrefrencedNamedDestinations) ? optimizeFlag |= Int(CPDF_OPTIMIZE_FLAG.CPDFOPTIMIZE_FLAG_RMBK.rawValue) : nil
  124. model.otherDataOptions.contains(.pageContent) ? optimizeFlag |= Int(CPDF_OPTIMIZE_FLAG.CPDFOPTIMIZE_FLAG_RMUNUSEDTARGET.rawValue) : nil
  125. optimizeFlag |= Int(CPDF_OPTIMIZE_FLAG.CPDFOPTIMIZE_FLAG_BCOMPRESSIMAGE.rawValue) // 开启图片压缩
  126. optimizeFlag |= Int(CPDF_OPTIMIZE_FLAG.CPDFOPTIMIZE_FLAG_RMNOTUSE.rawValue) // 移除未使用的对象
  127. optimizeFlag |= Int(CPDF_OPTIMIZE_FLAG.CPDFOPTIMIZE_FLAG_RMEPTOBJ.rawValue) // 移除空白对象
  128. //数据转换
  129. var imageOptionValue: NSValue?
  130. // 使用 `withUnsafeBytes` 获取 UnsafeRawPointer
  131. withUnsafeBytes(of: imageOption) { rawBufferPointer in
  132. guard let baseAddress = rawBufferPointer.baseAddress else {
  133. return
  134. }
  135. // 获取结构体的 Objective-C 类型编码
  136. let objCType = KMCompressTool.encodeForStruct()
  137. // let objCType = "{CPDFOptimizeImageOption=iiiii}"
  138. imageOptionValue = NSValue(bytes: baseAddress, objCType: objCType)
  139. }
  140. // 创建字典
  141. let options: [String: Any] = [
  142. CPDFOptimizeCOLImageOptionKey: imageOptionValue as Any,
  143. CPDFOptimizeFlagKey: NSNumber(value: optimizeFlag)
  144. ]
  145. // 调用方法
  146. document.writeOptimize(to: url, withOptimizeOptions: options,
  147. progressHandler: { (currentPage, totalPages) in
  148. // 进度处理
  149. print("当前页:\(currentPage), 总页数:\(totalPages)")
  150. tProgressHandler(currentPage, totalPages)
  151. },
  152. cancelHandler: {
  153. // 返回是否取消
  154. let shouldCancel = false
  155. return shouldCancel
  156. // return tCancelHandler()
  157. },
  158. completionHandler: { (finished) in
  159. // 完成处理
  160. if finished {
  161. print("优化完成")
  162. } else {
  163. print("优化未完成")
  164. }
  165. tCompletionHandler(finished)
  166. })
  167. }
  168. }