KMOCRPDFWindowController.swift 37 KB

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