首先說(shuō)下接觸Exif的背景,公司有拍照取證的功能,比如,發(fā)生了車禍,需要拍下現(xiàn)場(chǎng)照片取證,除了添加拍攝地點(diǎn),經(jīng)緯度,app賬號(hào),還有防偽功能。也就是防止別人篡改照片,修改相應(yīng)的信息。故提出在照片的exif信息中添加防偽的唯一信息。
什么是Exif
可交換圖像文件格式(英語(yǔ):Exchangeable image file format,官方簡(jiǎn)稱Exif),是專門為數(shù)碼相機(jī)的照片設(shè)定的,可以記錄數(shù)碼照片的屬性信息和拍攝數(shù)據(jù)。
Exif最初由日本電子工業(yè)發(fā)展協(xié)會(huì)在1996年制定,版本為1.0。1998年,升級(jí)到2.1,增加了對(duì)音頻文件的支持。2002年3月,發(fā)表了2.2版。
Exif可以附加于JPEG、TIFF、RIFF等文件之中,為其增加有關(guān)數(shù)碼相機(jī)拍攝信息的內(nèi)容和索引圖或圖像處理軟件的版本信息。
Windows 7操作系統(tǒng)具備對(duì)Exif的原生支持,通過(guò)鼠標(biāo)右鍵點(diǎn)擊圖片打開菜單,點(diǎn)擊屬性并切換到詳細(xì)信息標(biāo)簽下即可直接查看Exif信息。
Exif信息是可以被任意編輯的,因此只有參考的功能。Exif信息以0xFFE1作為開頭標(biāo)記,后兩個(gè)字節(jié)表示Exif信息的長(zhǎng)度。所以Exif信息最大為64 kb,而內(nèi)部采用TIFF格式。
一、 imageIO的初步了解
ImageIO框架提供了讀取與寫入圖片數(shù)據(jù)的基本方法,使用它可以直接獲取到圖片文件的內(nèi)容數(shù)據(jù),ImageIO框架中包含6個(gè)頭文件,其中完成主要功能的是CGImageSource和CGImageDestination兩個(gè)頭文件中定義的方法:
- CGImageSource: 負(fù)責(zé)圖片數(shù)據(jù)的讀取。
- CGImageDestination: 負(fù)責(zé)圖片數(shù)據(jù)的寫入。
- CGImageMetadata: 圖片文件數(shù)據(jù)類。
- CGImageProperties: 框架中用到的字符串常量和宏。
- ImageIOBase: 預(yù)處理邏輯。
二、 項(xiàng)目應(yīng)用
圖片信息操作都是針對(duì)二進(jìn)制數(shù)據(jù)Data來(lái)操作的,注(在圖片轉(zhuǎn)data和data轉(zhuǎn)Image的時(shí)候,蘋果會(huì)將部分exif信息抹除,所以如果需要保存數(shù)據(jù)到本地,最好通過(guò)data數(shù)據(jù)保存,上傳圖片數(shù)據(jù)到服務(wù)器,也建議通過(guò)data上傳)。
廢話不多說(shuō)先上代碼:
- 通過(guò)圖片data數(shù)據(jù)獲取圖片圖片資源引用。
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)imageData, NULL);
- 再通過(guò)圖片資源獲取圖片信息
NSDictionary *imageInfo = (__bridge NSDictionary*)CGImageSourceCopyPropertiesAtIndex(source, 0, NULL);
imageInfo其實(shí)都是key是定義好的字典對(duì)象。
IMAGEIO_EXTERN const CFStringRef kCGImagePropertyTIFFDictionary IMAGEIO_AVAILABLE_STARTING(10.4, 4.0);
IMAGEIO_EXTERN const CFStringRef kCGImagePropertyGIFDictionary IMAGEIO_AVAILABLE_STARTING(10.4, 4.0);
IMAGEIO_EXTERN const CFStringRef kCGImagePropertyJFIFDictionary IMAGEIO_AVAILABLE_STARTING(10.4, 4.0);
IMAGEIO_EXTERN const CFStringRef kCGImagePropertyExifDictionary IMAGEIO_AVAILABLE_STARTING(10.4, 4.0);
IMAGEIO_EXTERN const CFStringRef kCGImagePropertyPNGDictionary IMAGEIO_AVAILABLE_STARTING(10.4, 4.0);
IMAGEIO_EXTERN const CFStringRef kCGImagePropertyIPTCDictionary IMAGEIO_AVAILABLE_STARTING(10.4, 4.0);
IMAGEIO_EXTERN const CFStringRef kCGImagePropertyGPSDictionary IMAGEIO_AVAILABLE_STARTING(10.4, 4.0);
IMAGEIO_EXTERN const CFStringRef kCGImagePropertyRawDictionary IMAGEIO_AVAILABLE_STARTING(10.4, 4.0);
IMAGEIO_EXTERN const CFStringRef kCGImagePropertyCIFFDictionary IMAGEIO_AVAILABLE_STARTING(10.4, 4.0);
IMAGEIO_EXTERN const CFStringRef kCGImagePropertyMakerCanonDictionary IMAGEIO_AVAILABLE_STARTING(10.5, 4.0);
IMAGEIO_EXTERN const CFStringRef kCGImagePropertyMakerNikonDictionary IMAGEIO_AVAILABLE_STARTING(10.5, 4.0);
IMAGEIO_EXTERN const CFStringRef kCGImagePropertyMakerMinoltaDictionary IMAGEIO_AVAILABLE_STARTING(10.5, 4.0);
IMAGEIO_EXTERN const CFStringRef kCGImagePropertyMakerFujiDictionary IMAGEIO_AVAILABLE_STARTING(10.5, 4.0);
IMAGEIO_EXTERN const CFStringRef kCGImagePropertyMakerOlympusDictionary IMAGEIO_AVAILABLE_STARTING(10.5, 4.0);
IMAGEIO_EXTERN const CFStringRef kCGImagePropertyMakerPentaxDictionary IMAGEIO_AVAILABLE_STARTING(10.5, 4.0);
IMAGEIO_EXTERN const CFStringRef kCGImageProperty8BIMDictionary IMAGEIO_AVAILABLE_STARTING(10.4, 4.0);
IMAGEIO_EXTERN const CFStringRef kCGImagePropertyDNGDictionary IMAGEIO_AVAILABLE_STARTING(10.5, 4.0);
IMAGEIO_EXTERN const CFStringRef kCGImagePropertyExifAuxDictionary IMAGEIO_AVAILABLE_STARTING(10.5, 4.0);
IMAGEIO_EXTERN const CFStringRef kCGImagePropertyOpenEXRDictionary IMAGEIO_AVAILABLE_STARTING(10.9, 11.3);
IMAGEIO_EXTERN const CFStringRef kCGImagePropertyMakerAppleDictionary IMAGEIO_AVAILABLE_STARTING(10.10, 7.0);
IMAGEIO_EXTERN const CFStringRef kCGImagePropertyFileContentsDictionary IMAGEIO_AVAILABLE_STARTING(10.13, 11.0);
- 接下來(lái)重頭戲來(lái)了,也就是對(duì)圖片信息進(jìn)行編輯。
首先對(duì)copy一份新的json對(duì)象,然后再對(duì)拷貝的新對(duì)象進(jìn)行修改。
NSMutableDictionary *metaDataDic = [imageInfo mutableCopy];
NSMutableDictionary *exifDic =[[metaDataDic objectForKey:(NSString*)kCGImagePropertyExifDictionary]mutableCopy];
添加自定義的對(duì)象,但key是固定的,使用的是kCGImagePropertyExifUserComment。
自定義對(duì)象里用的是進(jìn)行加密后的手機(jī)號(hào)碼。記住,exif信息對(duì)數(shù)據(jù)的結(jié)構(gòu)有嚴(yán)格要求,必須嚴(yán)格遵守,不然,可能無(wú)法添加成功。這里我只找到了UserComment字段來(lái)存放自定義的字符串信息。
/****** begin 添加手機(jī)號(hào)碼 ******/
NSString *dateString = [self.dateFormatter stringFromDate:[NSDate date]];
[exifDic setValue:dateString forKey:(NSString *)kCGImagePropertyExifDateTimeOriginal];
NSString *phone = [NSString stringWithFormat:@"xxxsdk_%@",[PMPluginLoader sharedInstance].pluginMobilePhone];
NSString *decryptKey = [PMPluginUtils pluginRunEnv] == PluginEnv_stg ? kDescryptKey_stg: kDescryptKey_prd;
phone = [PMAESUtils threeEncrypt:phone withKey:decryptKey];
[exifDic setObject:phone forKey:(NSString *)kCGImagePropertyExifUserComment];
/****** end 添加手機(jī)號(hào)碼 ******/
/****** begin 添加定位信息 ******/
NSMutableDictionary *GPSDic =[[metaDataDic objectForKey:(NSString*)kCGImagePropertyGPSDictionary]mutableCopy];
if (!GPSDic) {
GPSDic = [NSMutableDictionary dictionaryWithCapacity:2];
}
CGFloat latitudeValue = self.locaiton.coordinate.latitude;
CGFloat longitudeValue = self.locaiton.coordinate.longitude;
[GPSDic setObject: [NSNumber numberWithFloat:latitudeValue] forKey:(NSString*)kCGImagePropertyGPSLatitude];
[GPSDic setObject:(latitudeValue>0?@"N":@"S") forKey:(NSString*)kCGImagePropertyGPSLatitudeRef];
[GPSDic setObject: [NSNumber numberWithFloat:longitudeValue] forKey:(NSString*)kCGImagePropertyGPSLongitude];
[GPSDic setObject:(longitudeValue>0?@"E":@"W") forKey:(NSString*)kCGImagePropertyGPSLongitudeRef];
//注意: 設(shè)置的value值必須是數(shù)值對(duì)象。且必須同步設(shè)置經(jīng)緯度,以及東西經(jīng)、南北緯。少一個(gè)都會(huì)設(shè)置失敗。
/****** end 添加定位信息 ******/
/****** begin 添加時(shí)間DateTime ******/
NSMutableDictionary *TIFFDic =[[metaDataDic objectForKey:(NSString*)kCGImagePropertyTIFFDictionary]mutableCopy];
if (!TIFFDic) {
TIFFDic = [NSMutableDictionary dictionary];
}
NSString *dateTimeString = [[TIFFDic objectForKey:(NSString*)kCGImagePropertyTIFFDateTime]mutableCopy];
if (!dateTimeString) {
[TIFFDic setObject:dateString forKey:(NSString *)kCGImagePropertyTIFFDateTime];
}
/****** end 添加時(shí)間DateTime ******/
//將修改后的信息重新設(shè)置到對(duì)應(yīng)的json對(duì)象里。
[metaDataDic setValue:GPSDic forKey:(NSString *)kCGImagePropertyGPSDictionary];
[metaDataDic setValue:exifDic forKey:(NSString *)kCGImagePropertyExifDictionary];
[metaDataDic setValue:TIFFDic forKey:(NSString *)kCGImagePropertyTIFFDictionary];
- 保存添加exif信息后的圖片data。
CFStringRef UTI = CGImageSourceGetType(source);
//創(chuàng)建空的data對(duì)象
NSMutableData *newImageData = [NSMutableData data];
//根據(jù)容器data,UTl對(duì)象創(chuàng)建圖片寫入句柄對(duì)象。
CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)newImageData, UTI, 1,NULL);
//將圖片信息寫入圖片句柄對(duì)象。
CGImageDestinationAddImageFromSource(destination, source, 0, (__bridge CFDictionaryRef)metaDataDic);
BOOL success = CGImageDestinationFinalize(destination);
CFRelease(UTI);
CFRelease(destination);
CFRelease(source);
if (!success) {
NSLog(@"添加exif信息失敗");
}
return newImageData; //添加exif信息后的圖片data對(duì)象。
最后,在項(xiàng)目里面因?yàn)橐灿玫搅颂砑铀〉墓δ埽俅我矊⑾鄳?yīng)代碼貼出,供有需要的同學(xué)參考。
+ (UIImage *)addImageWatermark:(UIImage *)image text:(NSString *)text{
CGFloat imageWidth = image.size.width;
CGFloat imageHeight = image.size.height;
if (imageWidth/imageHeight > kScreenWidth/kScreenHeight) {
CGFloat finalImageWidth = (imageHeight*kScreenWidth/kScreenHeight);
image = [self tp_imagecutWithOriginalImage:image withCutRect:CGRectMake((imageWidth - finalImageWidth)*0.5, 0, finalImageWidth, imageHeight)];
}else{
CGFloat finalImageHeight = (imageWidth*kScreenHeight/kScreenWidth);
image = [self tp_imagecutWithOriginalImage:image withCutRect:CGRectMake(0, (imageHeight - finalImageHeight)*0.5, imageWidth, finalImageHeight)];
}
image = [UIImage getWaterMarkImage:image andTitle:text andMarkFont:[UIFont boldSystemFontOfSize:20] andMarkColor:[UIColor colorWithRed:192/255.0 green:192/255.0 blue:192/255.0 alpha:1] markMutilple:NO];
return image;
}
+ (UIImage *)tp_imagecutWithOriginalImage:(UIImage *)originalImage withCutRect:(CGRect)rect {
CGImageRef subImageRef = CGImageCreateWithImageInRect(originalImage.CGImage, rect);
CGRect smallRect = CGRectMake(0, 0, CGImageGetWidth(subImageRef), CGImageGetHeight(subImageRef));
// 開啟圖形上下文
UIGraphicsBeginImageContext(smallRect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextDrawImage(context, smallRect, subImageRef);
UIImage * image = [UIImage imageWithCGImage:subImageRef];
// 關(guān)閉圖形上下文
UIGraphicsEndImageContext();
CGImageRelease(subImageRef);
return image;
}
結(jié)語(yǔ): 此篇文章的目的,主要是為了記錄自己使用過(guò)的知識(shí)點(diǎn),一來(lái)加深印象,二來(lái)也是為自己做下筆記。所以,文章大部分都以代碼為主,讀起來(lái)很是生涉,請(qǐng)諒解。