KMOCRPDFWindowController.swift 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873
  1. //
  2. // KMOCRPDFWindowController.swift
  3. // PDF Master
  4. //
  5. // Created by liujiajie on 2023/11/14.
  6. //
  7. import Cocoa
  8. import PDFKit
  9. class KMOCRPDFWindowController: NSWindowController, NSWindowDelegate, KMGOCRManagerDelegate, NSPopoverDelegate,NSTextFieldDelegate{
  10. var resultString: String = ""
  11. var ocrDictionary: NSMutableDictionary?
  12. var currentIndexPage: Int = 0{
  13. didSet {
  14. if PDFView.document.pageCount > currentIndexPage {
  15. currentIndexPage = 0
  16. self.currentPageLabel.stringValue = "\(currentIndexPage + 1)"
  17. // let page = self.PDFView.document.page(at: UInt(currentIndexPage))
  18. self.PDFView.go(toPageIndex: currentIndexPage, animated: true)
  19. }
  20. }
  21. }
  22. var PDFView: CPDFView!
  23. @IBOutlet var prePdfBGView: NSView!
  24. @IBOutlet var currentPageLabel: NSTextField!
  25. @IBOutlet var totalPageLabel: NSTextField!
  26. @IBOutlet var pageLabel: NSTextField!
  27. @IBOutlet var planLabel: NSTextField!
  28. @IBOutlet var ocrCopyButton: NSButton!
  29. @IBOutlet var pageRangeBox: NSComboBox!
  30. @IBOutlet var planComboBox: NSPopUpButton!
  31. @IBOutlet var progressIndicator: NSProgressIndicator!
  32. @IBOutlet var progressControl: NSProgressIndicator!
  33. @IBOutlet var failedBox: NSBox!
  34. @IBOutlet var failedLabel: NSTextField!
  35. @IBOutlet var cancelButton: NSButton!
  36. @IBOutlet var startButton: NSButton!
  37. @IBOutlet var languageLabel: NSTextField!
  38. @IBOutlet var ocrResultLabel: NSTextField!
  39. @IBOutlet var languageButton: NSButton!
  40. @IBOutlet var txtTextView: NSTextView!
  41. @IBOutlet var deleteButton: NSButton!
  42. @IBOutlet var emptyBox: NSBox!
  43. @IBOutlet var emptyLabel: NSTextField!
  44. var errorOCRArrays: Array<Any>?
  45. var pageIndexs: Array<Any>?
  46. var password: String = ""
  47. var pathFile: String = ""
  48. var pdfDocument: CPDFDocument?
  49. @IBOutlet var saveButton: NSButton!
  50. @IBOutlet var box1: NSBox!
  51. @IBOutlet var boxLabel1: NSTextField!
  52. @IBOutlet var previewLabel: NSTextField!
  53. @IBOutlet var savePDFButton: NSButton!
  54. var ocrCurrentIndex: Int = 0
  55. var appleOCRManger: KMGOCRManager?
  56. var googleOCRManger: KMGOCRManager?
  57. var savedFileName: String = ""
  58. func getOCRResrultsFolderPath() -> String {
  59. var path = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.applicationSupportDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).last
  60. path?.append("/")
  61. path?.append(Bundle.main.bundleIdentifier!)
  62. if (FileManager.default.fileExists(atPath: path!) == false) {
  63. try?FileManager.default.createDirectory(atPath: path!, withIntermediateDirectories: false)
  64. }
  65. path?.append("/")
  66. path?.append("OCR_Resrults")
  67. return path!
  68. }
  69. convenience init(cpdfDocument: CPDFDocument, pwd: String?) {
  70. self.init(windowNibName: "KMOCRPDFWindowController")
  71. self.pdfDocument = cpdfDocument
  72. self.password = pwd ?? ""
  73. let url: URL? = cpdfDocument.documentURL
  74. var lastPathComponent = url?.deletingPathExtension()
  75. if let str = lastPathComponent?.lastPathComponent {
  76. if str.count > 0 {
  77. self.pathFile = str
  78. }else {
  79. self.pathFile = NSLocalizedString("Untitled", comment: "")
  80. }
  81. } else {
  82. self.pathFile = NSLocalizedString("Untitled", comment: "")
  83. }
  84. }
  85. convenience init(filePath: String, pwd: String?) {
  86. self.init(windowNibName: "KMOCRPDFWindowController")
  87. self.password = pwd ?? ""
  88. let pathExtension = filePath.lastPathComponent.customPathExtension
  89. if pathExtension.count > 0{
  90. if pathExtension.lowercased() == "pdf" {
  91. pdfDocument = CPDFDocument(url: URL(fileURLWithPath: filePath))
  92. } else {
  93. if let image = NSImage(contentsOf: URL(fileURLWithPath: filePath)) {
  94. self.pdfDocument = CPDFDocument()
  95. _ = pdfDocument?.km_insertPage(image.size, withImage: filePath, at: pdfDocument?.pageCount ?? 0)
  96. }
  97. }
  98. }
  99. self.pathFile = filePath.lastPathComponent.deletingPathExtension
  100. }
  101. deinit {
  102. NotificationCenter.default.removeObserver(self)
  103. KMGOCRManager.default().delegate = nil
  104. appleOCRManger?.cancelRecognition()
  105. appleOCRManger?.delegate = nil
  106. googleOCRManger?.cancelRecognition()
  107. googleOCRManger?.delegate = nil
  108. }
  109. override func windowDidLoad() {
  110. super.windowDidLoad()
  111. let preView: CPDFView = CPDFView(frame: self.prePdfBGView.bounds)
  112. self.prePdfBGView.addSubview(preView)
  113. self.PDFView = preView
  114. self.pageLabel.stringValue = NSLocalizedString("Page Range", comment: "")
  115. self.planLabel.stringValue = NSLocalizedString("OCR Plan", comment: "")
  116. self.planComboBox.removeAllItems()
  117. self.planComboBox.addItems(withTitles: [NSLocalizedString("Plan 1 (Online)", comment: ""), NSLocalizedString("Plan 2 (Offline)", comment: "")])
  118. let plan = UserDefaults.standard.integer(forKey: "KMOCRCurrentPlanKey")
  119. if plan == 0 {
  120. KMGOCRManager.default().ocrType = .google
  121. self.planComboBox.selectItem(at: 0)
  122. } else {
  123. KMGOCRManager.default().ocrType = .apple
  124. self.planComboBox.selectItem(at: 1)
  125. }
  126. self.pageRangeBox.addItems(withObjectValues: [NSLocalizedString("All Pages", comment: ""), NSLocalizedString("Current Page", comment: ""), NSLocalizedString("Odd Pages Only", comment: ""), NSLocalizedString("Even Pages Only", comment: ""), NSLocalizedString("e.g. 1,3-5,10", comment: "")])
  127. self.pageRangeBox.placeholderString = NSLocalizedString("e.g. 1,3-5,10", comment: "")
  128. self.pageRangeBox.selectItem(at: 0)
  129. self.pageRangeBox.isEditable = false
  130. if self.pageRangeBox.indexOfSelectedItem == 4 {
  131. self.window?.makeFirstResponder(self.pageRangeBox)
  132. self.pageRangeBox.stringValue = ""
  133. self.pageRangeBox.isEditable = true
  134. }
  135. self.emptyLabel.stringValue = NSLocalizedString("Recognize text from Image-based or Scanned PDF with OCR", comment: "")
  136. self.failedLabel.stringValue = NSLocalizedString("OCR failed. Please try to change the OCR Plan to \"Plan 2 (Offline)\"", comment: "")
  137. self.txtTextView.textColor = NSColor.textColor
  138. self.txtTextView.enclosingScrollView?.autohidesScrollers = true
  139. self.ocrResultLabel.stringValue = NSLocalizedString("OCR Results", comment: "")
  140. self.ocrCopyButton?.toolTip = KMLocalizedString("Copy and edit text from documents with OCR.", nil)
  141. self.deleteButton?.toolTip = NSLocalizedString("Delete", comment: "")
  142. self.deleteButton?.title = NSLocalizedString("Delete", comment: "")
  143. self.ocrCopyButton?.title = NSLocalizedString("Copy", comment: "")
  144. self.errorOCRArrays = []
  145. self.pageIndexs = []
  146. self.ocrCopyButton.isEnabled = false
  147. self.deleteButton.isEnabled = false
  148. self.saveButton.isEnabled = false
  149. self.savePDFButton.isEnabled = false
  150. self.boxLabel1.stringValue = NSLocalizedString("Settings", comment: "")
  151. self.previewLabel.stringValue = NSLocalizedString("Preview", comment: "")
  152. self.saveButton.title = NSLocalizedString("Save as TXT", comment: "")
  153. self.savePDFButton.title = NSLocalizedString("Save as PDF", comment: "")
  154. self.saveButton.toolTip = NSLocalizedString("Export as a searchable PDF or text file.", comment: "")
  155. self.savePDFButton.toolTip = NSLocalizedString("Export as a searchable PDF or text file.", comment: "")
  156. self.cancelButton.title = NSLocalizedString("Cancel", comment: "")
  157. self.startButton.title = NSLocalizedString("OCR", comment: "")
  158. self.languageLabel.stringValue = NSLocalizedString("Select OCR Language:", comment: "")
  159. self.updateLanguageButton((KMGOCRManager.default().selectedLanguages?.value(forKeyPath: KMGOCRLanguageStringKey) as? [String]))
  160. NotificationCenter.default.addObserver(self, selector: #selector(OCRSelectedLanguagesChangeNotification(notification:)), name: NSNotification.Name("KMOCRSelectedLanguagesChangeNotification"), object: nil)
  161. NotificationCenter.default.addObserver(self, selector: #selector(OCRSelectedPlanChangeNotification(notification:)), name: NSNotification.Name("KMOCRSelectedPlanChangeNotification"), object: nil)
  162. self.progressControl.isHidden = true
  163. emptyBox.isHidden = false
  164. failedBox.isHidden = true
  165. self.PDFView.document = self.pdfDocument
  166. self.PDFView.autoScales = true
  167. self.reloadPDFData()
  168. if !(self.pdfDocument?.isLocked ?? false) && ((self.pdfDocument?.unlock(withPassword: self.password)) != nil) {
  169. } else {
  170. DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
  171. let com = PasswordWindowController(windowNibName: "PasswordWindowController")
  172. com.fileURL = self.pdfDocument?.documentURL
  173. NSWindow.currentWindow().km_beginSheet(windowC: com)
  174. com.closeCallBack = { passwordString in
  175. if passwordString.count > 0 {
  176. self.pdfDocument?.unlock(withPassword: passwordString)
  177. self.password = passwordString
  178. self.reloadPDFData()
  179. } else {
  180. self.close()
  181. }
  182. }
  183. }
  184. }
  185. NotificationCenter.default.addObserver(self, selector: #selector(PDFViewDocumentChangedNotification(notification:)), name: NSNotification.Name.PDFViewPageChanged, object: nil)
  186. }
  187. func windowShouldClose(_ sender: NSWindow) -> Bool {
  188. close()
  189. return true
  190. }
  191. override func close() {
  192. if ((self.window?.isSheet) != nil) {
  193. // self.km_endSheet()
  194. self.window?.sheetParent?.endSheet(self.window!)
  195. } else {
  196. super.close()
  197. }
  198. }
  199. func reloadPDFData() { // 隐藏PDFView滑动条
  200. self.PDFView.documentView().enclosingScrollView?.hasVerticalScroller = false
  201. self.PDFView.documentView().enclosingScrollView?.hasHorizontalScroller = false
  202. let pageCount: Int = Int(self.pdfDocument?.pageCount ?? 0)
  203. let currentPageIndex = self.pdfDocument?.index(for: self.PDFView.currentPage())
  204. self.currentPageLabel.stringValue = "\((currentPageIndex ?? 0) + 1)"
  205. self.totalPageLabel.stringValue = "/ \(pageCount)"
  206. }
  207. //MARK: KMOCRSelectedLanguagesChangeNotification
  208. @objc func OCRSelectedLanguagesChangeNotification(notification: NSNotification) {
  209. if let selectedLanguages = notification.object as? [String] {
  210. updateLanguageButton(selectedLanguages)
  211. }
  212. }
  213. @objc func OCRSelectedPlanChangeNotification(notification: NSNotification) {
  214. let plan = UserDefaults.standard.integer(forKey: "KMOCRCurrentPlanKey")
  215. if plan == 0 {
  216. self.planComboBox.selectItem(at: 0)
  217. } else {
  218. self.planComboBox.selectItem(at: 1)
  219. }
  220. KMGOCRManager.default().selectedLanguages = NSMutableArray()
  221. updateLanguageButton(KMGOCRManager.default().selectedLanguages?.value(forKeyPath: KMGOCRLanguageStringKey) as? [String])
  222. }
  223. @objc func PDFViewDocumentChangedNotification(notification: NSNotification) {
  224. let page: CPDFPage = self.PDFView.currentPage()
  225. let pageIndex = self.PDFView.document.index(for: page)
  226. self.currentPageLabel.stringValue = "\(pageIndex + 1)"
  227. }
  228. func updateLanguageButton(_ languages: [String]?) {
  229. if languages?.count ?? 0 < 1 {
  230. self.languageButton.title = NSLocalizedString("Auto Detection", comment: "")
  231. return
  232. }
  233. var languageName: String? = nil
  234. if languages?.count ?? 0 > 0 {
  235. for i in 0..<(languages?.count ?? 0) {
  236. let language = languages?[i]
  237. if i == 0 {
  238. languageName = language
  239. } else {
  240. languageName = languageName?.appendingFormat(",%@", language ?? "")
  241. }
  242. }
  243. } else {
  244. languageName = ""
  245. }
  246. self.languageButton.title = languageName ?? ""
  247. }
  248. func imageRep(withSize size: NSSize, scale: CGFloat, drawingHandler: (NSRect) -> Void) -> Any? {
  249. let bmpImageRep = NSBitmapImageRep(bitmapDataPlanes: nil, pixelsWide: Int(size.width * scale), pixelsHigh: Int(size.height * scale), bitsPerSample: 8, samplesPerPixel: 4, hasAlpha: true, isPlanar: false, colorSpaceName: .calibratedRGB, bitmapFormat: .alphaFirst, bytesPerRow: 0, bitsPerPixel: 0)
  250. // bmpImageRep?.bitmapImageRepByRetaggingWithColorSpace = .sRGB
  251. bmpImageRep?.size = size
  252. NSGraphicsContext.saveGraphicsState()
  253. NSGraphicsContext.current = NSGraphicsContext(bitmapImageRep: bmpImageRep!)
  254. // drawingHandler(.zero, size)
  255. drawingHandler(NSRect(origin: NSZeroPoint, size: size))
  256. NSGraphicsContext.restoreGraphicsState()
  257. return bmpImageRep
  258. }
  259. func bitmapImage(withSize size: NSSize, drawingHandler: (NSRect) -> Void) -> NSImage? {
  260. let image = NSImage(size: size)
  261. let scale = KMImageScale
  262. image.addRepresentation(imageRep(withSize: size, scale: scale, drawingHandler: drawingHandler) as! NSBitmapImageRep)
  263. return image
  264. }
  265. func thumbnail(with page: CPDFPage) -> NSImage? {
  266. let bounds = page.bounds(for: .cropBox)
  267. var pageSize = bounds.size
  268. var scale: CGFloat = 1.0
  269. var thumbnailSize: NSSize
  270. var pageRect = NSZeroRect
  271. var image: NSImage?
  272. let aSize = pageSize.width
  273. if page.rotation % 180 == 90 {
  274. pageSize = NSSize(width: pageSize.height, height: pageSize.width)
  275. }
  276. if aSize > 0.0 {
  277. if pageSize.height > pageSize.width {
  278. thumbnailSize = NSSize(width: round(aSize * pageSize.width / pageSize.height), height: aSize)
  279. } else {
  280. thumbnailSize = NSSize(width: aSize, height: round(aSize * pageSize.height / pageSize.width))
  281. }
  282. let kKMMaxPixelsLimit = 75000000
  283. let totalPixelNumber = Int((thumbnailSize.width * KMImageScale) * (thumbnailSize.height * KMImageScale))
  284. if totalPixelNumber >= kKMMaxPixelsLimit {
  285. let sizeScale = sqrt(Float(kKMMaxPixelsLimit - 1000) / Float(totalPixelNumber))
  286. thumbnailSize = NSSize(width: floor(thumbnailSize.width * CGFloat(sizeScale)), height: floor(thumbnailSize.height * CGFloat(sizeScale)))
  287. }
  288. scale = max(thumbnailSize.width / pageSize.width, (thumbnailSize.height) / pageSize.height)
  289. } else {
  290. thumbnailSize = NSSize(width: pageSize.width, height: pageSize.height)
  291. }
  292. if thumbnailSize.width.isNaN || thumbnailSize.height.isNaN || thumbnailSize.width == 0.0 || thumbnailSize.height == 0.0 {
  293. thumbnailSize = NSSize(width: 186.0, height: 256.0)
  294. }
  295. pageRect.size = thumbnailSize
  296. image = bitmapImage(withSize: thumbnailSize) { rect in
  297. NSGraphicsContext.current?.imageInterpolation = .high
  298. NSGraphicsContext.saveGraphicsState()
  299. NSColor.white.setFill()
  300. pageRect.fill()
  301. NSGraphicsContext.restoreGraphicsState()
  302. if abs(scale - 1.0) > 0.0 {
  303. let transform = NSAffineTransform()
  304. transform.scale(by: scale)
  305. transform.concat()
  306. }
  307. page.draw(with: .cropBox, to: (NSGraphicsContext.current?.cgContext as! CGContext))
  308. NSGraphicsContext.current?.imageInterpolation = .default
  309. }
  310. return image
  311. }
  312. func savedName() -> String {
  313. var resultArrays: Array<Any> = []
  314. var ocrIndexArrays: Array<Any> = []
  315. let sortedKeys = self.ocrDictionary?.allKeys.sorted(by: {
  316. ($0 as? String)?.compare($1 as? String ?? "") == .orderedAscending
  317. })
  318. for i in 0 ..< (sortedKeys?.count ?? 0) {
  319. let keyS = sortedKeys?[i]
  320. ocrIndexArrays.append(keyS as Any)
  321. resultArrays.append(self.ocrDictionary?[keyS as Any] as Any)
  322. }
  323. var fileName = self.savedFileName
  324. if fileName.count > 50 {
  325. fileName = String(fileName.prefix(50))
  326. }
  327. if sortedKeys?.count ?? 0 > 1 {
  328. fileName = "\(self.pathFile) Pages \(fileName) _OCR"
  329. } else {
  330. fileName = "\(self.pathFile) Page \(fileName) _OCR"
  331. }
  332. var returnName = fileName
  333. switch self.pageRangeBox.indexOfSelectedItem {
  334. case 0:
  335. returnName = "\(self.pathFile)_OCR"
  336. case 2:
  337. returnName = "\(self.pathFile) Odd_OCR"
  338. case 3:
  339. returnName = "\(self.pathFile) Even_OCR"
  340. default:
  341. break
  342. }
  343. return returnName
  344. }
  345. func savePDF() {
  346. var resultArrays: Array<Any> = []
  347. var ocrIndexArrays: Array<Any> = []
  348. let sortedKeys = self.ocrDictionary?.allKeys.sorted(by: { ($0 as? String)?.compare($1 as? String ?? "") == .orderedAscending })
  349. for i in 0 ..< (sortedKeys?.count ?? 0) {
  350. let keyS = sortedKeys?[i]
  351. ocrIndexArrays.append(keyS as Any)
  352. resultArrays.append(self.ocrDictionary?[keyS as Any] as Any)
  353. }
  354. let fileName = self.savedFileName
  355. let saveAccessCtr = KMSavePanelAccessoryController()
  356. let outputSavePanel = NSSavePanel()
  357. outputSavePanel.allowedFileTypes = ["pdf"]
  358. outputSavePanel.nameFieldStringValue = fileName
  359. outputSavePanel.accessoryView = saveAccessCtr.view
  360. outputSavePanel.beginSheetModal(for: self.window!) { [self] (result) in
  361. if result == .OK {
  362. let savePDFPath = outputSavePanel.url?.path ?? ""
  363. if !FileManager.default.fileExists(atPath: getOCRResrultsFolderPath()) {
  364. try? FileManager.default.createDirectory(atPath: getOCRResrultsFolderPath(), withIntermediateDirectories: false, attributes: nil)
  365. }
  366. var imagePath = [Any]()
  367. for i in 0..<ocrIndexArrays.count {
  368. let rPath = (getOCRResrultsFolderPath() as NSString).appendingPathComponent("\(i).png")
  369. if let index = ocrIndexArrays[i] as? NSNumber, let page = self.pdfDocument?.page(at: UInt(index.intValue)), let image = self.thumbnail(with: page) {
  370. try? image.tiffRepresentation?.write(to: URL(fileURLWithPath: rPath), options: .atomic)
  371. }
  372. imagePath.append(rPath)
  373. }
  374. KMGOCRManager.default().createPDFFile(savePDFPath, imagePaths: imagePath, results: resultArrays, scale: KMImageScale)
  375. if saveAccessCtr.openAutomaticButton.state == .on {
  376. self.cancelButtonAction("")
  377. NSDocumentController.shared.openDocument(withContentsOf: URL(fileURLWithPath: savePDFPath), display: true, completionHandler: {document,documentWasAlreadyOpen,error in
  378. })
  379. } else {
  380. self.viewFileAtFinder(savePDFPath)
  381. }
  382. }
  383. }
  384. }
  385. func viewFileAtFinder(_ fileName: String) {
  386. let workspace = NSWorkspace.shared
  387. let url = URL(fileURLWithPath: fileName)
  388. workspace.activateFileViewerSelecting([url])
  389. }
  390. func saveText() {
  391. let sortedKeys = self.ocrDictionary?.allKeys.sorted(by: { ($0 as? String)?.compare($1 as? String ?? "") == .orderedAscending })
  392. let fileName = self.savedFileName
  393. let saveAccessCtr = KMSavePanelAccessoryController()
  394. let outputSavePanel = NSSavePanel()
  395. outputSavePanel.allowedFileTypes = ["txt"]
  396. outputSavePanel.nameFieldStringValue = fileName
  397. outputSavePanel.accessoryView = saveAccessCtr.view
  398. outputSavePanel.beginSheetModal(for: self.window!) { (result) in
  399. if result == .OK {
  400. let outputURL = outputSavePanel.url
  401. try? self.txtTextView?.string.write(to: outputURL!, atomically: true, encoding: .utf8)
  402. if saveAccessCtr.openAutomaticButton?.state == .on {
  403. NSWorkspace.shared.open(outputURL!)
  404. } else {
  405. self.viewFileAtFinder(outputURL?.path ?? "")
  406. }
  407. }
  408. }
  409. }
  410. func fileNameWithNums(_ nums: [NSNumber]) -> String {
  411. var fileName: String? = nil
  412. if nums.count > 0 {
  413. if nums.count == 1 {
  414. if let num = nums.first {
  415. let idx = num.intValue + 1
  416. return "\(idx)"
  417. }
  418. }
  419. var sortIndex = NSSet()
  420. for num in nums {
  421. let idx = num.intValue + 1
  422. // sortIndex.insert(NSNumber(value: idx))
  423. sortIndex.adding(NSNumber(value: idx))
  424. }
  425. let sort = NSSortDescriptor(key: nil, ascending: true)
  426. let sortDesc = [sort]
  427. let sortArray = sortIndex.sortedArray(using: sortDesc)
  428. var a = 0
  429. var b = 0
  430. if sortArray.count == 1 {
  431. let num: NSNumber = sortArray.last as! NSNumber
  432. fileName = "\(num.intValue)"
  433. return fileName ?? ""
  434. }
  435. for i in 0 ..< sortArray.count {
  436. let num = sortArray[i]
  437. if fileName?.count ?? 0 > 0 {
  438. if (num as AnyObject).intValue == b + 1 {
  439. b = (num as AnyObject).intValue
  440. if (i == sortArray.count - 1) {
  441. fileName = (fileName ?? "") + "\(a)-\(b)"
  442. }
  443. } else {
  444. if a == b {
  445. fileName = (fileName ?? "") + "\(a),"
  446. } else {
  447. fileName = (fileName ?? "") + "\(a)-\(b),"
  448. }
  449. a = (num as AnyObject).intValue
  450. b = (num as AnyObject).intValue
  451. if (i == sortArray.count - 1) {
  452. fileName = (fileName ?? "") + "\(a)"
  453. }
  454. }
  455. } else {
  456. fileName = ""
  457. a = (num as AnyObject).intValue
  458. b = (num as AnyObject).intValue
  459. }
  460. }
  461. return fileName ?? ""
  462. }
  463. return ""
  464. }
  465. @IBAction func cancelButtonAction(_ sender: Any) {
  466. KMGOCRManager.default().cancelRecognition()
  467. self.close()
  468. }
  469. @IBAction func startButtonAction(_ sender: Any) {
  470. self.ocrCurrentIndex = 0
  471. txtTextView.string = ""
  472. ocrDictionary = NSMutableDictionary()
  473. let pdfDocument = self.pdfDocument
  474. errorOCRArrays?.removeAll()
  475. pageIndexs?.removeAll()
  476. if self.pageRangeBox.indexOfSelectedItem == 0 {
  477. for i in 0..<(pdfDocument?.pageCount ?? 0) {
  478. pageIndexs?.append(NSNumber(value: i))
  479. }
  480. } else if self.pageRangeBox.indexOfSelectedItem == 1 {
  481. let page = PDFView.currentPage()
  482. let pageIndex = PDFView.document?.index(for: page)
  483. pageIndexs?.append(NSNumber(value: pageIndex!))
  484. } else if self.pageRangeBox.indexOfSelectedItem == 2 {
  485. for i in 0..<(pdfDocument?.pageCount ?? 0) where i % 2 == 0 {
  486. pageIndexs?.append(NSNumber(value: i))
  487. }
  488. } else if self.pageRangeBox.indexOfSelectedItem == 3 {
  489. for i in 0..<(pdfDocument?.pageCount ?? 0) where i % 2 != 0 {
  490. pageIndexs?.append(NSNumber(value: i))
  491. }
  492. } else {
  493. let attribute = KMFileAttribute()
  494. attribute.pdfDocument = pdfDocument
  495. attribute.bAllPage = false
  496. attribute.pagesString = self.pageRangeBox.stringValue
  497. let selectPages = attribute.fetchSelectPages()
  498. if selectPages.count > 0 {
  499. for num in selectPages {
  500. pageIndexs?.append(NSNumber(value: num.intValue - 1))
  501. }
  502. }
  503. }
  504. batchesOCR()
  505. }
  506. func batchesOCR() {
  507. savedFileName = savedName()
  508. let intervalOCR: UInt = 10
  509. var selctPageImages: NSMutableArray = NSMutableArray()
  510. for i in 0..<intervalOCR {
  511. if ocrCurrentIndex + Int(i) >= self.pageIndexs?.count ?? 0 { continue }
  512. autoreleasepool {
  513. let index = self.pageIndexs?[self.ocrCurrentIndex + Int(i)] as! NSNumber
  514. let page = self.pdfDocument?.page(at: UInt(index.intValue))
  515. let img: NSImage = thumbnail(with: page!)!
  516. let data: NSData = img.tiffRepresentation! as NSData
  517. selctPageImages.add(data)
  518. }
  519. }
  520. if selctPageImages.count < 1 && ocrCurrentIndex == 0 {
  521. let alert = NSAlert()
  522. alert.alertStyle = .critical
  523. alert.messageText = NSLocalizedString("Invalid page range or the page number is out of range. Please try again.", comment: "")
  524. if #available(OSX 10.11, *) {
  525. alert.beginSheetModal(for: self.window!, completionHandler: nil)
  526. } else {
  527. alert.runModal()
  528. }
  529. }
  530. if selctPageImages.count < 1 {
  531. return
  532. }
  533. if appleOCRManger != nil {
  534. appleOCRManger?.cancelRecognition()
  535. appleOCRManger?.delegate = nil
  536. appleOCRManger = nil
  537. }
  538. if googleOCRManger != nil {
  539. googleOCRManger?.cancelRecognition()
  540. googleOCRManger?.delegate = nil
  541. googleOCRManger = nil
  542. }
  543. ocrCopyButton.isEnabled = false
  544. saveButton.isEnabled = false
  545. savePDFButton.isEnabled = false
  546. startButton.isEnabled = false
  547. deleteButton.isEnabled = false
  548. progressControl.isHidden = false
  549. progressControl.startAnimation(nil)
  550. emptyBox.isHidden = true
  551. failedBox.isHidden = true
  552. DispatchQueue.main.async {
  553. let languages = KMGOCRManager.default().selectedLanguages?.value(forKeyPath: KMGOCRLanguageCodeKey)
  554. if self.planComboBox.indexOfSelectedItem == 0 {
  555. self.googleOCRManger = KMGOCRManager()
  556. self.googleOCRManger?.ocrType = .google
  557. self.googleOCRManger?.delegate = self
  558. self.googleOCRManger?.recognitionImages((selctPageImages as! [Any]), withLanguages: languages as? [Any])
  559. } else {
  560. self.appleOCRManger = KMGOCRManager()
  561. self.appleOCRManger?.ocrType = .apple
  562. self.appleOCRManger?.delegate = self
  563. self.appleOCRManger?.recognitionImages((selctPageImages as! [Any]), withLanguages: languages as? [Any])
  564. }
  565. }
  566. }
  567. @IBAction func planSelectButtonAction(_ sender: NSPopUpButton) {
  568. let plan = UserDefaults.standard.integer(forKey: "KMOCRCurrentPlanKey")
  569. if plan != sender.indexOfSelectedItem {
  570. KMGOCRManager.default().selectedLanguages = NSMutableArray()
  571. updateLanguageButton(KMGOCRManager.default().selectedLanguages?.value(forKeyPath: KMGOCRLanguageStringKey) as? [String])
  572. }
  573. UserDefaults.standard.set(sender.indexOfSelectedItem, forKey: "KMOCRCurrentPlanKey")
  574. UserDefaults.standard.synchronize()
  575. if self.planComboBox.indexOfSelectedItem == 0 {
  576. KMGOCRManager.default().ocrType = .google
  577. } else {
  578. KMGOCRManager.default().ocrType = .apple
  579. }
  580. NotificationCenter.default.post(name: NSNotification.Name("KMOCRSelectedPlanChangeNotification"), object: nil)
  581. }
  582. @IBAction func languageButtonAction(_ sender: Any) {
  583. let popover = NSPopover()
  584. popover.delegate = self
  585. popover.contentViewController = KMLanguageViewController(nibName: "KMLanguageViewController", bundle: Bundle.main)
  586. popover.animates = true
  587. popover.behavior = .transient
  588. popover.show(relativeTo: (sender as! NSView).bounds, of: sender as! NSView, preferredEdge: .minX)
  589. }
  590. @IBAction func helpButtonAction(_ sender: Any) {
  591. let helpController = NSViewController()
  592. let textView = NSTextView(frame: NSRectToCGRect(NSMakeRect(0, 0, 300.0, 50.0)))
  593. textView.backgroundColor = NSColor.clear
  594. textView.isEditable = false
  595. textView.layer?.cornerRadius = 6
  596. let tStrAuto = NSLocalizedString("Choose automatic language detection for better OCR results.", comment: "")
  597. textView.alignment = .justified
  598. textView.string = "\n\(tStrAuto)"
  599. helpController.view = textView
  600. let popover = NSPopover()
  601. popover.delegate = self
  602. popover.contentViewController = helpController
  603. popover.animates = true
  604. popover.behavior = .transient
  605. popover.show(relativeTo: (sender as! NSView).bounds, of: sender as! NSView, preferredEdge: .minY)
  606. }
  607. @IBAction func buttonItemClick_CopyTxt(_ sender: NSButton) {
  608. let pasteboard = NSPasteboard.general
  609. pasteboard.clearContents()
  610. let str: NSPasteboardWriting = self.txtTextView.string as NSPasteboardWriting
  611. pasteboard.writeObjects([str])
  612. let _ = CustomAlertView.alertView(message: NSLocalizedString("Copy successful!", comment: ""), fromView: sender.superview!, withStyle: .black)
  613. }
  614. @IBAction func buttonItemClick_Delete(_ sender: NSButton) {
  615. ocrCopyButton.isEnabled = false
  616. saveButton.isEnabled = false
  617. savePDFButton.isEnabled = false
  618. deleteButton.isEnabled = false
  619. emptyBox.isHidden = false
  620. failedBox.isHidden = true
  621. resultString = ""
  622. txtTextView.string = ""
  623. ocrDictionary = NSMutableDictionary()
  624. }
  625. @IBAction func boxItemClicked_PageRange(_ sender: NSButton) {
  626. if 4 != pageRangeBox.indexOfSelectedItem {
  627. self.window?.makeFirstResponder(self)
  628. pageRangeBox.isEditable = false
  629. } else {
  630. pageRangeBox.stringValue = ""
  631. pageRangeBox.isEditable = true
  632. self.window?.makeFirstResponder(pageRangeBox)
  633. }
  634. }
  635. @IBAction func nextButtonAction(_ sender: NSButton) {
  636. self.PDFView.goToNextPage(nil)
  637. self.reloadPDFData()
  638. }
  639. @IBAction func previousButtonAction(_ sender: NSButton) {
  640. self.PDFView.goToPreviousPage(nil)
  641. self.reloadPDFData()
  642. }
  643. @IBAction func buttonClicked_SaveButton(_ sender: NSButton) {
  644. self.saveText()
  645. }
  646. @IBAction func savePDFButtonAction(_ sender: NSButton) {
  647. self.savePDF()
  648. }
  649. func controlTextDidEndEditing(_ notification: Notification) {
  650. guard let textField = notification.object as? NSTextField else { return }
  651. if textField == currentPageLabel {
  652. let index = Int(currentPageLabel.stringValue) ?? 0
  653. let pageCount = pdfDocument?.pageCount
  654. let currentPageIndex = pdfDocument?.index(for: PDFView.currentPage())
  655. if index > 0 && index <= pageCount ?? 0 {
  656. PDFView.go(to: pdfDocument?.page(at: UInt(index-1))!)
  657. reloadPDFData()
  658. } else {
  659. currentPageLabel.stringValue = "(currentPageIndex+1)"
  660. }
  661. }
  662. }
  663. func controlTextDidChange(_ notification: Notification) {
  664. guard let textField = notification.object as? NSTextField else { return }
  665. if textField == currentPageLabel {
  666. let string = textField.formatter?.string(for: NSNumber(value: Int(textField.stringValue) ?? 0))
  667. textField.stringValue = string ?? ""
  668. }
  669. }
  670. func popoverDidClose(_ notification: Notification) {
  671. }
  672. func gocrManagerDidStartOCR(_ manager: KMGOCRManager!) {
  673. }
  674. //MARK: KMGOCRManagerDelegate
  675. func gocrManagerDidFinishOCR(_ manager: KMGOCRManager!) {
  676. self.batchesOCR()
  677. }
  678. func gocrManager(_ manager: KMGOCRManager!, didCancelOCRImageAt index: Int) {
  679. }
  680. func gocrManager(_ manager: KMGOCRManager!, didStartOCRImageAt index: Int) {
  681. DispatchQueue.main.async {
  682. self.pageRangeBox.isEnabled = false
  683. self.languageButton.isEnabled = false
  684. self.planComboBox.isEnabled = false
  685. }
  686. }
  687. func gocrManager(_ manager: KMGOCRManager!, didFinishOCRImageAt index: Int, results: [KMGOCRResult]!) {
  688. self.dealWithResults(results, OCRImageAtIndex: index)
  689. }
  690. func gocrManager(_ manager: KMGOCRManager!, didFailureOCRImageAt index: Int, error: Error!) {
  691. let pagenum = self.pageIndexs?[index] as! NSNumber
  692. self.errorOCRArrays?.append(pagenum)
  693. self.dealWithResults([], OCRImageAtIndex: index)
  694. }
  695. func dealWithResults(_ rlts: [KMGOCRResult]?, OCRImageAtIndex index: Int) {
  696. if index >= self.pageIndexs?.count ?? 0 {
  697. return
  698. }
  699. let key = self.pageIndexs?[self.ocrCurrentIndex] as! NSNumber
  700. if ocrDictionary == nil {
  701. ocrDictionary = NSMutableDictionary()
  702. }
  703. ocrDictionary?.setObject(rlts as Any, forKey: key)
  704. let sortedKeys = self.ocrDictionary?.allKeys.sorted(by: { ($0 as! NSNumber).compare($1 as! NSNumber) == .orderedAscending })
  705. var textString = ""
  706. for key in sortedKeys! {
  707. let results: Array<KMGOCRResult> = self.ocrDictionary?.object(forKey: key) as! Array<KMGOCRResult>
  708. var rStr = ""
  709. if results.count > 0 {
  710. rStr = results[0].text
  711. }
  712. if textString.count > 0 {
  713. textString += "\n\n"
  714. }
  715. textString += String(format: NSLocalizedString("Page %ld", comment: ""), (key as! NSNumber).intValue + 1)
  716. textString += "\n"
  717. textString += rStr
  718. }
  719. self.txtTextView.string = textString
  720. self.resultString = textString
  721. self.ocrCurrentIndex += 1
  722. if self.ocrCurrentIndex >= (self.pageIndexs?.count ?? 0) - 1 {
  723. self.progressControl.stopAnimation(nil)
  724. self.progressControl.isHidden = true
  725. self.saveButton.isEnabled = true
  726. self.savePDFButton.isEnabled = true
  727. self.ocrCopyButton.isEnabled = true
  728. self.deleteButton.isEnabled = true
  729. self.startButton.isEnabled = true
  730. self.planComboBox.isEnabled = true
  731. self.pageRangeBox.isEnabled = true
  732. self.languageButton.isEnabled = true
  733. if self.errorOCRArrays?.count ?? 0 < 1 {
  734. } else if self.errorOCRArrays?.count == self.pageIndexs?.count {
  735. if KMGOCRManager.default().ocrType == .google {
  736. self.failedLabel.stringValue = NSLocalizedString("OCR failed.Please try to change the OCR Plan to \"Plan 2 (Offline)\"", comment: "")
  737. self.failedBox.isHidden = false
  738. } else {
  739. let alert = NSAlert()
  740. alert.alertStyle = NSAlert.Style.critical
  741. alert.messageText = ""
  742. if #available(macOS 10.15, *) {
  743. alert.informativeText = NSLocalizedString("Unable to perform OCR on this document. Please try again later.", comment: "")
  744. } else {
  745. alert.informativeText = NSLocalizedString("OCR failed, please try again. Note: OCR Plan 2(Offline) is supported in macOS 10.15+.", comment: "")
  746. self.failedLabel.stringValue = NSLocalizedString("OCR failed, please try again. Note: OCR Plan 2(Offline) is supported in macOS 10.15+.", comment: "")
  747. self.failedBox.isHidden = false
  748. }
  749. alert.addButton(withTitle: NSLocalizedString("OK", comment: ""))
  750. let response = alert.runModal()
  751. if response == NSApplication.ModalResponse.alertFirstButtonReturn {
  752. }
  753. }
  754. } else {
  755. var contextString = String(format: "%@", NSLocalizedString("Some problems occurred during the last operation:", comment: ""))
  756. contextString += "\n"
  757. if self.errorOCRArrays?.count ?? 0 > 1 {
  758. contextString += NSLocalizedString("Pages", comment: "")
  759. } else {
  760. contextString += NSLocalizedString("Page", comment: "")
  761. }
  762. if self.errorOCRArrays?.count ?? 0 > 0 {
  763. contextString += String(format: ": %@", self.fileNameWithNums(self.errorOCRArrays as! Array<NSNumber>))
  764. }
  765. let alert = NSAlert()
  766. alert.alertStyle = NSAlert.Style.critical
  767. alert.messageText = NSLocalizedString("Completed", comment: "")
  768. alert.informativeText = contextString
  769. alert.addButton(withTitle: NSLocalizedString("OK", comment: ""))
  770. let response = alert.runModal()
  771. if response == NSApplication.ModalResponse.alertFirstButtonReturn {
  772. }
  773. }
  774. }
  775. }
  776. }