// // KMPDFSignatureImageView.m // PDF Reader // // Created by 丁林圭 on 2018/6/8. // Copyright © 2018年 Kdan Mobile. All rights reserved. // #import "KMPDFSignatureImageView.h" #import #import "KMImageAccessoryController.h" #import "NSButton+TitleColor.h" #define KMSignatureMaxWidth 400 #define KMSignatureMaxHeight 300 @interface KMPDFSignatureImageView() @property (nonatomic, retain) NSImage * picImage; @property (nonatomic) IBOutlet NSView *pictureView; @property (nonatomic) IBOutlet NSTextField *emptyTipLbl; @property (nonatomic) IBOutlet NSButton *dragButton; @property (nonatomic) IBOutlet NSImageView *emptyImg; @property (nonatomic, copy) NSTrackingArea *trackingArea; @property (nonatomic, strong) NSURL *imageURL; @end @implementation KMPDFSignatureImageView - (void)dealloc { [self removeTrackingArea:self.trackingArea]; } - (id)initWithFrame:(NSRect)frameRect { if (self = [super initWithFrame:frameRect]) { self.wantsLayer = YES; self.layer.borderWidth = 1.0; self.layer.borderColor = [NSColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.05].CGColor; self.layer.backgroundColor = [NSColor colorWithRed:1 green:1 blue:1 alpha:0.9].CGColor;; [self registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, nil]]; } return self; } - (void)awakeFromNib { [super awakeFromNib]; self.emptyTipLbl.stringValue = NSLocalizedString(@"Select image file", nil); self.emptyTipLbl.textColor = [NSColor labelColor]; self.dragButton.wantsLayer = YES; [self.dragButton setTitleColor:[NSColor labelColor]]; self.pictureView.wantsLayer = YES; self.emptyImg.image = [NSImage imageNamed:@"signPicture_nor"]; self.trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds] options:NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingActiveAlways owner:self userInfo:nil]; [self addTrackingArea:self.trackingArea]; } - (NSArray *)supportedImageTypes { return [NSArray arrayWithObjects:@"jpg",@"cur",@"bmp",@"jpeg",@"gif",@"png",@"tiff",@"tif",@"ico",@"icns",@"tga",@"psd",@"eps",@"hdr",@"jp2",@"jpc",@"pict",@"sgi",@"pdf", nil]; } - (void)drawRect:(NSRect)dirtyRect { [super drawRect:dirtyRect]; if (self.pictureView) { CGRect rect = NSZeroRect; CIImage *imageCIImage = [CIImage imageWithData:self.picImage.TIFFRepresentation]; NSSize size = [imageCIImage extent].size; CGFloat scale = MIN((dirtyRect.size.width - 30)/size.width, (dirtyRect.size.height - 30)/size.height); if (scale > 1) { rect = CGRectMake(0, 0, size.width, size.height); } else { rect = CGRectMake(0, 0, size.width * scale, size.height *scale); } [self.picImage drawInRect:CGRectMake((dirtyRect.size.width - rect.size.width)/2, (dirtyRect.size.height - rect.size.height)/2, rect.size.width,rect.size.height) fromRect:NSZeroRect operation:NSCompositingOperationSourceOver fraction:1.0]; } } - (void)setClearBackground:(BOOL)clearBackground { _clearBackground = clearBackground; if (self.imageURL && self.imageURL.path.length > 0) { [self loadImageViewPath:self.imageURL isRemoveBGColor:self.clearBackground]; } } #pragma mark Public - (void)clearImage { self.picImage = nil; self.imageURL = nil; self.pictureView.hidden = self.emptyTipLbl.hidden = NO; [self setNeedsDisplay:YES]; if (self.changeSignatureImageCallback) { self.changeSignatureImageCallback(YES); } } - (void)reSelectImage { [self buttonItemClick_SelectPhoto:nil]; } - (NSImage *)signatureImage { CGRect rect = CGRectZero; if (!self.picImage) { return nil; } else { CIImage *imageCIImage = [CIImage imageWithData:self.picImage.TIFFRepresentation]; NSSize size = [imageCIImage extent].size; CGFloat scale = MIN(KMSignatureMaxWidth/size.width, KMSignatureMaxHeight/size.height); if (scale > 1) { rect = CGRectMake(0, 0, size.width, size.height); } else { rect = CGRectMake(0, 0, size.width * scale, size.height *scale); } } NSImage *image = [[NSImage alloc] initWithSize:rect.size]; [image lockFocus]; [self.picImage drawInRect:rect fromRect:NSZeroRect operation:NSCompositingOperationSourceOver fraction:1.0]; [image unlockFocus]; return image; } - (void)loadImageViewPath:(NSURL *)url isRemoveBGColor:(BOOL)isRemove { self.imageURL = url; NSString * filePath = url.path; if ([filePath.pathExtension.lowercaseString isEqualToString:@"pdf"]) { PDFDocument *pdf = [[PDFDocument alloc] initWithURL:url]; if (pdf.isEncrypted) { return; } } NSImage *image = [[NSImage alloc] initWithContentsOfFile:filePath]; if (isRemove) { NSData *imageData = [image TIFFRepresentation]; CGImageRef imageRef; if (imageData) { CGImageSourceRef imageSource = CGImageSourceCreateWithData((CFDataRef)imageData, NULL); imageRef = CGImageSourceCreateImageAtIndex(imageSource, 0, NULL); } const int imageWidth = image.size.width; const int imageHeight = image.size.height; size_t bytesPerRow = imageWidth * 4; uint32_t* rgbImageBuf = (uint32_t*)malloc(bytesPerRow * imageHeight); CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); CGContextRef context = CGBitmapContextCreate(rgbImageBuf, imageWidth, imageHeight, 8, bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipLast); CGContextDrawImage(context, CGRectMake(0, 0, imageWidth, imageHeight), imageRef); int pixelNum = imageWidth * imageHeight; uint32_t* pCurPtr = rgbImageBuf; for (int i = 0; i < pixelNum; i++, pCurPtr++) { uint8_t* ptr = (uint8_t*)pCurPtr; if (ptr[1] > 240 && ptr[2] > 240 && ptr[3] > 240) { ptr[0] = 0; } } CGDataProviderRef dataProvider = CGDataProviderCreateWithData(NULL, rgbImageBuf, bytesPerRow * imageHeight, nil); imageRef = CGImageCreate(imageWidth, imageHeight, 8, 32, bytesPerRow, colorSpace, kCGImageAlphaLast |kCGBitmapByteOrder32Little, dataProvider, NULL, true, kCGRenderingIntentDefault); CGDataProviderRelease(dataProvider); NSImage *newImage = nil; if (imageRef) { NSRect imageRect = NSMakeRect(0.0, 0.0, 0.0, 0.0); CGContextRef imageContext = nil; // Get the image dimensions. imageRect.size.height = CGImageGetHeight(imageRef); imageRect.size.width = CGImageGetWidth(imageRef); // Create a new image to receive the Quartz image data. newImage = [[NSImage alloc] initWithSize:imageRect.size]; [newImage lockFocus]; // Get the Quartz context and draw. imageContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; CGContextDrawImage(imageContext, *(CGRect*)&imageRect, imageRef); [newImage unlockFocus]; CGImageRelease(imageRef); } if (newImage) { image = newImage; } } if (image) { self.picImage = image; self.pictureView.hidden = self.emptyTipLbl.hidden = YES; [self setNeedsDisplay:YES]; } } - (CAGradientLayer *)setGradualChanging { CAGradientLayer *gradientLayer = [CAGradientLayer layer]; gradientLayer.frame = _dragButton.bounds; gradientLayer.colors = @[(__bridge id)[NSColor lightGrayColor].CGColor,(__bridge id)[NSColor whiteColor].CGColor]; gradientLayer.startPoint = CGPointMake(0, 0); gradientLayer.endPoint = CGPointMake(1, 1); gradientLayer.locations = @[@0,@1]; return gradientLayer; } - (BOOL)isDamageImage:(NSImage *)image imagePath:(NSString *)path { NSString *addImageAnnotation = [[[NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:[NSBundle mainBundle].bundleIdentifier] stringByAppendingPathComponent:@"addImageAnnotation"]; if (![[NSFileManager defaultManager] fileExistsAtPath:addImageAnnotation]) { [[NSFileManager defaultManager] createDirectoryAtPath:addImageAnnotation withIntermediateDirectories:NO attributes:nil error:nil]; } NSData *data = [image TIFFRepresentation]; NSBitmapImageRep *imageRep = [NSBitmapImageRep imageRepWithData:data]; [imageRep setSize:[image size]]; NSData *imageData = nil; if ([path.lowercaseString isEqualToString:@"png"]) { imageData = [imageRep representationUsingType:NSBitmapImageFileTypePNG properties:@{}]; } else { imageData = [imageRep representationUsingType:NSJPEGFileType properties:@{}]; } NSString *rPath = [addImageAnnotation stringByAppendingPathComponent:[[self tagString] stringByAppendingPathExtension:@"png"]]; if (![imageData writeToFile:rPath atomically:YES]) { return YES; } else { return NO; } } - (NSString *)tagString { NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; dateFormatter.dateFormat = @"yyMMddHHmmss"; return [NSString stringWithFormat:@"%@%04d", [dateFormatter stringFromDate:[NSDate date]], rand()%10000]; } #pragma mark - Button Action - (IBAction)buttonItemClick_SelectPhoto:(id)sender { NSOpenPanel *openPanel = [NSOpenPanel openPanel]; [openPanel setAllowedFileTypes:[self supportedImageTypes]]; [openPanel setAllowedFileTypes:[KMImageAccessoryController supportedImageTypes]]; if ([openPanel respondsToSelector:@selector(setAccessoryViewDisclosed:)]) { [openPanel setAccessoryViewDisclosed:YES]; } [openPanel setAllowsMultipleSelection:NO]; [openPanel beginSheetModalForWindow:[self window] completionHandler:^(NSInteger result) { if (result == NSModalResponseOK) { NSURL *url = [openPanel URL]; //判断PDF文件是否已损坏 NSString * filePath = url.path; if ([filePath.pathExtension.lowercaseString isEqualToString:@"pdf"]) { PDFDocument *pdf = [[PDFDocument alloc] initWithURL:url]; if (!pdf) { NSAlert *alert = [[NSAlert alloc] init]; [alert setAlertStyle:NSAlertStyleCritical]; [alert setMessageText:[NSString stringWithFormat:@"%@",NSLocalizedString(@"An error occurred while opening this document. The file is damaged and could not be repaired.", nil)]]; [alert runModal]; return; } } NSImage *image = [[NSImage alloc] initWithContentsOfFile:url.path]; if ([self isDamageImage:image imagePath:url.path]) { NSAlert *alert = [[NSAlert alloc] init]; [alert setAlertStyle:NSAlertStyleCritical]; [alert setMessageText: [NSString stringWithFormat:NSLocalizedString(@"The file \"%@\" could not be opened.", nil), url.path.lastPathComponent]]; [alert setInformativeText:NSLocalizedString(@"It may be damaged or use a file format that PDF Reader Pro doesn’t recognize.", nil)]; [alert addButtonWithTitle:NSLocalizedString(@"Cancel", nil)]; [alert beginSheetModalForWindow:[NSApp mainWindow] completionHandler:^(NSModalResponse returnCode) { if (returnCode == NSAlertFirstButtonReturn) { } }]; return; } [self loadImageViewPath:url isRemoveBGColor:self.clearBackground]; if (self.changeSignatureImageCallback) { self.changeSignatureImageCallback(YES); } } }]; } - (void)mouseEntered:(NSEvent *)event { [super mouseEntered:event]; self.emptyImg.image = [NSImage imageNamed:@"signPicture_hover"]; } - (void)mouseMoved:(NSEvent *)event { [super mouseMoved:event]; CGPoint point = [event locationInWindow]; CGPoint convertPoint = [self.pictureView convertPoint:point fromView:self.window.contentView]; if (CGRectContainsPoint(self.pictureView.bounds, convertPoint)) { self.emptyImg.image = [NSImage imageNamed:@"signPicture_hover"]; } else { self.emptyImg.image = [NSImage imageNamed:@"signPicture_nor"]; } } - (void)mouseExited:(NSEvent *)event { [super mouseExited:event]; self.emptyImg.image = [NSImage imageNamed:@"signPicture_nor"]; } #pragma mark - Drag - (NSDragOperation)draggingEntered:(id)sender { NSPasteboard *pboard = [sender draggingPasteboard]; if ([pboard availableTypeFromArray:[NSArray arrayWithObject:NSFilenamesPboardType]]) { NSArray *fileNames = [pboard propertyListForType:NSFilenamesPboardType]; if (fileNames.count == 1) { NSArray *supportArray= [self supportedImageTypes]; NSString* path = fileNames.firstObject; if ([supportArray containsObject:[path.pathExtension lowercaseString]]) { return NSDragOperationEvery; } } } return NSDragOperationNone; } - (BOOL)prepareForDragOperation:(id)sender { NSPasteboard *pboard = [sender draggingPasteboard]; if ([pboard availableTypeFromArray:[NSArray arrayWithObject:NSFilenamesPboardType]]) { NSMutableArray *fileNames = [pboard propertyListForType:NSFilenamesPboardType]; if (fileNames.count == 1) { NSArray *supportArray= [self supportedImageTypes]; NSString* path = fileNames.firstObject; //判断PDF文件是否已损坏 NSString * filePath = path; if ([filePath.pathExtension.lowercaseString isEqualToString:@"pdf"]) { PDFDocument *pdf = [[PDFDocument alloc] initWithURL:[NSURL fileURLWithPath:path]]; if (!pdf) { NSAlert *alert = [[NSAlert alloc] init]; [alert setAlertStyle:NSAlertStyleCritical]; [alert setMessageText:[NSString stringWithFormat:@"%@",NSLocalizedString(@"An error occurred while opening this document. The file is damaged and could not be repaired.", nil)]]; [alert runModal]; return NO; } } if ([supportArray containsObject:[path.pathExtension lowercaseString]]) { [self loadImageViewPath:[NSURL fileURLWithPath:path] isRemoveBGColor:self.clearBackground]; if (self.changeSignatureImageCallback) { self.changeSignatureImageCallback(YES); } } } } return YES; } @end