KMBatchManager.swift 50 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201
  1. //
  2. // KMBatchManager.swift
  3. // PDF Master
  4. //
  5. // Created by lizhe on 2023/2/17.
  6. //
  7. import Cocoa
  8. enum KMBatchManagerSate: Int, CaseIterable {
  9. case unknow = 0
  10. case begin
  11. case processing
  12. case complete
  13. case error
  14. }
  15. let kBacthFilesProcessNotification = "kBacthFilesProcessNotification"
  16. let kBacthProcessNotification = "kBacthProcessNotification"
  17. let supportDirectory = NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).last
  18. let mainBundleIdentifier = Bundle.main.bundleIdentifier ?? ""
  19. let kTempSavePath = supportDirectory?.stringByAppendingPathComponent(mainBundleIdentifier)
  20. class KMBatchManager: NSObject {
  21. public static let manager = KMBatchManager()
  22. fileprivate(set) var state: KMBatchManagerSate = .unknow
  23. var outputFolderPath: String = ""
  24. var convert: KMPDFConvert = KMPDFConvert()
  25. var isCancel = false
  26. var filesData: [KMBatchProcessingTableViewModel] = []
  27. var batchFilesData: [KMBatchProcessingTableViewModel] {
  28. get {
  29. var resultArray:[KMBatchProcessingTableViewModel] = []
  30. for item in filesData {
  31. if !item.isLock {
  32. resultArray.append(item)
  33. }
  34. }
  35. return resultArray
  36. }
  37. }
  38. func batch(type: KMBatchCollectionViewType, data: KMBatchSettingItemViewModel) {
  39. self.isCancel = false
  40. if self.isContainLockFiles() {
  41. let window = NSWindow()
  42. let currentWindow = NSWindow.currentWindow()
  43. let controller = KMBatchProcessingPasswordAlertViewController.init(nibName: "KMBatchProcessingPasswordAlertViewController", bundle: nil)
  44. controller.cancelAction = {
  45. currentWindow.endSheet(window)
  46. }
  47. controller.continueAction = {
  48. currentWindow.endSheet(window)
  49. }
  50. window.contentViewController = controller
  51. currentWindow.beginSheet(window)
  52. return
  53. }
  54. let panel = NSOpenPanel()
  55. panel.canChooseFiles = false
  56. panel.canChooseDirectories = true
  57. panel.canCreateDirectories = true
  58. panel.beginSheetModal(for: NSWindow.currentWindow()) { response in
  59. if response == .cancel {
  60. return
  61. }
  62. let outputFolderPath = (panel.url?.path)!
  63. self.outputFolderPath = outputFolderPath
  64. //
  65. self.batchUnkown()
  66. self.batchBegin()
  67. switch type {
  68. case .convertPDF:
  69. self.convertPDFExport(data: data, outputFolderPath: outputFolderPath)
  70. break
  71. case .OCR:
  72. self.convertOCRExport(data: data, outputFolderPath: outputFolderPath)
  73. break
  74. case .compress:
  75. self.compressExport(data: data, outputFolderPath: outputFolderPath)
  76. break
  77. case .security:
  78. self.securityExport(data: data, outputFolderPath: outputFolderPath)
  79. break
  80. case .watermark:
  81. self.waterMarkApplay(data: data, outputFolderPath: outputFolderPath)
  82. break
  83. case .background:
  84. self.backgroundApplay(data: data, outputFolderPath: outputFolderPath)
  85. break
  86. case .headerAndFooter:
  87. self.headAndFooterApplay(data: data, outputFolderPath: outputFolderPath)
  88. break
  89. case .batesNumber:
  90. self.batesApplay(data: data, outputFolderPath: outputFolderPath)
  91. break
  92. case .batchRemove:
  93. self.removeApplay(data: data, outputFolderPath: outputFolderPath)
  94. break
  95. case .imageToPDF:
  96. self.imageToPDFExport(data: data, outputFolderPath: outputFolderPath)
  97. break
  98. default:
  99. KMPrint("找不到")
  100. break
  101. }
  102. }
  103. }
  104. func cancel(type: KMBatchCollectionViewType = .convertPDF) {
  105. self.isCancel = true
  106. switch type {
  107. case .convertPDF:
  108. KMPDFConvertManager.defaultManager.cancel(convert: self.convert)
  109. break
  110. case .OCR:
  111. KMOCRManager.manager.cancelRecognition()
  112. break
  113. case .compress:
  114. KMCompressManager.shared.cancel()
  115. break
  116. default:
  117. KMPrint("没有此功能")
  118. break
  119. }
  120. self.batchFailure()
  121. }
  122. }
  123. //MARK: 批量
  124. extension KMBatchManager {
  125. //MARK: 转档
  126. func convertPDFExport(data: KMBatchSettingItemViewModel, outputFolderPath: String) {
  127. self.convertFile(outputFolderPath: outputFolderPath, data: data, filesData: self.batchFilesData)
  128. }
  129. func convertFile(outputFolderPath: String, data: KMBatchSettingItemViewModel, filesData: [KMBatchProcessingTableViewModel]) {
  130. guard !filesData.isEmpty else { return }
  131. func processFile(at index: Int) {
  132. guard index < filesData.count else {
  133. self.batchSuccess()
  134. return
  135. }
  136. if isCancel {
  137. return
  138. }
  139. let item = filesData[index]
  140. let filePath = item.filePath
  141. let document = self.fetchDocument(filePath: filePath, model: item)
  142. let settingData = data as? KMBatchConvertPDFViewModel ?? KMBatchConvertPDFViewModel()
  143. let path = self.fetchFilePath(type: .convertPDF, filePath: filePath, outputFolderPath: outputFolderPath)
  144. let convert = self.addConvertParameter(settingData)
  145. self.convert = convert
  146. let pageCount = document.pageCount
  147. // 获取页面
  148. let pages: [Int] = Array(1...Int(pageCount))
  149. convert.outputFolderPath = outputFolderPath
  150. convert.filePath = filePath
  151. convert.password = item.password
  152. convert.outputFileName = path.deletingPathExtension.lastPathComponent
  153. convert.pages = pages
  154. convert.isAllowOCR = settingData.needRecognizeText
  155. convert.ocrLanguage = settingData.languageType
  156. item.state = .clock
  157. self.itemProgress(item: item, processValue: 0)
  158. KMPDFConvertManager.defaultManager.convert(convert: convert, progress: { [unowned self] progressValue in
  159. print("转档进度 - \(progressValue)")
  160. let progress = Float(progressValue) / Float(pageCount)
  161. self.itemProgress(item: item, processValue: progress)
  162. }, completion: { [unowned self] finished, error in
  163. if finished {
  164. self.itemSuccess(item: item)
  165. } else {
  166. self.itemFailure(item: item, error: error! as NSError)
  167. }
  168. processFile(at: index + 1)
  169. })
  170. }
  171. // 开始处理第一个文件
  172. processFile(at: 0)
  173. }
  174. func addConvertParameter(_ data: KMBatchConvertPDFViewModel) -> KMPDFConvert {
  175. let settingData = data
  176. var convert = KMPDFConvert()
  177. switch settingData.convertPDFType {
  178. case .word:
  179. convert = KMPDFConvertWord()
  180. if settingData.layoutSettingType == .flowingText {
  181. convert.isAllInOneSheet = false
  182. } else {
  183. convert.isAllInOneSheet = true
  184. }
  185. case .excel:
  186. convert = KMPDFConvertExcel()
  187. if settingData.excelContent == .onlyText {
  188. convert.isExtractText = true
  189. } else if settingData.excelContent == .allContent {
  190. convert.isAllInOneSheet = settingData.isAllInOneSheet
  191. } else if settingData.excelContent == .onlyTable {
  192. convert.isExtractTable = true
  193. if settingData.excelWorksheet == .forEachTable {
  194. convert.extractTableIndex = 0
  195. } else if settingData.excelWorksheet == .forEachPage {
  196. convert.extractTableIndex = 1
  197. } else if settingData.excelWorksheet == .forTheDocument {
  198. convert.extractTableIndex = 2
  199. }
  200. }
  201. case .ppt:
  202. convert = KMPDFConvertPPT()
  203. case .csv:
  204. convert = KMPDFConvertCSV()
  205. convert.isAllInOneSheet = settingData.isAllInOneSheet
  206. case .image, .jpeg, .jpg, .jpeg2000, .bmp, .tiff, .png, .tga:
  207. convert = KMPDFConvertImage()
  208. convert.convertType = data.imageType
  209. var dpi: Int = 150
  210. if data.imageDpiIndex == 0 {
  211. dpi = 50
  212. } else if data.imageDpiIndex == 1 {
  213. dpi = 72
  214. } else if data.imageDpiIndex == 2 {
  215. dpi = 96
  216. } else if data.imageDpiIndex == 3 {
  217. dpi = 150
  218. } else if data.imageDpiIndex == 4 {
  219. dpi = 300
  220. } else if data.imageDpiIndex == 5 {
  221. dpi = 600
  222. } else {
  223. dpi = 150
  224. }
  225. if (data.imageType == .jpeg) {
  226. (convert as! KMPDFConvertImage).imageType = .JPEG
  227. (convert as! KMPDFConvertImage).imageDpi = dpi
  228. } else if (data.imageType == .png) {
  229. (convert as! KMPDFConvertImage).imageType = .PNG
  230. (convert as! KMPDFConvertImage).imageDpi = dpi
  231. } else {
  232. (convert as! KMPDFConvertImage).imageDpi = 150
  233. }
  234. case .html:
  235. convert = KMPDFConvertHTML()
  236. case .rtf:
  237. convert = KMPDFConvertRTF()
  238. case .json:
  239. convert = KMPDFConvertJson()
  240. if settingData.jsonType == .extractText {
  241. convert.isAllInOneSheet = false
  242. } else {
  243. convert.isAllInOneSheet = true
  244. }
  245. case .text:
  246. convert = KMPDFConvertText()
  247. default:
  248. KMPrint("不清楚")
  249. }
  250. return convert
  251. }
  252. //MARK: OCR
  253. func convertOCRExport(data: KMBatchSettingItemViewModel, outputFolderPath: String) {
  254. self.convertOCR(outputFolderPath: outputFolderPath, data: data as! KMOCRModel, filesData: self.batchFilesData)
  255. }
  256. func convertOCR(outputFolderPath: String, data: KMOCRModel, filesData: [KMBatchProcessingTableViewModel]?) {
  257. // 如果文件数据为空,直接返回
  258. guard let filesData = filesData, !filesData.isEmpty else { return }
  259. // 递归处理文件
  260. func processFile(at index: Int) {
  261. // 如果索引超出范围,说明所有文件都已经处理完毕
  262. guard index < filesData.count else {
  263. self.batchSuccess() // 所有文件处理完毕,调用批处理成功方法
  264. return
  265. }
  266. if isCancel {
  267. return
  268. }
  269. // 获取当前文件项
  270. let item = filesData[index]
  271. let document = CPDFDocument.init(url: URL(fileURLWithPath: item.filePath))
  272. // 如果文档有效,开始 OCR 转换
  273. if document != nil {
  274. // 获取 OCR 输出文件路径
  275. let path = self.fetchFilePath(type: .OCR, filePath: item.filePath, outputFolderPath: outputFolderPath)
  276. // 获取需要处理的页面范围
  277. data.pageRange = self.fetchValidPageIndexs(document!, model: item) ?? []
  278. // 执行 OCR 转换
  279. self.itemProgress(item: item, processValue: 0)
  280. self.convertOCR(outputFolderPath: outputFolderPath, document: document!, fileName: path.deletingPathExtension.lastPathComponent, data: data) { [unowned self] progress in
  281. // 更新进度
  282. self.itemProgress(item: item, processValue: progress)
  283. } complete: { [unowned self] document, text, error in
  284. // 处理 OCR 转换结果
  285. if error == nil {
  286. self.itemSuccess(item: item)
  287. } else {
  288. self.itemFailure(item: item, error: error! as NSError)
  289. }
  290. // 递归调用处理下一个文件
  291. processFile(at: index + 1)
  292. }
  293. } else {
  294. // 如果文件无效,跳过并处理下一个文件
  295. processFile(at: index + 1)
  296. }
  297. }
  298. // 从第一个文件开始处理
  299. processFile(at: 0)
  300. }
  301. // func convertOCR(outputFolderPath: String, data: KMOCRModel, filesData: [KMBatchProcessingTableViewModel]?) {
  302. // guard let filesData = filesData else { return }
  303. // for i in 0..<filesData.count {
  304. // let item = (filesData[i])
  305. // let document = CPDFDocument.init(url: URL(fileURLWithPath: item.filePath))
  306. // if document != nil {
  307. // //计算需要处理的页面
  308. //
  309. // let path = self.fetchFilePath(type: .OCR, filePath: item.filePath, outputFolderPath: outputFolderPath)
  310. // data.pageRange = self.fetchValidPageIndexs(document!, model: item) ?? []
  311. //
  312. // self.convertOCR(outputFolderPath: outputFolderPath, document: document!, fileName: path.deletingPathExtension.lastPathComponent, data: data) { [unowned self] progress in
  313. // self.itemProgress(item: item, processValue: progress)
  314. // } complete: { [unowned self] document, text, error in
  315. // if error == nil {
  316. // self.itemSuccess(item: item)
  317. // } else {
  318. // self.itemFailure(item: item, error: error! as NSError)
  319. // }
  320. //
  321. // if i == filesData.count - 1 {
  322. // self.batchSuccess()
  323. // }
  324. // }
  325. // }
  326. // }
  327. // }
  328. func convertOCR(outputFolderPath: String,
  329. document: CPDFDocument,
  330. fileName: String,
  331. data: KMOCRModel,
  332. progress: @escaping KMOCRManagerOCRProgress,
  333. complete: @escaping KMOCRManagerOCRComplete) {
  334. //计算需要处理的页面
  335. let path = outputFolderPath + "/" + fileName + ".pdf"
  336. KMOCRManager.manager.convertBatchOCR(document: document, saveFilePath: path, model: data, progress: { [unowned self] progressValue in
  337. progress(progressValue)
  338. }) { [unowned self] document, text, error in
  339. complete(document, text, error)
  340. }
  341. }
  342. //MARK: 压缩
  343. func compressExport(data: KMBatchSettingItemViewModel, outputFolderPath: String) {
  344. self.compressFile(outputFolderPath: outputFolderPath, data: (data as? KMCompressSettingModel)!, filesData: self.batchFilesData)
  345. }
  346. func compressFile(outputFolderPath: String, data: KMCompressSettingModel, filesData: [KMBatchProcessingTableViewModel]) {
  347. // 如果文件数组为空,直接返回
  348. guard !filesData.isEmpty else { return }
  349. // 递归处理每个文件
  350. func processFile(at index: Int) {
  351. // 如果索引超出范围,说明所有文件都已经处理完毕
  352. guard index < filesData.count else {
  353. self.batchSuccess() // 所有文件处理完毕,调用批处理成功方法
  354. return
  355. }
  356. if isCancel {
  357. return
  358. }
  359. // 获取当前文件项
  360. let item = filesData[index]
  361. let document = CPDFDocument.init(url: URL(fileURLWithPath: item.filePath))
  362. // 如果文档存在,则进行压缩
  363. if document != nil {
  364. // 获取压缩后的目标路径
  365. let path = self.fetchFilePath(type: .compress, filePath: item.filePath, outputFolderPath: outputFolderPath)
  366. // 调用压缩管理器进行压缩
  367. self.itemProgress(item: item, processValue: 0)
  368. KMCompressManager.shared.compress(documentURL: URL(fileURLWithPath: item.filePath), fileURL: URL(fileURLWithPath: path), limit: false, model: data) { currentPage, totalPages in
  369. // 计算进度
  370. let progress = Float(currentPage) / Float(totalPages)
  371. self.itemProgress(item: item, processValue: progress)
  372. } cancelHandler: {
  373. return false // 如果需要处理取消逻辑,可以在这里返回取消条件
  374. } completionHandler: { [unowned self] isFinish in
  375. if isFinish {
  376. self.itemSuccess(item: item) // 如果压缩成功,标记文件成功
  377. } else {
  378. self.itemFailure(item: item, error: nil) // 如果失败,标记失败
  379. }
  380. // 递归调用处理下一个文件
  381. processFile(at: index + 1)
  382. }
  383. } else {
  384. // 如果文件无效,直接跳过并继续处理下一个文件
  385. processFile(at: index + 1)
  386. }
  387. }
  388. // 从第一个文件开始处理
  389. processFile(at: 0)
  390. }
  391. // func compressFile(outputFolderPath: String, data: KMCompressSettingModel, filesData: [KMBatchProcessingTableViewModel]) {
  392. // if filesData.count != 0 {
  393. // DispatchQueue.global().async {
  394. // for i in 0..<filesData.count {
  395. // let item = filesData[i]
  396. // let document = CPDFDocument.init(url: URL(fileURLWithPath: item.filePath))
  397. // if document != nil {
  398. // let path = self.fetchFilePath(type: .compress, filePath: item.filePath, outputFolderPath: outputFolderPath)
  399. //
  400. // KMCompressManager.shared.compress(documentURL: URL(fileURLWithPath: item.filePath), fileURL: URL(fileURLWithPath: path), limit: false, model: data) { currentPage, totalPages in
  401. // let progress = Float(currentPage) / Float(totalPages)
  402. // self.itemProgress(item: item, processValue: progress)
  403. // } cancelHandler: {
  404. // return false
  405. // } completionHandler: { [unowned self] isFinish in
  406. // if isFinish {
  407. // self.itemSuccess(item: item)
  408. // } else {
  409. // self.itemFailure(item: item, error: nil)
  410. // }
  411. //
  412. // if i == filesData.count - 1 {
  413. // self.batchSuccess()
  414. // }
  415. // }
  416. // }
  417. //
  418. // }
  419. // }
  420. // }
  421. // }
  422. //MARK: 安全
  423. func securityExport(data: KMBatchSettingItemViewModel, outputFolderPath: String) {
  424. self.securityFile(outputFolderPath: outputFolderPath, data: data as! KMBatchSecurityViewModel, filesData: self.batchFilesData)
  425. }
  426. func securityFile(outputFolderPath: String, data: KMBatchSecurityViewModel, filesData: [KMBatchProcessingTableViewModel]) {
  427. if filesData.count != 0 {
  428. for i in 0..<filesData.count {
  429. let item = filesData[i]
  430. let docuemt = CPDFDocument.init(url: URL(fileURLWithPath: item.filePath))
  431. if (docuemt != nil) {
  432. let path = self.fetchFilePath(type: .security, filePath: item.filePath, outputFolderPath: outputFolderPath)
  433. var options: [CPDFDocumentWriteOption : Any] = [:]
  434. //开启密码
  435. if data.isOpenPassword &&
  436. !data.openPasswordString.isEmpty {
  437. options.updateValue(data.openPasswordString, forKey: .userPasswordOption)
  438. }
  439. //
  440. //权限密码
  441. if data.isPermission &&
  442. !data.permissionString.isEmpty {
  443. options.updateValue(data.permissionString, forKey: .ownerPasswordOption)
  444. }
  445. // 限制打印
  446. if data.restrictOptions.contains(.print) {
  447. options.updateValue(false, forKey: .allowsPrintingOption)
  448. } else {
  449. options.updateValue(true, forKey: .allowsPrintingOption)
  450. }
  451. //限制复制
  452. if data.restrictOptions.contains(.copy) {
  453. options.updateValue(false, forKey: .allowsCopyingOption)
  454. } else {
  455. options.updateValue(true, forKey: .allowsCopyingOption)
  456. }
  457. let result = docuemt!.write(toFile: path, withOptions: options)
  458. // let result = docuemt!.write(to: URL(fileURLWithPath: path), withOptions: options)
  459. if result {
  460. KMPrint("成功")
  461. self.itemSuccess(item: item)
  462. } else {
  463. KMPrint("失败")
  464. self.itemFailure(item: item, error: nil)
  465. }
  466. if i == filesData.count - 1 {
  467. self.batchSuccess()
  468. }
  469. }
  470. }
  471. }
  472. }
  473. //MARK: 水印
  474. func waterMarkApplay(data: KMBatchSettingItemViewModel, outputFolderPath: String) {
  475. if let data = data as? KMBatchWatermarkModel {
  476. self.waterMarkFile(outputFolderPath: outputFolderPath, data: data, filesData: self.batchFilesData)
  477. }
  478. }
  479. func waterMarkFile(outputFolderPath: String, data: KMBatchWatermarkModel, filesData: [KMBatchProcessingTableViewModel]) {
  480. if filesData.count != 0 {
  481. for i in 0..<filesData.count {
  482. let item = filesData[i]
  483. let path = self.fetchFilePath(type: .watermark, filePath: item.filePath, outputFolderPath: outputFolderPath)
  484. let document = CPDFDocument.init(url: URL(fileURLWithPath: item.filePath))
  485. if (document?.allowsPrinting == false || document?.allowsCopying == false) {
  486. let alert = NSAlert()
  487. alert.alertStyle = .critical
  488. alert.messageText = "此文档不允许修改"
  489. alert.runModal()
  490. return
  491. }
  492. if let model = data.watermarkModel, let document = document {
  493. let pageString = self.fetchValidPageIndexString(document, model: item)
  494. let watermark = KMWatermarkModel.returnWaterMarkWith(model, document)
  495. watermark.pageString = pageString
  496. document.addWatermark(watermark)
  497. }
  498. if (FileManager.default.fileExists(atPath: path)) {
  499. try?FileManager.default.removeItem(atPath: path)
  500. }
  501. let result = document?.write(to: URL(fileURLWithPath: path)) ?? false
  502. if (result) {
  503. KMPrint("removeFile成功")
  504. self.itemSuccess(item: item)
  505. } else {
  506. KMPrint("removeFile失败")
  507. self.itemFailure(item: item, error: nil)
  508. }
  509. if i == filesData.count - 1 {
  510. self.batchSuccess()
  511. }
  512. }
  513. }
  514. }
  515. //MARK: 背景
  516. func backgroundApplay(data: KMBatchSettingItemViewModel, outputFolderPath: String) {
  517. if let data = data as? KMBatchBackgroundModel {
  518. self.backgroundFile(outputFolderPath: outputFolderPath, data: data, filesData: self.batchFilesData)
  519. }
  520. }
  521. func backgroundFile(outputFolderPath: String, data: KMBatchBackgroundModel, filesData: [KMBatchProcessingTableViewModel]) {
  522. if filesData.count != 0 {
  523. for i in 0..<filesData.count {
  524. let item = filesData[i]
  525. let path = self.fetchFilePath(type: .background, filePath: item.filePath, outputFolderPath: outputFolderPath)
  526. let document = CPDFDocument.init(url: URL(fileURLWithPath: item.filePath))
  527. if (document?.allowsPrinting == false || document?.allowsCopying == false) {
  528. let alert = NSAlert()
  529. alert.alertStyle = .critical
  530. alert.messageText = "此文档不允许修改"
  531. alert.runModal()
  532. return
  533. }
  534. if let background = document?.background(), let model = data.backgroundModel, let document = document {
  535. KMBackgroundManager.defaultManager.updateBackground(background, withModel: model)
  536. let pageIndexString = self.fetchValidPageIndexString(document, model: item)
  537. background.pageString = pageIndexString
  538. background.update()
  539. }
  540. if (FileManager.default.fileExists(atPath: path)) {
  541. try?FileManager.default.removeItem(atPath: path)
  542. }
  543. let result = document?.write(to: URL(fileURLWithPath: path)) ?? false
  544. if (result) {
  545. KMPrint("removeFile成功")
  546. self.itemSuccess(item: item)
  547. } else {
  548. KMPrint("removeFile失败")
  549. self.itemFailure(item: item, error: nil)
  550. }
  551. if i == filesData.count - 1 {
  552. self.batchSuccess()
  553. }
  554. }
  555. }
  556. }
  557. //MARK: 页眉页脚
  558. func headAndFooterApplay(data: KMBatchSettingItemViewModel, outputFolderPath: String) {
  559. if let data = data as? KMBatchHeaderAndFooterModel {
  560. self.headAndFooterFile(outputFolderPath: outputFolderPath, data: data, filesData: self.batchFilesData)
  561. }
  562. }
  563. func headAndFooterFile(outputFolderPath: String, data: KMBatchHeaderAndFooterModel, filesData: [KMBatchProcessingTableViewModel]) {
  564. if filesData.count != 0 {
  565. for i in 0..<filesData.count {
  566. let item = filesData[i]
  567. let path = self.fetchFilePath(type: .headerAndFooter, filePath: item.filePath, outputFolderPath: outputFolderPath)
  568. let document = CPDFDocument.init(url: URL(fileURLWithPath: item.filePath))
  569. if (document?.allowsPrinting == false || document?.allowsCopying == false) {
  570. let alert = NSAlert()
  571. alert.alertStyle = .critical
  572. alert.messageText = "此文档不允许修改"
  573. alert.runModal()
  574. return
  575. }
  576. if let headerFooter = document?.headerFooter(), let model = data.headerFooterModel, let document = document {
  577. let pageString = self.fetchValidPageIndexString(document, model: item)
  578. KMHeaderFooterManager.defaultManager.updateCPDFHeaderFooter(headerFooter, withModel: model, Int(document.pageCount))
  579. headerFooter.pageString = pageString
  580. headerFooter.update()
  581. }
  582. if (FileManager.default.fileExists(atPath: path)) {
  583. try?FileManager.default.removeItem(atPath: path)
  584. }
  585. let result = document?.write(to: URL(fileURLWithPath: path)) ?? false
  586. if (result) {
  587. KMPrint("removeFile成功")
  588. self.itemSuccess(item: item)
  589. } else {
  590. KMPrint("removeFile失败")
  591. self.itemFailure(item: item, error: nil)
  592. }
  593. if i == filesData.count - 1 {
  594. self.batchSuccess()
  595. }
  596. }
  597. }
  598. }
  599. //MARK: 贝茨码
  600. func batesApplay(data: KMBatchSettingItemViewModel, outputFolderPath: String) {
  601. if let data = data as? KMBatchBatesModel {
  602. self.batesFile(outputFolderPath: outputFolderPath, data: data, filesData: self.batchFilesData)
  603. }
  604. }
  605. func batesFile(outputFolderPath: String, data: KMBatchBatesModel, filesData: [KMBatchProcessingTableViewModel]) {
  606. if filesData.count != 0 {
  607. for i in 0..<filesData.count {
  608. let item = filesData[i]
  609. let path = self.fetchFilePath(type: .batesNumber, filePath: item.filePath, outputFolderPath: outputFolderPath)
  610. let document = CPDFDocument.init(url: URL(fileURLWithPath: item.filePath))
  611. if (document?.allowsPrinting == false || document?.allowsCopying == false) {
  612. let alert = NSAlert()
  613. alert.alertStyle = .critical
  614. alert.messageText = "此文档不允许修改"
  615. alert.runModal()
  616. return
  617. }
  618. if let bates = document?.bates(), let model = data.batesModel, let document = document {
  619. let pageString = self.fetchValidPageIndexString(document, model: item)
  620. KMBatesManager.defaultManager.updateCPDFBates(bates, withModel: model, Int(document.pageCount))
  621. bates.pageString = pageString
  622. bates.update()
  623. }
  624. if (FileManager.default.fileExists(atPath: path)) {
  625. try?FileManager.default.removeItem(atPath: path)
  626. }
  627. let result = document?.write(to: URL(fileURLWithPath: path)) ?? false
  628. if (result) {
  629. KMPrint("removeFile成功")
  630. self.itemSuccess(item: item)
  631. } else {
  632. KMPrint("removeFile失败")
  633. self.itemFailure(item: item, error: nil)
  634. }
  635. if i == filesData.count - 1 {
  636. self.batchSuccess()
  637. }
  638. }
  639. }
  640. }
  641. //MARK: 移除
  642. func removeApplay(data: KMBatchSettingItemViewModel, outputFolderPath: String) {
  643. self.removeFile(outputFolderPath: outputFolderPath, data: data as! KMBatchRemoveViewModel, filesData: self.batchFilesData)
  644. }
  645. func removeFile(outputFolderPath: String, data: KMBatchRemoveViewModel, filesData: [KMBatchProcessingTableViewModel]) {
  646. if filesData.count != 0 {
  647. for i in 0..<filesData.count {
  648. let item = filesData[i]
  649. // DispatchQueue.global().async {
  650. let path = self.fetchFilePath(type: .batchRemove, filePath: item.filePath, outputFolderPath: outputFolderPath)
  651. let document = CPDFDocument.init(url: URL(fileURLWithPath: item.filePath))
  652. if document != nil {
  653. if (document!.allowsPrinting == false || document!.allowsCopying == false) {
  654. let alert = NSAlert()
  655. alert.alertStyle = .critical
  656. alert.messageText = "此文档不允许修改"
  657. alert.runModal()
  658. return
  659. }
  660. if (data.options.contains(.security)) {
  661. }
  662. if (data.options.contains(.batesNumber)) {
  663. let property = document!.bates()
  664. property?.clear()
  665. }
  666. if (data.options.contains(.headerAndFooter)) {
  667. let property = document!.headerFooter()
  668. property?.clear()
  669. }
  670. if (data.options.contains(.background)) {
  671. let property = document!.background()
  672. property?.clear()
  673. }
  674. if (data.options.contains(.watermark)) {
  675. let array: Array<CPDFWatermark> = document!.watermarks() ?? []
  676. for model in array {
  677. document!.removeWatermark(model)
  678. }
  679. }
  680. if (FileManager.default.fileExists(atPath: path)) {
  681. try?FileManager.default.removeItem(atPath: path)
  682. }
  683. let result = document!.write(to: URL(fileURLWithPath: path))
  684. if (result) {
  685. KMPrint("removeFile成功")
  686. self.itemSuccess(item: item)
  687. } else {
  688. KMPrint("removeFile失败")
  689. self.itemFailure(item: item, error: nil)
  690. }
  691. if i == filesData.count - 1 {
  692. self.batchSuccess()
  693. }
  694. }
  695. // }
  696. }
  697. }
  698. }
  699. //MARK: 图片转PDF
  700. func imageToPDFExport(data: KMBatchSettingItemViewModel, outputFolderPath: String) {
  701. self.imageToPDFFile(outputFolderPath: outputFolderPath, data: data as! KMBatchImageToPDFModel, filesData: self.batchFilesData)
  702. }
  703. func imageToPDFFile(outputFolderPath: String, data: KMBatchImageToPDFModel, filesData: [KMBatchProcessingTableViewModel]) {
  704. if filesData.count != 0 {
  705. self.batchProgress()
  706. if data.isNewPDF {
  707. if data.isMergeAll {
  708. let item = filesData[0]
  709. let path = self.fetchFilePath(type: .imageToPDF, filePath: item.filePath, outputFolderPath: outputFolderPath)
  710. let pdfDocument = CPDFDocument()
  711. for item in filesData {
  712. pdfDocument?.km_insert(image: item.image, at: pdfDocument?.pageCount ?? 0)
  713. }
  714. if data.isOCR {
  715. let model = KMOCRModel()
  716. model.showType = .page
  717. model.saveAsPDF = true
  718. model.ocrType = data.ocrType
  719. model.languageType = data.languageType
  720. model.needTxT = data.isExtractText
  721. model.pageRangeType = .all
  722. //计算需要处理的页面
  723. let pages:[Int] = KMOCRManager.fetchPageIndex(document: pdfDocument!, model: model)
  724. model.pageRange = pages
  725. self.batchProgress()
  726. self.convertOCR(outputFolderPath: outputFolderPath, document: pdfDocument!, fileName: path.deletingPathExtension.lastPathComponent, data: model) { progress in
  727. self.batchProgress()
  728. } complete: { document, text, error in
  729. self.batchSuccess()
  730. }
  731. } else {
  732. let success = pdfDocument?.write(toFile: path)
  733. if success != nil {
  734. for item in filesData {
  735. self.itemSuccess(item: item)
  736. }
  737. self.batchSuccess()
  738. } else {
  739. self.batchFailure()
  740. }
  741. }
  742. } else {
  743. processFile(at: 0, outputFolderPath: outputFolderPath, data: data)
  744. }
  745. } else {
  746. let selectFilePath = data.selectFilePath
  747. if selectFilePath.count == 0 {
  748. let alert = NSAlert()
  749. alert.alertStyle = .critical
  750. alert.messageText = KMLocalizedString("文件未选择")
  751. alert.runModal()
  752. return
  753. }
  754. var fileName = selectFilePath.deletingPathExtension.lastPathComponent
  755. let path = self.fetchFilePath(type: .imageToPDF, filePath: selectFilePath, outputFolderPath: outputFolderPath)
  756. var pdfDocument = CPDFDocument(url: NSURL(fileURLWithPath: selectFilePath) as URL)
  757. let count: Int = Int(pdfDocument?.pageCount ?? 0)
  758. for item in filesData {
  759. pdfDocument?.km_insert(image: item.image, at: UInt(count))
  760. }
  761. if data.isOCR {
  762. let model = KMOCRModel()
  763. model.showType = .page
  764. model.saveAsPDF = true
  765. model.ocrType = data.ocrType
  766. model.languageType = data.languageType
  767. model.needTxT = data.isExtractText
  768. model.pageRangeType = .all
  769. //计算需要处理的页面
  770. let pages:[Int] = KMOCRManager.fetchPageIndex(document: pdfDocument!, model: model)
  771. model.pageRange = pages
  772. self.convertOCR(outputFolderPath: outputFolderPath, document: pdfDocument!, fileName: fileName, data: model) { progress in
  773. } complete: { document, text, error in
  774. if (error != nil) {
  775. self.batchFailure()
  776. } else {
  777. for item in filesData {
  778. self.itemSuccess(item: item)
  779. }
  780. self.batchSuccess()
  781. }
  782. }
  783. } else {
  784. let success = pdfDocument?.write(toFile: path)
  785. if success != nil {
  786. for item in filesData {
  787. self.itemSuccess(item: item)
  788. }
  789. self.batchSuccess()
  790. } else {
  791. self.batchFailure()
  792. }
  793. }
  794. }
  795. }
  796. }
  797. func processFile(at index: Int, outputFolderPath: String, data: KMBatchImageToPDFModel) {
  798. guard index < filesData.count else {
  799. self.batchSuccess()
  800. return
  801. }
  802. let item = filesData[index]
  803. if data.isOCR {
  804. let path = self.fetchFilePath(type: .imageToPDF, filePath: item.filePath, outputFolderPath: outputFolderPath)
  805. let pdfDocument = CPDFDocument()
  806. pdfDocument?.km_insert(image: item.image, at: pdfDocument?.pageCount ?? 0)
  807. let model = KMOCRModel()
  808. model.showType = .page
  809. model.saveAsPDF = true
  810. model.ocrType = data.ocrType
  811. model.languageType = data.languageType
  812. model.needTxT = data.isExtractText
  813. model.pageRangeType = .all
  814. let pages: [Int] = KMOCRManager.fetchPageIndex(document: pdfDocument!, model: model)
  815. model.pageRange = pages
  816. self.itemProgress(item: item, processValue: 0)
  817. self.convertOCR(outputFolderPath: outputFolderPath, document: pdfDocument!, fileName: path.deletingPathExtension.lastPathComponent, data: model) { [unowned self] progress in
  818. self.itemProgress(item: item, processValue: progress)
  819. } complete: { [unowned self] document, text, error in
  820. self.itemSuccess(item: filesData[index])
  821. processFile(at: index + 1, outputFolderPath: outputFolderPath, data: data)
  822. }
  823. } else {
  824. let path = self.fetchFilePath(type: .imageToPDF, filePath: item.filePath, outputFolderPath: outputFolderPath)
  825. let pdfDocument = CPDFDocument()
  826. pdfDocument?.km_insert(image: item.image, at: pdfDocument?.pageCount ?? 0)
  827. let success = pdfDocument?.write(toFile: path)
  828. if success != nil {
  829. self.itemSuccess(item: item)
  830. processFile(at: index + 1, outputFolderPath: outputFolderPath, data: data)
  831. } else {
  832. self.itemFailure(item: item, error: nil)
  833. }
  834. }
  835. }
  836. }
  837. //MARK: private
  838. extension KMBatchManager {
  839. func fetchValidPageIndexString(_ document: CPDFDocument, model: KMBatchProcessingTableViewModel) -> String? {
  840. if model.pageRange == .all {
  841. let pages = Array(0..<Int(document.pageCount))
  842. let pageIndexString = pages.isEmpty ? "" : pages.map { "\($0)" }.joined(separator: ",")
  843. return pageIndexString
  844. } else {
  845. let data = KMOCRModel()
  846. data.pageRangeType = model.pageRange
  847. data.pageRangeString = model.pageRangeString
  848. let pages:[Int] = KMOCRManager.fetchPageIndex(document: document, model: data)
  849. let pageIndexString = pages.isEmpty ? "" : pages.map { "\($0)" }.joined(separator: ",")
  850. return pageIndexString
  851. }
  852. return nil
  853. }
  854. func fetchValidPageIndexs(_ document: CPDFDocument, model: KMBatchProcessingTableViewModel) -> [Int]? {
  855. if model.pageRange == .all {
  856. let pages = Array(0..<Int(document.pageCount))
  857. return pages
  858. } else {
  859. let data = KMOCRModel()
  860. data.pageRangeType = model.pageRange
  861. data.pageRangeString = model.pageRangeString
  862. let pages:[Int] = KMOCRManager.fetchPageIndex(document: document, model: data)
  863. return pages
  864. }
  865. return []
  866. }
  867. func fetchDocument(filePath: String, model: KMBatchProcessingTableViewModel) -> CPDFDocument {
  868. var document = CPDFDocument(url: URL(fileURLWithPath: filePath))
  869. if model.password.count != 0 {
  870. document?.unlock(withPassword: model.password)
  871. }
  872. if model.pageRange == .all {
  873. } else {
  874. let data = KMOCRModel()
  875. data.pageRangeType = model.pageRange
  876. data.pageRangeString = model.pageRangeString
  877. let pages:[Int] = KMOCRManager.fetchPageIndex(document: document!, model: data)
  878. var tempDocument = CPDFDocument()
  879. for i in 0..<pages.count {
  880. let page = document?.page(at: UInt(i))
  881. tempDocument?.insertPageObject(page, at: tempDocument?.pageCount ?? 0)
  882. }
  883. let fileName = filePath.deletingPathExtension.lastPathComponent
  884. let isSuccess = tempDocument?.write(toFile: self.fetchTempFilePath(fileName: fileName))
  885. if isSuccess != nil {
  886. document = tempDocument
  887. }
  888. }
  889. return document ?? CPDFDocument()
  890. }
  891. func fetchTempFilePath(fileName: String) -> String {
  892. let floderPath = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.applicationSupportDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).last?.stringByAppendingPathComponent(Bundle.main.bundleIdentifier!).stringByAppendingPathComponent("BatchTemp")
  893. let filePath = floderPath?.stringByAppendingPathComponent("\(fileName).pdf")
  894. if let data = floderPath, !FileManager.default.fileExists(atPath: data) {
  895. try?FileManager.default.createDirectory(atPath: data, withIntermediateDirectories: false)
  896. }
  897. if let data = filePath, !FileManager.default.fileExists(atPath: data) {
  898. FileManager.default.createFile(atPath: data, contents: nil)
  899. }
  900. return filePath ?? ""
  901. }
  902. func removeTempFilePath(filePath: String) {
  903. let fileName = filePath.deletingPathExtension.lastPathComponent
  904. let path = self.fetchTempFilePath(fileName: fileName)
  905. if (FileManager.default.fileExists(atPath: path)) {
  906. try?FileManager.default.removeItem(atPath: path)
  907. }
  908. }
  909. func fetchFilePath(type: KMBatchCollectionViewType, filePath: String, outputFolderPath: String) -> String {
  910. var fileName = filePath.deletingPathExtension.lastPathComponent
  911. if fileName.isEmpty {
  912. fileName = NSLocalizedString("Untitled", comment: "")
  913. }
  914. var typeString = ""
  915. switch type {
  916. case .compress:
  917. typeString = "_compress"
  918. case .OCR:
  919. typeString = "_OCR"
  920. case .security:
  921. typeString = "_security"
  922. case .watermark:
  923. typeString = "_watermark"
  924. case .background:
  925. typeString = "_background"
  926. case .headerAndFooter:
  927. typeString = "_headerAndFooter"
  928. case .batesNumber:
  929. typeString = "_bates"
  930. default:
  931. typeString = ""
  932. }
  933. var path = "\(outputFolderPath)/\(fileName)\(typeString).pdf"
  934. return path
  935. }
  936. func isContainLockFiles() -> Bool {
  937. var isContainFiles = false
  938. for item in filesData {
  939. if !item.isLock {
  940. } else {
  941. isContainFiles = true
  942. break
  943. }
  944. }
  945. return isContainFiles
  946. }
  947. static func supportedImageTypes() -> [String] {
  948. return ["jpg", "cur", "bmp", "jpeg", "gif", "png", "tiff", "tif", "ico", "icns", "tga", "psd", "eps", "hdr", "jp2", "jpc", "pict", "sgi"]
  949. }
  950. }
  951. //MARK: Alert
  952. extension KMBatchManager {
  953. func batchUnkown() {
  954. for item in filesData {
  955. self.itemUnkown(item: item)
  956. }
  957. self.state = .unknow
  958. NotificationCenter.default.post(name: NSNotification.Name(kBacthProcessNotification), object: nil)
  959. }
  960. func batchBegin() {
  961. for item in filesData {
  962. self.itemUnkown(item: item)
  963. }
  964. self.state = .begin
  965. NotificationCenter.default.post(name: NSNotification.Name(kBacthProcessNotification), object: nil)
  966. }
  967. func batchProgress() {
  968. for item in filesData {
  969. self.itemProgress(item: item, processValue: 0)
  970. }
  971. self.state = .processing
  972. NotificationCenter.default.post(name: NSNotification.Name(kBacthProcessNotification), object: nil)
  973. }
  974. func batchSuccess() {
  975. if FileManager.default.fileExists(atPath: self.outputFolderPath) {
  976. NSWorkspace.shared.activateFileViewerSelecting([URL(fileURLWithPath: self.outputFolderPath)])
  977. }
  978. self.state = .complete
  979. NotificationCenter.default.post(name: NSNotification.Name(kBacthProcessNotification), object: nil)
  980. }
  981. func batchFailure() {
  982. if FileManager.default.fileExists(atPath: self.outputFolderPath) {
  983. NSWorkspace.shared.activateFileViewerSelecting([URL(fileURLWithPath: self.outputFolderPath)])
  984. }
  985. self.state = .error
  986. NotificationCenter.default.post(name: NSNotification.Name(kBacthProcessNotification), object: nil)
  987. }
  988. func itemUnkown(item: KMBatchProcessingTableViewModel) {
  989. item.state = .clock
  990. NotificationCenter.default.post(name: NSNotification.Name(kBacthFilesProcessNotification), object: item)
  991. }
  992. func itemProgress(item: KMBatchProcessingTableViewModel, processValue: Float) {
  993. if processValue > 0.7 {
  994. item.state = .loading70
  995. } else {
  996. item.state = .loading
  997. }
  998. NotificationCenter.default.post(name: NSNotification.Name(kBacthFilesProcessNotification), object: item)
  999. }
  1000. func itemSuccess(item: KMBatchProcessingTableViewModel) {
  1001. self.removeTempFilePath(filePath: item.filePath)
  1002. item.state = .success
  1003. NotificationCenter.default.post(name: NSNotification.Name(kBacthFilesProcessNotification), object: item)
  1004. }
  1005. func itemFailure(item: KMBatchProcessingTableViewModel, error: NSError?) {
  1006. self.removeTempFilePath(filePath: item.filePath)
  1007. item.state = .error
  1008. NotificationCenter.default.post(name: NSNotification.Name(kBacthFilesProcessNotification), object: item)
  1009. guard let error = error else { return }
  1010. var errorString = ""
  1011. let myError: NSError = error as NSError
  1012. if myError.code == 1 {
  1013. errorString = NSLocalizedString("Password required or incorrect password. Please re-enter your password and try again", comment: "")
  1014. } else if myError.code == 2 {
  1015. errorString = NSLocalizedString("The license doesn't allow the permission", comment: "")
  1016. } else if myError.code == 3 {
  1017. errorString = NSLocalizedString("Malloc failure", comment: "")
  1018. } else if myError.code == 4 {
  1019. errorString = NSLocalizedString("Unknown error in processing conversion. Please try again later", comment: "")
  1020. } else if myError.code == 5 {
  1021. errorString = NSLocalizedString("Unknown error in processing PDF. Please try again later", comment: "")
  1022. } else if myError.code == 6 {
  1023. errorString = NSLocalizedString("File not found or could not be opened. Check if your file exists or choose another file to convert", comment: "")
  1024. } else if myError.code == 7 {
  1025. errorString = NSLocalizedString("File not in PDF format or corruptead. Change a PDF file and try again", comment: "")
  1026. } else if myError.code == 8 {
  1027. errorString = NSLocalizedString("Unsupported security scheme", comment: "")
  1028. } else if myError.code == 9 {
  1029. errorString = NSLocalizedString("Page not found or content error", comment: "")
  1030. } else if myError.code == 404 {
  1031. errorString = NSLocalizedString("Please check if the information is wrong or the network is error.", comment: "")
  1032. } else {
  1033. errorString = NSLocalizedString("Table not found", comment: "")
  1034. }
  1035. let alert = NSAlert()
  1036. alert.alertStyle = .critical
  1037. alert.messageText = NSLocalizedString("Conversion Failed", comment: "")
  1038. alert.informativeText = errorString
  1039. alert.addButton(withTitle: NSLocalizedString("OK", comment: ""))
  1040. alert.runModal()
  1041. }
  1042. }