Exemple #1
0
 /// <summary>
 /// Extracts and caches the metadata for an image.
 /// </summary>
 /// <param name="pictureFileInfo">The FileInfo to the image.</param>
 /// <returns>The MetaData for the image.</returns>
 internal static Metadata GetImageData(FileInfo pictureFileInfo)
 {
     try
     {
         string cacheKey = "data(" + pictureFileInfo.FullName + ")";
         Cache  cache    = HttpContext.Current.Cache;
         object cached   = cache[cacheKey];
         if (cached == null)
         {
             Metadata   data = new Metadata();
             ExifReader exif = new ExifReader(pictureFileInfo);
             exif.Extract(data);
             IptcReader iptc = new IptcReader(pictureFileInfo);
             iptc.Extract(data);
             JpegReader jpeg = new JpegReader(pictureFileInfo);
             jpeg.Extract(data);
             cache.Insert(cacheKey, data, new CacheDependency(pictureFileInfo.FullName));
             return(data);
         }
         return((Metadata)cached);
     }
     catch
     {
         return(new Metadata());
     }
 }
Exemple #2
0
        public static IptcDirectory ProcessBytes([NotNull] string filePath)
        {
            var bytes     = File.ReadAllBytes(filePath);
            var directory = new IptcReader().Extract(new SequentialByteArrayReader(bytes), bytes.Length);

            Assert.NotNull(directory);
            return(directory);
        }
        private static IptcDirectory ProcessBytes([NotNull] string filePath)
        {
            var bytes     = TestDataUtil.GetBytes(filePath);
            var directory = new IptcReader().Extract(new SequentialByteArrayReader(bytes), bytes.Length);

            Assert.NotNull(directory);
            return(directory);
        }
        public DirectoryList Extract(SequentialReader reader, int length)
        {
            var directory = new PhotoshopDirectory();

            var directories = new List <Directory> {
                directory
            };

            // Data contains a sequence of Image Resource Blocks (IRBs):
            //
            // 4 bytes - Signature; mostly "8BIM" but "PHUT", "AgHg" and "DCSR" are also found
            // 2 bytes - Resource identifier
            // String  - Pascal string, padded to make length even
            // 4 bytes - Size of resource data which follows
            // Data    - The resource data, padded to make size even
            //
            // http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577409_pgfId-1037504
            var pos = 0;
            int clippingPathCount = 0;

            while (pos < length)
            {
                try
                {
                    // 4 bytes for the signature ("8BIM", "PHUT", etc.)
                    var signature = reader.GetString(4, Encoding.UTF8);
                    pos += 4;

                    // 2 bytes for the resource identifier (tag type).
                    var tagType = reader.GetUInt16();
                    pos += 2;

                    // A variable number of bytes holding a pascal string (two leading bytes for length).
                    var descriptionLength = reader.GetByte();
                    pos += 1;

                    // Some basic bounds checking
                    if (descriptionLength + pos > length)
                    {
                        throw new ImageProcessingException("Invalid string length");
                    }

                    // Get name (important for paths)
                    var description = new StringBuilder();
                    // Loop through each byte and append to string
                    while (descriptionLength > 0)
                    {
                        description.Append((char)reader.GetByte());
                        pos++;
                        descriptionLength--;
                    }

                    // The number of bytes is padded with a trailing zero, if needed, to make the size even.
                    if (pos % 2 != 0)
                    {
                        reader.Skip(1);
                        pos++;
                    }

                    // 4 bytes for the size of the resource data that follows.
                    var byteCount = reader.GetInt32();
                    pos += 4;

                    // The resource data.
                    var tagBytes = reader.GetBytes(byteCount);
                    pos += byteCount;

                    // The number of bytes is padded with a trailing zero, if needed, to make the size even.
                    if (pos % 2 != 0)
                    {
                        reader.Skip(1);
                        pos++;
                    }

                    // Skip any unsupported IRBs
                    if (signature != "8BIM")
                    {
                        continue;
                    }

                    switch (tagType)
                    {
                    case PhotoshopDirectory.TagIptc:
                        var iptcDirectory = new IptcReader().Extract(new SequentialByteArrayReader(tagBytes), tagBytes.Length);
                        iptcDirectory.Parent = directory;
                        directories.Add(iptcDirectory);
                        break;

                    case PhotoshopDirectory.TagIccProfileBytes:
                        var iccDirectory = new IccReader().Extract(new ByteArrayReader(tagBytes));
                        iccDirectory.Parent = directory;
                        directories.Add(iccDirectory);
                        break;

                    case PhotoshopDirectory.TagExifData1:
                    case PhotoshopDirectory.TagExifData3:
                        var exifDirectories = new ExifReader().Extract(new ByteArrayReader(tagBytes));
                        foreach (var exifDirectory in exifDirectories.Where(d => d.Parent == null))
                        {
                            exifDirectory.Parent = directory;
                        }
                        directories.AddRange(exifDirectories);
                        break;

                    case PhotoshopDirectory.TagXmpData:
                        var xmpDirectory = new XmpReader().Extract(tagBytes);
                        xmpDirectory.Parent = directory;
                        directories.Add(xmpDirectory);
                        break;

                    default:
                        if (tagType >= PhotoshopDirectory.TagClippingPathBlockStart && tagType <= PhotoshopDirectory.TagClippingPathBlockEnd)
                        {
                            clippingPathCount++;
                            Array.Resize(ref tagBytes, tagBytes.Length + description.Length + 1);
                            // Append description(name) to end of byte array with 1 byte before the description representing the length
                            for (int i = tagBytes.Length - description.Length - 1; i < tagBytes.Length; i++)
                            {
                                if (i % (tagBytes.Length - description.Length - 1 + description.Length) == 0)
                                {
                                    tagBytes[i] = (byte)description.Length;
                                }
                                else
                                {
                                    tagBytes[i] = (byte)description[i - (tagBytes.Length - description.Length - 1)];
                                }
                            }
                            PhotoshopDirectory.TagNameMap[PhotoshopDirectory.TagClippingPathBlockStart + clippingPathCount - 1] = "Path Info " + clippingPathCount;
                            directory.Set(PhotoshopDirectory.TagClippingPathBlockStart + clippingPathCount - 1, tagBytes);
                        }
                        else
                        {
                            directory.Set(tagType, tagBytes);
                        }
                        break;
                    }

                    if (tagType >= 0x0fa0 && tagType <= 0x1387)
                    {
                        PhotoshopDirectory.TagNameMap[tagType] = $"Plug-in {tagType - 0x0fa0 + 1} Data";
                    }
                }
                catch (Exception ex)
                {
                    directory.AddError(ex.Message);
                    break;
                }
            }

            return(directories);
        }
        public DirectoryList Extract(SequentialReader reader, int length)
        {
            var directory = new PhotoshopDirectory();

            var directories = new List <Directory> {
                directory
            };

            // Data contains a sequence of Image Resource Blocks (IRBs):
            //
            // 4 bytes - Signature; mostly "8BIM" but "PHUT", "AgHg" and "DCSR" are also found
            // 2 bytes - Resource identifier
            // String  - Pascal string, padded to make length even
            // 4 bytes - Size of resource data which follows
            // Data    - The resource data, padded to make size even
            //
            // http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577409_pgfId-1037504
            var pos = 0;

            while (pos < length)
            {
                try
                {
                    // 4 bytes for the signature ("8BIM", "PHUT", etc.)
                    var signature = reader.GetString(4, Encoding.UTF8);
                    pos += 4;

                    // 2 bytes for the resource identifier (tag type).
                    var tagType = reader.GetUInt16();
                    pos += 2;

                    // A variable number of bytes holding a pascal string (two leading bytes for length).
                    var descriptionLength = reader.GetByte();
                    pos += 1;

                    // Some basic bounds checking
                    if (descriptionLength + pos > length)
                    {
                        throw new ImageProcessingException("Invalid string length");
                    }

                    // We don't use the string value here
                    reader.Skip(descriptionLength);
                    pos += descriptionLength;

                    // The number of bytes is padded with a trailing zero, if needed, to make the size even.
                    if (pos % 2 != 0)
                    {
                        reader.Skip(1);
                        pos++;
                    }

                    // 4 bytes for the size of the resource data that follows.
                    var byteCount = reader.GetInt32();
                    pos += 4;

                    // The resource data.
                    var tagBytes = reader.GetBytes(byteCount);
                    pos += byteCount;

                    // The number of bytes is padded with a trailing zero, if needed, to make the size even.
                    if (pos % 2 != 0)
                    {
                        reader.Skip(1);
                        pos++;
                    }

                    // Skip any unsupported IRBs
                    if (signature != "8BIM")
                    {
                        continue;
                    }

                    switch (tagType)
                    {
                    case PhotoshopDirectory.TagIptc:
                        var iptcDirectory = new IptcReader().Extract(new SequentialByteArrayReader(tagBytes), tagBytes.Length);
                        iptcDirectory.Parent = directory;
                        directories.Add(iptcDirectory);
                        break;

                    case PhotoshopDirectory.TagIccProfileBytes:
                        var iccDirectory = new IccReader().Extract(new ByteArrayReader(tagBytes));
                        iccDirectory.Parent = directory;
                        directories.Add(iccDirectory);
                        break;

                    case PhotoshopDirectory.TagExifData1:
                    case PhotoshopDirectory.TagExifData3:
                        var exifDirectories = new ExifReader().Extract(new ByteArrayReader(tagBytes));
                        foreach (var exifDirectory in exifDirectories.Where(d => d.Parent == null))
                        {
                            exifDirectory.Parent = directory;
                        }
                        directories.AddRange(exifDirectories);
                        break;

                    case PhotoshopDirectory.TagXmpData:
                        var xmpDirectory = new XmpReader().Extract(tagBytes);
                        xmpDirectory.Parent = directory;
                        directories.Add(xmpDirectory);
                        break;

                    default:
                        directory.Set(tagType, tagBytes);
                        break;
                    }

                    if (tagType >= 0x0fa0 && tagType <= 0x1387)
                    {
                        PhotoshopDirectory.TagNameMap[tagType] = $"Plug-in {tagType - 0x0fa0 + 1} Data";
                    }
                }
                catch (Exception ex)
                {
                    directory.AddError(ex.Message);
                    break;
                }
            }

            return(directories);
        }
        public override bool CustomProcessTag(int tagOffset, ICollection <int> processedIfdOffsets, IndexedReader reader, int tagId, int byteCount)
        {
            // Some 0x0000 tags have a 0 byteCount. Determine whether it's bad.
            if (tagId == 0)
            {
                if (CurrentDirectory.ContainsTag(tagId))
                {
                    // Let it go through for now. Some directories handle it, some don't.
                    return(false);
                }

                // Skip over 0x0000 tags that don't have any associated bytes. No idea what it contains in this case, if anything.
                if (byteCount == 0)
                {
                    return(true);
                }
            }

            // Custom processing for the Makernote tag
            if (tagId == ExifDirectoryBase.TagMakernote && CurrentDirectory is ExifSubIfdDirectory)
            {
                return(ProcessMakernote(tagOffset, processedIfdOffsets, reader));
            }

            // Custom processing for embedded IPTC data
            if (tagId == ExifDirectoryBase.TagIptcNaa && CurrentDirectory is ExifIfd0Directory)
            {
                // NOTE Adobe sets type 4 for IPTC instead of 7
                if (reader.GetSByte(tagOffset) == 0x1c)
                {
                    var iptcBytes     = reader.GetBytes(tagOffset, byteCount);
                    var iptcDirectory = new IptcReader().Extract(new SequentialByteArrayReader(iptcBytes), iptcBytes.Length);
                    iptcDirectory.Parent = CurrentDirectory;
                    Directories.Add(iptcDirectory);
                    return(true);
                }
                return(false);
            }

            // Custom processing for embedded XMP data
            if (tagId == ExifDirectoryBase.TagApplicationNotes && CurrentDirectory is ExifIfd0Directory)
            {
                var xmpDirectory = new XmpReader().Extract(reader.GetNullTerminatedBytes(tagOffset, byteCount));
                xmpDirectory.Parent = CurrentDirectory;
                Directories.Add(xmpDirectory);
                return(true);
            }

            if (HandlePrintIM(CurrentDirectory, tagId))
            {
                var dirPrintIm = new PrintIMDirectory();
                dirPrintIm.Parent = CurrentDirectory;
                Directories.Add(dirPrintIm);
                ProcessPrintIM(dirPrintIm, tagOffset, reader, byteCount);
                return(true);
            }

            // Note: these also appear in TryEnterSubIfd because some are IFD pointers while others begin immediately
            // for the same directories
            if (CurrentDirectory is OlympusMakernoteDirectory)
            {
                switch (tagId)
                {
                case OlympusMakernoteDirectory.TagEquipment:
                    PushDirectory(new OlympusEquipmentMakernoteDirectory());
                    TiffReader.ProcessIfd(this, reader, processedIfdOffsets, tagOffset);
                    return(true);

                case OlympusMakernoteDirectory.TagCameraSettings:
                    PushDirectory(new OlympusCameraSettingsMakernoteDirectory());
                    TiffReader.ProcessIfd(this, reader, processedIfdOffsets, tagOffset);
                    return(true);

                case OlympusMakernoteDirectory.TagRawDevelopment:
                    PushDirectory(new OlympusRawDevelopmentMakernoteDirectory());
                    TiffReader.ProcessIfd(this, reader, processedIfdOffsets, tagOffset);
                    return(true);

                case OlympusMakernoteDirectory.TagRawDevelopment2:
                    PushDirectory(new OlympusRawDevelopment2MakernoteDirectory());
                    TiffReader.ProcessIfd(this, reader, processedIfdOffsets, tagOffset);
                    return(true);

                case OlympusMakernoteDirectory.TagImageProcessing:
                    PushDirectory(new OlympusImageProcessingMakernoteDirectory());
                    TiffReader.ProcessIfd(this, reader, processedIfdOffsets, tagOffset);
                    return(true);

                case OlympusMakernoteDirectory.TagFocusInfo:
                    PushDirectory(new OlympusFocusInfoMakernoteDirectory());
                    TiffReader.ProcessIfd(this, reader, processedIfdOffsets, tagOffset);
                    return(true);

                case OlympusMakernoteDirectory.TagRawInfo:
                    PushDirectory(new OlympusRawInfoMakernoteDirectory());
                    TiffReader.ProcessIfd(this, reader, processedIfdOffsets, tagOffset);
                    return(true);

                case OlympusMakernoteDirectory.TagMainInfo:
                    PushDirectory(new OlympusMakernoteDirectory());
                    TiffReader.ProcessIfd(this, reader, processedIfdOffsets, tagOffset);
                    return(true);
                }
            }

            if (CurrentDirectory is PanasonicRawIfd0Directory)
            {
                // these contain binary data with specific offsets, and can't be processed as regular ifd's.
                // The binary data is broken into 'fake' tags and there is a pattern.
                switch (tagId)
                {
                case PanasonicRawIfd0Directory.TagWbInfo:
                    var dirWbInfo = new PanasonicRawWbInfoDirectory();
                    dirWbInfo.Parent = CurrentDirectory;
                    Directories.Add(dirWbInfo);
                    ProcessBinary(dirWbInfo, tagOffset, reader, byteCount, false, 2);
                    return(true);

                case PanasonicRawIfd0Directory.TagWbInfo2:
                    var dirWbInfo2 = new PanasonicRawWbInfo2Directory();
                    dirWbInfo2.Parent = CurrentDirectory;
                    Directories.Add(dirWbInfo2);
                    ProcessBinary(dirWbInfo2, tagOffset, reader, byteCount, false, 3);
                    return(true);

                case PanasonicRawIfd0Directory.TagDistortionInfo:
                    var dirDistort = new PanasonicRawDistortionDirectory();
                    dirDistort.Parent = CurrentDirectory;
                    Directories.Add(dirDistort);
                    ProcessBinary(dirDistort, tagOffset, reader, byteCount);
                    return(true);
                }
            }

            // Panasonic RAW sometimes contains an embedded version of the data as a JPG file.
            if (tagId == PanasonicRawIfd0Directory.TagJpgFromRaw && CurrentDirectory is PanasonicRawIfd0Directory)
            {
                var jpegrawbytes = reader.GetBytes(tagOffset, byteCount);

                // Extract information from embedded image since it is metadata-rich
                var jpegmem       = new MemoryStream(jpegrawbytes);
                var jpegDirectory = Jpeg.JpegMetadataReader.ReadMetadata(jpegmem);
                foreach (var dir in jpegDirectory)
                {
                    dir.Parent = CurrentDirectory;
                    Directories.Add(dir);
                }
                return(true);
            }

            return(false);
        }