KMGOCRManager.m 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711
  1. //
  2. // KMGOCRManager.m
  3. //
  4. //
  5. //
  6. //
  7. //
  8. #import "KMGOCRManager.h"
  9. #import <PDF_Reader_Pro-Swift.h>
  10. #import <Vision/VNRecognizeTextRequest.h>
  11. #import <Vision/VNRequestHandler.h>
  12. #import <Vision/VNObservation.h>
  13. #define KMImageScale 4.0
  14. //#import "KMOCRPreferences.h" //恢复OCR架上版本样式
  15. /*
  16. https://cloud.google.com/vision/reference/rest/v1/images/annotate#AnnotateImageResponse
  17. languageHints[]
  18. List of languages to use for TEXT_DETECTION. In most cases, an empty value yields the best results since it enables automatic language detection. For languages based on the Latin alphabet, setting languageHints is not needed. In rare cases, when the language of the text in the image is known, setting a hint will help get better results (although it will be a significant hindrance if the hint is wrong). Text detection returns an error if one or more of the specified languages is not one of the supported languages.
  19. */
  20. NSString * KMGOCRLanguageCodeKey = @"KMGOCRLanguageCodeKey";
  21. NSString * KMGOCRLanguageStringKey = @"KMGOCRLanguageStringKey";
  22. #pragma mark - KMGOCRResult
  23. @class KMOCROperationQueue;
  24. @interface KMGOCRResult ()
  25. @end
  26. @implementation KMGOCRResult
  27. - (void)dealloc {
  28. }
  29. @end
  30. #pragma mark - KMGOCRManager
  31. @interface KMGOCRManager () <KMGOCROperationDelegate, KMOCROperationDelegate>
  32. @property (nonatomic, retain) NSMutableArray *languages;
  33. @property (nonatomic, assign) NSString *fileType;
  34. @property (nonatomic, retain) NSMutableArray *ocrPath;
  35. @property (nonatomic, retain) NSURL *filePath;
  36. @property (nonatomic, assign) NSUInteger finishIndex;
  37. //@property (nonatomic, retain) KMOCRPreferences *ocrPreferences; //恢复OCR架上版本样式
  38. @property (nonatomic,retain) VNRecognizeTextRequest *appleRequest;
  39. @property (nonatomic, assign) VNRequestTextRecognitionLevel appleRecognitionMode;
  40. @property (nonatomic,retain) NSMutableArray *images;
  41. @end
  42. @implementation KMGOCRManager
  43. #pragma mark - Init Methods
  44. - (id)init {
  45. if (self = [super init]) {
  46. }
  47. return self;
  48. }
  49. - (void)dealloc {
  50. _delegate = nil;
  51. if (@available(macOS 10.15, *)) {
  52. }
  53. }
  54. #pragma mark - Public Methods
  55. + (KMGOCRManager *)defaultManager {
  56. static KMGOCRManager *singleton = nil;
  57. static dispatch_once_t pred;
  58. dispatch_once(&pred, ^{
  59. singleton = [[KMGOCRManager alloc] init];
  60. });
  61. return singleton;
  62. }
  63. + (NSArray *)languages {
  64. if ([KMGOCRManager defaultManager].OCRType == KMOCRType_Google) {
  65. return @[@{KMGOCRLanguageCodeKey:@"af", KMGOCRLanguageStringKey:@"Afrikaans"},
  66. @{KMGOCRLanguageCodeKey:@"sq", KMGOCRLanguageStringKey:@"Albanian"},
  67. @{KMGOCRLanguageCodeKey:@"ar", KMGOCRLanguageStringKey:@"Arabic"},
  68. @{KMGOCRLanguageCodeKey:@"hy", KMGOCRLanguageStringKey:@"Armenian"},
  69. @{KMGOCRLanguageCodeKey:@"az", KMGOCRLanguageStringKey:@"Azerbaijani"},
  70. @{KMGOCRLanguageCodeKey:@"eu", KMGOCRLanguageStringKey:@"Basque"},
  71. @{KMGOCRLanguageCodeKey:@"be", KMGOCRLanguageStringKey:@"Belarusian"},
  72. @{KMGOCRLanguageCodeKey:@"bn", KMGOCRLanguageStringKey:@"Bengali"},
  73. @{KMGOCRLanguageCodeKey:@"bs", KMGOCRLanguageStringKey:@"Bosnian"},
  74. @{KMGOCRLanguageCodeKey:@"bg", KMGOCRLanguageStringKey:@"Bulgarian"},
  75. @{KMGOCRLanguageCodeKey:@"ca", KMGOCRLanguageStringKey:@"Catalan"},
  76. @{KMGOCRLanguageCodeKey:@"ceb", KMGOCRLanguageStringKey:@"Cebuano"},
  77. @{KMGOCRLanguageCodeKey:@"ny", KMGOCRLanguageStringKey:@"Chichewa"},
  78. @{KMGOCRLanguageCodeKey:@"zh-CN", KMGOCRLanguageStringKey:@"Chinese Simplified"},
  79. @{KMGOCRLanguageCodeKey:@"zh-TW", KMGOCRLanguageStringKey:@"Chinese Traditional"},
  80. @{KMGOCRLanguageCodeKey:@"hr", KMGOCRLanguageStringKey:@"Croatian"},
  81. @{KMGOCRLanguageCodeKey:@"cs", KMGOCRLanguageStringKey:@"Czech"},
  82. @{KMGOCRLanguageCodeKey:@"da", KMGOCRLanguageStringKey:@"Danish"},
  83. @{KMGOCRLanguageCodeKey:@"nl", KMGOCRLanguageStringKey:@"Dutch"},
  84. @{KMGOCRLanguageCodeKey:@"en", KMGOCRLanguageStringKey:@"English"},
  85. @{KMGOCRLanguageCodeKey:@"eo", KMGOCRLanguageStringKey:@"Esperanto"},
  86. @{KMGOCRLanguageCodeKey:@"et", KMGOCRLanguageStringKey:@"Estonian"},
  87. @{KMGOCRLanguageCodeKey:@"tl", KMGOCRLanguageStringKey:@"Filipino"},
  88. @{KMGOCRLanguageCodeKey:@"fi", KMGOCRLanguageStringKey:@"Finnish"},
  89. @{KMGOCRLanguageCodeKey:@"fr", KMGOCRLanguageStringKey:@"French"},
  90. @{KMGOCRLanguageCodeKey:@"gl", KMGOCRLanguageStringKey:@"Galician"},
  91. @{KMGOCRLanguageCodeKey:@"ka", KMGOCRLanguageStringKey:@"Georgian"},
  92. @{KMGOCRLanguageCodeKey:@"de", KMGOCRLanguageStringKey:@"German"},
  93. @{KMGOCRLanguageCodeKey:@"el", KMGOCRLanguageStringKey:@"Greek"},
  94. @{KMGOCRLanguageCodeKey:@"gu", KMGOCRLanguageStringKey:@"Gujarati"},
  95. @{KMGOCRLanguageCodeKey:@"ht", KMGOCRLanguageStringKey:@"Haitian Creole"},
  96. @{KMGOCRLanguageCodeKey:@"ha", KMGOCRLanguageStringKey:@"Hausa"},
  97. @{KMGOCRLanguageCodeKey:@"iw", KMGOCRLanguageStringKey:@"Hebrew"},
  98. @{KMGOCRLanguageCodeKey:@"hi", KMGOCRLanguageStringKey:@"Hindi"},
  99. @{KMGOCRLanguageCodeKey:@"hmn", KMGOCRLanguageStringKey:@"Hmong"},
  100. @{KMGOCRLanguageCodeKey:@"hu", KMGOCRLanguageStringKey:@"Hungarian"},
  101. @{KMGOCRLanguageCodeKey:@"is", KMGOCRLanguageStringKey:@"Icelandic"},
  102. @{KMGOCRLanguageCodeKey:@"ig", KMGOCRLanguageStringKey:@"Igbo"},
  103. @{KMGOCRLanguageCodeKey:@"id", KMGOCRLanguageStringKey:@"Indonesian"},
  104. @{KMGOCRLanguageCodeKey:@"ga", KMGOCRLanguageStringKey:@"Irish"},
  105. @{KMGOCRLanguageCodeKey:@"it", KMGOCRLanguageStringKey:@"Italian"},
  106. @{KMGOCRLanguageCodeKey:@"ja", KMGOCRLanguageStringKey:@"Japanese"},
  107. @{KMGOCRLanguageCodeKey:@"jw", KMGOCRLanguageStringKey:@"Javanese"},
  108. @{KMGOCRLanguageCodeKey:@"kn", KMGOCRLanguageStringKey:@"Kannada"},
  109. @{KMGOCRLanguageCodeKey:@"kk", KMGOCRLanguageStringKey:@"Kazakh"},
  110. @{KMGOCRLanguageCodeKey:@"km", KMGOCRLanguageStringKey:@"Khmer"},
  111. @{KMGOCRLanguageCodeKey:@"ko", KMGOCRLanguageStringKey:@"Korean"},
  112. @{KMGOCRLanguageCodeKey:@"lo", KMGOCRLanguageStringKey:@"Lao"},
  113. @{KMGOCRLanguageCodeKey:@"la", KMGOCRLanguageStringKey:@"Latin"},
  114. @{KMGOCRLanguageCodeKey:@"lv", KMGOCRLanguageStringKey:@"Latvian"},
  115. @{KMGOCRLanguageCodeKey:@"lt", KMGOCRLanguageStringKey:@"Lithuanian"},
  116. @{KMGOCRLanguageCodeKey:@"mk", KMGOCRLanguageStringKey:@"Macedonian"},
  117. @{KMGOCRLanguageCodeKey:@"mg", KMGOCRLanguageStringKey:@"Malagasy"},
  118. @{KMGOCRLanguageCodeKey:@"ms", KMGOCRLanguageStringKey:@"Malay"},
  119. @{KMGOCRLanguageCodeKey:@"ml", KMGOCRLanguageStringKey:@"Malayalam"},
  120. @{KMGOCRLanguageCodeKey:@"mt", KMGOCRLanguageStringKey:@"Maltese"},
  121. @{KMGOCRLanguageCodeKey:@"mi", KMGOCRLanguageStringKey:@"Maori"},
  122. @{KMGOCRLanguageCodeKey:@"mr", KMGOCRLanguageStringKey:@"Marathi"},
  123. @{KMGOCRLanguageCodeKey:@"mn", KMGOCRLanguageStringKey:@"Mongolian"},
  124. @{KMGOCRLanguageCodeKey:@"my", KMGOCRLanguageStringKey:@"Myanmar (Burmese)"},
  125. @{KMGOCRLanguageCodeKey:@"ne", KMGOCRLanguageStringKey:@"Nepali"},
  126. @{KMGOCRLanguageCodeKey:@"no", KMGOCRLanguageStringKey:@"Norwegian"},
  127. @{KMGOCRLanguageCodeKey:@"fa", KMGOCRLanguageStringKey:@"Persian"},
  128. @{KMGOCRLanguageCodeKey:@"pl", KMGOCRLanguageStringKey:@"Polish"},
  129. @{KMGOCRLanguageCodeKey:@"pt", KMGOCRLanguageStringKey:@"Portuguese"},
  130. @{KMGOCRLanguageCodeKey:@"ma", KMGOCRLanguageStringKey:@"Punjabi"},
  131. @{KMGOCRLanguageCodeKey:@"ro", KMGOCRLanguageStringKey:@"Romanian"},
  132. @{KMGOCRLanguageCodeKey:@"ru", KMGOCRLanguageStringKey:@"Russian"},
  133. @{KMGOCRLanguageCodeKey:@"sr", KMGOCRLanguageStringKey:@"Serbian"},
  134. @{KMGOCRLanguageCodeKey:@"st", KMGOCRLanguageStringKey:@"Sesotho"},
  135. @{KMGOCRLanguageCodeKey:@"si", KMGOCRLanguageStringKey:@"Sinhala"},
  136. @{KMGOCRLanguageCodeKey:@"sk", KMGOCRLanguageStringKey:@"Slovak"},
  137. @{KMGOCRLanguageCodeKey:@"sl", KMGOCRLanguageStringKey:@"Slovenian"},
  138. @{KMGOCRLanguageCodeKey:@"so", KMGOCRLanguageStringKey:@"Somali"},
  139. @{KMGOCRLanguageCodeKey:@"es", KMGOCRLanguageStringKey:@"Spanish"},
  140. @{KMGOCRLanguageCodeKey:@"su", KMGOCRLanguageStringKey:@"Sudanese"},
  141. @{KMGOCRLanguageCodeKey:@"sw", KMGOCRLanguageStringKey:@"Swahili"},
  142. @{KMGOCRLanguageCodeKey:@"sv", KMGOCRLanguageStringKey:@"Swedish"},
  143. @{KMGOCRLanguageCodeKey:@"tg", KMGOCRLanguageStringKey:@"Tajik"},
  144. @{KMGOCRLanguageCodeKey:@"ta", KMGOCRLanguageStringKey:@"Tamil"},
  145. @{KMGOCRLanguageCodeKey:@"te", KMGOCRLanguageStringKey:@"Telugu"},
  146. @{KMGOCRLanguageCodeKey:@"th", KMGOCRLanguageStringKey:@"Thai"},
  147. @{KMGOCRLanguageCodeKey:@"tr", KMGOCRLanguageStringKey:@"Turkish"},
  148. @{KMGOCRLanguageCodeKey:@"uk", KMGOCRLanguageStringKey:@"Ukrainian"},
  149. @{KMGOCRLanguageCodeKey:@"ur", KMGOCRLanguageStringKey:@"Urdu"},
  150. @{KMGOCRLanguageCodeKey:@"uz", KMGOCRLanguageStringKey:@"Uzbek"},
  151. @{KMGOCRLanguageCodeKey:@"vi", KMGOCRLanguageStringKey:@"Vietnamese"},
  152. @{KMGOCRLanguageCodeKey:@"cy", KMGOCRLanguageStringKey:@"Welsh"},
  153. @{KMGOCRLanguageCodeKey:@"yi", KMGOCRLanguageStringKey:@"Yiddish"},
  154. @{KMGOCRLanguageCodeKey:@"yo", KMGOCRLanguageStringKey:@"Yoruba"},
  155. @{KMGOCRLanguageCodeKey:@"zu", KMGOCRLanguageStringKey:@"Zulu"}];
  156. }
  157. return @[@{KMGOCRLanguageCodeKey:@"en-US", KMGOCRLanguageStringKey:@"English"},
  158. @{KMGOCRLanguageCodeKey:@"fr-FR", KMGOCRLanguageStringKey:@"French"},
  159. @{KMGOCRLanguageCodeKey:@"it-IT", KMGOCRLanguageStringKey:@"Italian"},
  160. @{KMGOCRLanguageCodeKey:@"de-DE", KMGOCRLanguageStringKey:@"German"},
  161. @{KMGOCRLanguageCodeKey:@"es-ES", KMGOCRLanguageStringKey:@"Spanish"},
  162. @{KMGOCRLanguageCodeKey:@"pt-BR", KMGOCRLanguageStringKey:@"Portuguese"},
  163. @{KMGOCRLanguageCodeKey:@"zh-Hant", KMGOCRLanguageStringKey:@"Chinese Traditional"},
  164. @{KMGOCRLanguageCodeKey:@"zh-Hans", KMGOCRLanguageStringKey:@"Chinese Simplified"}];
  165. }
  166. static inline NSFont * FontWithSize(NSString *strChar, CGSize size) {
  167. CGFloat fontsize = 1.0;
  168. NSFont *font = [NSFont systemFontOfSize:fontsize];
  169. CGSize strSize = [strChar sizeWithAttributes:@{NSFontAttributeName:font}];
  170. while ((fontsize<127) && (strSize.width<size.width || strSize.height<size.height)) {
  171. fontsize += 1.0;
  172. font = [NSFont systemFontOfSize:fontsize];
  173. strSize = [strChar sizeWithAttributes:@{NSFontAttributeName:font}];
  174. }
  175. return [NSFont systemFontOfSize:fontsize-1];
  176. }
  177. - (void)createPDFFile:(NSString *)filePath imagePaths:(NSArray *)paths results:(NSArray *)resultsArray scale:(CGFloat)scale {
  178. if (paths.count < 1) {
  179. return;
  180. }
  181. CFStringRef path = (__bridge CFStringRef)filePath;
  182. CFURLRef url = CFURLCreateWithFileSystemPath(NULL, path, kCFURLPOSIXPathStyle, 0);
  183. CFMutableDictionaryRef myDictionary = CFDictionaryCreateMutable(NULL,
  184. 0,
  185. &kCFTypeDictionaryKeyCallBacks,
  186. &kCFTypeDictionaryValueCallBacks);
  187. CFDictionarySetValue(myDictionary, kCGPDFContextCreator, CFSTR("Kdan Mobile PDF Reader"));
  188. CGContextRef pdfContext = CGPDFContextCreateWithURL(url, &CGRectZero, myDictionary);
  189. CGContextSetRGBFillColor(pdfContext, 1.0, 0.0, 0.0, 0.0);
  190. CGContextSetTextDrawingMode(pdfContext, kCGTextFill);
  191. CFRelease(myDictionary);
  192. CFRelease(url);
  193. for (int i=0; i<paths.count; i++) {
  194. NSString *path = [paths objectAtIndex:i];
  195. NSImage *image = [[NSImage alloc] initWithContentsOfFile:path];
  196. CIImage *imageCIImage = [CIImage imageWithContentsOfURL:[NSURL fileURLWithPath:path]];
  197. NSSize size = [imageCIImage extent].size;
  198. CGRect pageRect = CGRectMake(0, 0, size.width/scale, size.height/scale);
  199. CFMutableDictionaryRef pageDictionary = CFDictionaryCreateMutable(NULL,
  200. 0,
  201. &kCFTypeDictionaryKeyCallBacks,
  202. &kCFTypeDictionaryValueCallBacks);
  203. CFDataRef boxData = CFDataCreate(NULL,(const UInt8 *)&pageRect, sizeof (CGRect));
  204. CFDictionarySetValue(pageDictionary, kCGPDFContextMediaBox, boxData);
  205. CGPDFContextBeginPage (pdfContext, pageDictionary);
  206. NSData *imageData = [NSData dataWithContentsOfFile:path];
  207. CGImageSourceRef imageSource = CGImageSourceCreateWithData((CFDataRef)imageData, NULL);
  208. CGImageRef imageRef = CGImageSourceCreateImageAtIndex(imageSource, 0, NULL);
  209. CGContextSaveGState(pdfContext);
  210. CGContextDrawImage(pdfContext, pageRect, imageRef);
  211. CGContextRestoreGState(pdfContext);
  212. CGImageRelease(imageRef);
  213. [NSGraphicsContext saveGraphicsState];
  214. [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:pdfContext flipped:NO]];
  215. NSArray *results = nil;
  216. if (i < resultsArray.count) {
  217. results = resultsArray[i];
  218. CGFloat newScale = scale;
  219. if([KMGOCRManager defaultManager].OCRType == KMOCRType_Apple)
  220. newScale = 1;
  221. if (results.count == 1) {
  222. KMGOCRResult *result = results[0];
  223. NSRect bounds = NSMakeRect((result.textBounds.origin.x)/newScale,
  224. pageRect.size.height-(result.textBounds.origin.y+result.textBounds.size.height)/newScale,
  225. (result.textBounds.size.width)/newScale,
  226. (result.textBounds.size.height)/newScale);
  227. NSDictionary *dic = @{NSFontAttributeName:FontWithSize(result.text, CGSizeMake(result.textBounds.size.width/newScale, result.textBounds.size.height/newScale)),
  228. NSForegroundColorAttributeName:[NSColor clearColor]};
  229. [result.text drawInRect:bounds withAttributes:dic];
  230. } else {
  231. for (int i=1; i<results.count; i++) {
  232. KMGOCRResult *result = results[i];
  233. NSRect bounds = NSMakeRect((result.textBounds.origin.x)/newScale,
  234. pageRect.size.height-(result.textBounds.origin.y+result.textBounds.size.height)/newScale,
  235. (result.textBounds.size.width)/newScale,
  236. (result.textBounds.size.height)/newScale);
  237. NSDictionary *dic = @{NSFontAttributeName:FontWithSize(result.text, CGSizeMake(result.textBounds.size.width/newScale, result.textBounds.size.height/newScale)),
  238. NSForegroundColorAttributeName:[NSColor clearColor]};
  239. [result.text drawInRect:bounds withAttributes:dic];
  240. }
  241. }
  242. }
  243. [NSGraphicsContext restoreGraphicsState];
  244. CGPDFContextEndPage (pdfContext);
  245. CFRelease(pageDictionary);
  246. CFRelease(boxData);
  247. }
  248. CGPDFContextClose(pdfContext);
  249. CGContextRelease (pdfContext);
  250. }
  251. - (void)recognitionImages:(NSArray *)images withLanguages:(NSArray *)languages {
  252. [self recognitionImages:images withLanguages:languages fileType:nil filePath:nil];
  253. }
  254. - (void)recognitionImages:(NSArray *)images withLanguages:(NSArray *)languages fileType:(NSString * _Nullable)fileType filePath:(NSURL *)filePath {
  255. self.ocrPath = [[NSMutableArray alloc] init];
  256. self.finishIndex = 0;
  257. self.fileType = @"PDF";
  258. self.images = [NSMutableArray arrayWithArray:images];
  259. if (!filePath) {
  260. self.filePath = [NSURL URLWithString:[NSSearchPathForDirectoriesInDomains(NSDesktopDirectory, NSUserDomainMask, YES) objectAtIndex:0]];
  261. } else {
  262. self.filePath = filePath;
  263. }
  264. //恢复OCR架上版本样式
  265. // self.ocrPreferences = [KMOCRPreferences new];
  266. // _isOCR = self.ocrPreferences.planPopUpBtnCount;
  267. // if (_isOCR) {
  268. // self.fileType = fileType;
  269. // [self ocrRecognitionImages:images withLanguages:languages];
  270. // } else {
  271. // [self gocrRecognitionImages:images withLanguages:languages];
  272. // }
  273. if (self.OCRType == KMOCRType_Google) {
  274. [self gocrRecognitionImages:images withLanguages:languages];
  275. }else{
  276. if (@available(macOS 10.15, *)) {
  277. if (_appleRequest) {
  278. [_appleRequest cancel];
  279. _appleRequest = nil;
  280. }
  281. [self recognitionAppleImageAtIndex:self.finishIndex];
  282. } else {
  283. if ([self.delegate respondsToSelector:@selector(GOCRManager:didFailureOCRImageAtIndex:error:)]) {
  284. [self.delegate GOCRManager:self didFailureOCRImageAtIndex:self.finishIndex error:nil];
  285. }
  286. }
  287. }
  288. }
  289. - (void)cancelRecognition {
  290. if (@available(macOS 10.15, *)) {
  291. if (_appleRequest) {
  292. [_appleRequest cancel];
  293. _appleRequest = nil;
  294. }
  295. }
  296. [self.images removeAllObjects];
  297. self.languages = nil;
  298. self.finishIndex = 0;
  299. KMOCROperationQueue *queue = [KMOCROperationQueue sharedInstance];
  300. if (queue.operations.count > 0) {
  301. for (KMGOCROperation *op in queue.operations) {
  302. op.operationDelegate = nil;
  303. }
  304. [queue cancelAll];
  305. }
  306. if ([self.delegate respondsToSelector:@selector(GOCRManager:didCancelOCRImageAtIndex:)]) {
  307. [self.delegate GOCRManager:self didCancelOCRImageAtIndex:self.finishIndex];
  308. }
  309. self.delegate = nil;
  310. }
  311. #pragma mark - Apple OCR
  312. - (void)recognitionAppleImageAtIndex:(NSUInteger)index {
  313. if (index >= self.images.count) {
  314. if (@available(macOS 10.15, *)) {
  315. [_appleRequest cancel];
  316. }
  317. _appleRequest = nil;
  318. if ([self.delegate respondsToSelector:@selector(GOCRManagerDidFinishOCR:)]) {
  319. [self.delegate GOCRManagerDidFinishOCR:self];
  320. }
  321. return;
  322. }
  323. self.finishIndex = index;
  324. if ([self.delegate respondsToSelector:@selector(GOCRManager:didStartOCRImageAtIndex:)]) {
  325. [self.delegate GOCRManager:self didStartOCRImageAtIndex:self.finishIndex];
  326. }
  327. if([self.images[self.finishIndex] isKindOfClass:[NSImage class]]) {
  328. [self recognitionAppleImage:self.images[self.finishIndex]];
  329. } else {
  330. NSData *data = self.images[self.finishIndex];
  331. NSImage *s = [[NSImage alloc] initWithData:data];
  332. if(_appleRequest) {
  333. [_appleRequest cancel];
  334. _appleRequest = nil;
  335. }
  336. [self recognitionAppleImage:s];
  337. }
  338. }
  339. - (void)recognitionAppleImage:(NSImage *)image {
  340. dispatch_async(dispatch_get_global_queue(0, 0), ^{
  341. __block typeof(self) blockSelf = self;
  342. _appleRequest = [[VNRecognizeTextRequest alloc] initWithCompletionHandler:^(VNRequest * _Nonnull request, NSError * _Nullable error) {
  343. NSArray *results = nil;
  344. if (request.results.count > 0) {
  345. results = [blockSelf responseDataRequest:request dictionary:nil imageSize:image.size];
  346. }
  347. NSMutableArray *resultArray = [[NSMutableArray alloc] init];
  348. for (KMGOCRResult *result in results) {
  349. NSDictionary *dic = @{@"x":@(result.textBounds.origin.x),
  350. @"y":@(result.textBounds.origin.y),
  351. @"width":@(result.textBounds.size.width),
  352. @"height":@(result.textBounds.size.height),
  353. @"text":result.text
  354. };
  355. [resultArray addObject:dic];
  356. }
  357. dispatch_async(dispatch_get_main_queue(), ^{
  358. if (error || !results) {
  359. if (self.delegate && [blockSelf.delegate respondsToSelector:@selector(GOCRManager:didFailureOCRImageAtIndex:error:)]) {
  360. [blockSelf.delegate GOCRManager:blockSelf didFailureOCRImageAtIndex:blockSelf.finishIndex error:error];
  361. }
  362. } else {
  363. if (self.delegate && [blockSelf.delegate respondsToSelector:@selector(GOCRManager:didFinishOCRImageAtIndex:results:)]) {
  364. [blockSelf.delegate GOCRManager:blockSelf didFinishOCRImageAtIndex:blockSelf.finishIndex results:results];
  365. }
  366. }
  367. [blockSelf recognitionAppleImageAtIndex:blockSelf.finishIndex+1];
  368. });
  369. }];
  370. self->_appleRequest.usesCPUOnly = YES;
  371. self->_appleRequest.recognitionLevel = self.appleRecognitionMode;
  372. if (self.languages.count > 0) {
  373. NSMutableArray *array = [[NSMutableArray alloc] initWithArray:blockSelf.languages];
  374. if ([self.languages containsObject:@"zh-Hant"]) {
  375. [array removeObject:@"zh-Hant"];
  376. [array insertObject:@"zh-Hant" atIndex:0];
  377. }
  378. if ([self.languages containsObject:@"zh-Hans"]) {
  379. [array removeObject:@"zh-Hans"];
  380. [array insertObject:@"zh-Hans" atIndex:0];
  381. }
  382. self.languages = array;
  383. self->_appleRequest.recognitionLanguages = self.languages;
  384. }else{
  385. self->_appleRequest.recognitionLanguages = @[@"zh-Hans",@"zh-Hant"];
  386. }
  387. NSError *error = nil;
  388. VNImageRequestHandler *handle = [[VNImageRequestHandler alloc] initWithCGImage:[self nsImageToCGImageRef:image] options:@{}];
  389. if (self->_appleRequest){
  390. [handle performRequests:@[self->_appleRequest] error:&error];
  391. }
  392. });
  393. }
  394. #pragma mark - GOCRManager Methods
  395. - (void)gocrRecognitionImages:(NSArray *)images withLanguages:(NSArray *)languages {
  396. if (!images || images.count == 0) {
  397. return;
  398. }
  399. if ([self.delegate respondsToSelector:@selector(GOCRManagerDidStartOCR:)]) {
  400. [self.delegate GOCRManagerDidStartOCR:self];
  401. }
  402. for (NSUInteger i=0; i<images.count; i++) {
  403. if ([self.delegate respondsToSelector:@selector(GOCRManager:didStartOCRImageAtIndex:)]) {
  404. [self.delegate GOCRManager:self didStartOCRImageAtIndex:i];
  405. }
  406. KMOCROperationQueue *queue = [KMOCROperationQueue sharedInstance];
  407. queue.maxConcurrentOperationCount = 1;
  408. NSImage *image = [[NSImage alloc] init];
  409. if([images[i] isKindOfClass:[NSImage class]]) {
  410. image = images[i];
  411. } else {
  412. NSData *data = images[i];
  413. image = [[NSImage alloc] initWithData:data];
  414. }
  415. KMGOCROperation *op = [[KMGOCROperation alloc] initWithRecognitionImg:image imgIndex:i];
  416. op.selectedLanguages = [NSMutableArray arrayWithArray:languages];
  417. op.operationDelegate = self;
  418. [queue addOperation:op];
  419. }
  420. }
  421. #pragma mark - OCRManager Methods
  422. - (void)ocrRecognitionImages:(NSArray *)images withLanguages:(NSArray *)languages {
  423. self.languages = [[NSMutableArray alloc] init];
  424. if (!images || images.count == 0) {
  425. return;
  426. }
  427. if (languages.count == 0) {
  428. for (NSDictionary *dict in [KMGOCRManager languages]) {
  429. [self.languages addObject:dict[KMGOCRLanguageCodeKey]];
  430. }
  431. } else {
  432. [self.languages addObjectsFromArray:languages];
  433. }
  434. for (NSUInteger i=0; i<images.count; i++) {
  435. if ([self.delegate respondsToSelector:@selector(GOCRManager:didStartOCRImageAtIndex:)]) {
  436. [self.delegate GOCRManager:self didStartOCRImageAtIndex:i];
  437. }
  438. NSString *desktopPath = [NSSearchPathForDirectoriesInDomains(NSDesktopDirectory, NSUserDomainMask, YES) objectAtIndex:0];
  439. NSDateFormatter *formatter = [[NSDateFormatter alloc ] init];
  440. [formatter setDateFormat:@"YYYY-MM-dd-hh-mm-ss-SSS"];
  441. NSString *dateString = [formatter stringFromDate:[NSDate date]];
  442. NSString *fileName;
  443. if ([self.fileType isEqualToString:@"PDF"]) {
  444. fileName = [NSString stringWithFormat:@"%@-%ld.pdf",dateString,i];
  445. } else {
  446. fileName = [NSString stringWithFormat:@"%@-%ld.txt",dateString,i];
  447. }
  448. NSString *strPath = [desktopPath stringByAppendingPathComponent:fileName];
  449. [_ocrPath addObject:strPath];
  450. KMOCROperationQueue *queue = [KMOCROperationQueue sharedInstance];
  451. queue.maxConcurrentOperationCount = images.count;
  452. NSImage *image = [[NSImage alloc] init];
  453. if([images[i] isKindOfClass:[NSImage class]]) {
  454. image = images[i];
  455. } else {
  456. NSData *data = images[i];
  457. image = [[NSImage alloc] initWithData:data];
  458. }
  459. KMOCROperation *op = [[KMOCROperation alloc] initWithRecognitionImg:image imgIndex:i];
  460. op.fileType = self.fileType;
  461. op.selectedLanguages = self.languages;
  462. op.filePath = fileName;
  463. op.operationDelegate = self;
  464. [queue addOperation:op];
  465. }
  466. }
  467. #pragma mark - Private Methods
  468. - (void)setIsOCR:(BOOL)isOCR {
  469. _isOCR = isOCR;
  470. }
  471. - (NSString *)joinPDF:(NSArray *)listOfPaths {
  472. // File paths
  473. NSDateFormatter *formatter = [[NSDateFormatter alloc ] init];
  474. [formatter setDateFormat:@"YYYY-MM-dd-hh-mm-ss-SSS"];
  475. NSString *dateString = [formatter stringFromDate:[NSDate date]];
  476. NSString *fileName;
  477. if ([self.fileType isEqualToString:@"PDF"]) {
  478. fileName = [NSString stringWithFormat:@"%@.pdf",dateString];
  479. } else {
  480. fileName = [NSString stringWithFormat:@"%@.txt",dateString];
  481. }
  482. // NSString *pdfPathOutput = [[NSSearchPathForDirectoriesInDomains(NSDesktopDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:dateString];
  483. NSString *pdfPathOutput = [[_filePath path] stringByAppendingPathComponent:fileName];
  484. CFURLRef pdfURLOutput = (CFURLRef)CFBridgingRetain([NSURL fileURLWithPath:pdfPathOutput]);
  485. NSInteger numberOfPages = 0;
  486. CGContextRef writeContext = CGPDFContextCreateWithURL(pdfURLOutput, NULL, NULL);
  487. for (NSString *source in listOfPaths) {
  488. CFURLRef pdfURL = (CFURLRef)CFBridgingRetain([[NSURL alloc] initFileURLWithPath:source]);
  489. CGPDFDocumentRef pdfRef = CGPDFDocumentCreateWithURL((CFURLRef)pdfURL);
  490. numberOfPages = CGPDFDocumentGetNumberOfPages(pdfRef);
  491. CGPDFPageRef page;
  492. CGRect mediaBox;
  493. for (int i=1; i<=numberOfPages; i++) {
  494. page = CGPDFDocumentGetPage(pdfRef, i);
  495. mediaBox = CGPDFPageGetBoxRect(page, kCGPDFMediaBox);
  496. CGContextBeginPage(writeContext, &mediaBox);
  497. CGContextDrawPDFPage(writeContext, page);
  498. CGContextEndPage(writeContext);
  499. }
  500. CGPDFDocumentRelease(pdfRef);
  501. CFRelease(pdfURL);
  502. }
  503. CFRelease(pdfURLOutput);
  504. CGPDFContextClose(writeContext);
  505. CGContextRelease(writeContext);
  506. if ([self.delegate respondsToSelector:@selector(GOCRManagerDidFinishOCR:)]) {
  507. [self.delegate GOCRManagerDidFinishOCR:self];
  508. }
  509. NSLog(@"pdfPathOutput == %@",pdfPathOutput);
  510. return pdfPathOutput;
  511. }
  512. #pragma mark - KMOCROperationDelegate
  513. - (void)OCROperation:(KMOCROperation *)operation startOCRImageAtIndex:(NSInteger)index {
  514. if ([self.delegate respondsToSelector:@selector(GOCRManager:didStartOCRImageAtIndex:)]) {
  515. [self.delegate GOCRManager:self didStartOCRImageAtIndex:index];
  516. }
  517. }
  518. - (void)OCROperation:(KMOCROperation *)operation cancelOCRImageAtIndex:(NSInteger)index {
  519. if ([self.delegate respondsToSelector:@selector(GOCRManager:didCancelOCRImageAtIndex:)]) {
  520. [self.delegate GOCRManager:self didCancelOCRImageAtIndex:index];
  521. }
  522. }
  523. - (void)OCROperation:(KMOCROperation *)operation finishOCRImageAtIndex:(NSInteger)index results:(NSArray *)result {
  524. self.finishIndex++;
  525. if ([self.delegate respondsToSelector:@selector(CHOCRManager:didFinishOCRImageAtIndex:results:)]) {
  526. [self.delegate CHOCRManager:self didFinishOCRImageAtIndex:_finishIndex results:result];
  527. }
  528. }
  529. - (void)OCROperation:(KMOCROperation *)operation failureOCRImageAtIndex:(NSInteger)index error:(NSError *)error {
  530. if ([self.delegate respondsToSelector:@selector(GOCRManager:didFailureOCRImageAtIndex:error:)]) {
  531. [self.delegate GOCRManager:self didFailureOCRImageAtIndex:index error:error];
  532. }
  533. }
  534. #pragma mark - KMGOCROperationDelegate
  535. - (void)GOCROperation:(KMGOCROperation *)operation startOCRImageAtIndex:(NSInteger)index{
  536. if ([self.delegate respondsToSelector:@selector(GOCRManager:didStartOCRImageAtIndex:)]) {
  537. [self.delegate GOCRManager:self didStartOCRImageAtIndex:index];
  538. }
  539. }
  540. - (void)GOCROperation:(KMGOCROperation *)operation cancelOCRImageAtIndex:(NSInteger)index{
  541. if ([self.delegate respondsToSelector:@selector(GOCRManager:didCancelOCRImageAtIndex:)]) {
  542. [self.delegate GOCRManager:self didCancelOCRImageAtIndex:index];
  543. }
  544. }
  545. - (void)GOCROperation:(KMGOCROperation *)operation finishOCRImageAtIndex:(NSInteger)index results:(NSArray *)results {
  546. if ([self.delegate respondsToSelector:@selector(GOCRManager:didFinishOCRImageAtIndex:results:)]) {
  547. [self.delegate GOCRManager:self didFinishOCRImageAtIndex:index results:results];
  548. }
  549. self.finishIndex++;
  550. if(self.finishIndex == self.images.count) {
  551. if ([self.delegate respondsToSelector:@selector(GOCRManagerDidFinishOCR:)]) {
  552. [self.delegate GOCRManagerDidFinishOCR:self];
  553. }
  554. }
  555. }
  556. - (void)GOCROperation:(KMGOCROperation *)operation failureOCRImageAtIndex:(NSInteger)index error:(NSError *)error {
  557. if ([self.delegate respondsToSelector:@selector(GOCRManager:didFailureOCRImageAtIndex:error:)]) {
  558. [self.delegate GOCRManager:self didFailureOCRImageAtIndex:index error:error];
  559. }
  560. self.finishIndex++;
  561. }
  562. // Apple OCR 结果解析
  563. - (NSArray *)responseDataRequest:(VNRequest *)request dictionary:(NSDictionary *)dictionary imageSize:(CGSize)imageSize{
  564. NSMutableArray *results = [NSMutableArray array];
  565. NSInteger maximumCandidates = 1;
  566. NSString *OCRStr = @"";
  567. if (request) {//Apple SDK识别出来的数据
  568. for (VNRecognizedTextObservation *observation in request.results) {
  569. VNRecognizedText *text = [observation topCandidates:maximumCandidates].firstObject;
  570. OCRStr = [OCRStr stringByAppendingString:[NSString stringWithFormat:@"%@\n",text.string]];
  571. NSError *error = nil;
  572. VNRectangleObservation *rectangleObservation = [text boundingBoxForRange:NSMakeRange(0, text.string.length) error:&error];
  573. CGFloat x = 0, y = 0, width = 0, height = 0;
  574. x = rectangleObservation.topLeft.x * imageSize.width;
  575. y = (1 - rectangleObservation.topLeft.y) * imageSize.height;
  576. width = rectangleObservation.boundingBox.size.width * imageSize.width;
  577. height = rectangleObservation.boundingBox.size.height * imageSize.height;
  578. KMGOCRResult *result = [[KMGOCRResult alloc] init];
  579. result.text = text.string;
  580. result.locale = @"";
  581. result.textBounds = CGRectMake(x, y, width, height);
  582. [results addObject:result];
  583. }
  584. //按照Google逻辑 array第一个元素为整个Image的文本
  585. if (results.count > 0) {
  586. KMGOCRResult *result = [[KMGOCRResult alloc] init];
  587. result.text = OCRStr;
  588. if (self.languages.count > 0) {
  589. result.locale = self.languages[0];
  590. }
  591. result.textBounds = CGRectZero;
  592. [results insertObject:result atIndex:0];
  593. }
  594. }
  595. return results;
  596. }
  597. - (CGImageRef)nsImageToCGImageRef:(NSImage*)image;
  598. {
  599. NSData * imageData = [image TIFFRepresentation];
  600. CGImageRef imageRef;
  601. if(imageData)
  602. {
  603. CGImageSourceRef imageSource =
  604. CGImageSourceCreateWithData(
  605. (CFDataRef)imageData, NULL);
  606. imageRef = CGImageSourceCreateImageAtIndex(
  607. imageSource, 0, NULL);
  608. }
  609. return imageRef;
  610. }
  611. @end
  612. @interface KMOCROperationQueue ()
  613. @end
  614. @implementation KMOCROperationQueue
  615. - (void)dealloc {
  616. }
  617. + (instancetype)sharedInstance {
  618. static KMOCROperationQueue *sharedInstance;
  619. static dispatch_once_t onceToken;
  620. dispatch_once(&onceToken, ^{
  621. sharedInstance = [KMOCROperationQueue new];
  622. });
  623. return sharedInstance;
  624. }
  625. - (void)addOCROperation:(NSOperation *)op {
  626. [self addOperation:op];
  627. }
  628. - (void)cancelAll {
  629. [self cancelAllOperations];
  630. }
  631. @end