KMOCRPDFWindowController.swift 39 KB

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