Last active
September 28, 2024 12:58
-
-
Save nyg/c90f36abbd30f72c8b6681ef23db886b to your computer and use it in GitHub Desktop.
Get and set an EXIF UserComment to a JPEG image using the ImageIO framework.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Note: to add a JPEG COM marker go here: | |
// https://gist.github.com/nyg/bdeae8190a41b4b56bde8e13dd471ecc | |
import Foundation | |
import ImageIO | |
#if os(iOS) | |
import MobileCoreServices | |
#endif | |
// See CGImageMetadataCopyTagWithPath documentation for an explanation | |
let exifUserCommentPath = "\(kCGImageMetadataPrefixExif):\(kCGImagePropertyExifUserComment)" as CFString | |
/* Set EXIF UserComment */ | |
// using metadata functions | |
func setEXIFUserComment(_ comment: String, using sourceURL: URL, destination destinationURL: URL) { | |
guard let imageDestination = CGImageDestinationCreateWithURL(destinationURL as CFURL, kUTTypeJPEG, 1, nil) | |
else { fatalError("Image destination not created") } | |
guard let metadataTag = CGImageMetadataTagCreate(kCGImageMetadataNamespaceExif, kCGImageMetadataPrefixExif, kCGImagePropertyExifUserComment, .string, comment as CFString) | |
else { fatalError("Metadata tag not created") } | |
let metadata = CGImageMetadataCreateMutable() | |
CGImageMetadataSetTagWithPath(metadata, nil, exifUserCommentPath, metadataTag) | |
guard let imageSource = CGImageSourceCreateWithURL(sourceURL as CFURL, nil) | |
else { fatalError("Image source not created") } | |
guard let image = CGImageSourceCreateImageAtIndex(imageSource, 0, nil) | |
else { fatalError("Image not created from source") } | |
CGImageDestinationAddImageAndMetadata(imageDestination, image, metadata, nil) | |
CGImageDestinationFinalize(imageDestination) | |
} | |
// using image property functions | |
func setEXIFUserComment2(_ comment: String, using sourceURL: URL, destination destinationURL: URL) { | |
guard let outputImage = CGImageDestinationCreateWithURL(destinationURL as CFURL, kUTTypeJPEG, 1, nil) | |
else { fatalError("Image destination not created") } | |
guard let imageSource = CGImageSourceCreateWithURL(sourceURL as CFURL, nil) | |
else { fatalError("Image source not created") } | |
let exifDictionary: [NSString: AnyObject] = [ kCGImagePropertyExifUserComment: comment as CFString ] | |
let properties: [NSString: AnyObject] = [ kCGImagePropertyExifDictionary: exifDictionary as CFDictionary ] | |
CGImageDestinationAddImageFromSource(outputImage, imageSource, 0, properties as CFDictionary) | |
CGImageDestinationFinalize(outputImage) | |
} | |
/* Get EXIF UserComment */ | |
// using image metadata functions | |
func getEXIFUserComment(from image: URL) -> String? { | |
guard let imageSource = CGImageSourceCreateWithURL(image as CFURL, nil) | |
else { fatalError("Image source not created") } | |
guard let metadata = CGImageSourceCopyMetadataAtIndex(imageSource, 0, nil) | |
else { return nil } | |
// // Solution 1 | |
// | |
//// // Solution 1a | |
//// guard let userCommentTag = CGImageMetadataCopyTagWithPath(metadata, nil, exifUserCommentPath) | |
//// else { return nil } | |
//// | |
//// // Solution 1b | |
//// guard let userCommentTag = CGImageMetadataCopyTagMatchingImageProperty(metadata, kCGImagePropertyExifDictionary, kCGImagePropertyExifUserComment) | |
//// else { return nil } | |
// | |
// guard let userComment = CGImageMetadataTagCopyValue(userCommentTag) as? String | |
// else { return nil } | |
// Solution 2 | |
guard let userComment = CGImageMetadataCopyStringValueWithPath(metadata, nil, exifUserCommentPath) as String? | |
else { return nil } | |
return userComment | |
} | |
// using image property functions | |
func getEXIFUserComment2(from image: URL) -> String? { | |
guard let imageSource = CGImageSourceCreateWithURL(image as CFURL, nil) | |
else { fatalError("Image source not created") } | |
guard let properties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil) as? [NSString: AnyObject], | |
let exifDictonary = properties[kCGImagePropertyExifDictionary], | |
let userComment = exifDictonary[kCGImagePropertyExifUserComment] as? String | |
else { return nil } | |
return userComment | |
} | |
/* Debug */ | |
func getProperties(from image: URL) -> [NSString: AnyObject]? { | |
guard let source = CGImageSourceCreateWithURL(image as CFURL, nil) | |
else { fatalError("Image source not created") } | |
return CGImageSourceCopyPropertiesAtIndex(source, 0, nil) as? [NSString: AnyObject] | |
} | |
func getMetadata(from image: URL) -> [[String: Any]]? { | |
guard let imageSource = CGImageSourceCreateWithURL(image as CFURL, nil) | |
else { fatalError("Image source not created") } | |
guard let metadata = CGImageSourceCopyMetadataAtIndex(imageSource, 0, nil) | |
else { return nil } | |
guard let tags = CGImageMetadataCopyTags(metadata) as? [CGImageMetadataTag] | |
else { return nil } | |
return tags.map { tag in | |
var info = [String: Any]() | |
if let namespace = CGImageMetadataTagCopyNamespace(tag) as String? { | |
info["namespance"] = namespace | |
} | |
if let prefix = CGImageMetadataTagCopyPrefix(tag) as String? { | |
info["prefix"] = prefix | |
} | |
if let name = CGImageMetadataTagCopyName(tag) as String? { | |
info["name"] = name | |
} | |
if let value = CGImageMetadataTagCopyValue(tag) as AnyObject? { | |
info["value"] = value | |
} | |
info["type"] = CGImageMetadataTagGetType(tag).rawValue | |
if let qualifiers = CGImageMetadataTagCopyQualifiers(tag) as? [CGImageMetadataTag] { | |
info["qualifiers"] = qualifiers | |
} | |
return info | |
} | |
} | |
/* Testing */ | |
guard let sourceURL = Bundle.main.url(forResource: "jpg_comm", withExtension: "jpg") | |
else { fatalError("Image not found") } | |
print(getEXIFUserComment(from: sourceURL) ?? "No EXIF UserComment") | |
//print(getProperties(from: sourceURL) ?? "No properties") | |
//print(getMetadata(from: sourceURL) ?? "No metadata") | |
guard let destinationURL = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false).appendingPathComponent("img2.jpg") | |
else { fatalError("Destination URL not created") } | |
setEXIFUserComment("Here is an EXIF UserComment!", using: sourceURL, destination: destinationURL) | |
print(getEXIFUserComment(from: destinationURL) ?? "No EXIF UserComment") | |
//print(getProperties(from: destinationURL) ?? "No properties") | |
//print(getMetadata(from: destinationURL) ?? "No metadata") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment