123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237 |
- //
- // KMCompressManager.swift
- // PDF Reader Pro
- //
- // Created by lizhe on 2024/12/17.
- //
- import Cocoa
- import ComPDFKit
- import KMComponentLibrary
- class KMCompressManager: NSObject {
- static let shared = KMCompressManager()
-
- var progressWindow: ComponentProgressWindowController?
-
- var isCancelCompress = false
-
- func compress(documentURL: URL,
- password: String = "",
- limit: Bool = false,
- model: KMCompressSettingModel,
- view: NSView?,
- completionHandler: (@escaping (Bool, URL) -> Void)) {
- DispatchQueue.main.async {
- NSPanel.savePanel(NSWindow.currentWindow(), true) { panel in
- let url: URL = documentURL
- panel.nameFieldStringValue = ""+url.deletingPathExtension().lastPathComponent+"_Compressed"
- panel.allowedFileTypes = ["pdf"]
- } completion: { response, url, isOpen in
- if (response == .cancel) {
- return
- }
-
- DispatchQueue.global().async {
- DispatchQueue.main.async {
- self.showLoadingWindow(window: view?.window)
- }
-
- self.compress(documentURL: documentURL, fileURL: url!, password: password, limit: limit, model: model) { [unowned self] currentPage, totalPages in
- DispatchQueue.main.async {
- self.updateLoadingProgress(value: Float(CGFloat(currentPage) / CGFloat(totalPages)))
- }
- } cancelHandler: {
- return false
- } completionHandler: { [unowned self] isFinish in
- DispatchQueue.main.async {
- self.dismissLoadiingWindow(window: view?.window)
- if (isFinish) {
- if isOpen { /// 开启文档
- NSDocumentController.shared.km_safe_openDocument(withContentsOf: url!, display: true) { _, _, _ in
- }
- } else {
- NSWorkspace.shared.activateFileViewerSelecting([url!])
- }
- }
- completionHandler(isFinish, url!)
- }
- }
- }
- }
-
- }
- }
-
- func compress(documentURL: URL,
- fileURL: URL,
- password: String = "",
- limit: Bool,
- model: KMCompressSettingModel,
- progressHandler: (@escaping (Float, Float) -> Void),
- cancelHandler: (@escaping () -> Bool),
- completionHandler: (@escaping (Bool) -> Void)) {
- DispatchQueue.global().async {
- let docuemt = CPDFDocument.init(url: documentURL)
- if (docuemt?.isLocked)! && password.count != 0 {
- docuemt?.unlock(withPassword: password)
- }
- if (limit) {
- if let _document = docuemt, let _ = KMTools.saveWatermarkDocumentForCompress(document: _document, to: fileURL, imageQuality: 20) {
-
- }
- } else if (docuemt != nil) {
- self.compress(document: docuemt!,
- fileURL: fileURL,
- model: model,
- tProgressHandler: progressHandler,
- tCancelHandler: cancelHandler,
- tCompletionHandler: completionHandler)
- }
- }
- }
-
- func compress(document: CPDFDocument,
- fileURL: URL,
- model: KMCompressSettingModel,
- tProgressHandler: (@escaping (Float, Float) -> Void),
- tCancelHandler: (@escaping () -> Bool),
- tCompletionHandler: (@escaping (Bool) -> Void)) {
- let url = fileURL
- // 创建 imageOption 对象
- var imageOption = CPDFOptimizeImageOption()
- imageOption.uperPpi = Int32(model.ppi);
- imageOption.targetPpi = Int32(model.maxPpi);
- imageOption.compAlg = .CPDFCOMP_ALG_JPEG2000
- imageOption.blockSize = 256
-
- if model.imageQualityType == .hight {
- imageOption.quality = 100;
- } else if model.imageQualityType == .medium {
- imageOption.quality = 60;
- } else if model.imageQualityType == .low {
- imageOption.quality = 20;
- }
-
- // 创建 optimizeFlag
- var optimizeFlag: Int = 0
- if model.fontUnembed {
-
- } else {
- optimizeFlag |= Int(CPDF_OPTIMIZE_FLAG.CPDFOPTIMIZE_FLAG_RMEMBFONT.rawValue)
- }
-
- model.objectOptions.contains(.formAndAction) ? optimizeFlag |= Int(CPDF_OPTIMIZE_FLAG.CPDFOPTIMIZE_FLAG_RMFORMCOMMITIMPORTRESETACTION.rawValue) : nil
- model.objectOptions.contains(.javaScript) ? optimizeFlag |= Int(CPDF_OPTIMIZE_FLAG.CPDFOPTIMIZE_FLAG_RMJSACTION.rawValue) : nil
- model.objectOptions.contains(.thumbnails) ? optimizeFlag |= Int(CPDF_OPTIMIZE_FLAG.CPDFOPTIMIZE_FLAG_RMPAGETHUMBNAIL.rawValue) : nil
- model.objectOptions.contains(.documentTags) ? optimizeFlag |= Int(CPDF_OPTIMIZE_FLAG.CPDFOPTIMIZE_FLAG_RMLABEL.rawValue) : nil
- model.objectOptions.contains(.bookmarks) ? optimizeFlag |= Int(CPDF_OPTIMIZE_FLAG.CPDFOPTIMIZE_FLAG_RMBK.rawValue) : nil
-
- if model.userDataOptions.contains(.commentsFormAndMultimedia) {
- optimizeFlag |= Int(CPDF_OPTIMIZE_FLAG.CPDFOPTIMIZE_FLAG_RMMULMEDIA.rawValue)
- optimizeFlag |= Int(CPDF_OPTIMIZE_FLAG.CPDFOPTIMIZE_FLAG_RMFORM.rawValue)
- // optimizeFlag |= Int(CPDF_OPTIMIZE_FLAG.CPDFOPTIMIZE_FLAG_RMANNOT.rawValue)
- }
-
- if model.userDataOptions.contains(.documentInfomationAndMetadata) {
- optimizeFlag |= Int(CPDF_OPTIMIZE_FLAG.CPDFOPTIMIZE_FLAG_RMDOCINFO.rawValue)
- optimizeFlag |= Int(CPDF_OPTIMIZE_FLAG.CPDFOPTIMIZE_FLAG_RMMEDTADATA.rawValue)
- }
-
- model.userDataOptions.contains(.allObject) ? optimizeFlag |= Int(CPDF_OPTIMIZE_FLAG.CPDFOPTIMIZE_FLAG_RMOBJDATA.rawValue) : nil
- model.userDataOptions.contains(.fileAttachments) ? optimizeFlag |= Int(CPDF_OPTIMIZE_FLAG.CPDFOPTIMIZE_FLAG_RMFILEATTACHMENT.rawValue) : nil
- if model.userDataOptions.contains(.hiddenLayerContentAndFlattenVisibleLayers) {
- optimizeFlag |= Int(CPDF_OPTIMIZE_FLAG.CPDFOPTIMIZE_FLAG_RMHIDERLAYER.rawValue)
- optimizeFlag |= Int(CPDF_OPTIMIZE_FLAG.CPDFOPTIMIZE_FLAG_MERGEVISIBLELAYER.rawValue)
- }
-
- model.otherDataOptions.contains(.invalidBookmarks) ? optimizeFlag |= Int(CPDF_OPTIMIZE_FLAG.CPDFOPTIMIZE_FLAG_RMINVABK.rawValue) : nil
- model.otherDataOptions.contains(.invalidLinks) ? optimizeFlag |= Int(CPDF_OPTIMIZE_FLAG.CPDFOPTIMIZE_FLAG_RMINVALINK.rawValue) : nil
- model.otherDataOptions.contains(.unrefrencedNamedDestinations) ? optimizeFlag |= Int(CPDF_OPTIMIZE_FLAG.CPDFOPTIMIZE_FLAG_RMUNUSEDTARGET.rawValue) : nil
- model.otherDataOptions.contains(.pageContent) ? optimizeFlag |= Int(CPDF_OPTIMIZE_FLAG.CPDFOPTIMIZE_FLAG_OPTIMIZEPAGECONTENT.rawValue) : nil
-
- optimizeFlag |= Int(CPDF_OPTIMIZE_FLAG.CPDFOPTIMIZE_FLAG_BCOMPRESSIMAGE.rawValue) // 开启图片压缩
- optimizeFlag |= Int(CPDF_OPTIMIZE_FLAG.CPDFOPTIMIZE_FLAG_RMNOTUSE.rawValue) // 移除未使用的对象
- optimizeFlag |= Int(CPDF_OPTIMIZE_FLAG.CPDFOPTIMIZE_FLAG_RMEPTOBJ.rawValue) // 移除空白对象
-
- //数据转换
- var imageOptionValue: NSValue?
- // 使用 `withUnsafeBytes` 获取 UnsafeRawPointer
- withUnsafeBytes(of: imageOption) { rawBufferPointer in
- guard let baseAddress = rawBufferPointer.baseAddress else {
- return
- }
- // 获取结构体的 Objective-C 类型编码
- let objCType = KMCompressTool.encodeForStruct()
- // let objCType = "{CPDFOptimizeImageOption=iiiii}"
- imageOptionValue = NSValue(bytes: baseAddress, objCType: objCType)
- }
-
- // 创建字典
- let options: [String: Any] = [
- CPDFOptimizeCOLImageOptionKey: imageOptionValue as Any,
- CPDFOptimizeFlagKey: NSNumber(value: optimizeFlag)
- ]
-
- // 调用方法
- self.isCancelCompress = false
- document.writeOptimize(to: url, withOptimizeOptions: options,
- progressHandler: { (currentPage, totalPages) in
- // 进度处理
- print("当前页:\(currentPage), 总页数:\(totalPages)")
- DispatchQueue.main.async {
- tProgressHandler(currentPage, totalPages)
- }
- },
- cancelHandler: {
- // 返回是否取消
- let shouldCancel = self.isCancelCompress
- return shouldCancel
- // return tCancelHandler()
- },
- completionHandler: { (finished) in
- // 完成处理
- if finished {
- print("优化完成")
- } else {
- print("优化未完成")
- }
-
- DispatchQueue.main.async {
- tCompletionHandler(finished)
- }
- })
-
- document.cancelExtractImage()
- }
- }
- extension KMCompressManager {
- func showLoadingWindow(window: NSWindow?) {
- self.progressWindow = ComponentProgressWindowController(windowNibName: "ComponentProgressWindowController")
- self.progressWindow?.properties.cancelString = KMLocalizedString("Cancel")
- self.progressWindow?.reloadData()
- self.progressWindow?.setTarget(self, action: #selector(closeProgress))
- window?.beginSheet((self.progressWindow?.window ?? NSWindow()))
- }
-
- func updateLoadingProgress(value: Float) {
- DispatchQueue.main.async {
- self.progressWindow?.properties.progress = CGFloat(value)
- self.progressWindow?.reloadData()
- }
- }
-
- func dismissLoadiingWindow(window: NSWindow?) {
- DispatchQueue.main.async {
- guard let progress = self.progressWindow else { return }
-
- window?.endSheet(progress.window ?? NSWindow())
- }
- }
-
- @objc func closeProgress() {
- self.isCancelCompress = true
- }
- }
|