public virtual void TestToString()
 {
     Com.Drew.Metadata.Metadata metadata = new Com.Drew.Metadata.Metadata();
     Sharpen.Tests.AreEqual("Metadata (0 directories)", Sharpen.Extensions.ConvertToString(metadata));
     metadata.AddDirectory(new ExifIFD0Directory());
     Sharpen.Tests.AreEqual("Metadata (1 directory)", Sharpen.Extensions.ConvertToString(metadata));
     metadata.AddDirectory(new ExifSubIFDDirectory());
     Sharpen.Tests.AreEqual("Metadata (2 directories)", Sharpen.Extensions.ConvertToString(metadata));
 }
 public virtual void TestToString()
 {
     Com.Drew.Metadata.Metadata metadata = new Com.Drew.Metadata.Metadata();
     Sharpen.Tests.AreEqual("Metadata (0 directories)", Sharpen.Extensions.ConvertToString(metadata));
     metadata.AddDirectory(new ExifIFD0Directory());
     Sharpen.Tests.AreEqual("Metadata (1 directory)", Sharpen.Extensions.ConvertToString(metadata));
     metadata.AddDirectory(new ExifSubIFDDirectory());
     Sharpen.Tests.AreEqual("Metadata (2 directories)", Sharpen.Extensions.ConvertToString(metadata));
 }
Exemple #3
0
        public virtual void Extract(sbyte[] segmentBytes, Com.Drew.Metadata.Metadata metadata, JpegSegmentType segmentType)
        {
            JpegDirectory directory = new JpegDirectory();

            metadata.AddDirectory(directory);
            // The value of TAG_COMPRESSION_TYPE is determined by the segment type found
            directory.SetInt(JpegDirectory.TagCompressionType, segmentType.byteValue - JpegSegmentType.Sof0.byteValue);
            SequentialReader reader = new SequentialByteArrayReader(segmentBytes);

            try
            {
                directory.SetInt(JpegDirectory.TagDataPrecision, reader.GetUInt8());
                directory.SetInt(JpegDirectory.TagImageHeight, reader.GetUInt16());
                directory.SetInt(JpegDirectory.TagImageWidth, reader.GetUInt16());
                short componentCount = reader.GetUInt8();
                directory.SetInt(JpegDirectory.TagNumberOfComponents, componentCount);
                // for each component, there are three bytes of data:
                // 1 - Component ID: 1 = Y, 2 = Cb, 3 = Cr, 4 = I, 5 = Q
                // 2 - Sampling factors: bit 0-3 vertical, 4-7 horizontal
                // 3 - Quantization table number
                for (int i = 0; i < (int)componentCount; i++)
                {
                    int           componentId             = reader.GetUInt8();
                    int           samplingFactorByte      = reader.GetUInt8();
                    int           quantizationTableNumber = reader.GetUInt8();
                    JpegComponent component = new JpegComponent(componentId, samplingFactorByte, quantizationTableNumber);
                    directory.SetObject(JpegDirectory.TagComponentData1 + i, component);
                }
            }
            catch (IOException ex)
            {
                directory.AddError(ex.Message);
            }
        }
 public virtual void TestHasErrors()
 {
     ExifSubIFDDirectory directory = new ExifSubIFDDirectory();
     directory.AddError("Test Error 1");
     Com.Drew.Metadata.Metadata metadata = new Com.Drew.Metadata.Metadata();
     Sharpen.Tests.IsFalse(metadata.HasErrors());
     metadata.AddDirectory(directory);
     Sharpen.Tests.IsTrue(metadata.HasErrors());
 }
Exemple #5
0
 public virtual void ReadJpegSegments([NotNull] Iterable <sbyte[]> segments, [NotNull] Com.Drew.Metadata.Metadata metadata, [NotNull] JpegSegmentType segmentType)
 {
     foreach (sbyte[] segmentBytes in segments)
     {
         JpegCommentDirectory directory = new JpegCommentDirectory();
         metadata.AddDirectory(directory);
         // The entire contents of the directory are the comment
         directory.SetString(JpegCommentDirectory.TagComment, Sharpen.Runtime.GetStringForBytes(segmentBytes));
     }
 }
        public virtual void TestHasErrors()
        {
            ExifSubIFDDirectory directory = new ExifSubIFDDirectory();

            directory.AddError("Test Error 1");
            Com.Drew.Metadata.Metadata metadata = new Com.Drew.Metadata.Metadata();
            Sharpen.Tests.IsFalse(metadata.HasErrors());
            metadata.AddDirectory(directory);
            Sharpen.Tests.IsTrue(metadata.HasErrors());
        }
Exemple #7
0
 public virtual void ProcessChunk([NotNull] string fourCC, [NotNull] sbyte[] payload)
 {
     //        System.out.println("Chunk " + fourCC + " " + payload.length + " bytes");
     if (fourCC.Equals("EXIF"))
     {
         new ExifReader().Extract(new ByteArrayReader(payload), _metadata);
     }
     else
     {
         if (fourCC.Equals("ICCP"))
         {
             new IccReader().Extract(new ByteArrayReader(payload), _metadata);
         }
         else
         {
             if (fourCC.Equals("XMP "))
             {
                 new XmpReader().Extract(payload, _metadata);
             }
             else
             {
                 if (fourCC.Equals("VP8X") && payload.Length == 10)
                 {
                     RandomAccessReader reader = new ByteArrayReader(payload);
                     reader.SetMotorolaByteOrder(false);
                     try
                     {
                         // Flags
                         //                boolean hasFragments = reader.getBit(0);
                         bool isAnimation = reader.GetBit(1);
                         //                boolean hasXmp = reader.getBit(2);
                         //                boolean hasExif = reader.getBit(3);
                         bool hasAlpha = reader.GetBit(4);
                         //                boolean hasIcc = reader.getBit(5);
                         // Image size
                         int           widthMinusOne  = reader.GetInt24(4);
                         int           heightMinusOne = reader.GetInt24(7);
                         WebpDirectory directory      = new WebpDirectory();
                         directory.SetInt(WebpDirectory.TagImageWidth, widthMinusOne + 1);
                         directory.SetInt(WebpDirectory.TagImageHeight, heightMinusOne + 1);
                         directory.SetBoolean(WebpDirectory.TagHasAlpha, hasAlpha);
                         directory.SetBoolean(WebpDirectory.TagIsAnimation, isAnimation);
                         _metadata.AddDirectory(directory);
                     }
                     catch (IOException e)
                     {
                         Sharpen.Runtime.PrintStackTrace(e, System.Console.Error);
                     }
                 }
             }
         }
     }
 }
Exemple #8
0
        public virtual void Extract([NotNull] SequentialReader reader, [NotNull] Com.Drew.Metadata.Metadata metadata)
        {
            reader.SetMotorolaByteOrder(false);
            PcxDirectory directory = new PcxDirectory();

            metadata.AddDirectory(directory);
            try
            {
                sbyte identifier = reader.GetInt8();
                if (identifier != unchecked ((int)(0x0A)))
                {
                    throw new ImageProcessingException("Invalid PCX identifier byte");
                }
                directory.SetInt(PcxDirectory.TagVersion, reader.GetInt8());
                sbyte encoding = reader.GetInt8();
                if (encoding != unchecked ((int)(0x01)))
                {
                    throw new ImageProcessingException("Invalid PCX encoding byte");
                }
                directory.SetInt(PcxDirectory.TagBitsPerPixel, reader.GetUInt8());
                directory.SetInt(PcxDirectory.TagXmin, reader.GetUInt16());
                directory.SetInt(PcxDirectory.TagYmin, reader.GetUInt16());
                directory.SetInt(PcxDirectory.TagXmax, reader.GetUInt16());
                directory.SetInt(PcxDirectory.TagYmax, reader.GetUInt16());
                directory.SetInt(PcxDirectory.TagHorizontalDpi, reader.GetUInt16());
                directory.SetInt(PcxDirectory.TagVerticalDpi, reader.GetUInt16());
                directory.SetByteArray(PcxDirectory.TagPalette, reader.GetBytes(48));
                reader.Skip(1);
                directory.SetInt(PcxDirectory.TagColorPlanes, reader.GetUInt8());
                directory.SetInt(PcxDirectory.TagBytesPerLine, reader.GetUInt16());
                int paletteType = reader.GetUInt16();
                if (paletteType != 0)
                {
                    directory.SetInt(PcxDirectory.TagPaletteType, paletteType);
                }
                int hScrSize = reader.GetUInt16();
                if (hScrSize != 0)
                {
                    directory.SetInt(PcxDirectory.TagHscrSize, hScrSize);
                }
                int vScrSize = reader.GetUInt16();
                if (vScrSize != 0)
                {
                    directory.SetInt(PcxDirectory.TagVscrSize, vScrSize);
                }
            }
            catch (Exception ex)
            {
                directory.AddError("Exception reading PCX file metadata: " + ex.Message);
            }
        }
Exemple #9
0
 protected internal DirectoryTiffHandler(Com.Drew.Metadata.Metadata metadata, Type initialDirectoryClass)
 {
     _metadata = metadata;
     try
     {
         _currentDirectory = (Directory)System.Activator.CreateInstance(initialDirectoryClass);
     }
     catch (InstantiationException e)
     {
         throw new RuntimeException(e);
     }
     catch (MemberAccessException e)
     {
         throw new RuntimeException(e);
     }
     _metadata.AddDirectory(_currentDirectory);
 }
Exemple #10
0
 protected internal DirectoryTiffHandler(Com.Drew.Metadata.Metadata metadata, Type initialDirectoryClass)
 {
     _metadata = metadata;
     try
     {
         _currentDirectory = (Directory)System.Activator.CreateInstance(initialDirectoryClass);
     }
     catch (InstantiationException e)
     {
         throw new RuntimeException(e);
     }
     catch (MemberAccessException e)
     {
         throw new RuntimeException(e);
     }
     _metadata.AddDirectory(_currentDirectory);
 }
Exemple #11
0
 protected internal virtual void PushDirectory([NotNull] Type directoryClass)
 {
     _directoryStack.Push(_currentDirectory);
     try
     {
         _currentDirectory = (Directory)System.Activator.CreateInstance(directoryClass);
     }
     catch (InstantiationException e)
     {
         throw new RuntimeException(e);
     }
     catch (MemberAccessException e)
     {
         throw new RuntimeException(e);
     }
     _metadata.AddDirectory(_currentDirectory);
 }
        /// <summary>
        /// Performs the XMP data extraction, adding found values to the specified instance of
        /// <see cref="Com.Drew.Metadata.Metadata"/>
        /// .
        /// <p>
        /// The extraction is done with Adobe's XMPCore library.
        /// </summary>
        public virtual void Extract([NotNull] string xmpString, [NotNull] Com.Drew.Metadata.Metadata metadata)
        {
            XmpDirectory directory = new XmpDirectory();

            try
            {
                XMPMeta xmpMeta = XMPMetaFactory.ParseFromString(xmpString);
                ProcessXmpTags(directory, xmpMeta);
            }
            catch (XMPException e)
            {
                directory.AddError("Error processing XMP data: " + e.Message);
            }
            if (!directory.IsEmpty())
            {
                metadata.AddDirectory(directory);
            }
        }
Exemple #13
0
        /// <exception cref="System.IO.IOException"/>
        public virtual void Read([NotNull] FilePath file, [NotNull] Com.Drew.Metadata.Metadata metadata)
        {
            if (!file.IsFile())
            {
                throw new IOException("File object must reference a file");
            }
            if (!file.Exists())
            {
                throw new IOException("File does not exist");
            }
            if (!file.CanRead())
            {
                throw new IOException("File is not readable");
            }
            FileMetadataDirectory directory = new FileMetadataDirectory();

            directory.SetString(FileMetadataDirectory.TagFileName, file.GetName());
            directory.SetLong(FileMetadataDirectory.TagFileSize, file.Length());
            directory.SetDate(FileMetadataDirectory.TagFileModifiedDate, Sharpen.Extensions.CreateDate(file.LastModified()));
            metadata.AddDirectory(directory);
        }
Exemple #14
0
 public virtual void Extract([NotNull] SequentialReader reader, [NotNull] Com.Drew.Metadata.Metadata metadata)
 {
     Com.Drew.Metadata.Directory directory = new AdobeJpegDirectory();
     metadata.AddDirectory(directory);
     try
     {
         reader.SetMotorolaByteOrder(false);
         if (!reader.GetString(Preamble.Length).Equals(Preamble))
         {
             directory.AddError("Invalid Adobe JPEG data header.");
             return;
         }
         directory.SetInt(AdobeJpegDirectory.TagDctEncodeVersion, reader.GetUInt16());
         directory.SetInt(AdobeJpegDirectory.TagApp14Flags0, reader.GetUInt16());
         directory.SetInt(AdobeJpegDirectory.TagApp14Flags1, reader.GetUInt16());
         directory.SetInt(AdobeJpegDirectory.TagColorTransform, reader.GetInt8());
     }
     catch (IOException ex)
     {
         directory.AddError("IO exception processing data: " + ex.Message);
     }
 }
Exemple #15
0
        public virtual void Extract([NotNull] SequentialReader reader, [NotNull] Com.Drew.Metadata.Metadata metadata)
        {
            PsdHeaderDirectory directory = new PsdHeaderDirectory();

            metadata.AddDirectory(directory);
            // FILE HEADER SECTION
            try
            {
                int signature = reader.GetInt32();
                if (signature != unchecked ((int)(0x38425053)))
                {
                    // "8BPS"
                    directory.AddError("Invalid PSD file signature");
                    return;
                }
                int version = reader.GetUInt16();
                if (version != 1 && version != 2)
                {
                    directory.AddError("Invalid PSD file version (must be 1 or 2)");
                    return;
                }
                // 6 reserved bytes are skipped here.  They should be zero.
                reader.Skip(6);
                int channelCount = reader.GetUInt16();
                directory.SetInt(PsdHeaderDirectory.TagChannelCount, channelCount);
                // even though this is probably an unsigned int, the max height in practice is 300,000
                int imageHeight = reader.GetInt32();
                directory.SetInt(PsdHeaderDirectory.TagImageHeight, imageHeight);
                // even though this is probably an unsigned int, the max width in practice is 300,000
                int imageWidth = reader.GetInt32();
                directory.SetInt(PsdHeaderDirectory.TagImageWidth, imageWidth);
                int bitsPerChannel = reader.GetUInt16();
                directory.SetInt(PsdHeaderDirectory.TagBitsPerChannel, bitsPerChannel);
                int colorMode = reader.GetUInt16();
                directory.SetInt(PsdHeaderDirectory.TagColorMode, colorMode);
            }
            catch (IOException)
            {
                directory.AddError("Unable to read PSD header");
                return;
            }
            // COLOR MODE DATA SECTION
            try
            {
                long sectionLength = reader.GetUInt32();

                /*
                 * Only indexed color and duotone (see the mode field in the File header section) have color mode data.
                 * For all other modes, this section is just the 4-byte length field, which is set to zero.
                 *
                 * Indexed color images: length is 768; color data contains the color table for the image,
                 *                       in non-interleaved order.
                 * Duotone images: color data contains the duotone specification (the format of which is not documented).
                 *                 Other applications that read Photoshop files can treat a duotone image as a gray	image,
                 *                 and just preserve the contents of the duotone information when reading and writing the
                 *                 file.
                 */
                reader.Skip(sectionLength);
            }
            catch (IOException)
            {
                return;
            }
            // IMAGE RESOURCES SECTION
            try
            {
                long sectionLength = reader.GetUInt32();
                System.Diagnostics.Debug.Assert((sectionLength <= int.MaxValue));
                new PhotoshopReader().Extract(reader, (int)sectionLength, metadata);
            }
            catch (IOException)
            {
            }
        }
        /// <summary>
        /// Performs the IPTC data extraction, adding found values to the specified instance of
        /// <see cref="Com.Drew.Metadata.Metadata"/>
        /// .
        /// </summary>
        public virtual void Extract([NotNull] SequentialReader reader, [NotNull] Com.Drew.Metadata.Metadata metadata, long length)
        {
            IptcDirectory directory = new IptcDirectory();

            metadata.AddDirectory(directory);
            int offset = 0;

            // for each tag
            while (offset < length)
            {
                // identifies start of a tag
                short startByte;
                try
                {
                    startByte = reader.GetUInt8();
                    offset++;
                }
                catch (IOException)
                {
                    directory.AddError("Unable to read starting byte of IPTC tag");
                    return;
                }
                if (startByte != unchecked ((int)(0x1c)))
                {
                    // NOTE have seen images where there was one extra byte at the end, giving
                    // offset==length at this point, which is not worth logging as an error.
                    if (offset != length)
                    {
                        directory.AddError("Invalid IPTC tag marker at offset " + (offset - 1) + ". Expected '0x1c' but got '0x" + Sharpen.Extensions.ToHexString(startByte) + "'.");
                    }
                    return;
                }
                // we need at least five bytes left to read a tag
                if (offset + 5 >= length)
                {
                    directory.AddError("Too few bytes remain for a valid IPTC tag");
                    return;
                }
                int directoryType;
                int tagType;
                int tagByteCount;
                try
                {
                    directoryType = reader.GetUInt8();
                    tagType       = reader.GetUInt8();
                    // TODO support Extended DataSet Tag (see 1.5(c), p14, IPTC-IIMV4.2.pdf)
                    tagByteCount = reader.GetUInt16();
                    offset      += 4;
                }
                catch (IOException)
                {
                    directory.AddError("IPTC data segment ended mid-way through tag descriptor");
                    return;
                }
                if (offset + tagByteCount > length)
                {
                    directory.AddError("Data for tag extends beyond end of IPTC segment");
                    return;
                }
                try
                {
                    ProcessTag(reader, directory, directoryType, tagType, tagByteCount);
                }
                catch (IOException)
                {
                    directory.AddError("Error processing IPTC tag");
                    return;
                }
                offset += tagByteCount;
            }
        }
Exemple #17
0
        public virtual void Extract([NotNull] SequentialReader reader, [NotNull] Com.Drew.Metadata.Metadata metadata)
        {
            BmpHeaderDirectory directory = new BmpHeaderDirectory();

            metadata.AddDirectory(directory);
            // FILE HEADER
            //
            // 2 - magic number (0x42 0x4D = "BM")
            // 4 - size of BMP file in bytes
            // 2 - reserved
            // 2 - reserved
            // 4 - the offset of the pixel array
            //
            // BITMAP INFORMATION HEADER
            //
            // The first four bytes of the header give the size, which is a discriminator of the actual header format.
            // See this for more information http://en.wikipedia.org/wiki/BMP_file_format
            //
            // BITMAPINFOHEADER (size = 40)
            //
            // 4 - size of header
            // 4 - pixel width (signed)
            // 4 - pixel height (signed)
            // 2 - number of colour planes (must be set to 1)
            // 2 - number of bits per pixel
            // 4 - compression being used (needs decoding)
            // 4 - pixel data length (not total file size, just pixel array)
            // 4 - horizontal resolution, pixels/meter (signed)
            // 4 - vertical resolution, pixels/meter (signed)
            // 4 - number of colours in the palette (0 means no palette)
            // 4 - number of important colours (generally ignored)
            //
            // BITMAPCOREHEADER (size = 12)
            //
            // 4 - size of header
            // 2 - pixel width
            // 2 - pixel height
            // 2 - number of colour planes (must be set to 1)
            // 2 - number of bits per pixel
            //
            // COMPRESSION VALUES
            //
            // 0 = None
            // 1 = RLE 8-bit/pixel
            // 2 = RLE 4-bit/pixel
            // 3 = Bit field (or Huffman 1D if BITMAPCOREHEADER2 (size 64))
            // 4 = JPEG (or RLE-24 if BITMAPCOREHEADER2 (size 64))
            // 5 = PNG
            // 6 = Bit field
            reader.SetMotorolaByteOrder(false);
            try
            {
                int magicNumber = reader.GetUInt16();
                if (magicNumber != unchecked ((int)(0x4D42)))
                {
                    directory.AddError("Invalid BMP magic number");
                    return;
                }
                // skip past the rest of the file header
                reader.Skip(4 + 2 + 2 + 4);
                int headerSize = reader.GetInt32();
                directory.SetInt(BmpHeaderDirectory.TagHeaderSize, headerSize);
                // We expect the header size to be either 40 (BITMAPINFOHEADER) or 12 (BITMAPCOREHEADER)
                if (headerSize == 40)
                {
                    // BITMAPINFOHEADER
                    directory.SetInt(BmpHeaderDirectory.TagImageWidth, reader.GetInt32());
                    directory.SetInt(BmpHeaderDirectory.TagImageHeight, reader.GetInt32());
                    directory.SetInt(BmpHeaderDirectory.TagColourPlanes, reader.GetInt16());
                    directory.SetInt(BmpHeaderDirectory.TagBitsPerPixel, reader.GetInt16());
                    directory.SetInt(BmpHeaderDirectory.TagCompression, reader.GetInt32());
                    // skip the pixel data length
                    reader.Skip(4);
                    directory.SetInt(BmpHeaderDirectory.TagXPixelsPerMeter, reader.GetInt32());
                    directory.SetInt(BmpHeaderDirectory.TagYPixelsPerMeter, reader.GetInt32());
                    directory.SetInt(BmpHeaderDirectory.TagPaletteColourCount, reader.GetInt32());
                    directory.SetInt(BmpHeaderDirectory.TagImportantColourCount, reader.GetInt32());
                }
                else
                {
                    if (headerSize == 12)
                    {
                        directory.SetInt(BmpHeaderDirectory.TagImageWidth, reader.GetInt16());
                        directory.SetInt(BmpHeaderDirectory.TagImageHeight, reader.GetInt16());
                        directory.SetInt(BmpHeaderDirectory.TagColourPlanes, reader.GetInt16());
                        directory.SetInt(BmpHeaderDirectory.TagBitsPerPixel, reader.GetInt16());
                    }
                    else
                    {
                        directory.AddError("Unexpected DIB header size: " + headerSize);
                    }
                }
            }
            catch (IOException)
            {
                directory.AddError("Unable to read BMP header");
            }
        }
Exemple #18
0
        public virtual void Extract([NotNull] SequentialReader reader, [NotNull] Com.Drew.Metadata.Metadata metadata)
        {
            GifHeaderDirectory directory = new GifHeaderDirectory();

            metadata.AddDirectory(directory);
            // FILE HEADER
            //
            // 3 - signature: "GIF"
            // 3 - version: either "87a" or "89a"
            //
            // LOGICAL SCREEN DESCRIPTOR
            //
            // 2 - pixel width
            // 2 - pixel height
            // 1 - screen and color map information flags (0 is LSB)
            //       0-2  Size of the global color table
            //       3    Color table sort flag (89a only)
            //       4-6  Color resolution
            //       7    Global color table flag
            // 1 - background color index
            // 1 - pixel aspect ratio
            reader.SetMotorolaByteOrder(false);
            try
            {
                string signature = reader.GetString(3);
                if (!signature.Equals("GIF"))
                {
                    directory.AddError("Invalid GIF file signature");
                    return;
                }
                string version = reader.GetString(3);
                if (!version.Equals(Gif87aVersionIdentifier) && !version.Equals(Gif89aVersionIdentifier))
                {
                    directory.AddError("Unexpected GIF version");
                    return;
                }
                directory.SetString(GifHeaderDirectory.TagGifFormatVersion, version);
                directory.SetInt(GifHeaderDirectory.TagImageWidth, reader.GetUInt16());
                directory.SetInt(GifHeaderDirectory.TagImageHeight, reader.GetUInt16());
                short flags = reader.GetUInt8();
                // First three bits = (BPP - 1)
                int colorTableSize = 1 << ((flags & 7) + 1);
                directory.SetInt(GifHeaderDirectory.TagColorTableSize, colorTableSize);
                if (version.Equals(Gif89aVersionIdentifier))
                {
                    bool isColorTableSorted = (flags & 8) != 0;
                    directory.SetBoolean(GifHeaderDirectory.TagIsColorTableSorted, isColorTableSorted);
                }
                int bitsPerPixel = ((flags & unchecked ((int)(0x70))) >> 4) + 1;
                directory.SetInt(GifHeaderDirectory.TagBitsPerPixel, bitsPerPixel);
                bool hasGlobalColorTable = (flags & unchecked ((int)(0xf))) != 0;
                directory.SetBoolean(GifHeaderDirectory.TagHasGlobalColorTable, hasGlobalColorTable);
                directory.SetInt(GifHeaderDirectory.TagTransparentColorIndex, reader.GetUInt8());
                int aspectRatioByte = reader.GetUInt8();
                if (aspectRatioByte != 0)
                {
                    float pixelAspectRatio = (float)((aspectRatioByte + 15d) / 64d);
                    directory.SetFloat(GifHeaderDirectory.TagPixelAspectRatio, pixelAspectRatio);
                }
            }
            catch (IOException)
            {
                directory.AddError("Unable to read BMP header");
            }
        }
        public virtual void Extract([NotNull] SequentialReader reader, int length, [NotNull] Com.Drew.Metadata.Metadata metadata)
        {
            PhotoshopDirectory directory = new PhotoshopDirectory();

            metadata.AddDirectory(directory);
            // Data contains a sequence of Image Resource Blocks (IRBs):
            //
            // 4 bytes - Signature "8BIM"
            // 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
            int pos = 0;

            while (pos < length)
            {
                try
                {
                    // 4 bytes for the signature.  Should always be "8BIM".
                    string signature = reader.GetString(4);
                    if (!signature.Equals("8BIM"))
                    {
                        throw new ImageProcessingException("Expecting 8BIM marker");
                    }
                    pos += 4;
                    // 2 bytes for the resource identifier (tag type).
                    int tagType = reader.GetUInt16();
                    // segment type
                    pos += 2;
                    // A variable number of bytes holding a pascal string (two leading bytes for length).
                    short descriptionLength = reader.GetUInt8();
                    pos += 1;
                    // Some basic bounds checking
                    if (descriptionLength < 0 || 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.
                    int byteCount = reader.GetInt32();
                    pos += 4;
                    // The resource data.
                    sbyte[] 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++;
                    }
                    if (tagType == PhotoshopDirectory.TagIptc)
                    {
                        new IptcReader().Extract(new SequentialByteArrayReader(tagBytes), metadata, tagBytes.Length);
                    }
                    else
                    {
                        if (tagType == PhotoshopDirectory.TagIccProfileBytes)
                        {
                            new IccReader().Extract(new ByteArrayReader(tagBytes), metadata);
                        }
                        else
                        {
                            if (tagType == PhotoshopDirectory.TagExifData1 || tagType == PhotoshopDirectory.TagExifData3)
                            {
                                new ExifReader().Extract(new ByteArrayReader(tagBytes), metadata);
                            }
                            else
                            {
                                if (tagType == PhotoshopDirectory.TagXmpData)
                                {
                                    new XmpReader().Extract(tagBytes, metadata);
                                }
                                else
                                {
                                    directory.SetByteArray(tagType, tagBytes);
                                }
                            }
                        }
                    }
                    if (tagType >= unchecked ((int)(0x0fa0)) && tagType <= unchecked ((int)(0x1387)))
                    {
                        PhotoshopDirectory._tagNameMap.Put(tagType, Sharpen.Extensions.StringFormat("Plug-in %d Data", tagType - unchecked ((int)(0x0fa0)) + 1));
                    }
                }
                catch (Exception ex)
                {
                    directory.AddError(ex.Message);
                    return;
                }
            }
        }
        /// <exception cref="Com.Drew.Imaging.Png.PngProcessingException"/>
        /// <exception cref="System.IO.IOException"/>
        private static void ProcessChunk([NotNull] Com.Drew.Metadata.Metadata metadata, [NotNull] PngChunk chunk)
        {
            PngChunkType chunkType = chunk.GetChunkType();

            sbyte[] bytes = chunk.GetBytes();
            if (chunkType.Equals(PngChunkType.Ihdr))
            {
                PngHeader    header    = new PngHeader(bytes);
                PngDirectory directory = new PngDirectory(PngChunkType.Ihdr);
                directory.SetInt(PngDirectory.TagImageWidth, header.GetImageWidth());
                directory.SetInt(PngDirectory.TagImageHeight, header.GetImageHeight());
                directory.SetInt(PngDirectory.TagBitsPerSample, header.GetBitsPerSample());
                directory.SetInt(PngDirectory.TagColorType, header.GetColorType().GetNumericValue());
                directory.SetInt(PngDirectory.TagCompressionType, header.GetCompressionType());
                directory.SetInt(PngDirectory.TagFilterMethod, header.GetFilterMethod());
                directory.SetInt(PngDirectory.TagInterlaceMethod, header.GetInterlaceMethod());
                metadata.AddDirectory(directory);
            }
            else
            {
                if (chunkType.Equals(PngChunkType.Plte))
                {
                    PngDirectory directory = new PngDirectory(PngChunkType.Plte);
                    directory.SetInt(PngDirectory.TagPaletteSize, bytes.Length / 3);
                    metadata.AddDirectory(directory);
                }
                else
                {
                    if (chunkType.Equals(PngChunkType.tRNS))
                    {
                        PngDirectory directory = new PngDirectory(PngChunkType.tRNS);
                        directory.SetInt(PngDirectory.TagPaletteHasTransparency, 1);
                        metadata.AddDirectory(directory);
                    }
                    else
                    {
                        if (chunkType.Equals(PngChunkType.sRGB))
                        {
                            int          srgbRenderingIntent = new SequentialByteArrayReader(bytes).GetInt8();
                            PngDirectory directory           = new PngDirectory(PngChunkType.sRGB);
                            directory.SetInt(PngDirectory.TagSrgbRenderingIntent, srgbRenderingIntent);
                            metadata.AddDirectory(directory);
                        }
                        else
                        {
                            if (chunkType.Equals(PngChunkType.cHRM))
                            {
                                PngChromaticities          chromaticities = new PngChromaticities(bytes);
                                PngChromaticitiesDirectory directory      = new PngChromaticitiesDirectory();
                                directory.SetInt(PngChromaticitiesDirectory.TagWhitePointX, chromaticities.GetWhitePointX());
                                directory.SetInt(PngChromaticitiesDirectory.TagWhitePointX, chromaticities.GetWhitePointX());
                                directory.SetInt(PngChromaticitiesDirectory.TagRedX, chromaticities.GetRedX());
                                directory.SetInt(PngChromaticitiesDirectory.TagRedY, chromaticities.GetRedY());
                                directory.SetInt(PngChromaticitiesDirectory.TagGreenX, chromaticities.GetGreenX());
                                directory.SetInt(PngChromaticitiesDirectory.TagGreenY, chromaticities.GetGreenY());
                                directory.SetInt(PngChromaticitiesDirectory.TagBlueX, chromaticities.GetBlueX());
                                directory.SetInt(PngChromaticitiesDirectory.TagBlueY, chromaticities.GetBlueY());
                                metadata.AddDirectory(directory);
                            }
                            else
                            {
                                if (chunkType.Equals(PngChunkType.gAMA))
                                {
                                    int          gammaInt  = new SequentialByteArrayReader(bytes).GetInt32();
                                    PngDirectory directory = new PngDirectory(PngChunkType.gAMA);
                                    directory.SetDouble(PngDirectory.TagGamma, gammaInt / 100000.0);
                                    metadata.AddDirectory(directory);
                                }
                                else
                                {
                                    if (chunkType.Equals(PngChunkType.iCCP))
                                    {
                                        SequentialReader reader      = new SequentialByteArrayReader(bytes);
                                        string           profileName = reader.GetNullTerminatedString(79);
                                        PngDirectory     directory   = new PngDirectory(PngChunkType.iCCP);
                                        directory.SetString(PngDirectory.TagIccProfileName, profileName);
                                        sbyte compressionMethod = reader.GetInt8();
                                        if (compressionMethod == 0)
                                        {
                                            // Only compression method allowed by the spec is zero: deflate
                                            // This assumes 1-byte-per-char, which it is by spec.
                                            int                 bytesLeft         = bytes.Length - profileName.Length - 2;
                                            sbyte[]             compressedProfile = reader.GetBytes(bytesLeft);
                                            InflaterInputStream inflateStream     = new InflaterInputStream(new ByteArrayInputStream(compressedProfile));
                                            new IccReader().Extract(new RandomAccessStreamReader(inflateStream), metadata);
                                            inflateStream.Close();
                                        }
                                        metadata.AddDirectory(directory);
                                    }
                                    else
                                    {
                                        if (chunkType.Equals(PngChunkType.bKGD))
                                        {
                                            PngDirectory directory = new PngDirectory(PngChunkType.bKGD);
                                            directory.SetByteArray(PngDirectory.TagBackgroundColor, bytes);
                                            metadata.AddDirectory(directory);
                                        }
                                        else
                                        {
                                            if (chunkType.Equals(PngChunkType.tEXt))
                                            {
                                                SequentialReader reader        = new SequentialByteArrayReader(bytes);
                                                string           keyword       = reader.GetNullTerminatedString(79);
                                                int    bytesLeft               = bytes.Length - keyword.Length - 1;
                                                string value                   = reader.GetNullTerminatedString(bytesLeft);
                                                IList <KeyValuePair> textPairs = new AList <KeyValuePair>();
                                                textPairs.Add(new KeyValuePair(keyword, value));
                                                PngDirectory directory = new PngDirectory(PngChunkType.iTXt);
                                                directory.SetObject(PngDirectory.TagTextualData, textPairs);
                                                metadata.AddDirectory(directory);
                                            }
                                            else
                                            {
                                                if (chunkType.Equals(PngChunkType.iTXt))
                                                {
                                                    SequentialReader reader            = new SequentialByteArrayReader(bytes);
                                                    string           keyword           = reader.GetNullTerminatedString(79);
                                                    sbyte            compressionFlag   = reader.GetInt8();
                                                    sbyte            compressionMethod = reader.GetInt8();
                                                    string           languageTag       = reader.GetNullTerminatedString(bytes.Length);
                                                    string           translatedKeyword = reader.GetNullTerminatedString(bytes.Length);
                                                    int    bytesLeft = bytes.Length - keyword.Length - 1 - 1 - 1 - languageTag.Length - 1 - translatedKeyword.Length - 1;
                                                    string text      = null;
                                                    if (compressionFlag == 0)
                                                    {
                                                        text = reader.GetNullTerminatedString(bytesLeft);
                                                    }
                                                    else
                                                    {
                                                        if (compressionFlag == 1)
                                                        {
                                                            if (compressionMethod == 0)
                                                            {
                                                                text = StringUtil.FromStream(new InflaterInputStream(new ByteArrayInputStream(bytes, bytes.Length - bytesLeft, bytesLeft)));
                                                            }
                                                            else
                                                            {
                                                                PngDirectory directory = new PngDirectory(PngChunkType.iTXt);
                                                                directory.AddError("Invalid compression method value");
                                                                metadata.AddDirectory(directory);
                                                            }
                                                        }
                                                        else
                                                        {
                                                            PngDirectory directory = new PngDirectory(PngChunkType.iTXt);
                                                            directory.AddError("Invalid compression flag value");
                                                            metadata.AddDirectory(directory);
                                                        }
                                                    }
                                                    if (text != null)
                                                    {
                                                        if (keyword.Equals("XML:com.adobe.xmp"))
                                                        {
                                                            // NOTE in testing images, the XMP has parsed successfully, but we are not extracting tags from it as necessary
                                                            new XmpReader().Extract(text, metadata);
                                                        }
                                                        else
                                                        {
                                                            IList <KeyValuePair> textPairs = new AList <KeyValuePair>();
                                                            textPairs.Add(new KeyValuePair(keyword, text));
                                                            PngDirectory directory = new PngDirectory(PngChunkType.iTXt);
                                                            directory.SetObject(PngDirectory.TagTextualData, textPairs);
                                                            metadata.AddDirectory(directory);
                                                        }
                                                    }
                                                }
                                                else
                                                {
                                                    if (chunkType.Equals(PngChunkType.tIME))
                                                    {
                                                        SequentialByteArrayReader reader = new SequentialByteArrayReader(bytes);
                                                        int year   = reader.GetUInt16();
                                                        int month  = reader.GetUInt8() - 1;
                                                        int day    = reader.GetUInt8();
                                                        int hour   = reader.GetUInt8();
                                                        int minute = reader.GetUInt8();
                                                        int second = reader.GetUInt8();
                                                        Sharpen.Calendar calendar = Sharpen.Calendar.GetInstance(Sharpen.Extensions.GetTimeZone("UTC"));
                                                        //noinspection MagicConstant
                                                        calendar.Set(year, month, day, hour, minute, second);
                                                        PngDirectory directory = new PngDirectory(PngChunkType.tIME);
                                                        directory.SetDate(PngDirectory.TagLastModificationTime, calendar.GetTime());
                                                        metadata.AddDirectory(directory);
                                                    }
                                                    else
                                                    {
                                                        if (chunkType.Equals(PngChunkType.pHYs))
                                                        {
                                                            SequentialByteArrayReader reader = new SequentialByteArrayReader(bytes);
                                                            int          pixelsPerUnitX      = reader.GetInt32();
                                                            int          pixelsPerUnitY      = reader.GetInt32();
                                                            sbyte        unitSpecifier       = reader.GetInt8();
                                                            PngDirectory directory           = new PngDirectory(PngChunkType.pHYs);
                                                            directory.SetInt(PngDirectory.TagPixelsPerUnitX, pixelsPerUnitX);
                                                            directory.SetInt(PngDirectory.TagPixelsPerUnitY, pixelsPerUnitY);
                                                            directory.SetInt(PngDirectory.TagUnitSpecifier, unitSpecifier);
                                                            metadata.AddDirectory(directory);
                                                        }
                                                        else
                                                        {
                                                            if (chunkType.Equals(PngChunkType.sBIT))
                                                            {
                                                                PngDirectory directory = new PngDirectory(PngChunkType.sBIT);
                                                                directory.SetByteArray(PngDirectory.TagSignificantBits, bytes);
                                                                metadata.AddDirectory(directory);
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        public virtual void Extract([NotNull] SequentialReader reader, [NotNull] Com.Drew.Metadata.Metadata metadata)
        {
            reader.SetMotorolaByteOrder(false);
            int type;
            int imageCount;

            // Read header (ICONDIR structure)
            try
            {
                int reserved = reader.GetUInt16();
                if (reserved != 0)
                {
                    IcoDirectory directory = new IcoDirectory();
                    directory.AddError("Invalid header bytes");
                    metadata.AddDirectory(directory);
                    return;
                }
                type = reader.GetUInt16();
                if (type != 1 && type != 2)
                {
                    IcoDirectory directory = new IcoDirectory();
                    directory.AddError("Invalid type " + type + " -- expecting 1 or 2");
                    metadata.AddDirectory(directory);
                    return;
                }
                imageCount = reader.GetUInt16();
                if (imageCount == 0)
                {
                    IcoDirectory directory = new IcoDirectory();
                    directory.AddError("Image count cannot be zero");
                    metadata.AddDirectory(directory);
                    return;
                }
            }
            catch (IOException ex)
            {
                IcoDirectory directory = new IcoDirectory();
                directory.AddError("Exception reading ICO file metadata: " + ex.Message);
                metadata.AddDirectory(directory);
                return;
            }
            // Read each embedded image
            IcoDirectory directory_1 = null;

            try
            {
                for (int imageIndex = 0; imageIndex < imageCount; imageIndex++)
                {
                    directory_1 = new IcoDirectory();
                    metadata.AddDirectory(directory_1);
                    directory_1.SetInt(IcoDirectory.TagImageType, type);
                    directory_1.SetInt(IcoDirectory.TagImageWidth, reader.GetUInt8());
                    directory_1.SetInt(IcoDirectory.TagImageHeight, reader.GetUInt8());
                    directory_1.SetInt(IcoDirectory.TagColourPaletteSize, reader.GetUInt8());
                    // Ignore this byte (normally zero, though .NET's System.Drawing.Icon.Save method writes 255)
                    reader.GetUInt8();
                    if (type == 1)
                    {
                        // Icon
                        directory_1.SetInt(IcoDirectory.TagColourPlanes, reader.GetUInt16());
                        directory_1.SetInt(IcoDirectory.TagBitsPerPixel, reader.GetUInt16());
                    }
                    else
                    {
                        // Cursor
                        directory_1.SetInt(IcoDirectory.TagCursorHotspotX, reader.GetUInt16());
                        directory_1.SetInt(IcoDirectory.TagCursorHotspotY, reader.GetUInt16());
                    }
                    directory_1.SetLong(IcoDirectory.TagImageSizeBytes, reader.GetUInt32());
                    directory_1.SetLong(IcoDirectory.TagImageOffsetBytes, reader.GetUInt32());
                }
            }
            catch (IOException ex)
            {
                System.Diagnostics.Debug.Assert((directory_1 != null));
                directory_1.AddError("Exception reading ICO file metadata: " + ex.Message);
            }
        }