123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334 |
- //
- // KMOCROperation.m
- // PDF Reader Pro Edition
- //
- // Created by 万军 on 2020/3/23.
- //
- #import "KMOCROperation.h"
- #import "KMGOCRManager.h"
- #import <AppKit/AppKit.h>
- #define KMOCR_API_URL @"https://api.convertio.co/convert"
- #if VERSION_FREE
- #define KMOCR_API_KEY @"d1a0c1a4cc7fcac5eb08730570217f97"
- #else
- #define KMOCR_API_KEY @"d1a0c1a4cc7fcac5eb08730570217f97"
- #endif
- #define KMTIMER_MAXIMUM_CYCLE 10
- @interface KMOCROperation ()
- @property (assign, nonatomic, getter = isExecuting) BOOL executing;
- @property (assign, nonatomic, getter = isFinished) BOOL finished;
- @property (assign, nonatomic, getter = isCancelled) BOOL cancelled;
- @property (nonatomic, assign) NSInteger timerCycleCount;
- @property (nonatomic, assign) NSInteger imageIndex;
- @property (nonatomic, strong) NSURLSessionDataTask *task;
- @property (nonatomic, strong) NSString *fileID;
- @property (nonatomic, strong) NSString *fileName;
- @property (nonatomic, strong) NSImage *orcImage;
- @property (nonatomic, strong) NSURL *fileURL;
- @end
- @implementation KMOCROperation
- - (void)dealloc {
- }
- - (instancetype)initWithRecognitionImage:(NSImage *)image withImageIndex:(NSInteger)imageIndex {
- self = [super init];
- if (self) {
- self.timerCycleCount = 0;
- self.orcImage = image;
- self.imageIndex = imageIndex;
- self.fileName = [self fileNameWithDate];
- self.fileID = nil;
- self.fileURL = nil;
-
- self.queuePriority = NSOperationQueuePriorityNormal;
-
- self.name = self.fileName;
- self.executing = NO;
- self.finished = NO;
- }
- return self;
- }
- #pragma mark - overwrite
- - (void)start {
- if ([self p_checkCancelled]) {
- return;
- }
-
- self.executing = YES;
-
- [NSThread detachNewThreadSelector:@selector(main) toTarget:self withObject:nil];
- }
- - (void)main {
- @try {
- if ([self p_checkCancelled]) {
- return;
- }
- [self recognitionImage:_orcImage];
-
- while (self.executing) {
- if ([self p_checkCancelled]) {
- return;
- }
- }
- }
- @catch (NSException * e) {
- NSLog(@"Exception %@", e);
- }
- }
- - (void)cancel
- {
- [super cancel];
-
- if (self.task) {
- [self.task cancel];
- self.task = nil;
- }
-
- if ([self.operationDelegate respondsToSelector:@selector(OCROperation:cancelOCRImageAtIndex:)]) {
- [self.operationDelegate OCROperation:self cancelOCRImageAtIndex:_imageIndex];
- }
-
- if (self.executing) {
- self.executing = NO;
- self.finished = YES;
- } else {
- self.finished = NO;
- }
- self.cancelled = YES;
- }
- #pragma mark - private methods
- - (void)p_done
- {
- self.executing = NO;
- self.finished = YES;
- }
- - (BOOL)p_checkCancelled
- {
- if (self.cancelled) {
- self.finished = YES;
- return YES;
- }
- return NO;
- }
- - (void)recognitionImage:(NSImage *)image {
- __weak typeof(self)weakSelf = self;
- NSString *binaryImageData = [self base64EncodeImage:image];
- if (!binaryImageData) {
- return;
- }
- NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:KMOCR_API_URL]];
- [request setHTTPMethod: @"POST"];
- [request addValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
- NSDictionary *paramsDictionary = @{@"apikey":KMOCR_API_KEY,
- @"input":@"base64",
- @"file":binaryImageData,
- @"outputformat":self.fileType,
- @"filename":@"1.pdf",
- @"options":@{@"ocr_enabled":@(true),
- @"ocr_settings":@{@"langs":self.selectedLanguages}}};
- NSError *error;
- NSData *requestData = [NSJSONSerialization dataWithJSONObject:paramsDictionary options:0 error:&error];
- [request setHTTPBody:requestData];
- NSURLSession *URLSession = [NSURLSession sharedSession];
- NSURLSessionDataTask *convertTask = [URLSession dataTaskWithRequest:request completionHandler:^ (NSData *data, NSURLResponse *response, NSError *error) {
- if (!data) {
- return;
- }
- if (NSURLErrorCancelled == error.code) {
- return ;
- }
- NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
- NSString *statusStr = dictionary[@"status"];
- NSLog(@"dictionary == %@",dictionary);
-
- if ([statusStr isEqualToString:@"ok"]) {
- weakSelf.fileID = dictionary[@"data"][@"id"];
-
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(15.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
- [weakSelf requestQuery];
- });
- } else {
- [weakSelf cancel];
- // NSError *err = [self errorWithContent:dictionary[@"error"]];
- if ([weakSelf.operationDelegate respondsToSelector:@selector(OCROperation:failureOCRImageAtIndex:error:)]) {
- [weakSelf.operationDelegate OCROperation:weakSelf failureOCRImageAtIndex:weakSelf.imageIndex error:error];
- }
- }
- }];
- [convertTask resume];
- }
- #pragma mark - Private Methods
- - (NSString *)fileNameWithDate
- {
- NSDateFormatter *formatter = [[NSDateFormatter alloc ] init];
- [formatter setDateFormat:@"YYYY-MM-dd-hh-mm-ss-SSS"];
- NSString *dateString = [formatter stringFromDate:[NSDate date]];
- NSString *fileName = [NSString stringWithFormat:@"%@ %ld",dateString, (long)_imageIndex];
- return fileName;
- }
- - (NSData *)compressImageData:(NSData *)imageData toMaxFileSize:(NSInteger)maxFileSize {
- CGFloat compression = 0.9f;
- CGFloat maxCompression = 0.1f;
- NSData *compressImageData = imageData;
- while ([imageData length] > maxFileSize && compression > maxCompression) {
- compression -= 0.1;
- NSBitmapImageRep *imageRep = [NSBitmapImageRep imageRepWithData:imageData];
- compressImageData = [imageRep representationUsingType:NSJPEGFileType
- properties:@{NSImageCompressionFactor:@(compression)}];
- }
- return compressImageData;
- }
- - (NSString *)base64EncodeImage:(NSImage *)image {
- NSData *data = [image TIFFRepresentation];
- NSBitmapImageRep *imageRep = [NSBitmapImageRep imageRepWithData:data];
- [imageRep setSize:[image size]];
- NSData *imagedata = [imageRep representationUsingType:NSBitmapImageFileTypePNG properties:@{}];
-
- // Resize the image if it exceeds the 4MB API limit
- if ([imagedata length] > 4194304) {
- imagedata = [self compressImageData:imagedata toMaxFileSize:4194304];
- }
-
- NSString *base64String = nil;
- if ([imagedata respondsToSelector:@selector(base64EncodedStringWithOptions:)]) {
- base64String = [imagedata base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithCarriageReturn];
- } else {
- base64String = [imagedata base64Encoding];
- }
- return base64String;
- }
- - (NSError *)errorWithContent:(NSString *)content {
- NSError *error;
- NSString *domain = @"com.MyCompany.MyApplication.ErrorDomain";
- NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : content };
- error = [NSError errorWithDomain:domain code:-101 userInfo:userInfo];
-
- return error;
- }
- #pragma mark - NSTimer
- - (void)requestQuery {
- __weak typeof(self)weakSelf = self;
- NSString *urlString = [NSString stringWithFormat:@"%@/%@/status",KMOCR_API_URL,_fileID];
- NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlString]];
- [request setHTTPMethod: @"GET"];
-
- NSURLSession *URLSession = [NSURLSession sharedSession];
- self.task = [URLSession dataTaskWithRequest:request completionHandler:^ (NSData *data, NSURLResponse *response, NSError *error) {
- if (NSURLErrorCancelled == error.code) {
- return ;
- }
- NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
- NSString *statusStr = dictionary[@"status"];
- NSLog(@"dictionary == %@",dictionary);
- weakSelf.fileID = dictionary[@"data"][@"id"];
- if ([statusStr isEqualToString:@"ok"]) {
- if (weakSelf.task) {
- [weakSelf.task cancel];
- weakSelf.task = nil;
- }
- NSDictionary *fileData = dictionary[@"data"];
- if ([fileData[@"step_percent"] intValue] == 100) {
- weakSelf.fileURL = [NSURL URLWithString:fileData[@"output"][@"url"]];
- [weakSelf getResultFileContent];
- } else {
- if (weakSelf.timerCycleCount >= KMTIMER_MAXIMUM_CYCLE) {
- [weakSelf cancel];
- // NSError *err = [self errorWithContent:@"Network Timeout"];
- if ([weakSelf.operationDelegate respondsToSelector:@selector(OCROperation:failureOCRImageAtIndex:error:)]) {
- [weakSelf.operationDelegate OCROperation:weakSelf failureOCRImageAtIndex:weakSelf.imageIndex error:error];
- }
- } else {
- weakSelf.timerCycleCount++;
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
- [weakSelf requestQuery];
- });
- }
- }
- } else {
- [weakSelf cancel];
- // NSError *err = [self errorWithContent:dictionary[@"error"]];
- if ([weakSelf.operationDelegate respondsToSelector:@selector(OCROperation:failureOCRImageAtIndex:error:)]) {
- [weakSelf.operationDelegate OCROperation:weakSelf failureOCRImageAtIndex:weakSelf.imageIndex error:error];
- }
- }
- }];
- [self.task resume];
- }
- #pragma mark - Get Result File Content
- - (void)getResultFileContent {
- __weak typeof(self)weakSelf = self;
- NSString *urlString = [NSString stringWithFormat:@"%@/%@/dl",KMOCR_API_URL,_fileID];
- NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlString]];
- [request setHTTPMethod: @"GET"];
-
- NSURLSession *URLSession = [NSURLSession sharedSession];
- NSURLSessionDataTask *fileContentTask = [URLSession dataTaskWithRequest:request completionHandler:^ (NSData *data, NSURLResponse *response, NSError *error) {
- if (NSURLErrorCancelled == error.code) {
- return ;
- }
- NSData *fileData;
- NSError *err;
- NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
- NSLog(@"dictionary == %@",dictionary);
- NSString *statusStr = dictionary[@"status"];
- if ([statusStr isEqualToString:@"ok"]) {
- fileData = [self responseDataResults:dictionary];
- } else {
- // err = [self errorWithContent:dictionary[@"error"]];
- }
-
- dispatch_async(dispatch_get_main_queue(), ^{
- if (error || !fileData) {
- [self cancel];
- if ([weakSelf.operationDelegate respondsToSelector:@selector(OCROperation:failureOCRImageAtIndex:error:)]) {
- [weakSelf.operationDelegate OCROperation:self failureOCRImageAtIndex:weakSelf.imageIndex error:error];
- }
- } else {
- NSArray *arr = @[fileData];
- NSLog(@"self.imageIndex == %ld",(long)self.imageIndex);
- if ([weakSelf.operationDelegate respondsToSelector:@selector(OCROperation:finishOCRImageAtIndex:results:)]) {
- [weakSelf.operationDelegate OCROperation:weakSelf finishOCRImageAtIndex:weakSelf.imageIndex results:arr];
- }
- [self p_done];
- }
- });
- // if ([statusStr isEqualToString:@"ok"]) {
- // [self p_done];
- // } else {
- // [self cancel];
- // }
- }];
- [fileContentTask resume];
- }
- - (NSData *)responseDataResults:(NSDictionary *)dictionary {
- NSDictionary *responses = [dictionary isKindOfClass:[NSDictionary class]] ? [dictionary objectForKey:@"data"] : nil;
- NSString *stringBase64 = responses[@"content"];
- NSData *data = [[NSData alloc] initWithBase64EncodedString:stringBase64 options:0];
- [data writeToFile:self.filePath atomically:YES];
- return data;
- }
- @end
|