// // KMURLToPDFWindowController.swift // PDF Reader Pro // // Created by wanjun on 2023/1/29. // import Cocoa let kUrlToPDFFolderPath = (try? FileManager.default.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true).appendingPathComponent(Bundle.main.bundleIdentifier ?? "").appendingPathComponent("WebPage"))?.path ?? "" typealias KMURLToPDFWindowControllerComplete = (_ filePath: String) -> Void class KMURLToPDFWindowController: NSWindowController, NSTextFieldDelegate { @IBOutlet var urlBtn: NSButton! @IBOutlet var localHtmlBtn: NSButton! @IBOutlet var outputBackView: NSView! @IBOutlet var outputText: NSTextField! @IBOutlet var outputButton: NSButton! @IBOutlet var pageSizeComboBox: NSComboBox! @IBOutlet var pageViewLabel: NSTextField! @IBOutlet var pageSizeLabel: NSTextField! @IBOutlet var pageGapLabel: NSTextField! @IBOutlet var pageGapTextField: NSTextField! @IBOutlet var pagesGapStepper: NSStepper! @IBOutlet var btnConvert: NSButton! @IBOutlet var btnCancel: NSButton! @IBOutlet var urlTextField: NSTextField! var filePath: String? var posterMaskView: KMBookletMaskView? var gap: CGFloat = 0 var pageSize: CGSize = NSMakeSize(298, 420) var handler: KMURLToPDFWindowControllerComplete? var beginWindow: NSWindow? override init(window: NSWindow?) { super.init(window: window) } required init?(coder: NSCoder) { super.init(coder: coder) } override func windowDidLoad() { super.windowDidLoad() window?.title = NSLocalizedString("Web Page To PDF", comment: "") pageViewLabel.stringValue = NSLocalizedString("Page View", comment: "") pageSizeLabel.stringValue = NSLocalizedString("Page size:", comment: "") pageGapLabel.stringValue = "\(NSLocalizedString("Spacing", comment: "")):" urlBtn.title = NSLocalizedString("URL", comment: "") localHtmlBtn.title = NSLocalizedString("File", comment: "") btnConvert.title = NSLocalizedString("Save as PDF", comment: "") btnCancel.title = NSLocalizedString("Cancel", comment: "") outputButton.title = NSLocalizedString("Choose...", comment: "") outputBackView.wantsLayer = true outputBackView.layer?.borderWidth = 0.8 outputBackView.layer?.borderColor = NSColor(red: 177.0/255, green: 178.0/255, blue: 177.0/255, alpha: 1).cgColor urlTextField.placeholderString = "https://www.pdfreaderpro.com" urlTextField.delegate = self outputText.isEditable = false pageSizeComboBox.addItems(withObjectValues: [ NSLocalizedString("Automatically Resize", comment: "Menu item title"), "4A0 1682 × 2378 mm", "2A0 1189 × 1682 mm", "A0 841 × 1189 mm", "A1 594 × 841 mm", "A2 420 × 594 mm", "A3 297 × 420 mm", "A4 210 × 297 mm", "A5 148 × 210 mm", "A6 105 × 148 mm", "A7 74 × 105 mm", "A8 52 × 74 mm", "A9 37 × 52 mm", "A10 26 × 37 mm" ]) pageSizeComboBox.selectItem(at: 0) pageSizeComboBox.isEditable = false pageSize = NSMakeSize(298, 420) gap = 0 pagesGapStepper.stringValue = "0" pagesGapStepper.isEnabled = false pageGapTextField.isEnabled = false if urlBtn.state == .on { if urlTextField.stringValue.count > 0 { btnConvert.isEnabled = true } else { btnConvert.isEnabled = false } } else { if outputText.stringValue.count > 0 { btnConvert.isEnabled = true } else { btnConvert.isEnabled = false } } outputText.placeholderString = "\(NSLocalizedString("Select a File", comment: "")) (.html, .webarchive)" outputButton.isEnabled = false } func isUrl(_ urlString: String?) -> Bool { guard let urlString = urlString else { return false } // 实现方法有问题,暂不使用 var url: String if urlString.count > 4, urlString.prefix(4) == "www." { url = "http://\(urlString)" } else { url = urlString } let urlRegex = "\\bhttps?://[a-zA-Z0-9\\-.]+(?::(\\d+))?(?:(?:/[a-zA-Z0-9\\-._?,'+\\&%$=~*!():@\\\\]*)+)?" let urlTest = NSPredicate(format: "SELF MATCHES %@", urlRegex) return urlTest.evaluate(with: url) } func beginSheetModalForWindow(_ window: NSWindow, completionHandler handler: ((String) -> Void)?) { self.handler = handler self.beginWindow = window self.beginWindow?.beginSheet(self.window!) { response in self.handler?(self.filePath ?? "") } } @IBAction func buttonItemClicked_Cancel(_ sender: NSButton) { self.beginWindow?.endSheet(self.window!) self.window?.orderOut(self) } func urlValueEncode(_ str: String) -> String? { let allowedCharacterSet = CharacterSet(charactersIn: "!*'();:@&=+$,?%#[]{}").inverted return str.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet) } @IBAction func buttonItemClicked_Start(_ sender: NSButton) { if !FileManager.default.fileExists(atPath: kUrlToPDFFolderPath) { try? FileManager.default.createDirectory(atPath: kUrlToPDFFolderPath, withIntermediateDirectories: false, attributes: nil) } var url: URL? var fileName: String? if urlBtn.state == .on { let urlString = urlTextField.stringValue var tUrl = URL(string: urlString) if tUrl?.scheme?.count ?? 0 < 1 { tUrl = URL(string: "http://\(urlString)") } url = tUrl } else { url = URL(fileURLWithPath: outputText.stringValue) fileName = outputText.stringValue.deletingPathExtension.lastPathComponent } let string = pageGapTextField.stringValue let unitScale: CGFloat = (595.0 / 21.0) * 2.54 if string.stringToCGFloat() <= 0 { pageGapTextField.stringValue = "0" } else if string.stringToCGFloat() * unitScale > pageSize.width / 2 { let maxF = pageSize.width / (string.stringToCGFloat() * 2) pageGapTextField.stringValue = "\(maxF)" } gap = formatFloat(Float(string.stringToCGFloat() * unitScale)).stringToCGFloat() if let url = url { showWaitting() let convert = KMConvertURLToPDF.shareInstance() convert.fileName = fileName ?? "" convert.convertUrl(toPDF: [url], toPath: kUrlToPDFFolderPath, pageSize: pageSize, gap: gap, progress: { value in // Progress update }, completionHandler: { successArray, failArray in self.hideWaitting() if failArray.isEmpty { if let filePath = successArray.first as? String, FileManager.default.fileExists(atPath: filePath) { self.filePath = filePath NSApp.endSheet(self.window!, returnCode: sender.tag) self.window?.orderOut(self) } } else { let alert = NSAlert() alert.alertStyle = .critical alert.informativeText = NSLocalizedString("Conversion Failed", comment: "") alert.messageText = "" alert.addButton(withTitle: NSLocalizedString("OK", comment: "")) alert.runModal() } }) } } @IBAction func buttonItemClick_Add(_ sender: Any) { urlBtn.state = .off localHtmlBtn.state = .on urlTextField.isEditable = false urlTextField.stringValue = "" btnConvert.isEnabled = false let panel = NSOpenPanel() panel.canChooseFiles = true panel.canChooseDirectories = false panel.allowsMultipleSelection = false panel.allowedFileTypes = ["HTML", "html", "webarchive", "htm"] panel.beginSheetModal(for: window!) { response in if response == .OK { self.outputText.stringValue = panel.url?.path ?? "" self.btnConvert.isEnabled = true } } } @IBAction func buttonItemClick_ChangeType(_ sender: NSButton) { if urlBtn.state == .on { urlTextField.isEditable = true urlTextField.becomeFirstResponder() outputText.stringValue = "" outputButton.isEnabled = false if urlTextField.stringValue.count > 0 { btnConvert.isEnabled = true } else { btnConvert.isEnabled = false } } else { urlTextField.isEditable = false urlTextField.stringValue = "" outputButton.isEnabled = true if outputText.stringValue.count > 0 { btnConvert.isEnabled = true } else { btnConvert.isEnabled = false } } } @IBAction func comboBoxItemClick_PageSize(_ sender: NSComboBox) { switch sender.indexOfSelectedItem { case 0: pageSize = NSMakeSize(298, 420) case 1: pageSize = CGSize(width: 4760, height: 6736) case 2: pageSize = CGSize(width: 3368, height: 4760) case 3: pageSize = CGSize(width: 2380, height: 3368) case 4: pageSize = CGSize(width: 1684, height: 2380) case 5: pageSize = CGSize(width: 1190, height: 1684) case 6: pageSize = CGSize(width: 842, height: 1190) case 7: pageSize = CGSize(width: 595, height: 842) case 8: pageSize = CGSize(width: 420, height: 595) case 9: pageSize = CGSize(width: 297, height: 420) default: pageSize = CGSize(width: 210, height: 297) } if sender.indexOfSelectedItem != 0 { pagesGapStepper.stringValue = "0" pageGapTextField.stringValue = "0" pageGapTextField.isEnabled = true pagesGapStepper.isEnabled = true } else { pagesGapStepper.stringValue = "0" pageGapTextField.stringValue = "" pageGapTextField.isEnabled = false pagesGapStepper.isEnabled = false } } @IBAction func stepperItemClick_Gap(_ sender: NSStepper) { let unitScale: CGFloat = (595.0 / 21.0) * 2.54 pageGapTextField.stringValue = String(format: "%.2f", pagesGapStepper.floatValue) gap = formatFloat(pagesGapStepper.floatValue * Float(unitScale)).stringToCGFloat() } func controlTextDidEndEditing(_ obj: Notification) { if let object = obj.object as? NSTextField, object == pageGapTextField { let unitScale: CGFloat = (595.0 / 21.0) * 2.54 if object.stringValue.stringToCGFloat() <= 0 { pageGapTextField.stringValue = "0" } else if object.stringValue.stringToCGFloat() * unitScale > pageSize.width / 2 { let maxF = pageSize.width / (object.stringValue.stringToCGFloat() * 2) pageGapTextField.stringValue = "\(maxF)" } gap = formatFloat(Float(object.stringValue.stringToCGFloat() * unitScale)).stringToCGFloat() pagesGapStepper.floatValue = Float(object.stringValue.stringToCGFloat()) } } func controlTextDidChange(_ notification: Notification) { if urlBtn.state == .on { if urlTextField.stringValue.count > 0 { btnConvert.isEnabled = true } else { btnConvert.isEnabled = false } } else { if outputText.stringValue.count > 0 { btnConvert.isEnabled = true } else { btnConvert.isEnabled = false } } } func formatFloat(_ f: Float) -> String { if f.truncatingRemainder(dividingBy: 1) == 0 { return String(format: "%.0f", f) } else if (f * 10).truncatingRemainder(dividingBy: 1) == 0 { return String(format: "%.1f", f) } else { return String(format: "%.2f", f) } } func showWaitting() { if posterMaskView == nil { posterMaskView = KMBookletMaskView(frame: NSMakeRect(0, 0, window?.frame.size.width ?? 0, window?.frame.size.height ?? 0)) } window?.contentView?.addSubview(posterMaskView!) } func hideWaitting() { posterMaskView?.removeFromSuperview() } }