Compare commits

..

2 Commits

Author SHA1 Message Date
Manuel Beck
277e21336f fix(ios): Improve code for meta data processing in resultForImage:`
- Fix: Use local variable `imageDataToWrite` instead of `self.data` to create the `CGImageSourceRef`. This code was wrong.
2026-02-04 16:17:16 +01:00
Manuel Beck
64b28461c6 refactor(ios): small refactoring on resultForImage:
- Document meta data processing
- Remove any reference of `self.cdvUIImagePickerController` since it is not needed here and also not set, when `PHPickerViewController` is used
- Use `pictureOptions.sourceType` instead of `self.cdvUIImagePickerController.sourceType``
- Use `pictureOptions` parameter instead of `self.cdvUIImagePickerController.pictureOptions`
2026-02-03 12:36:34 +01:00

View File

@@ -421,17 +421,37 @@ static NSString* MIME_JPEG = @"image/jpeg";
}
/**
Prepares the image and metadata obtained from PHPickerImageViewController which will be processed in
resultForImage:. After that CDVPluginResult is returned.
Processes an image obtained from PHPickerViewController according to specified pictureOptions,
after it returns the CDVPluginResult.
The processing of the image is similar what retrieveImage: and processImage: is doing for UIImagePickerController.
*/
- (void)processPHPickerImage:(UIImage*)image
metadata:(NSDictionary*)metadata
callbackId:(NSString*)callbackId
options:(CDVPictureOptions*)options API_AVAILABLE(ios(14))
{
// To shrink the file size, only selected meta data like EXIF, TIFF, and GPS is used,
// which will be stored in self.metadata, which is set to the image in resultForImage:
// This code replicates the logic from processImage: for the UIImagePickerController
// Process image according to options
// The same is done in retrieveImage:
UIImage *processedImage = image;
if (options.correctOrientation) {
processedImage = [processedImage imageCorrectedForCaptureOrientation];
}
// Scale with optional cropping
if ((options.targetSize.width > 0) && (options.targetSize.height > 0)) {
// Scale and crop to target size
if (options.cropToSize) {
processedImage = [processedImage imageByScalingAndCroppingForSize:options.targetSize];
// Scale with no cropping
} else {
processedImage = [processedImage imageByScalingNotCroppingForSize:options.targetSize];
}
}
// Prepare self.metadata, which replicates the logic from processImage: for the UIImagePickerController
// self.metadata which will be set to the image in resultForImage:
if (metadata.count > 0) {
self.metadata = [NSMutableDictionary dictionary];
@@ -451,19 +471,18 @@ static NSString* MIME_JPEG = @"image/jpeg";
}
}
// Mimic the info dictionary which would be created by UIImagePickerController
// Add image, which will be used in retrieveImage: to get the image and do processing
NSMutableDictionary *info = [@{UIImagePickerControllerOriginalImage : image} mutableCopy];
// Return Cordova result to WebView
// Needed weakSelf for completion block
__weak CDVCamera* weakSelf = self;
// Create info dictionary similar to UIImagePickerController
// Will be used in retrieveImage: to get the image and do processing like here was done
NSMutableDictionary *info = [@{ UIImagePickerControllerOriginalImage : processedImage } mutableCopy];
// Add metadata if available
if (metadata.count > 0) {
// This is not used anywhere and can be removed
info[UIImagePickerControllerMediaMetadata] = metadata;
}
// Return Cordova result to WebView
// Needed weakSelf for completion block
__weak CDVCamera* weakSelf = self;
// Process and return result
[self resultForImage:options info:info completion:^(CDVPluginResult* pluginResult) {
@@ -695,50 +714,44 @@ static NSString* MIME_JPEG = @"image/jpeg";
- (UIImage*)retrieveImage:(NSDictionary*)info options:(CDVPictureOptions*)options
{
UIImage* pickedImage = nil;
// Get the picked image from info dictionary
// get the image
UIImage* image = nil;
if (options.allowsEditing && [info objectForKey:UIImagePickerControllerEditedImage]) {
pickedImage = [info objectForKey:UIImagePickerControllerEditedImage];
image = [info objectForKey:UIImagePickerControllerEditedImage];
} else {
pickedImage = [info objectForKey:UIImagePickerControllerOriginalImage];
image = [info objectForKey:UIImagePickerControllerOriginalImage];
}
// Process image according to options
UIImage *processedImage = pickedImage;
if (options.correctOrientation) {
processedImage = [processedImage imageCorrectedForCaptureOrientation];
image = [image imageCorrectedForCaptureOrientation];
}
// Scale with optional cropping
if (options.targetSize.width > 0 && options.targetSize.height > 0) {
// Scale and crop to target size
if (options.cropToSize) {
processedImage = [processedImage imageByScalingAndCroppingForSize:options.targetSize];
// Scale with no cropping
UIImage* scaledImage = nil;
if ((options.targetSize.width > 0) && (options.targetSize.height > 0)) {
// if cropToSize, resize image and crop to target size, otherwise resize to fit target without cropping
if (options.cropToSize) {
scaledImage = [image imageByScalingAndCroppingForSize:options.targetSize];
} else {
processedImage = [processedImage imageByScalingNotCroppingForSize:options.targetSize];
scaledImage = [image imageByScalingNotCroppingForSize:options.targetSize];
}
}
return processedImage;
return (scaledImage == nil ? image : scaledImage);
}
- (void)resultForImage:(CDVPictureOptions*)options
- (void)resultForImage:(CDVPictureOptions*)pictureOptions
info:(NSDictionary*)info
completion:(void (^)(CDVPluginResult* res))completion
{
CDVPluginResult* result = nil;
BOOL saveToPhotoAlbum = options.saveToPhotoAlbum;
UIImage* image = nil;
switch (options.destinationType) {
switch (pictureOptions.destinationType) {
case DestinationTypeDataUrl:
{
image = [self retrieveImage:info options:options];
NSString* data = [self processImageAsDataUri:image info:info options:options];
image = [self retrieveImage:info options:pictureOptions];
NSString* data = [self processImageAsDataUri:image info:info options:pictureOptions];
if (data) {
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString: data];
}
@@ -746,48 +759,57 @@ static NSString* MIME_JPEG = @"image/jpeg";
break;
default: // DestinationTypeFileUri
{
image = [self retrieveImage:info options:options];
NSData* data = [self processImage:image info:info options:options];
image = [self retrieveImage:info options:pictureOptions];
NSData* imageData = [self processImage:image info:info options:pictureOptions];
if (data) {
if (self.cdvUIImagePickerController.sourceType == UIImagePickerControllerSourceTypePhotoLibrary) {
NSMutableData *imageDataWithExif = [NSMutableData data];
if (imageData) {
if (pictureOptions.sourceType == UIImagePickerControllerSourceTypePhotoLibrary) {
NSData *imageDataToWrite = imageData;
// Copy custom choosen meta data stored in self.metadata to the image
if (self.metadata) {
CGImageSourceRef sourceImage = CGImageSourceCreateWithData((__bridge CFDataRef)self.data, NULL);
NSMutableData *imageDataWithCustomMetaData = [NSMutableData data];
// Prepare source image
CGImageSourceRef sourceImage = CGImageSourceCreateWithData((__bridge CFDataRef)imageDataToWrite, NULL);
CFStringRef sourceType = CGImageSourceGetType(sourceImage);
CGImageDestinationRef destinationImage = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)imageDataWithExif, sourceType, 1, NULL);
// Prepare dest image
CGImageDestinationRef destinationImage = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)imageDataWithCustomMetaData, sourceType, 1, NULL);
// Copy source to dest with self.metadata
CGImageDestinationAddImageFromSource(destinationImage, sourceImage, 0, (__bridge CFDictionaryRef)self.metadata);
CGImageDestinationFinalize(destinationImage);
CFRelease(sourceImage);
CFRelease(destinationImage);
} else {
imageDataWithExif = [self.data mutableCopy];
imageDataToWrite = imageDataWithCustomMetaData;
}
NSString* tempFilePath = [self tempFilePathForExtension:pictureOptions.encodingType == EncodingTypePNG ? @"png":@"jpg"];
NSError* err = nil;
NSString* extension = self.cdvUIImagePickerController.pictureOptions.encodingType == EncodingTypePNG ? @"png":@"jpg";
NSString* filePath = [self tempFilePathForExtension:extension];
// save file
if (![imageDataWithExif writeToFile:filePath options:NSAtomicWrite error:&err]) {
// Write image to temp path
if ([imageDataToWrite writeToFile:tempFilePath options:NSAtomicWrite error:&err]) {
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
messageAsString:[[NSURL fileURLWithPath:tempFilePath] absoluteString]];
// Write was not successful
} else {
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION
messageAsString:[err localizedDescription]];
}
else {
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
messageAsString:[[NSURL fileURLWithPath:filePath] absoluteString]];
}
} else if (self.cdvUIImagePickerController.sourceType != UIImagePickerControllerSourceTypeCamera || !options.usesGeolocation) {
} else if (pictureOptions.sourceType != UIImagePickerControllerSourceTypeCamera || !pictureOptions.usesGeolocation) {
// No need to save file if usesGeolocation is true since it will be saved after the location is tracked
NSString* extension = options.encodingType == EncodingTypePNG? @"png" : @"jpg";
NSString* extension = pictureOptions.encodingType == EncodingTypePNG? @"png" : @"jpg";
NSString* filePath = [self tempFilePathForExtension:extension];
NSError* err = nil;
// save file
if (![data writeToFile:filePath options:NSAtomicWrite error:&err]) {
if (![imageData writeToFile:filePath options:NSAtomicWrite error:&err]) {
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION
messageAsString:[err localizedDescription]];
} else {
@@ -801,7 +823,8 @@ static NSString* MIME_JPEG = @"image/jpeg";
break;
};
if (saveToPhotoAlbum && image) {
// Save the image to the photo album after capture
if (pictureOptions.saveToPhotoAlbum && image) {
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
}