|
@@ -0,0 +1,354 @@
|
|
|
+//
|
|
|
+// KMURLCreatePDFWindowController.swift
|
|
|
+// PDF Reader Pro
|
|
|
+//
|
|
|
+// Created by Niehaoyu on 2024/10/9.
|
|
|
+//
|
|
|
+
|
|
|
+import Cocoa
|
|
|
+import KMComponentLibrary
|
|
|
+
|
|
|
+let kUrlToPDFFolderPath = (try? FileManager.default.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true).appendingPathComponent(Bundle.main.bundleIdentifier ?? "").appendingPathComponent("WebPage"))?.path ?? ""
|
|
|
+
|
|
|
+class KMURLCreatePDFWindowController: NSWindowController {
|
|
|
+
|
|
|
+ @IBOutlet var titleLabel: NSTextField!
|
|
|
+
|
|
|
+ @IBOutlet var urlRadio: ComponentRadio!
|
|
|
+ @IBOutlet var fileRadio: ComponentRadio!
|
|
|
+ @IBOutlet var inputView: ComponentInput!
|
|
|
+
|
|
|
+ @IBOutlet var fileInputBGView: NSView!
|
|
|
+ @IBOutlet var fileInputView: ComponentInput!
|
|
|
+ @IBOutlet var fileInputAddon: ComponentInputAddon!
|
|
|
+
|
|
|
+ @IBOutlet var pageConfigLabel: NSTextField!
|
|
|
+ @IBOutlet var pageSizeLabel: NSTextField!
|
|
|
+ @IBOutlet var pageSizeSelect: ComponentSelect!
|
|
|
+ @IBOutlet var spacingLabel: NSTextField!
|
|
|
+ @IBOutlet var spacingInputNumber: ComponentInputNumber!
|
|
|
+ @IBOutlet var mmLabel: NSTextField!
|
|
|
+
|
|
|
+ @IBOutlet var cancelButton: ComponentButton!
|
|
|
+ @IBOutlet var openButton: ComponentButton!
|
|
|
+
|
|
|
+ private var _isFromURL: Bool = true
|
|
|
+
|
|
|
+ private var parentWindow: NSWindow?
|
|
|
+ private var handler: ((String?) -> Void)!
|
|
|
+
|
|
|
+ private var posterMaskView: KMBookletMaskView?
|
|
|
+
|
|
|
+ var filePath: String?
|
|
|
+ var gap: CGFloat = 0
|
|
|
+ var pageSize: CGSize = NSMakeSize(298, 420)
|
|
|
+
|
|
|
+ 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.
|
|
|
+
|
|
|
+ self.setUpProperty()
|
|
|
+
|
|
|
+ self.reloadData()
|
|
|
+ }
|
|
|
+
|
|
|
+ var isFromURL: Bool {
|
|
|
+ get {
|
|
|
+ return _isFromURL
|
|
|
+ }
|
|
|
+ set {
|
|
|
+ _isFromURL = newValue
|
|
|
+
|
|
|
+ self.reloadData()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ func setUpProperty() {
|
|
|
+ urlRadio.properties = ComponentCheckBoxProperty(size: .s,
|
|
|
+ state: .normal,
|
|
|
+ text: KMLocalizedString("URL"),
|
|
|
+ checkboxType: .normal)
|
|
|
+ urlRadio.setTarget(self, action: #selector(typeRadioChanged(_:)))
|
|
|
+
|
|
|
+ fileRadio.properties = ComponentCheckBoxProperty(size: .s,
|
|
|
+ state: .normal,
|
|
|
+ text: KMLocalizedString("File"),
|
|
|
+ checkboxType: .normal)
|
|
|
+ fileRadio.setTarget(self, action: #selector(typeRadioChanged(_:)))
|
|
|
+
|
|
|
+ inputView.properties = ComponentInputProperty(size: .s,
|
|
|
+ state: .normal,
|
|
|
+ isError: false,
|
|
|
+ isDisabled: false,
|
|
|
+ placeholder: KMLocalizedString("https://pdfreaderpro.com"))
|
|
|
+
|
|
|
+ fileInputView.properties = ComponentInputProperty(size: .s,
|
|
|
+ state: .normal,
|
|
|
+ isError: false,
|
|
|
+ isDisabled: false,
|
|
|
+ placeholder: KMLocalizedString("Select File..."),
|
|
|
+ creatable: false)
|
|
|
+ fileInputView.properties.propertyInfo.cornerRadius_topRight = 0
|
|
|
+ fileInputView.properties.propertyInfo.cornerRadius_bottomRight = 0
|
|
|
+
|
|
|
+ fileInputAddon.properties = ComponentInputAddonProperty(size: .s,
|
|
|
+ state: .normal,
|
|
|
+ addOnBefore: false,
|
|
|
+ onlyRead: false,
|
|
|
+ addonType: .imageWithColor,
|
|
|
+ iconImage: NSImage(named: "file_icon"))
|
|
|
+ fileInputAddon.setTarget(self, action: #selector(chooseURLAction(_ :)))
|
|
|
+
|
|
|
+ pageSizeSelect.properties = ComponentSelectProperties(size: .s,
|
|
|
+ state: .normal,
|
|
|
+ text: "Automatic resizing")
|
|
|
+
|
|
|
+ var menuItemArr: [ComponentMenuitemProperty] = []
|
|
|
+ for string in ["Automatically Resize", "4A0 1682 × 2378 mm", "2A0 1189 × 1682 mm", "A0 841 × 1189 mm",
|
|
|
+ "A1 594 × 841 mm", "A2 420 × 594 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"] {
|
|
|
+ var itemProperty: ComponentMenuitemProperty = ComponentMenuitemProperty(multipleSelect: false, itemSelected: false, isDisabled: false, leftIcon: false, rightIcon: false, keyEquivalent: nil, text: KMLocalizedString(string, comment: ""))
|
|
|
+ if string == " " {
|
|
|
+ itemProperty = ComponentMenuitemProperty.divider()
|
|
|
+ }
|
|
|
+ menuItemArr.append(itemProperty)
|
|
|
+ }
|
|
|
+ pageSizeSelect.updateMenuItemsArr(menuItemArr)
|
|
|
+ pageSizeSelect.selectItemAtIndex(0)
|
|
|
+ pageSizeSelect.delegate = self
|
|
|
+
|
|
|
+ spacingInputNumber.properties = ComponentInputNumberProperty(size: .s,
|
|
|
+ minSize: 0,
|
|
|
+ maxSize: 1000,
|
|
|
+ text: "0",
|
|
|
+ valueType: .floatType)
|
|
|
+
|
|
|
+ cancelButton.properties = ComponentButtonProperty(type: .default_tertiary,
|
|
|
+ size: .s,
|
|
|
+ state: .normal,
|
|
|
+ buttonText: KMLocalizedString("Cancel"))
|
|
|
+ cancelButton.setTarget(self, action: #selector(cancelButtonClicked(_ :)))
|
|
|
+
|
|
|
+ openButton.properties = ComponentButtonProperty(type: .primary,
|
|
|
+ size: .s,
|
|
|
+ state: .normal,
|
|
|
+ buttonText: KMLocalizedString("Open"))
|
|
|
+ openButton.setTarget(self, action: #selector(openButtonClicked(_ :)))
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ func reloadData() {
|
|
|
+ urlRadio.properties.checkboxType = self.isFromURL ? .selected : .normal
|
|
|
+ fileRadio.properties.checkboxType = self.isFromURL ? .normal : .selected
|
|
|
+
|
|
|
+ fileInputBGView.isHidden = self.isFromURL ? true : false
|
|
|
+ inputView.isHidden = self.isFromURL ? false : true
|
|
|
+
|
|
|
+ if pageSizeSelect.indexOfSelect() == 0 {
|
|
|
+ spacingInputNumber.properties.isDisabled = true
|
|
|
+ } else {
|
|
|
+ spacingInputNumber.properties.isDisabled = false
|
|
|
+ }
|
|
|
+
|
|
|
+ cancelButton.properties.state = .normal
|
|
|
+ openButton.properties.state = .normal
|
|
|
+
|
|
|
+ inputView.reloadData()
|
|
|
+
|
|
|
+ fileInputView.reloadData()
|
|
|
+ fileInputAddon.reloadData()
|
|
|
+
|
|
|
+ urlRadio.reloadData()
|
|
|
+ fileRadio.reloadData()
|
|
|
+
|
|
|
+ spacingInputNumber.reloadData()
|
|
|
+
|
|
|
+
|
|
|
+ cancelButton.reloadData()
|
|
|
+ openButton.reloadData()
|
|
|
+ }
|
|
|
+
|
|
|
+ //MARK: - Action
|
|
|
+ @objc func typeRadioChanged(_ sender: NSView) {
|
|
|
+ if sender == self.fileRadio {
|
|
|
+ self.isFromURL = false
|
|
|
+ } else if sender == self.urlRadio {
|
|
|
+ self.isFromURL = true
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @objc func cancelButtonClicked(_ sender: NSView) {
|
|
|
+
|
|
|
+ self.handler?(nil)
|
|
|
+
|
|
|
+ parentWindow?.endSheet(self.window!)
|
|
|
+ }
|
|
|
+
|
|
|
+ @objc func openButtonClicked(_ sender: NSView) {
|
|
|
+
|
|
|
+ self.handler?("")
|
|
|
+
|
|
|
+ parentWindow?.endSheet(self.window!)
|
|
|
+ }
|
|
|
+
|
|
|
+ @objc func chooseURLAction(_ sender: NSView) {
|
|
|
+ let openPanel = NSOpenPanel()
|
|
|
+ openPanel.allowedFileTypes = ["html", "HTML"]
|
|
|
+ openPanel.allowsMultipleSelection = false
|
|
|
+ openPanel.beginSheetModal(for: self.window!) { [weak self] result in
|
|
|
+ if result == NSApplication.ModalResponse.OK {
|
|
|
+ if let url = openPanel.url {
|
|
|
+ self?.fileInputView.properties.text = url.path
|
|
|
+ self?.fileInputView.reloadData()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ func convertInfoToPDF() {
|
|
|
+ if !FileManager.default.fileExists(atPath: kUrlToPDFFolderPath) {
|
|
|
+ try? FileManager.default.createDirectory(atPath: kUrlToPDFFolderPath, withIntermediateDirectories: false, attributes: nil)
|
|
|
+ }
|
|
|
+
|
|
|
+ var url: URL?
|
|
|
+ var fileName: String?
|
|
|
+ if isFromURL {
|
|
|
+ let urlString = inputView.properties.text
|
|
|
+ var tUrl = URL(string: urlString)
|
|
|
+ if tUrl?.scheme?.count ?? 0 < 1 {
|
|
|
+ tUrl = URL(string: "http://\(urlString)")
|
|
|
+ }
|
|
|
+ url = tUrl
|
|
|
+ } else {
|
|
|
+ url = URL(fileURLWithPath: fileInputView.properties.text)
|
|
|
+ fileName = fileInputView.properties.text.deletingPathExtension.lastPathComponent
|
|
|
+ }
|
|
|
+
|
|
|
+ let string = spacingInputNumber.properties.text ?? ""
|
|
|
+ let unitScale: CGFloat = (595.0 / 21.0) * 2.54
|
|
|
+
|
|
|
+ if string.stringToCGFloat() <= 0 {
|
|
|
+ spacingInputNumber.properties.text = "0"
|
|
|
+ } else if string.stringToCGFloat() * unitScale > pageSize.width / 2 {
|
|
|
+ let maxF = pageSize.width / (string.stringToCGFloat() * 2)
|
|
|
+ spacingInputNumber.properties.text = "\(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
|
|
|
+
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ let alert = NSAlert()
|
|
|
+ alert.alertStyle = .critical
|
|
|
+ alert.informativeText = NSLocalizedString("Conversion Failed", comment: "")
|
|
|
+ alert.messageText = ""
|
|
|
+ alert.addButton(withTitle: NSLocalizedString("OK", comment: ""))
|
|
|
+ alert.runModal()
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ 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()
|
|
|
+ }
|
|
|
+
|
|
|
+ //MARK: - public
|
|
|
+ func own_beginSheetModal(for window: NSWindow?, completionHandler handler: ((String?) -> Void)?) {
|
|
|
+ if window != nil {
|
|
|
+ parentWindow = window
|
|
|
+ window!.beginSheet(self.window!) { ModalResponse in
|
|
|
+ self.handler?(nil)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ self.handler = handler
|
|
|
+ self.reloadData()
|
|
|
+ }
|
|
|
+
|
|
|
+ override func becomeFirstResponder() -> Bool {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+
|
|
|
+ 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 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 urlValueEncode(_ str: String) -> String? {
|
|
|
+ let allowedCharacterSet = CharacterSet(charactersIn: "!*'();:@&=+$,?%#[]{}").inverted
|
|
|
+ return str.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+extension KMURLCreatePDFWindowController: ComponentSelectDelegate {
|
|
|
+ func componentSelectDidSelect(view: ComponentSelect?, menuItemProperty: ComponentMenuitemProperty?) {
|
|
|
+
|
|
|
+ switch view?.indexOfSelect() {
|
|
|
+ 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)
|
|
|
+ }
|
|
|
+
|
|
|
+ self.reloadData()
|
|
|
+
|
|
|
+ }
|
|
|
+}
|