CFDictionaryRef readJPEGProperties()

in Frameworks/ImageIO/CGImageSource.mm [703:1102]


CFDictionaryRef readJPEGProperties(IWICMetadataQueryReader* imageMetadataReader) {
    PROPVARIANT propertyValue;
    PropVariantInit(&propertyValue);
    NSMutableDictionary* properties = [[NSMutableDictionary alloc] init];

    // JPEG Properties - Common
    // DPIHeight and DPIWidth are saved in different places for different image formats, and these locations only represent
    // DPI if {ushort=1} is 1, otherwise they are other units.
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/{ushort=282}", &propertyValue)) && propertyValue.vt == VT_UI8) {
        [properties setObject:[NSNumber numberWithDouble:(double)propertyValue.uhVal.LowPart / propertyValue.uhVal.HighPart]
                       forKey:(id)kCGImagePropertyDPIWidth];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/{ushort=283}", &propertyValue)) && propertyValue.vt == VT_UI8) {
        [properties setObject:[NSNumber numberWithDouble:(double)propertyValue.uhVal.LowPart / propertyValue.uhVal.HighPart]
                       forKey:(id)kCGImagePropertyDPIHeight];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/{ushort=256}", &propertyValue)) && propertyValue.vt == VT_UI2) {
        [properties setObject:[NSNumber numberWithInt:propertyValue.uiVal] forKey:(id)kCGImagePropertyPixelWidth];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/{ushort=256}", &propertyValue)) && propertyValue.vt == VT_UI4) {
        [properties setObject:[NSNumber numberWithInt:propertyValue.ulVal] forKey:(id)kCGImagePropertyPixelWidth];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/{ushort=257}", &propertyValue)) && propertyValue.vt == VT_UI2) {
        [properties setObject:[NSNumber numberWithInt:propertyValue.uiVal] forKey:(id)kCGImagePropertyPixelHeight];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/{ushort=257}", &propertyValue)) && propertyValue.vt == VT_UI4) {
        [properties setObject:[NSNumber numberWithInt:propertyValue.ulVal] forKey:(id)kCGImagePropertyPixelHeight];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/{ushort=258}", &propertyValue)) && propertyValue.vt == VT_UI2) {
        [properties setObject:[NSNumber numberWithInt:propertyValue.uiVal] forKey:(id)kCGImagePropertyDepth];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/{ushort=274}", &propertyValue)) && propertyValue.vt == VT_UI2) {
        [properties setObject:[NSNumber numberWithInt:propertyValue.uiVal] forKey:(id)kCGImagePropertyOrientation];
    }

    // JPEG Properties - Format-specific
    NSMutableDictionary* jfifProperties = [[NSMutableDictionary alloc] init];
    PropVariantClear(&propertyValue);

    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app0/{ushort=0}", &propertyValue)) && propertyValue.vt == VT_UI2) {
        // NSArray doesn't print properly but this is how iOS does it
        NSMutableArray* jfifVersionArray = [NSMutableArray array];
        [jfifVersionArray addObject:[NSNumber numberWithInt:(propertyValue.uiVal >> 8) & 0xF]];
        [jfifVersionArray addObject:[NSNumber numberWithInt:(propertyValue.uiVal >> 4) & 0xF]];
        [jfifVersionArray addObject:[NSNumber numberWithInt:propertyValue.uiVal & 0xF]];
        [jfifProperties setObject:jfifVersionArray forKey:(id)kCGImagePropertyJFIFVersion];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app0/{ushort=2}", &propertyValue)) && propertyValue.vt == VT_UI2) {
        [jfifProperties setObject:[NSNumber numberWithInt:propertyValue.uiVal] forKey:(id)kCGImagePropertyJFIFXDensity];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app0/{ushort=3}", &propertyValue)) && propertyValue.vt == VT_UI2) {
        [jfifProperties setObject:[NSNumber numberWithInt:propertyValue.uiVal] forKey:(id)kCGImagePropertyJFIFYDensity];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app0/{ushort=1}", &propertyValue)) && propertyValue.vt == VT_UI1) {
        [jfifProperties setObject:[NSNumber numberWithInt:propertyValue.bVal] forKey:(id)kCGImagePropertyJFIFDensityUnit];
    }

    // Add the JFIF dictionary to the properties if there are any JFIF properties
    if ([jfifProperties count] != 0) {
        [properties setObject:jfifProperties forKey:(id)kCGImagePropertyJFIFDictionary];
    }
    CFRelease(jfifProperties);

    // GPS Properties - JPEG
    NSMutableDictionary* gpsProperties = [[NSMutableDictionary alloc] init];
    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/gps/{ushort=6}", &propertyValue)) && propertyValue.vt == VT_UI8) {
        [gpsProperties setObject:[NSNumber numberWithDouble:(double)propertyValue.uhVal.LowPart / propertyValue.uhVal.HighPart]
                          forKey:(id)kCGImagePropertyGPSAltitude];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/gps/{ushort=5}", &propertyValue)) && propertyValue.vt == VT_UI1) {
        [gpsProperties setObject:[NSNumber numberWithInt:propertyValue.bVal] forKey:(id)kCGImagePropertyGPSAltitudeRef];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/gps/{ushort=29}", &propertyValue)) && propertyValue.vt == VT_LPSTR) {
        [gpsProperties setObject:[NSString stringWithUTF8String:propertyValue.pszVal] forKey:(id)kCGImagePropertyGPSDateStamp];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/gps/{ushort=11}", &propertyValue)) && propertyValue.vt == VT_UI8) {
        [gpsProperties setObject:[NSNumber numberWithDouble:(double)propertyValue.uhVal.LowPart / propertyValue.uhVal.HighPart]
                          forKey:(id)kCGImagePropertyGPSDOP];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/gps/{ushort=17}", &propertyValue)) && propertyValue.vt == VT_UI8) {
        [gpsProperties setObject:[NSNumber numberWithDouble:(double)propertyValue.uhVal.LowPart / propertyValue.uhVal.HighPart]
                          forKey:(id)kCGImagePropertyGPSImgDirection];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/gps/{ushort=16}", &propertyValue)) && propertyValue.vt == VT_LPSTR) {
        [gpsProperties setObject:[NSString stringWithUTF8String:propertyValue.pszVal] forKey:(id)kCGImagePropertyGPSImgDirectionRef];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/gps/{ushort=2}", &propertyValue)) &&
        propertyValue.vt == (VT_VECTOR | VT_UI8)) {
        if (propertyValue.caub.cElems == 3) { // Validate number of fields
            double degreesPart = (double)propertyValue.cauh.pElems[0].LowPart / propertyValue.cauh.pElems[0].HighPart;
            double minutesPart = (double)propertyValue.cauh.pElems[1].LowPart / propertyValue.cauh.pElems[1].HighPart;
            double secondsPart = (double)propertyValue.cauh.pElems[2].LowPart / propertyValue.cauh.pElems[2].HighPart;
            [gpsProperties
                setObject:[NSNumber numberWithDouble:degreesPart + minutesPart / c_minutesPerDegree + secondsPart / (c_secondsPerDegree)]
                   forKey:(id)kCGImagePropertyGPSLatitude];
        }
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/gps/{ushort=1}", &propertyValue)) && propertyValue.vt == VT_LPSTR) {
        [gpsProperties setObject:[NSString stringWithUTF8String:propertyValue.pszVal] forKey:(id)kCGImagePropertyGPSLatitudeRef];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/gps/{ushort=4}", &propertyValue)) &&
        propertyValue.vt == (VT_VECTOR | VT_UI8)) {
        if (propertyValue.caub.cElems == 3) { // Validate number of fields
            double degreesPart = (double)propertyValue.cauh.pElems[0].LowPart / propertyValue.cauh.pElems[0].HighPart;
            double minutesPart = (double)propertyValue.cauh.pElems[1].LowPart / propertyValue.cauh.pElems[1].HighPart;
            double secondsPart = (double)propertyValue.cauh.pElems[2].LowPart / propertyValue.cauh.pElems[2].HighPart;
            [gpsProperties
                setObject:[NSNumber numberWithDouble:degreesPart + minutesPart / c_minutesPerDegree + secondsPart / (c_secondsPerDegree)]
                   forKey:(id)kCGImagePropertyGPSLongitude];
        }
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/gps/{ushort=3}", &propertyValue)) && propertyValue.vt == VT_LPSTR) {
        [gpsProperties setObject:[NSString stringWithUTF8String:propertyValue.pszVal] forKey:(id)kCGImagePropertyGPSLongitudeRef];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/gps/{ushort=13}", &propertyValue)) && propertyValue.vt == VT_UI8) {
        [gpsProperties setObject:[NSNumber numberWithDouble:(double)propertyValue.uhVal.LowPart / propertyValue.uhVal.HighPart]
                          forKey:(id)kCGImagePropertyGPSSpeed];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/gps/{ushort=12}", &propertyValue)) && propertyValue.vt == VT_LPSTR) {
        [gpsProperties setObject:[NSString stringWithUTF8String:propertyValue.pszVal] forKey:(id)kCGImagePropertyGPSSpeedRef];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/gps/{ushort=7}", &propertyValue)) &&
        propertyValue.vt == (VT_VECTOR | VT_UI8)) {
        double timeStampHours = (double)propertyValue.cauh.pElems[0].LowPart / propertyValue.cauh.pElems[0].HighPart;
        double timeStampMinutes = (double)propertyValue.cauh.pElems[1].LowPart / propertyValue.cauh.pElems[1].HighPart;
        double timeStampSeconds = (double)propertyValue.cauh.pElems[2].LowPart / propertyValue.cauh.pElems[2].HighPart;
        [gpsProperties setObject:[NSString stringWithFormat:@"%.2d:%.2d:%.2f", (int)timeStampHours, (int)timeStampMinutes, timeStampSeconds]
                          forKey:(id)kCGImagePropertyGPSTimeStamp];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/gps/{ushort=15}", &propertyValue)) && propertyValue.vt == VT_UI8) {
        [gpsProperties setObject:[NSNumber numberWithDouble:(double)propertyValue.uhVal.LowPart / propertyValue.uhVal.HighPart]
                          forKey:(id)kCGImagePropertyGPSTrack];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/gps/{ushort=14}", &propertyValue)) && propertyValue.vt == VT_LPSTR) {
        [gpsProperties setObject:[NSString stringWithUTF8String:propertyValue.pszVal] forKey:(id)kCGImagePropertyGPSTrackRef];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/gps/{ushort=0}", &propertyValue)) &&
        propertyValue.vt == (VT_VECTOR | VT_UI1)) {
        if (propertyValue.caub.cElems == 4) {
            NSMutableArray* gpsVersionArray = [NSMutableArray array];
            for (int index = 0; index < 4; index++) {
                [gpsVersionArray addObject:[NSNumber numberWithInt:propertyValue.caub.pElems[index]]];
            }

            [gpsProperties setObject:gpsVersionArray forKey:(id)kCGImagePropertyGPSVersion];
        }
    }

    // Add the GPS dictionary to the properties if there are any GPS properties
    if ([gpsProperties count] != 0) {
        [properties setObject:gpsProperties forKey:(id)kCGImagePropertyGPSDictionary];
    }
    CFRelease(gpsProperties);

    // Exif Properties - JPEG
    NSMutableDictionary* exifProperties = [[NSMutableDictionary alloc] init];
    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/exif/{ushort=40962}", &propertyValue)) && propertyValue.vt == VT_UI2) {
        // iOS can get both the general image dimension properties as well as Exif ones from this value
        [exifProperties setObject:[NSNumber numberWithInt:propertyValue.uiVal] forKey:(id)kCGImagePropertyExifPixelXDimension];
        [properties setObject:[NSNumber numberWithInt:propertyValue.uiVal] forKey:(id)kCGImagePropertyPixelWidth];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/exif/{ushort=40962}", &propertyValue)) && propertyValue.vt == VT_UI4) {
        [exifProperties setObject:[NSNumber numberWithInt:propertyValue.ulVal] forKey:(id)kCGImagePropertyExifPixelXDimension];
        [properties setObject:[NSNumber numberWithInt:propertyValue.ulVal] forKey:(id)kCGImagePropertyPixelWidth];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/exif/{ushort=40963}", &propertyValue)) && propertyValue.vt == VT_UI2) {
        [exifProperties setObject:[NSNumber numberWithInt:propertyValue.uiVal] forKey:(id)kCGImagePropertyExifPixelYDimension];
        [properties setObject:[NSNumber numberWithInt:propertyValue.uiVal] forKey:(id)kCGImagePropertyPixelHeight];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/exif/{ushort=40963}", &propertyValue)) && propertyValue.vt == VT_UI4) {
        [exifProperties setObject:[NSNumber numberWithInt:propertyValue.ulVal] forKey:(id)kCGImagePropertyExifPixelYDimension];
        [properties setObject:[NSNumber numberWithInt:propertyValue.ulVal] forKey:(id)kCGImagePropertyPixelHeight];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/exif/{ushort=33434}", &propertyValue)) && propertyValue.vt == VT_UI8) {
        [exifProperties setObject:[NSNumber numberWithDouble:(double)propertyValue.uhVal.LowPart / propertyValue.uhVal.HighPart]
                           forKey:(id)kCGImagePropertyExifExposureTime];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/exif/{ushort=37378}", &propertyValue)) && propertyValue.vt == VT_UI8) {
        [exifProperties setObject:[NSNumber numberWithDouble:(double)propertyValue.uhVal.LowPart / propertyValue.uhVal.HighPart]
                           forKey:(id)kCGImagePropertyExifApertureValue];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/exif/{ushort=37379}", &propertyValue)) && propertyValue.vt == VT_UI8) {
        [exifProperties setObject:[NSNumber numberWithDouble:(double)propertyValue.uhVal.LowPart / propertyValue.uhVal.HighPart]
                           forKey:(id)kCGImagePropertyExifBrightnessValue];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/exif/{ushort=36868}", &propertyValue)) &&
        propertyValue.vt == VT_LPSTR) {
        [exifProperties setObject:[NSString stringWithUTF8String:propertyValue.pszVal] forKey:(id)kCGImagePropertyExifDateTimeDigitized];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/exif/{ushort=36867}", &propertyValue)) &&
        propertyValue.vt == VT_LPSTR) {
        [exifProperties setObject:[NSString stringWithUTF8String:propertyValue.pszVal] forKey:(id)kCGImagePropertyExifDateTimeOriginal];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/exif/{ushort=41988}", &propertyValue)) && propertyValue.vt == VT_UI8) {
        [exifProperties setObject:[NSNumber numberWithDouble:(double)propertyValue.uhVal.LowPart / propertyValue.uhVal.HighPart]
                           forKey:(id)kCGImagePropertyExifDigitalZoomRatio];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/exif/{ushort=41986}", &propertyValue)) && propertyValue.vt == VT_UI2) {
        [exifProperties setObject:[NSNumber numberWithInt:propertyValue.uiVal] forKey:(id)kCGImagePropertyExifExposureMode];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/exif/{ushort=34850}", &propertyValue)) && propertyValue.vt == VT_UI2) {
        [exifProperties setObject:[NSNumber numberWithInt:propertyValue.uiVal] forKey:(id)kCGImagePropertyExifExposureProgram];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/exif/{ushort=37385}", &propertyValue)) && propertyValue.vt == VT_UI2) {
        [exifProperties setObject:[NSNumber numberWithInt:propertyValue.uiVal] forKey:(id)kCGImagePropertyExifFlash];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/exif/{ushort=33437}", &propertyValue)) && propertyValue.vt == VT_UI8) {
        [exifProperties setObject:[NSNumber numberWithDouble:(double)propertyValue.uhVal.LowPart / propertyValue.uhVal.HighPart]
                           forKey:(id)kCGImagePropertyExifFNumber];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/exif/{ushort=37386}", &propertyValue)) && propertyValue.vt == VT_UI8) {
        [exifProperties setObject:[NSNumber numberWithDouble:(double)propertyValue.uhVal.LowPart / propertyValue.uhVal.HighPart]
                           forKey:(id)kCGImagePropertyExifFocalLength];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/exif/{ushort=34867}", &propertyValue)) && propertyValue.vt == VT_UI4) {
        [exifProperties setObject:[NSNumber numberWithInt:propertyValue.ulVal] forKey:(id)kCGImagePropertyExifISOSpeedRatings];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/exif/{ushort=42035}", &propertyValue)) &&
        propertyValue.vt == VT_LPSTR) {
        [exifProperties setObject:[NSString stringWithUTF8String:propertyValue.pszVal] forKey:(id)kCGImagePropertyExifLensMake];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/exif/{ushort=42036}", &propertyValue)) &&
        propertyValue.vt == VT_LPSTR) {
        [exifProperties setObject:[NSString stringWithUTF8String:propertyValue.pszVal] forKey:(id)kCGImagePropertyExifLensModel];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/exif/{ushort=37500}", &propertyValue)) &&
        propertyValue.vt == VT_BLOB) {
        [exifProperties setObject:[NSData dataWithBytes:propertyValue.blob.pBlobData length:propertyValue.blob.cbSize]
                           forKey:(id)kCGImagePropertyExifMakerNote];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/exif/{ushort=37383}", &propertyValue)) && propertyValue.vt == VT_UI2) {
        [exifProperties setObject:[NSNumber numberWithInt:propertyValue.uiVal] forKey:(id)kCGImagePropertyExifMeteringMode];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/exif/{ushort=41990}", &propertyValue)) && propertyValue.vt == VT_UI2) {
        [exifProperties setObject:[NSNumber numberWithInt:propertyValue.uiVal] forKey:(id)kCGImagePropertyExifSceneCaptureType];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/exif/{ushort=37377}", &propertyValue)) && propertyValue.vt == VT_I8) {
        [exifProperties setObject:[NSNumber numberWithDouble:(double)propertyValue.hVal.LowPart / propertyValue.hVal.HighPart]
                           forKey:(id)kCGImagePropertyExifShutterSpeedValue];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/exif/{ushort=37510}", &propertyValue)) &&
        propertyValue.vt == VT_LPWSTR) {
        [exifProperties setObject:[[NSString alloc] initWithBytes:propertyValue.pwszVal
                                                           length:wcslen(propertyValue.pwszVal) * sizeof(wchar_t)
                                                         encoding:NSUnicodeStringEncoding]
                           forKey:(id)kCGImagePropertyExifUserComment];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/exif/{ushort=36864}", &propertyValue)) &&
        propertyValue.vt == VT_BLOB) {
        NSData* exifVersion = [NSData dataWithBytesNoCopy:propertyValue.blob.pBlobData length:propertyValue.blob.cbSize freeWhenDone:YES];
        NSMutableArray* exifVersionArray = [NSMutableArray array];
        char* exifVersionCharacters = (char*)[exifVersion bytes];
        [exifVersionArray addObject:[NSNumber numberWithInt:exifVersionCharacters[1] - '0']];
        [exifVersionArray addObject:[NSNumber numberWithInt:exifVersionCharacters[2] - '0']];
        [exifVersionArray addObject:[NSNumber numberWithInt:exifVersionCharacters[3] - '0']];
        [exifProperties setObject:exifVersionArray forKey:(id)kCGImagePropertyExifVersion];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/exif/{ushort=41987}", &propertyValue)) && propertyValue.vt == VT_UI2) {
        [exifProperties setObject:[NSNumber numberWithInt:propertyValue.uiVal] forKey:(id)kCGImagePropertyExifWhiteBalance];
    }

    // Add the Exif dictionary to the properties if there are any Exif properties
    if ([exifProperties count] != 0) {
        [properties setObject:exifProperties forKey:(id)kCGImagePropertyExifDictionary];
    }
    CFRelease(exifProperties);

    // The following properties are in TIFF property dictionary, but for /app1/ifd/, which is not a TIFF directory
    // This information gets read for JPEG files on iOS, and do in fact get added to a TIFF dictionary, even for a JPEG
    NSMutableDictionary* tiffProperties = [[NSMutableDictionary alloc] init];
    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/{ushort=274}", &propertyValue)) && propertyValue.vt == VT_UI2) {
        [tiffProperties setObject:[NSNumber numberWithInt:propertyValue.uiVal] forKey:(id)kCGImagePropertyTIFFOrientation];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/{ushort=282}", &propertyValue)) && propertyValue.vt == VT_UI8) {
        [tiffProperties setObject:[NSNumber numberWithDouble:(double)propertyValue.uhVal.LowPart / propertyValue.uhVal.HighPart]
                           forKey:(id)kCGImagePropertyTIFFXResolution];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/{ushort=283}", &propertyValue)) && propertyValue.vt == VT_UI8) {
        [tiffProperties setObject:[NSNumber numberWithDouble:(double)propertyValue.uhVal.LowPart / propertyValue.uhVal.HighPart]
                           forKey:(id)kCGImagePropertyTIFFYResolution];
    }

    PropVariantClear(&propertyValue);
    if (SUCCEEDED(imageMetadataReader->GetMetadataByName(L"/app1/ifd/{ushort=296}", &propertyValue)) && propertyValue.vt == VT_UI2) {
        [tiffProperties setObject:[NSNumber numberWithInt:propertyValue.uiVal] forKey:(id)kCGImagePropertyTIFFResolutionUnit];
    }

    // Add the TIFF dictionary to the properties if there are any TIFF properties
    if ([tiffProperties count] != 0) {
        [properties setObject:tiffProperties forKey:(id)kCGImagePropertyTIFFDictionary];
    }
    CFRelease(tiffProperties);

    return (CFDictionaryRef)properties;
}