KMURLToPDFWindowController.swift 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. //
  2. // KMURLToPDFWindowController.swift
  3. // PDF Reader Pro
  4. //
  5. // Created by wanjun on 2023/1/29.
  6. //
  7. import Cocoa
  8. let kUrlToPDFFolderPath = (try? FileManager.default.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true).appendingPathComponent(Bundle.main.bundleIdentifier ?? "").appendingPathComponent("WebPage"))?.path ?? ""
  9. typealias KMURLToPDFWindowControllerComplete = (_ filePath: String) -> Void
  10. class KMURLToPDFWindowController: NSWindowController, NSTextFieldDelegate {
  11. @IBOutlet var urlBtn: NSButton!
  12. @IBOutlet var localHtmlBtn: NSButton!
  13. @IBOutlet var outputBackView: NSView!
  14. @IBOutlet var outputText: NSTextField!
  15. @IBOutlet var outputButton: NSButton!
  16. @IBOutlet var pageSizeComboBox: NSComboBox!
  17. @IBOutlet var pageViewLabel: NSTextField!
  18. @IBOutlet var pageSizeLabel: NSTextField!
  19. @IBOutlet var pageGapLabel: NSTextField!
  20. @IBOutlet var pageGapTextField: NSTextField!
  21. @IBOutlet var pagesGapStepper: NSStepper!
  22. @IBOutlet var btnConvert: NSButton!
  23. @IBOutlet var btnCancel: NSButton!
  24. @IBOutlet var urlTextField: NSTextField!
  25. var filePath: String?
  26. var posterMaskView: KMBookletMaskView?
  27. var gap: CGFloat = 0
  28. var pageSize: CGSize = NSMakeSize(298, 420)
  29. var handler: KMURLToPDFWindowControllerComplete?
  30. var beginWindow: NSWindow?
  31. override init(window: NSWindow?) {
  32. super.init(window: window)
  33. }
  34. required init?(coder: NSCoder) {
  35. super.init(coder: coder)
  36. }
  37. override func windowDidLoad() {
  38. super.windowDidLoad()
  39. window?.title = NSLocalizedString("Web Page To PDF", comment: "")
  40. pageViewLabel.stringValue = NSLocalizedString("Page View", comment: "")
  41. pageSizeLabel.stringValue = NSLocalizedString("Page size:", comment: "")
  42. pageGapLabel.stringValue = "\(NSLocalizedString("Spacing", comment: "")):"
  43. urlBtn.title = NSLocalizedString("URL", comment: "")
  44. localHtmlBtn.title = NSLocalizedString("File", comment: "")
  45. btnConvert.title = NSLocalizedString("Save as PDF", comment: "")
  46. btnCancel.title = NSLocalizedString("Cancel", comment: "")
  47. outputButton.title = NSLocalizedString("Choose...", comment: "")
  48. outputBackView.wantsLayer = true
  49. outputBackView.layer?.borderWidth = 0.8
  50. outputBackView.layer?.borderColor = NSColor(red: 177.0/255, green: 178.0/255, blue: 177.0/255, alpha: 1).cgColor
  51. urlTextField.placeholderString = "https://www.pdfreaderpro.com"
  52. urlTextField.delegate = self
  53. outputText.isEditable = false
  54. pageSizeComboBox.addItems(withObjectValues: [
  55. NSLocalizedString("Automatically Resize", comment: "Menu item title"),
  56. "4A0 1682 × 2378 mm",
  57. "2A0 1189 × 1682 mm",
  58. "A0 841 × 1189 mm",
  59. "A1 594 × 841 mm",
  60. "A2 420 × 594 mm",
  61. "A3 297 × 420 mm",
  62. "A4 210 × 297 mm",
  63. "A5 148 × 210 mm",
  64. "A6 105 × 148 mm",
  65. "A7 74 × 105 mm",
  66. "A8 52 × 74 mm",
  67. "A9 37 × 52 mm",
  68. "A10 26 × 37 mm"
  69. ])
  70. pageSizeComboBox.selectItem(at: 0)
  71. pageSizeComboBox.isEditable = false
  72. pageSize = NSMakeSize(298, 420)
  73. gap = 0
  74. pagesGapStepper.stringValue = "0"
  75. pagesGapStepper.isEnabled = false
  76. pageGapTextField.isEnabled = false
  77. if urlBtn.state == .on {
  78. if urlTextField.stringValue.count > 0 {
  79. btnConvert.isEnabled = true
  80. } else {
  81. btnConvert.isEnabled = false
  82. }
  83. } else {
  84. if outputText.stringValue.count > 0 {
  85. btnConvert.isEnabled = true
  86. } else {
  87. btnConvert.isEnabled = false
  88. }
  89. }
  90. outputText.placeholderString = "\(NSLocalizedString("Select a File", comment: "")) (.html, .webarchive)"
  91. outputButton.isEnabled = false
  92. }
  93. func isUrl(_ urlString: String?) -> Bool {
  94. guard let urlString = urlString else { return false }
  95. // 实现方法有问题,暂不使用
  96. var url: String
  97. if urlString.count > 4, urlString.prefix(4) == "www." {
  98. url = "http://\(urlString)"
  99. } else {
  100. url = urlString
  101. }
  102. let urlRegex = "\\bhttps?://[a-zA-Z0-9\\-.]+(?::(\\d+))?(?:(?:/[a-zA-Z0-9\\-._?,'+\\&%$=~*!():@\\\\]*)+)?"
  103. let urlTest = NSPredicate(format: "SELF MATCHES %@", urlRegex)
  104. return urlTest.evaluate(with: url)
  105. }
  106. func beginSheetModalForWindow(_ window: NSWindow, completionHandler handler: ((String) -> Void)?) {
  107. self.handler = handler
  108. self.beginWindow = window
  109. self.beginWindow?.beginSheet(self.window!) { response in
  110. self.handler?(self.filePath ?? "")
  111. }
  112. }
  113. @IBAction func buttonItemClicked_Cancel(_ sender: NSButton) {
  114. self.beginWindow?.endSheet(self.window!)
  115. self.window?.orderOut(self)
  116. }
  117. func urlValueEncode(_ str: String) -> String? {
  118. let allowedCharacterSet = CharacterSet(charactersIn: "!*'();:@&=+$,?%#[]{}").inverted
  119. return str.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet)
  120. }
  121. @IBAction func buttonItemClicked_Start(_ sender: NSButton) {
  122. if !FileManager.default.fileExists(atPath: kUrlToPDFFolderPath) {
  123. try? FileManager.default.createDirectory(atPath: kUrlToPDFFolderPath, withIntermediateDirectories: false, attributes: nil)
  124. }
  125. var url: URL?
  126. var fileName: String?
  127. if urlBtn.state == .on {
  128. let urlString = urlTextField.stringValue
  129. var tUrl = URL(string: urlString)
  130. if tUrl?.scheme?.count ?? 0 < 1 {
  131. tUrl = URL(string: "http://\(urlString)")
  132. }
  133. url = tUrl
  134. } else {
  135. url = URL(fileURLWithPath: outputText.stringValue)
  136. fileName = outputText.stringValue.deletingPathExtension.lastPathComponent
  137. }
  138. let string = pageGapTextField.stringValue
  139. let unitScale: CGFloat = (595.0 / 21.0) * 2.54
  140. if string.stringToCGFloat() <= 0 {
  141. pageGapTextField.stringValue = "0"
  142. } else if string.stringToCGFloat() * unitScale > pageSize.width / 2 {
  143. let maxF = pageSize.width / (string.stringToCGFloat() * 2)
  144. pageGapTextField.stringValue = "\(maxF)"
  145. }
  146. gap = formatFloat(Float(string.stringToCGFloat() * unitScale)).stringToCGFloat()
  147. if let url = url {
  148. showWaitting()
  149. let convert = KMConvertURLToPDF.shareInstance()
  150. convert.fileName = fileName ?? ""
  151. convert.convertUrl(toPDF: [url], toPath: kUrlToPDFFolderPath, pageSize: pageSize, gap: gap, progress: { value in
  152. // Progress update
  153. }, completionHandler: { successArray, failArray in
  154. self.hideWaitting()
  155. if failArray.isEmpty {
  156. if let filePath = successArray.first as? String, FileManager.default.fileExists(atPath: filePath) {
  157. self.filePath = filePath
  158. NSApp.endSheet(self.window!, returnCode: sender.tag)
  159. self.window?.orderOut(self)
  160. }
  161. } else {
  162. let alert = NSAlert()
  163. alert.alertStyle = .critical
  164. alert.informativeText = NSLocalizedString("Conversion Failed", comment: "")
  165. alert.messageText = ""
  166. alert.addButton(withTitle: NSLocalizedString("OK", comment: ""))
  167. alert.runModal()
  168. }
  169. })
  170. }
  171. }
  172. @IBAction func buttonItemClick_Add(_ sender: Any) {
  173. urlBtn.state = .off
  174. localHtmlBtn.state = .on
  175. urlTextField.isEditable = false
  176. urlTextField.stringValue = ""
  177. btnConvert.isEnabled = false
  178. let panel = NSOpenPanel()
  179. panel.canChooseFiles = true
  180. panel.canChooseDirectories = false
  181. panel.allowsMultipleSelection = false
  182. panel.allowedFileTypes = ["HTML", "html", "webarchive", "htm"]
  183. panel.beginSheetModal(for: window!) { response in
  184. if response == .OK {
  185. self.outputText.stringValue = panel.url?.path ?? ""
  186. self.btnConvert.isEnabled = true
  187. }
  188. }
  189. }
  190. @IBAction func buttonItemClick_ChangeType(_ sender: NSButton) {
  191. if urlBtn.state == .on {
  192. urlTextField.isEditable = true
  193. urlTextField.becomeFirstResponder()
  194. outputText.stringValue = ""
  195. outputButton.isEnabled = false
  196. if urlTextField.stringValue.count > 0 {
  197. btnConvert.isEnabled = true
  198. } else {
  199. btnConvert.isEnabled = false
  200. }
  201. } else {
  202. urlTextField.isEditable = false
  203. urlTextField.stringValue = ""
  204. outputButton.isEnabled = true
  205. if outputText.stringValue.count > 0 {
  206. btnConvert.isEnabled = true
  207. } else {
  208. btnConvert.isEnabled = false
  209. }
  210. }
  211. }
  212. @IBAction func comboBoxItemClick_PageSize(_ sender: NSComboBox) {
  213. switch sender.indexOfSelectedItem {
  214. case 0:
  215. pageSize = NSMakeSize(298, 420)
  216. case 1:
  217. pageSize = CGSize(width: 4760, height: 6736)
  218. case 2:
  219. pageSize = CGSize(width: 3368, height: 4760)
  220. case 3:
  221. pageSize = CGSize(width: 2380, height: 3368)
  222. case 4:
  223. pageSize = CGSize(width: 1684, height: 2380)
  224. case 5:
  225. pageSize = CGSize(width: 1190, height: 1684)
  226. case 6:
  227. pageSize = CGSize(width: 842, height: 1190)
  228. case 7:
  229. pageSize = CGSize(width: 595, height: 842)
  230. case 8:
  231. pageSize = CGSize(width: 420, height: 595)
  232. case 9:
  233. pageSize = CGSize(width: 297, height: 420)
  234. default:
  235. pageSize = CGSize(width: 210, height: 297)
  236. }
  237. if sender.indexOfSelectedItem != 0 {
  238. pagesGapStepper.stringValue = "0"
  239. pageGapTextField.stringValue = "0"
  240. pageGapTextField.isEnabled = true
  241. pagesGapStepper.isEnabled = true
  242. } else {
  243. pagesGapStepper.stringValue = "0"
  244. pageGapTextField.stringValue = ""
  245. pageGapTextField.isEnabled = false
  246. pagesGapStepper.isEnabled = false
  247. }
  248. }
  249. @IBAction func stepperItemClick_Gap(_ sender: NSStepper) {
  250. let unitScale: CGFloat = (595.0 / 21.0) * 2.54
  251. pageGapTextField.stringValue = String(format: "%.2f", pagesGapStepper.floatValue)
  252. gap = formatFloat(pagesGapStepper.floatValue * Float(unitScale)).stringToCGFloat()
  253. }
  254. func controlTextDidEndEditing(_ obj: Notification) {
  255. if let object = obj.object as? NSTextField, object == pageGapTextField {
  256. let unitScale: CGFloat = (595.0 / 21.0) * 2.54
  257. if object.stringValue.stringToCGFloat() <= 0 {
  258. pageGapTextField.stringValue = "0"
  259. } else if object.stringValue.stringToCGFloat() * unitScale > pageSize.width / 2 {
  260. let maxF = pageSize.width / (object.stringValue.stringToCGFloat() * 2)
  261. pageGapTextField.stringValue = "\(maxF)"
  262. }
  263. gap = formatFloat(Float(object.stringValue.stringToCGFloat() * unitScale)).stringToCGFloat()
  264. pagesGapStepper.floatValue = Float(object.stringValue.stringToCGFloat())
  265. }
  266. }
  267. func controlTextDidChange(_ notification: Notification) {
  268. if urlBtn.state == .on {
  269. if urlTextField.stringValue.count > 0 {
  270. btnConvert.isEnabled = true
  271. } else {
  272. btnConvert.isEnabled = false
  273. }
  274. } else {
  275. if outputText.stringValue.count > 0 {
  276. btnConvert.isEnabled = true
  277. } else {
  278. btnConvert.isEnabled = false
  279. }
  280. }
  281. }
  282. func formatFloat(_ f: Float) -> String {
  283. if f.truncatingRemainder(dividingBy: 1) == 0 {
  284. return String(format: "%.0f", f)
  285. } else if (f * 10).truncatingRemainder(dividingBy: 1) == 0 {
  286. return String(format: "%.1f", f)
  287. } else {
  288. return String(format: "%.2f", f)
  289. }
  290. }
  291. func showWaitting() {
  292. if posterMaskView == nil {
  293. posterMaskView = KMBookletMaskView(frame: NSMakeRect(0, 0, window?.frame.size.width ?? 0, window?.frame.size.height ?? 0))
  294. }
  295. window?.contentView?.addSubview(posterMaskView!)
  296. }
  297. func hideWaitting() {
  298. posterMaskView?.removeFromSuperview()
  299. }
  300. }