// // KMExtractWindowController.swift // PDF Reader Pro // // Created by kdanmobile on 2025/2/28. // import Cocoa import KMComponentLibrary import ComPDFKit class KMExtractWindowController: KMNBaseWindowController { @IBOutlet var contendBox: NSBox! @IBOutlet var titleLabel: NSTextField! @IBOutlet var previewBGView: NSView! @IBOutlet var pdfBGView: NSView! @IBOutlet var paginationView: ComponentPagination! @IBOutlet var pageRangeView: NSView! @IBOutlet var pageRangeLabel: NSTextField! @IBOutlet var allPageRadio: ComponentRadio! @IBOutlet var currentPageRadio: ComponentRadio! @IBOutlet var oddPageRadio: ComponentRadio! @IBOutlet var evenPageRadio: ComponentRadio! @IBOutlet var customPageRadio: ComponentRadio! @IBOutlet var customPageInput: ComponentInput! @IBOutlet var allPageBoxWidthConst: NSLayoutConstraint! @IBOutlet var curPageBoxWidthConst: NSLayoutConstraint! @IBOutlet var oddPageBoxWidthConst: NSLayoutConstraint! @IBOutlet var evenPageBoxWidthConst: NSLayoutConstraint! @IBOutlet var customPageBoxWidthConst: NSLayoutConstraint! @IBOutlet var settingView: NSView! @IBOutlet var settingLabel: NSTextField! @IBOutlet var eachPageCheckbox: ComponentCheckBox! @IBOutlet var eachPageBoxWidthConst: NSLayoutConstraint! @IBOutlet var cancelButton: ComponentButton! @IBOutlet var confirmButton: ComponentButton! @IBOutlet var cancelBtnWidthConst: NSLayoutConstraint! @IBOutlet var confirmBtnWidthConst: NSLayoutConstraint! var fileURL: URL? var pdfDocument: CPDFDocument? var pdfView: CPDFListView = CPDFListView.init() var typeIndex: Int = 1 var resultIndexs: [Int] = [] override func windowDidLoad() { super.windowDidLoad() // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file. pdfView.frame = pdfBGView.bounds pdfView.autoresizingMask = [.width, .height] pdfView.setDisplay(.singlePage) pdfView.toolMode = .CNoteToolMode pdfView.autoScales = true pdfView.delegate = self pdfView.pdfListViewDelegate = self pdfBGView.addSubview(pdfView) reloadData() } override func initContentView() { super.initContentView() guard let _ = self.contendBox else { return } paginationView.properties = ComponentPaginationProperty(doubleArrow_show: false) for box in [allPageRadio, currentPageRadio, oddPageRadio, evenPageRadio, customPageRadio] { box?.setTarget(self, action: #selector(checkBoxClicked(_:))) } cancelButton.setTarget(self, action: #selector(cancelButtonClicked(_ :))) cancelButton.keyEquivalent = KMKeyEquivalent.esc.string() confirmButton.setTarget(self, action: #selector(openButtonClicked(_ :))) confirmButton.keyEquivalent = KMKeyEquivalent.enter // Enter key } override func updateUIThemeColor() { super.updateUIThemeColor() self.reloadUI() } override func updateUILanguage() { super.updateUILanguage() self.reloadUI() } override func beginSheetFinish() { super.beginSheetFinish() typeIndex = 1 self.initContentView() reloadData() loadPDFPreview() self.window?.makeFirstResponder(nil) } // func reloadUI() { guard let _ = self.contendBox else { return } previewBGView.wantsLayer = true previewBGView.layer?.cornerRadius = 8 previewBGView.layer?.borderWidth = 1 previewBGView.layer?.backgroundColor = ComponentLibrary.shared.getComponentColorFromKey("colorFill/4").cgColor previewBGView.layer?.borderColor = ComponentLibrary.shared.getComponentColorFromKey("colorBorder/4").cgColor titleLabel.stringValue = KMLocalizedString("Extract") titleLabel.textColor = ComponentLibrary.shared.getComponentColorFromKey("colorText/1") titleLabel.font = ComponentLibrary.shared.getFontFromKey("mac/body-m-medium") pageRangeLabel.stringValue = KMLocalizedString("Page Range") pageRangeLabel.textColor = ComponentLibrary.shared.getComponentColorFromKey("colorText/2") pageRangeLabel.font = ComponentLibrary.shared.getFontFromKey("mac/body-s-medium") settingLabel.stringValue = KMLocalizedString("Settings") settingLabel.textColor = ComponentLibrary.shared.getComponentColorFromKey("colorText/2") settingLabel.font = ComponentLibrary.shared.getFontFromKey("mac/body-s-medium") allPageRadio.properties = ComponentCheckBoxProperty(size: .s, state: .normal, isDisabled: false, showhelp: false, text: KMLocalizedString("All Pages"), checkboxType: .normal) currentPageRadio.properties = ComponentCheckBoxProperty(size: .s, state: .normal, isDisabled: false, showhelp: false, text: KMLocalizedString("Current Page"), checkboxType: .normal) oddPageRadio.properties = ComponentCheckBoxProperty(size: .s, state: .normal, isDisabled: false, showhelp: false, text: KMLocalizedString("Odd Pages Only"), checkboxType: .normal) evenPageRadio.properties = ComponentCheckBoxProperty(size: .s, state: .normal, isDisabled: false, showhelp: false, text: KMLocalizedString("Even Pages Only"), checkboxType: .normal) customPageRadio.properties = ComponentCheckBoxProperty(size: .s, state: .normal, isDisabled: false, showhelp: false, text: KMLocalizedString("Custom"), checkboxType: .normal) customPageInput.properties = ComponentInputProperty(size: .s, state:.normal, isDisabled: true, placeholder: KMLocalizedString("e.g. 1,3-5,10"), text: "", creatable: true, regexString: "0123456789,-") eachPageCheckbox.properties = ComponentCheckBoxProperty(size: .s, state: .normal, isDisabled: false, showhelp: false, text: KMLocalizedString("Each page in a separate file"), checkboxType: .normal) cancelButton.properties = ComponentButtonProperty(type: .default_tertiary, size: .s, state: .normal, buttonText: KMLocalizedString("Cancel"), keepPressState: false) confirmButton.properties = ComponentButtonProperty(type: .primary, size: .s, state: .normal, buttonText: KMLocalizedString("Extract"), keepPressState: false) allPageBoxWidthConst.constant = allPageRadio.properties.propertyInfo.viewWidth curPageBoxWidthConst.constant = currentPageRadio.properties.propertyInfo.viewWidth oddPageBoxWidthConst.constant = oddPageRadio.properties.propertyInfo.viewWidth evenPageBoxWidthConst.constant = evenPageRadio.properties.propertyInfo.viewWidth customPageBoxWidthConst.constant = customPageRadio.properties.propertyInfo.viewWidth eachPageBoxWidthConst.constant = eachPageCheckbox.properties.propertyInfo.viewWidth cancelBtnWidthConst.constant = cancelButton.properties.propertyInfo.viewWidth confirmBtnWidthConst.constant = confirmButton.properties.propertyInfo.viewWidth } func loadPDFPreview() { guard let filePath = fileURL else { return } NSWindowController.checkPassword(url: filePath, type: .owner, password: "") { [unowned self] success, resultPassword in if success { DispatchQueue.main.async { self.pdfDocument = CPDFDocument.init(url: filePath) if resultPassword.isEmpty == false { self.pdfDocument?.unlock(withPassword: resultPassword) } self.pdfView.document = self.pdfDocument self.paginationView.properties.currentIndex = 1 self.paginationView.properties.totalCount = Int(self.pdfDocument?.pageCount ?? 1 ) self.paginationView.reloadData() } } } } func reloadData() { allPageRadio.properties.checkboxType = .normal currentPageRadio.properties.checkboxType = .normal oddPageRadio.properties.checkboxType = .normal evenPageRadio.properties.checkboxType = .normal customPageRadio.properties.checkboxType = .normal customPageInput.properties.isDisabled = true if typeIndex == 0 { allPageRadio.properties.checkboxType = .selected } else if typeIndex == 1 { currentPageRadio.properties.checkboxType = .selected } else if typeIndex == 2 { oddPageRadio.properties.checkboxType = .selected } else if typeIndex == 3 { evenPageRadio.properties.checkboxType = .selected } else if typeIndex == 4 { customPageRadio.properties.checkboxType = .selected customPageInput.properties.isDisabled = false } allPageRadio.reloadData() currentPageRadio.reloadData() oddPageRadio.reloadData() evenPageRadio.reloadData() customPageRadio.reloadData() customPageInput.reloadData() } //MARK: - Action @objc func checkBoxClicked(_ sender: ComponentCheckBox) { if sender == self.allPageRadio { self.typeIndex = 0 } else if sender == self.currentPageRadio { self.typeIndex = 1 } else if sender == self.oddPageRadio { self.typeIndex = 2 } else if sender == self.evenPageRadio { self.typeIndex = 3 } else if sender == self.customPageRadio { self.typeIndex = 4 } reloadData() } @objc func cancelButtonClicked(_ sender: NSView) { self.own_closeEndSheet() } @objc func openButtonClicked(_ sender: NSView) { guard let pdfDocument = self.pdfDocument else { return } var pageIndexs: [Int] = [] if typeIndex == 0 { pageIndexs = KMPageRangeSelectView.getSelectedPageIndex(pdfDocument, isAllPages: true) } else if typeIndex == 1 { pageIndexs = [self.pdfView.currentPageIndex + 1] } else if typeIndex == 2 { pageIndexs = KMPageRangeSelectView.getSelectedPageIndex(pdfDocument, isOddPage: true) } else if typeIndex == 3 { pageIndexs = KMPageRangeSelectView.getSelectedPageIndex(pdfDocument, isEvenPage: true) } else if typeIndex == 4 { if customPageInput.properties.text.isEmpty == true { let alert = NSAlert() alert.alertStyle = .warning alert.messageText = KMLocalizedString("Invalid page range .", comment: "") alert.runModal() return } pageIndexs = KMPageRangeSelectView.getSelectedPageIndex(pdfDocument, isCustom: customPageInput.properties.text) } if pageIndexs.count < 1 { let alert = NSAlert() alert.alertStyle = .warning alert.messageText = KMLocalizedString("Invalid page range .", comment: "") alert.runModal() return } self.resultIndexs.removeAll() for i in pageIndexs { resultIndexs.append(i - 1) } var fileName = pdfDocument.documentURL.deletingPathExtension().lastPathComponent if self.eachPageCheckbox.properties.checkboxType == .normal { fileName.append(" pages ") fileName.append(KMPageRangeTools.newParseSelectedIndexs(selectedIndex: resultIndexs.sorted())) fileName.append(".pdf") let panel = NSSavePanel() panel.nameFieldStringValue = fileName panel.canCreateDirectories = true panel.beginSheetModal(for: self.window!) { response in if response == .OK { if let pdf = CPDFDocument.init() { var extractPages: Array = [] for i in self.resultIndexs { extractPages.append(pdfDocument.page(at: UInt(i))) } let _ = pdf.extractAsOneDocument(withPages: extractPages, savePath: panel.url!.path) self.own_closeEndSheet() NSWorkspace.shared.activateFileViewerSelecting([panel.url!]) } } } } else { let panel = NSOpenPanel() panel.canChooseFiles = false panel.canChooseDirectories = true panel.canCreateDirectories = true panel.beginSheetModal(for: self.window!) { response in if response == .OK { let folderName = String((pdfDocument.documentURL!.lastPathComponent.split(separator: ".")[0])) + "_extract" var filePath = URL(fileURLWithPath: panel.url!.path).appendingPathComponent(folderName).path var i = 1 let testFilePath = filePath while FileManager.default.fileExists(atPath: filePath) { filePath = testFilePath + "\(i)" i += 1 } try? FileManager.default.createDirectory(atPath: filePath, withIntermediateDirectories: false, attributes: nil) /// 提取的页面 var extractPages: Array = [] for i in self.resultIndexs { extractPages.append(pdfDocument.page(at: UInt(i))) } var successArray: [URL]? successArray = pdfDocument.extractPerPageDocument(withPages: extractPages, folerPath: filePath) self.own_closeEndSheet() NSWorkspace.shared.activateFileViewerSelecting([URL(fileURLWithPath: filePath)]) } } } } } extension KMExtractWindowController: CPDFListViewDelegate, CPDFViewDelegate { func pdfViewCurrentPageDidChanged(_ pdfView: CPDFView!) { let index = pdfView.currentPageIndex paginationView.properties.currentIndex = index + 1 paginationView.reloadData() } }