// // SplitWindowController.swift // PDF Reader Pro // // Created by tangchao on 2023/11/14. // import Cocoa class SplitWindowController: KMBaseWindowController { /* @protocol SplitDelegate @optional - (void)didFinshedSplitWithURL:(NSURL*)url; @end @property (nonatomic,assign) id splitDelegate; */ var fileAttribute: KMFileAttribute? //转圈提示 @IBOutlet var progress: NSProgressIndicator! //分割后文档存储所在文件夹名称TextField容器View @IBOutlet var nameView: NSView! //分割后文档存储所在文件夹名称 TextField @IBOutlet var partFileNameLabel: NSTextField! @IBOutlet var originalFileButton: NSButton! //分割提示 Label @IBOutlet var splitLabel: NSTextField! @IBOutlet var splitMethodBox: NSBox! @IBOutlet var fileNameBox: NSBox! @IBOutlet var exampleLabel: NSTextField! //平均页数分割单位Pages Label @IBOutlet var pagesLabel: NSTextField! //平均分割为多少个PDF文件单位 Label @IBOutlet var filesLabel: NSTextField! //导出目录提示 Label @IBOutlet var labelingLabel: NSTextField! //按页分割页数输入 TextField @IBOutlet var byPageText: NSTextField! //按文档数量均分文档个数输入 TextField @IBOutlet var byFileText: NSTextField! //文档名称分割符输入 TextField @IBOutlet var separatorText: NSTextField! //新增文档命名拼接字段输入 TextField @IBOutlet var renameText: NSTextField! //按页分割 Button @IBOutlet var byPageButton: NSButton! //按分割文档个数分割 Button @IBOutlet var byFileButton: NSButton! //手动输入页分割 Button @IBOutlet var pageFromButton: NSButton! //取消 Button @IBOutlet var cancelButton: NSButton! //分割 Button @IBOutlet var splitButton: NSButton! //按文档个数分割,文档个数步进器 @IBOutlet var byFileStepper: NSStepper! //按页均分分割,单个文档页数步进器 @IBOutlet var byPageStepper: NSStepper! //新增文件夹名称拼接输入选择 Button @IBOutlet var renameButton: NSButton! //新增文件夹名字拼接 Button @IBOutlet var separatorButton: NSButton! //页面选择 ComboBox @IBOutlet var pageRangeComboBox: NSComboBox! @IBOutlet var splitMethodLabel: NSTextField! @IBOutlet var fileNameLabel: NSTextField! private var _fileURL: URL? private var _PDFDocument: CPDFDocument? private var _isSuccessfully = false //密码 var password: String? //分割样式 private var _splitType: Int = 0 convenience init(document: CPDFDocument) { self.init(windowNibName: "SplitWindowController") self.fileAttribute = KMFileAttribute() self._fileURL = document.documentURL self._PDFDocument = document self.fileAttribute?.pdfDocument = document if IAPProductsManager.default().isAvailableAllFunction() { self._splitType = 3 } } convenience init(fileURL documentPath: URL) { self.init(windowNibName: "SplitWindowController") self.fileAttribute = KMFileAttribute() self._fileURL = documentPath self._PDFDocument = CPDFDocument(url: documentPath) } override func windowDidLoad() { super.windowDidLoad() self._localizedLanguage() self._setInputRange() self._configuViews() } override func interfaceThemeDidChanged(_ appearance: NSAppearance.Name) { // KMPrint("appearance") } @IBAction func byPageStepperAction(_ sender: AnyObject) { self.byPageText.stringValue = String(format: "%ld", self.byPageStepper.integerValue) } @IBAction func byFileStepperAction(_ sender: AnyObject) { self.byFileText.stringValue = String(format: "%ld", self.byFileStepper.integerValue) } @IBAction func buttonItemClicked_Cancel(_ sender: AnyObject) { self.km_quick_endSheet() } @IBAction func buttonItemClicked_ByPage(_ sender: AnyObject) { self.byFileButton.state = .off self.pageFromButton.state = .off self.byPageText.isEditable = true self.pageRangeComboBox.isEditable = false self.pageRangeComboBox.isEditable = false self.window?.makeFirstResponder(self) self.pageRangeComboBox.isEnabled = false self.byFileText.isEditable = false self.byFileStepper.isEnabled = false self.byPageStepper.isEnabled = true self.fileAttribute?.bAllPage = true } @IBAction func buttonItemClicked_ByFile(_ sender: AnyObject) { self.byPageButton.state = .off self.pageFromButton.state = .off self.byPageText.isEditable = false self.pageRangeComboBox.isEditable = false self.pageRangeComboBox.isSelectable = false self.window?.makeFirstResponder(self) self.pageRangeComboBox.isEnabled = false self.byFileText.isEditable = true self.byFileStepper.isEnabled = true self.byPageStepper.isEnabled = false self.fileAttribute?.bAllPage = true } @IBAction func buttonItemClicked_ByRange(_ sender: AnyObject) { if !IAPProductsManager.default().isAvailableAllFunction() { pageFromButton.state = .off KMPurchaseCompareWindowController.sharedInstance().showWindow(nil) return; } self.byFileButton.state = .off self.byPageButton.state = .off self.byPageText.isEditable = false self.pageRangeComboBox.isEnabled = true if (self.pageRangeComboBox.indexOfSelectedItem == 2) { self.pageRangeComboBox.isSelectable = true self.pageRangeComboBox.isEditable = true self.pageRangeComboBox.stringValue = "" self.window?.makeFirstResponder(self.pageRangeComboBox) } self.byFileText.isEditable = false self.byFileStepper.isEnabled = false self.byPageStepper.isEnabled = false self.fileAttribute?.bAllPage = false } @IBAction func buttonItemClicked_Split(_ sender: AnyObject) { var fileAttribute: KMFileAttribute? if (self.pageFromButton.state == .on) { //选择page fileAttribute = self.fileAttribute fileAttribute?.filePath = self._fileURL?.path ?? "" fileAttribute?.bAllPage = false let pageCnt = self._PDFDocument?.pageCount ?? 0 if self.pageRangeComboBox.stringValue == KMLocalizedString("Odd Pages Only") { var tPagesString = "" for i in 0 ..< pageCnt { if (i%2 == 0) { if (tPagesString.isEmpty) { tPagesString.append("\(i+1)") }else{ tPagesString.append(",\(i+1)") } } } fileAttribute?.pagesString = tPagesString fileAttribute?.pagesType = .odd } else if self.pageRangeComboBox.stringValue == KMLocalizedString("Even Pages Only") { var tPagesString = "" for i in 0 ..< pageCnt { if (i%2 == 1) { if (tPagesString.isEmpty) { tPagesString.append("\(i+1)") }else{ tPagesString.append(",\(i+1)") } } } fileAttribute?.pagesString = tPagesString fileAttribute?.pagesType = .even } else { fileAttribute?.pagesString = self.pageRangeComboBox.stringValue fileAttribute?.pagesType = .custom } if let data = fileAttribute?.fetchSelectPages().isEmpty, data { KMAlertTool.runModelForMainThread(message: KMLocalizedString("Invalid page range or the page number is out of range. Please try again.")) return } } let panel = NSOpenPanel() panel.canChooseFiles = false panel.canChooseDirectories = true panel.canCreateDirectories = true panel.beginSheetModal(for: self.window!) { result in if (result == .OK) { let outputURL = panel.urls.first var tFolderPath = outputURL!.path var index = 0 let folderName = self.partFileNameLabel.stringValue.deletingPathExtension.lastPathComponent var folderPath = "\(tFolderPath)" + "/" + "\(folderName)" //创建目录 while FileManager.default.fileExists(atPath: folderPath) { index += 1 folderPath = "\(tFolderPath)" + "/" + "\(folderName)_\(index)" } tFolderPath = folderPath try?FileManager.default.createDirectory(atPath: tFolderPath, withIntermediateDirectories: true) self.progress.isHidden = false self.cancelButton.isEnabled = false self.splitButton.isEnabled = false self.progress.startAnimation(nil) if (self.byPageButton.state == .on) { let index = self.byPageStepper.integerValue; DispatchQueue.global().async { let successArray = self._PDFDocument?.splitByPagesWith(index, folerPath: tFolderPath, fileName: folderName) ?? [] DispatchQueue.main.async { self.progress.isHidden = true self.cancelButton.isEnabled = true self.splitButton.isEnabled = true if (successArray.isEmpty == false) { let response = KMAlertTool.runModelForMainThread_r(message: KMLocalizedString("Splitting completed. Tap 'OK' to open the output folder."), buttons: [KMLocalizedString("OK")]) if (response == .alertFirstButtonReturn ) { self.km_quick_endSheet() DispatchQueue.main.asyncAfter(deadline: .now()+0.5) { let filePath = successArray.first ?? "" self._viewFileAtFinder(filePath) } } } else { let response = KMAlertTool.runModelForMainThread_r(message: KMLocalizedString("Failed to split!")) if (response == .alertFirstButtonReturn) { self.km_quick_endSheet() } } } } } else if (self.byFileButton.state == .on) { let dex = self.byFileStepper.integerValue DispatchQueue.global().async { // let successArray = self._PDFDocument?.splitByPagesWith(dex, folerPath: tFolderPath, fileName: folderName) let successArray = self._PDFDocument?.splitByFileWith(dex, folerPath: tFolderPath, fileName: folderName) ?? [] DispatchQueue.main.async { self.progress.isHidden = true self.cancelButton.isEnabled = true self.splitButton.isEnabled = true if (successArray.isEmpty == false) { let response = KMAlertTool.runModelForMainThread_r(message: KMLocalizedString("Splitting completed. Tap 'OK' to open the output folder."), buttons: [KMLocalizedString("OK")]) if (response == .alertFirstButtonReturn) { self.km_quick_endSheet() DispatchQueue.main.asyncAfter(deadline: .now()+0.5) { let filePath = successArray.first ?? "" self._viewFileAtFinder(filePath) } } }else{ let response = KMAlertTool.runModelForMainThread_r(message: KMLocalizedString("Failed to split!")) if (response == .alertFirstButtonReturn) { self.km_quick_endSheet() } } } } } else { DispatchQueue.global().async { [self] in var pdfDocument1 = CPDFDocument() var pdfDocument2 = CPDFDocument() let pageCnt = self._PDFDocument?.pageCount ?? 0 let pages = fileAttribute?.fetchSelectPages() ?? [] for i in 0 ..< pageCnt { var isSelected = false for number in pages { if number == i+1 { isSelected = true // let page = self._PDFDocument?.page(at: i).copy() if let page = self._PDFDocument?.page(at: i) as? CPDFPage { pdfDocument1?.insertPageObject(page, at: pdfDocument1!.pageCount) } } } if (!isSelected) { // let page = self._PDFDocument?.page(at: i).copy() if let page = self._PDFDocument?.page(at: i) as? CPDFPage { pdfDocument2?.insertPageObject(page, at: pdfDocument2!.pageCount) } } } if (pdfDocument1!.pageCount > 0) { let tpath = "\(tFolderPath)/\(folderName) 1.pdf" _isSuccessfully = pdfDocument1?.write(toFile:tpath) ?? false } if (pdfDocument2!.pageCount > 0) { let tPath = "\(tFolderPath)/\(folderName) 2.pdf" pdfDocument2?.write(toFile: tPath) } DispatchQueue.main.async { self.progress.isHidden = true self.cancelButton.isEnabled = true self.splitButton.isEnabled = true if (_isSuccessfully) { // if ([_splitDelegate respondsToSelector:@selector(didFinshedSplitWithURL:)]) { // [_splitDelegate didFinshedSplitWithURL:_fileURL]; // } let response = KMAlertTool.runModelForMainThread_r(message: KMLocalizedString("Splitting completed. Tap 'OK' to open the output folder."), buttons: [KMLocalizedString("OK")]) if (response == .alertFirstButtonReturn) { self.km_quick_endSheet() DispatchQueue.main.asyncAfter(deadline: .now()+0.5) { let filePath = "\(tFolderPath)/\(folderName) 1.pdf" self._viewFileAtFinder(filePath) } } }else{ let response = KMAlertTool.runModelForMainThread_r(message: KMLocalizedString("Failed to split!")) if (response == .alertFirstButtonReturn) { self.km_quick_endSheet() } } } } } } } } @IBAction func buttonItemClicked_OriginalName(_ sender: AnyObject) { self._updateSplitFileName() } @IBAction func buttonItemClicked_Rename(_ sender: AnyObject) { self._updateSplitFileName() } @IBAction func comboBoxClicked_PageRange(_ sender: NSComboBox) { if (sender.indexOfSelectedItem == 0) { //奇数页 sender.isEditable = false sender.isSelectable = false self.window?.makeFirstResponder(self) } else if (sender.indexOfSelectedItem == 1){ //偶数页 sender.isEditable = false sender.isSelectable = false self.window?.makeFirstResponder(self) } else { //手动输入页数 sender.isEditable = true sender.isSelectable = true self.pageRangeComboBox.stringValue = self.pageRangeComboBox.stringValue (sender.cell as? NSComboBoxCell)?.placeholderString = KMLocalizedString("e.g. 1,3-5,10") self.window?.makeFirstResponder(sender) } } } // MARK: - Private Methods extension SplitWindowController { private func _localizedLanguage() { self.splitMethodLabel.stringValue = KMLocalizedString("Split Methods") // self.splitLabel.stringValue = KMLocalizedString("Split Methods") self.pagesLabel.stringValue = KMLocalizedString("page(s)") self.filesLabel.stringValue = KMLocalizedString("PDF file(s)") // self.labelingLabel.stringValue = KMLocalizedString("File Naming") self.fileNameLabel.stringValue = KMLocalizedString("File Naming") self.exampleLabel.stringValue = KMLocalizedString("example") self.byPageButton.title = KMLocalizedString("Split by every") self.byFileButton.title = KMLocalizedString("Split averagely to") self.pageFromButton.title = KMLocalizedString("Split by page range") self.cancelButton.title = KMLocalizedString("Cancel") self.splitButton.title = KMLocalizedString("Split") self.originalFileButton.title = KMLocalizedString("Keep the current file name in front of labels") self.renameButton.title = KMLocalizedString("Label") self.separatorButton.title = KMLocalizedString("Separator") self.byPageButton.toolTip = KMLocalizedString("Split by every") self.byFileButton.toolTip = KMLocalizedString("Split averagely to") self.pageFromButton.toolTip = KMLocalizedString("Split by page range") self.originalFileButton.toolTip = KMLocalizedString("Keep the current file name in front of labels") } private func _setInputRange() { if let data = self._PDFDocument?.isLocked, data { if let url = self._fileURL { Self.checkPassword(url: url, type: .owner) { [weak self] success, pwd in if success { self?.password = pwd self?._PDFDocument?.unlock(withPassword: pwd) let pageNumber = self?._PDFDocument?.pageCount ?? 0 let pdfName = self?._PDFDocument?.documentURL.deletingPathExtension().lastPathComponent ?? "" self?.byFileStepper.maxValue = Double(pageNumber) self?.byPageStepper.maxValue = Double(pageNumber) self?.partFileNameLabel.stringValue = String(format: "%@%@%@.pdf", pdfName, self?.separatorText.stringValue ?? "", self?.renameText.stringValue ?? "") } } } } else { let pageNumber = self._PDFDocument?.pageCount ?? 0 let pdfName = self._PDFDocument?.documentURL.deletingPathExtension().path.lastPathComponent ?? "" self.byFileStepper.maxValue = Double(pageNumber) self.byPageStepper.maxValue = Double(pageNumber) if let data = self._PDFDocument?.documentURL.path.isEmpty, data == false { self.partFileNameLabel.stringValue = String(format: "%@%@%@", pdfName, self.separatorText.stringValue, "\(self.renameText.stringValue).pdf") } else { self.partFileNameLabel.stringValue = String(format: "%@%@%@", "Untitled", self.separatorText.stringValue, "\(self.renameText.stringValue).pdf") } } } private func _configuViews() { self.nameView.wantsLayer = true // _nameView.layer.backgroundColor = [KMAppearance KMColor_Layout_L1].CGColor; //初始化ComboBox self.pageRangeComboBox.removeAllItems() let pageRangeArray = [KMLocalizedString("Odd Pages Only"), KMLocalizedString("Even Pages Only"), KMLocalizedString("e.g. 1,3-5,10")] (self.pageRangeComboBox.cell as? NSComboBoxCell)?.placeholderString = KMLocalizedString("e.g. 1,3-5,10") self.pageRangeComboBox.addItems(withObjectValues: pageRangeArray) self.pageRangeComboBox.selectItem(at: 0) self.pageRangeComboBox.isSelectable = false self.pageRangeComboBox.isEditable = false self.byFileText.isEditable = false self.byFileStepper.isEnabled = false self.byPageText.isEditable = false self.byPageStepper.isEnabled = true if (self._splitType == 3) { self.pageRangeComboBox.selectItem(at: 2) let isEmpty = self.fileAttribute?.pagesString.isEmpty ?? false if (isEmpty == false) { self.pageRangeComboBox.stringValue = self.fileAttribute?.pagesString ?? "" self.byPageButton.state = .off self.pageFromButton.state = .on self.pageRangeComboBox.isEnabled = true } else { self.pageRangeComboBox.stringValue = "" self.byPageButton.state = .on self.byPageText.isEditable = true self.pageFromButton.state = .off self.pageRangeComboBox.isEnabled = false } self.pageRangeComboBox.isEditable = true self.pageRangeComboBox.isSelectable = true } else { self.pageRangeComboBox.stringValue = "" self.pageRangeComboBox.isEnabled = false self.byPageText.isEditable = true } self.byPageStepper.minValue = 1.0 self.byFileStepper.minValue = 1.0 self.byPageStepper.integerValue = 1 self.byFileStepper.integerValue = 1 self.renameText.stringValue = "part" self.separatorText.stringValue = "-" self.byPageText.stringValue = "1" self.byFileText.stringValue = "1" self.byPageText.formatter = TextFieldFormatter() self.byFileText.formatter = TextFieldFormatter() self.byPageText.delegate = self self.byFileText.delegate = self self.renameText.delegate = self self.separatorText.delegate = self self.progress.isHidden = true } private func _viewFileAtFinder(_ filePath: String) { let workspace = NSWorkspace.shared let url = URL(fileURLWithPath: filePath) workspace.activateFileViewerSelecting([url]) } private func _updateSplitFileName() { let pdfName = self._PDFDocument?.documentURL.deletingPathExtension().path.lastPathComponent ?? "" var splitPartString = "" var separatorString = "" if (self.renameButton.state == .on) { splitPartString = self.renameText.stringValue } if (self.separatorButton.state == .on) { separatorString = self.separatorText.stringValue } var partName = "" if (self.originalFileButton.state == .on) { if (separatorString.isEmpty == false) { if (partName.isEmpty == false) { partName.append(separatorString) } else { partName = separatorString } } if (splitPartString.isEmpty == false) { if (partName.isEmpty == false) { partName.append(splitPartString) } else { partName = splitPartString } } self.partFileNameLabel.stringValue = String(format: "%@%@.pdf", pdfName, partName) } else { if (splitPartString.isEmpty == false) { if (partName.isEmpty == false) { partName.append(splitPartString) } else { partName = splitPartString } } if (separatorString.isEmpty == false) { if (partName.isEmpty == false) { partName.append(separatorString) } else { partName = separatorString } } self.partFileNameLabel.stringValue = String(format: "%@%@.pdf", partName, pdfName) } } } extension SplitWindowController: NSTextFieldDelegate { func controlTextDidChange(_ obj: Notification) { let textField = obj.object as? NSTextField if (self.byPageText.isEqual(to: textField)) { if let data = textField?.stringValue.isEmpty, data { self.byPageText.stringValue = "1" self.byPageStepper.integerValue = 1 }else if let data = Int(textField!.stringValue), data <= 0 { self.byPageText.stringValue = "1" self.byPageStepper.integerValue = 1 } else if (textField!.stringValue.stringToCGFloat() > self.byPageStepper.maxValue){ self.byPageText.stringValue = String(format: "%.0f", self.byPageStepper.maxValue) self.byPageStepper.integerValue = Int(self.byPageStepper.maxValue) } else { self.byPageStepper.integerValue = Int(self.byPageText.stringValue) ?? 0 } } else if (self.byFileText.isEqual(to: textField)){ if let data = textField?.stringValue.isEmpty, data { self.byFileText.stringValue = "1" self.byFileStepper.integerValue = 1 } else if let data = Int(textField!.stringValue), data <= 0 { self.byFileText.stringValue = "1" self.byFileStepper.integerValue = 1 }else if (textField!.stringValue.stringToCGFloat() > self.byFileStepper.maxValue){ self.byFileText.stringValue = String(format: "%.0f", self.byFileStepper.maxValue) self.byFileStepper.integerValue = Int(self.byFileStepper.maxValue) }else{ self.byFileStepper.integerValue = Int(self.byFileText.stringValue) ?? 0 } } else if (self.renameText.isEqual(to: textField) || self.separatorText.isEqual(to: textField )) { self._updateSplitFileName() } } }