KMMergeWindowController.swift 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. //
  2. // KMMergeWindowController.swift
  3. // PDF Master
  4. //
  5. // Created by lizhe on 2023/11/8.
  6. //
  7. import Cocoa
  8. typealias KMMergeWindowControllerCancelAction = (_ controller: KMMergeWindowController) -> Void
  9. typealias KMMergeWindowControllerAddFilesAction = (_ controller: KMMergeWindowController) -> Void
  10. typealias KMMergeWindowControllerMergeAction = (_ controller: KMMergeWindowController) -> Void
  11. typealias KMMergeWindowControllerClearAction = (_ controller: KMMergeWindowController) -> Void
  12. class KMMergeWindowController: NSWindowController {
  13. @IBOutlet weak var mergeView: KMMergeView!
  14. var cancelAction: KMMergeWindowControllerCancelAction?
  15. var pdfDocument: PDFDocument = PDFDocument()
  16. var password: String = ""
  17. var oriDucumentUrl: URL?
  18. // - (id)initWithPDFDocument:(PDFDocument *)document password:(NSString *)password
  19. // {
  20. // if (self = [super initWithWindowNibName:@"KMPDFEditAppendWindow"]) {
  21. //
  22. // // self.PDFDocument = document;
  23. // self.PDFDocument = [[PDFDocument alloc] init];
  24. // self.editType = KMPDFPageEditAppend;
  25. // _lockFilePathArr = [[NSMutableArray alloc] init];
  26. // _files = [[NSMutableArray alloc] init];
  27. //
  28. // KMFileAttribute *file = [[KMFileAttribute alloc] init];
  29. // file.myPDFDocument = document;
  30. // file.filePath = document.documentURL.path;
  31. // file.oriFilePath = self.oriDucumentUrl.path;
  32. // if (password && password.length > 0) {
  33. // file.password = password;
  34. // file.isLocked = YES;
  35. // }
  36. // [self.files addObject:file];
  37. // }
  38. // return self;
  39. // }
  40. convenience init(document: PDFDocument, password: String) {
  41. self.init(windowNibName: "KMMergeWindowController")
  42. }
  43. override func windowDidLoad() {
  44. super.windowDidLoad()
  45. self.window!.title = NSLocalizedString("Merge PDF Files", comment: "");
  46. // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
  47. mergeView.addFilesAction = { [unowned self] view in
  48. self.addFile()
  49. }
  50. mergeView.clearAction = { [unowned self] view in
  51. }
  52. mergeView.mergeAction = { [unowned self] view, files, size in
  53. self.mergeFiles(files: files, size: size)
  54. }
  55. mergeView.cancelAction = { [unowned self] view in
  56. cancelAction?(self)
  57. }
  58. }
  59. }
  60. extension KMMergeWindowController {
  61. func addFile() {
  62. // if (![IAPProductsManager defaultManager].isAvailableAllFunction) {
  63. // //免費版只支援2個檔案做合併小于20M的文件合并
  64. // if (_files.count >= 2 || self.allFileSize > (20 * 1024 * 1024)) {
  65. // #if VERSION_DMG
  66. // [[KMPurchaseCompareWindowController sharedInstance] showWindow:nil];
  67. // #else
  68. // KMToolCompareWindowController * vc = [KMToolCompareWindowController toolCompareWithType:KMCompareWithToolType_PageEdit setSelectIndex:1];
  69. // [vc showWindow:nil];
  70. // #endif
  71. // return;
  72. // }
  73. //
  74. // }
  75. let openPanel = NSOpenPanel()
  76. openPanel.allowedFileTypes = ["pdf"]
  77. if KMPurchaseManager.manager.state == .subscription {
  78. openPanel.allowsMultipleSelection = true
  79. openPanel.message = NSLocalizedString("Select files to merge. To select multiple files press cmd ⌘ button on the keyboard and click on the target files one by one.", comment: "")
  80. } else {
  81. openPanel.allowsMultipleSelection = false
  82. openPanel.message = NSLocalizedString("Select files to merge, only one file can be selected at a time.", comment: "")
  83. }
  84. openPanel.beginSheetModal(for: self.window!) { (result) in
  85. if result == NSApplication.ModalResponse.OK {
  86. var array: [URL] = []
  87. for fileURL in openPanel.urls {
  88. array.append(fileURL)
  89. // if let filePath = fileURL.path {
  90. // if !FileManager.default.isExecutableFile(atPath: filePath) {
  91. // continue
  92. // }
  93. //
  94. // do {
  95. // let attrib = try FileManager.default.attributesOfItem(atPath: filePath)
  96. //// if let fileSize = attrib[FileAttributeKey.size] as? NSNumber {
  97. //// self.allFileSize += fileSize.floatValue
  98. //// }
  99. ////
  100. //// if !IAPProductsManager.defaultManager.isAvailableAllFunction {
  101. //// if self.allFileSize > (20 * 1024 * 1024) || self.files.count >= 2 {
  102. //// let vc = KMToolCompareWindowController.toolCompare(withType: .pageEdit, setSelectIndex: 1)
  103. //// vc?.showWindow(nil)
  104. //// self.allFileSize -= fileSize.floatValue
  105. //// self.addFiles(array)
  106. //// return
  107. //// }
  108. //// }
  109. // array.append(fileURL)
  110. // } catch {
  111. // print("Error getting file attributes: \(error)")
  112. // }
  113. // }
  114. }
  115. self.mergeView.addFilePaths(urls: array)
  116. }
  117. }
  118. }
  119. func mergeFiles(files: [KMFileAttribute], size: CGSize = CGSizeZero) {
  120. if files.count <= 1 {
  121. let alert = NSAlert.init()
  122. alert.alertStyle = .critical
  123. alert.messageText = NSLocalizedString("To start merging, please select at least 2 files.", comment: "")
  124. alert.runModal()
  125. return
  126. }
  127. // _isSuccessfully = NO;
  128. // [self.nCancelVC setEnabled:NO];
  129. // self.canMerge = NO;
  130. //
  131. var rootPDFOutlineArray: [PDFOutline] = []
  132. var allPage = true //只有是全部才支持大纲的合并
  133. for file in files {
  134. if file.fetchSelectPages().count == 0 {
  135. let alert = NSAlert.init()
  136. alert.alertStyle = .critical
  137. alert.messageText = "\(file.filePath.lastPathComponent) + \(NSLocalizedString("Invalid page range or the page number is out of range. Please try again.", comment: ""))"
  138. alert.runModal()
  139. return
  140. }
  141. allPage = file.bAllPage
  142. let tDocument = PDFDocument(url: NSURL(fileURLWithPath: file.filePath) as URL)!
  143. var outlineArray: [PDFOutline] = []
  144. if file.isLocked {
  145. tDocument.unlock(withPassword: file.password!)
  146. }
  147. if tDocument.outlineRoot != nil {
  148. rootPDFOutlineArray.append((tDocument.outlineRoot)!)
  149. self.fetchAllOfChildren((tDocument.outlineRoot)!, containerArray: &outlineArray)
  150. outlineArray.removeObject((tDocument.outlineRoot)!)
  151. } else {
  152. let rootOutline = PDFOutline.init()
  153. tDocument.outlineRoot = rootOutline
  154. if tDocument.outlineRoot != nil {
  155. rootPDFOutlineArray.append(tDocument.outlineRoot!)
  156. }
  157. }
  158. for number in file.fetchSelectPages() {
  159. let page = tDocument.page(at: number.intValue - 1)
  160. self.pdfDocument.insert(page!, at: self.pdfDocument.pageCount)
  161. // self.insertIndexSet.addIndex:(self.pdfDocument.pageCount - 1)
  162. }
  163. }
  164. let fileName = (files.first?.filePath.lastPathComponent)! + "_Merged"
  165. DispatchQueue.main.async {
  166. self.pdfDocument.outlineRoot = PDFOutline.init()
  167. if allPage {
  168. var insertIndex = 0
  169. for i in 0..<rootPDFOutlineArray.count {
  170. let rootOutline = rootPDFOutlineArray[i]
  171. for j in 0..<rootOutline.numberOfChildren {
  172. self.pdfDocument.outlineRoot?.insertChild(rootOutline.child(at: j)!, at: insertIndex)
  173. }
  174. insertIndex = insertIndex + 1
  175. }
  176. self.handleReDraw()
  177. let savePanelAccessoryViewController = KMSavePanelAccessoryController.init()
  178. let savePanel = NSSavePanel()
  179. savePanel.nameFieldStringValue = fileName
  180. savePanel.allowedFileTypes = ["pdf"]
  181. savePanel.accessoryView = savePanelAccessoryViewController.view
  182. // self.savePanelAccessoryViewController = savePanelAccessoryViewController;
  183. savePanel.beginSheetModal(for: self.window!) { result in
  184. if result != nil {
  185. var outputSavePanel = savePanel.url?.path
  186. DispatchQueue.main.async {
  187. var success = self.pdfDocument.write(toFile: outputSavePanel!)
  188. if !success {
  189. success = ((try?self.pdfDocument.dataRepresentation()?.write(to: URL(string: outputSavePanel!)!)) != nil)
  190. }
  191. if success {
  192. if savePanelAccessoryViewController.needOpen {
  193. NSDocumentController.shared.openDocument(withContentsOf: savePanel.url!, display: true) { document, open, error in
  194. }
  195. } else {
  196. NSWorkspace.shared.activateFileViewerSelecting([NSURL(fileURLWithPath: outputSavePanel!) as URL])
  197. }
  198. } else {
  199. let alert = NSAlert.init()
  200. alert.alertStyle = .critical
  201. alert.messageText = "\(String(describing: files.first?.filePath.lastPathComponent)) + \(NSLocalizedString("Failed to merge!", comment: ""))"
  202. alert.runModal()
  203. }
  204. }
  205. }
  206. }
  207. }
  208. }
  209. }
  210. func fetchAllOfChildren(_ aOutline: PDFOutline, containerArray aMArray: inout [PDFOutline]) {
  211. if !aMArray.contains(aOutline) {
  212. aMArray.append(aOutline)
  213. }
  214. for i in 0..<aOutline.numberOfChildren {
  215. if let childOutline = aOutline.child(at: i) {
  216. aMArray.append(childOutline)
  217. fetchAllOfChildren(childOutline, containerArray: &aMArray)
  218. }
  219. }
  220. }
  221. func handleReDraw() {
  222. if mergeView.originalSizeButton.state == .on {
  223. } else {
  224. let size = self.mergeView.newPageSize
  225. if size.width < 0 {
  226. return
  227. }
  228. var pagesArray: [PDFPage] = []
  229. let pageCount = self.pdfDocument.pageCount
  230. for i in 0..<pageCount {
  231. pagesArray.append(self.pdfDocument.page(at: i)!)
  232. self.pdfDocument.removePage(at: 0)
  233. }
  234. for i in 0..<pageCount {
  235. let page: KMMergePDFPage = KMMergePDFPage.init()
  236. page.setBounds(CGRectMake(0, 0, size.width, size.height), for: .mediaBox)
  237. page.drawingPage = pagesArray[i]
  238. self.pdfDocument.insert(page, at: i)
  239. }
  240. if self.pdfDocument.outlineRoot != nil {
  241. let childCount = self.pdfDocument.outlineRoot?.numberOfChildren
  242. var outlineArray: [PDFOutline] = []
  243. for i in 0..<childCount! {
  244. outlineArray.append((self.pdfDocument.outlineRoot?.child(at: i))!)
  245. }
  246. for outline in outlineArray {
  247. outline.removeFromParent()
  248. }
  249. }
  250. }
  251. }
  252. }
  253. class KMMergePDFPage: PDFPage {
  254. var drawingPage: PDFPage?
  255. override func draw(with box: PDFDisplayBox, to context: CGContext) {
  256. super.draw(with: box, to: context)
  257. let pageSize = self.bounds(for: .cropBox).size
  258. self.drawPage(with: context, page: self.drawingPage!, pageSize: pageSize)
  259. }
  260. func drawPage(with context: CGContext, page: PDFPage, pageSize: CGSize) {
  261. var originalSize = page.bounds(for: .cropBox).size
  262. // 如果页面的旋转角度为90或者270,宽高交换
  263. if page.rotation % 180 != 0 {
  264. originalSize = CGSize(width: originalSize.height, height: originalSize.width)
  265. }
  266. let wRatio = pageSize.width / originalSize.width
  267. let hRatio = pageSize.height / originalSize.height
  268. let ratio = min(wRatio, hRatio)
  269. context.saveGState()
  270. let xTransform = (pageSize.width - originalSize.width * ratio) / 2
  271. let yTransform = (pageSize.height - originalSize.height * ratio) / 2
  272. context.translateBy(x: xTransform, y: yTransform)
  273. context.scaleBy(x: ratio, y: ratio)
  274. if #available(macOS 10.12, *) {
  275. page.draw(with: .cropBox, to: context)
  276. page.transformContext(for: .cropBox)
  277. } else {
  278. NSGraphicsContext.saveGraphicsState()
  279. NSGraphicsContext.current = NSGraphicsContext(cgContext: context, flipped: false)
  280. page.draw(with: .cropBox)
  281. NSGraphicsContext.restoreGraphicsState()
  282. page.transformContext(for: .cropBox)
  283. }
  284. context.restoreGState()
  285. }
  286. }