Example #1
0
        public virtual void Extract(sbyte[] segmentBytes, Com.Drew.Metadata.Metadata metadata, JpegSegmentType segmentType)
        {
            JpegCommentDirectory directory = metadata.GetOrCreateDirectory <JpegCommentDirectory>();

            // The entire contents of the directory are the comment
            directory.SetString(JpegCommentDirectory.TagComment, Sharpen.Runtime.GetStringForBytes(segmentBytes));
        }
Example #2
0
        public virtual void Extract(RandomAccessReader reader, Com.Drew.Metadata.Metadata metadata)
        {
            // TODO review whether the 'tagPtr' values below really do require ICC processing to work with a RandomAccessReader
            IccDirectory directory = metadata.GetOrCreateDirectory <IccDirectory>();

            try
            {
                directory.SetInt(IccDirectory.TagProfileByteCount, reader.GetInt32(IccDirectory.TagProfileByteCount));
                // For these tags, the int value of the tag is in fact it's offset within the buffer.
                Set4ByteString(directory, IccDirectory.TagCmmType, reader);
                SetInt32(directory, IccDirectory.TagProfileVersion, reader);
                Set4ByteString(directory, IccDirectory.TagProfileClass, reader);
                Set4ByteString(directory, IccDirectory.TagColorSpace, reader);
                Set4ByteString(directory, IccDirectory.TagProfileConnectionSpace, reader);
                SetDate(directory, IccDirectory.TagProfileDatetime, reader);
                Set4ByteString(directory, IccDirectory.TagSignature, reader);
                Set4ByteString(directory, IccDirectory.TagPlatform, reader);
                SetInt32(directory, IccDirectory.TagCmmFlags, reader);
                Set4ByteString(directory, IccDirectory.TagDeviceMake, reader);
                int temp = reader.GetInt32(IccDirectory.TagDeviceModel);
                if (temp != 0)
                {
                    if (temp <= unchecked ((int)(0x20202020)))
                    {
                        directory.SetInt(IccDirectory.TagDeviceModel, temp);
                    }
                    else
                    {
                        directory.SetString(IccDirectory.TagDeviceModel, GetStringFromInt32(temp));
                    }
                }
                SetInt32(directory, IccDirectory.TagRenderingIntent, reader);
                SetInt64(directory, IccDirectory.TagDeviceAttr, reader);
                float[] xyz = new float[] { reader.GetS15Fixed16(IccDirectory.TagXyzValues), reader.GetS15Fixed16(IccDirectory.TagXyzValues + 4), reader.GetS15Fixed16(IccDirectory.TagXyzValues + 8) };
                directory.SetObject(IccDirectory.TagXyzValues, xyz);
                // Process 'ICC tags'
                int tagCount = reader.GetInt32(IccDirectory.TagTagCount);
                directory.SetInt(IccDirectory.TagTagCount, tagCount);
                for (int i = 0; i < tagCount; i++)
                {
                    int     pos     = IccDirectory.TagTagCount + 4 + i * 12;
                    int     tagType = reader.GetInt32(pos);
                    int     tagPtr  = reader.GetInt32(pos + 4);
                    int     tagLen  = reader.GetInt32(pos + 8);
                    sbyte[] b       = reader.GetBytes(tagPtr, tagLen);
                    directory.SetByteArray(tagType, b);
                }
            }
            catch (IOException ex)
            {
                directory.AddError("Exception reading ICC profile: " + ex.Message);
            }
        }
Example #3
0
        public virtual void ExtractTiff(RandomAccessReader reader, Com.Drew.Metadata.Metadata metadata)
        {
            ExifIFD0Directory directory = metadata.GetOrCreateDirectory <ExifIFD0Directory>();

            try
            {
                ExtractTiff(reader, metadata, directory, 0);
            }
            catch (IOException e)
            {
                directory.AddError("IO problem: " + e.Message);
            }
        }
        /// <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(string xmpString, Com.Drew.Metadata.Metadata metadata)
        {
            XmpDirectory directory = metadata.GetOrCreateDirectory <XmpDirectory>();

            try
            {
                XMPMeta xmpMeta = XMPMetaFactory.ParseFromString(xmpString);
                ProcessXmpTags(directory, xmpMeta);
            }
            catch (XMPException e)
            {
                directory.AddError("Error processing XMP data: " + e.Message);
            }
        }
        /// <summary>
        /// Performs the Jfif data extraction, adding found values to the specified
        /// instance of
        /// <see cref="Com.Drew.Metadata.Metadata"/>
        /// .
        /// </summary>
        public virtual void Extract(RandomAccessReader reader, Com.Drew.Metadata.Metadata metadata)
        {
            JfifDirectory directory = metadata.GetOrCreateDirectory <JfifDirectory>();

            try
            {
                // For JFIF, the tag number is also the offset into the segment
                int ver = reader.GetUInt16(JfifDirectory.TagVersion);
                directory.SetInt(JfifDirectory.TagVersion, ver);
                int units = reader.GetUInt8(JfifDirectory.TagUnits);
                directory.SetInt(JfifDirectory.TagUnits, units);
                int height = reader.GetUInt16(JfifDirectory.TagResx);
                directory.SetInt(JfifDirectory.TagResx, height);
                int width = reader.GetUInt16(JfifDirectory.TagResy);
                directory.SetInt(JfifDirectory.TagResy, width);
            }
            catch (IOException me)
            {
                directory.AddError(me.Message);
            }
        }
 public virtual void Extract(SequentialReader reader, Com.Drew.Metadata.Metadata metadata)
 {
     Com.Drew.Metadata.Directory directory = metadata.GetOrCreateDirectory <AdobeJpegDirectory>();
     try
     {
         reader.SetMotorolaByteOrder(false);
         if (!reader.GetString(5).Equals("Adobe"))
         {
             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);
     }
 }
Example #7
0
        public virtual void Extract(sbyte[] segmentBytes, Com.Drew.Metadata.Metadata metadata, JpegSegmentType segmentType)
        {
            if (metadata.ContainsDirectory <JpegDirectory>())
            {
                // If this directory is already present, discontinue this operation.
                // We only store metadata for the *first* matching SOFn segment.
                return;
            }
            JpegDirectory directory = metadata.GetOrCreateDirectory <JpegDirectory>();

            // 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);
            }
        }
        /// <summary>Version specifically for dealing with XMP found in JPEG segments.</summary>
        /// <remarks>
        /// Version specifically for dealing with XMP found in JPEG segments. This form of XMP has a peculiar preamble, which
        /// must be removed before parsing the XML.
        /// </remarks>
        /// <param name="segmentBytes">The byte array from which the metadata should be extracted.</param>
        /// <param name="metadata">
        /// The
        /// <see cref="Com.Drew.Metadata.Metadata"/>
        /// object into which extracted values should be merged.
        /// </param>
        /// <param name="segmentType">
        /// The
        /// <see cref="Com.Drew.Imaging.Jpeg.JpegSegmentType"/>
        /// being read.
        /// </param>
        public virtual void Extract(sbyte[] segmentBytes, Com.Drew.Metadata.Metadata metadata, JpegSegmentType segmentType)
        {
            XmpDirectory directory = metadata.GetOrCreateDirectory <XmpDirectory>();
            // XMP in a JPEG file has a 29 byte preamble which is not valid XML.
            int preambleLength = 29;

            // check for the header length
            if (segmentBytes.Length <= preambleLength + 1)
            {
                directory.AddError(Sharpen.Extensions.StringFormat("Xmp data segment must contain at least %d bytes", preambleLength + 1));
                return;
            }
            ByteArrayReader reader   = new ByteArrayReader(segmentBytes);
            string          preamble = Sharpen.Runtime.GetStringForBytes(segmentBytes, 0, preambleLength);

            if (!"http://ns.adobe.com/xap/1.0/\x0".Equals(preamble))
            {
                directory.AddError("XMP data segment doesn't begin with 'http://ns.adobe.com/xap/1.0/'");
                return;
            }
            sbyte[] xmlBytes = new sbyte[segmentBytes.Length - preambleLength];
            System.Array.Copy(segmentBytes, 29, xmlBytes, 0, xmlBytes.Length);
            Extract(xmlBytes, metadata);
        }
        public virtual void Extract(RandomAccessReader reader, Com.Drew.Metadata.Metadata metadata)
        {
            PhotoshopDirectory directory = metadata.GetOrCreateDirectory <PhotoshopDirectory>();
            int pos;

            try
            {
                pos = reader.GetString(0, 13).Equals("Photoshop 3.0") ? 14 : 0;
            }
            catch (IOException)
            {
                directory.AddError("Unable to read header");
                return;
            }
            long length;

            try
            {
                length = reader.GetLength();
            }
            catch (IOException e)
            {
                directory.AddError("Unable to read Photoshop data: " + e.Message);
                return;
            }
            while (pos < length)
            {
                try
                {
                    // 4 bytes for the signature.  Should always be "8BIM".
                    //String signature = new String(data, pos, 4);
                    pos += 4;
                    // 2 bytes for the resource identifier (tag type).
                    int tagType = reader.GetUInt16(pos);
                    // segment type
                    pos += 2;
                    // A variable number of bytes holding a pascal string (two leading bytes for length).
                    int descriptionLength = reader.GetUInt16(pos);
                    pos += 2;
                    // Some basic bounds checking
                    if (descriptionLength < 0 || descriptionLength + pos > length)
                    {
                        return;
                    }
                    //String description = new String(data, pos, descriptionLength);
                    pos += descriptionLength;
                    // The number of bytes is padded with a trailing zero, if needed, to make the size even.
                    if (pos % 2 != 0)
                    {
                        pos++;
                    }
                    // 4 bytes for the size of the resource data that follows.
                    int byteCount = reader.GetInt32(pos);
                    pos += 4;
                    // The resource data.
                    sbyte[] tagBytes = reader.GetBytes(pos, byteCount);
                    pos += byteCount;
                    // The number of bytes is padded with a trailing zero, if needed, to make the size even.
                    if (pos % 2 != 0)
                    {
                        pos++;
                    }
                    directory.SetByteArray(tagType, tagBytes);
                    // TODO allow rebasing the reader with a new zero-point, rather than copying data here
                    if (tagType == PhotoshopDirectory.TagIptc)
                    {
                        new IptcReader().Extract(new SequentialByteArrayReader(tagBytes), metadata, tagBytes.Length);
                    }
                    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 (IOException ex)
                {
                    directory.AddError(ex.Message);
                    return;
                }
            }
        }
Example #10
0
        /// <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(SequentialReader reader, Com.Drew.Metadata.Metadata metadata, long length)
        {
            IptcDirectory directory = metadata.GetOrCreateDirectory <IptcDirectory>();
            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)))
                {
                    directory.AddError("Invalid start to IPTC tag");
                    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();
                    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;
            }
        }
Example #11
0
        public virtual void Extract(SequentialReader reader, Com.Drew.Metadata.Metadata metadata)
        {
            BmpHeaderDirectory directory = metadata.GetOrCreateDirectory <BmpHeaderDirectory>();

            // 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");
            }
        }
        public virtual void Extract(SequentialReader reader, Com.Drew.Metadata.Metadata metadata)
        {
            GifHeaderDirectory directory = metadata.GetOrCreateDirectory <GifHeaderDirectory>();

            // 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");
            }
        }
Example #13
0
 protected internal DirectoryTiffHandler(Com.Drew.Metadata.Metadata metadata, Type initialDirectory)
 {
     _metadata         = metadata;
     _currentDirectory = _metadata.GetOrCreateDirectory(initialDirectory);
 }
Example #14
0
        private static void ProcessMakernote(int makernoteOffset, ICollection <int> processedIfdOffsets, int tiffHeaderOffset, Com.Drew.Metadata.Metadata metadata, RandomAccessReader reader)
        {
            // Determine the camera model and makernote format
            Com.Drew.Metadata.Directory ifd0Directory = metadata.GetDirectory <ExifIFD0Directory>();
            if (ifd0Directory == null)
            {
                return;
            }
            string cameraMake       = ifd0Directory.GetString(ExifIFD0Directory.TagMake);
            string firstThreeChars  = reader.GetString(makernoteOffset, 3);
            string firstFourChars   = reader.GetString(makernoteOffset, 4);
            string firstFiveChars   = reader.GetString(makernoteOffset, 5);
            string firstSixChars    = reader.GetString(makernoteOffset, 6);
            string firstSevenChars  = reader.GetString(makernoteOffset, 7);
            string firstEightChars  = reader.GetString(makernoteOffset, 8);
            string firstTwelveChars = reader.GetString(makernoteOffset, 12);
            bool   byteOrderBefore  = reader.IsMotorolaByteOrder();

            if ("OLYMP".Equals(firstFiveChars) || "EPSON".Equals(firstFiveChars) || "AGFA".Equals(firstFourChars))
            {
                // Olympus Makernote
                // Epson and Agfa use Olympus makernote standard: http://www.ozhiker.com/electronics/pjmt/jpeg_info/
                ProcessIFD(metadata.GetOrCreateDirectory <OlympusMakernoteDirectory>(), processedIfdOffsets, makernoteOffset + 8, tiffHeaderOffset, metadata, reader);
            }
            else
            {
                if (cameraMake != null && Sharpen.Extensions.Trim(cameraMake).ToUpper().StartsWith("NIKON"))
                {
                    if ("Nikon".Equals(firstFiveChars))
                    {
                        switch (reader.GetUInt8(makernoteOffset + 6))
                        {
                        case 1:
                        {
                            /* There are two scenarios here:
                             * Type 1:                  **
                             * :0000: 4E 69 6B 6F 6E 00 01 00-05 00 02 00 02 00 06 00 Nikon...........
                             * :0010: 00 00 EC 02 00 00 03 00-03 00 01 00 00 00 06 00 ................
                             * Type 3:                  **
                             * :0000: 4E 69 6B 6F 6E 00 02 00-00 00 4D 4D 00 2A 00 00 Nikon....MM.*...
                             * :0010: 00 08 00 1E 00 01 00 07-00 00 00 04 30 32 30 30 ............0200
                             */
                            ProcessIFD(metadata.GetOrCreateDirectory <NikonType1MakernoteDirectory>(), processedIfdOffsets, makernoteOffset + 8, tiffHeaderOffset, metadata, reader);
                            break;
                        }

                        case 2:
                        {
                            ProcessIFD(metadata.GetOrCreateDirectory <NikonType2MakernoteDirectory>(), processedIfdOffsets, makernoteOffset + 18, makernoteOffset + 10, metadata, reader);
                            break;
                        }

                        default:
                        {
                            ifd0Directory.AddError("Unsupported Nikon makernote data ignored.");
                            break;
                        }
                        }
                    }
                    else
                    {
                        // The IFD begins with the first Makernote byte (no ASCII name).  This occurs with CoolPix 775, E990 and D1 models.
                        ProcessIFD(metadata.GetOrCreateDirectory <NikonType2MakernoteDirectory>(), processedIfdOffsets, makernoteOffset, tiffHeaderOffset, metadata, reader);
                    }
                }
                else
                {
                    if ("SONY CAM".Equals(firstEightChars) || "SONY DSC".Equals(firstEightChars))
                    {
                        ProcessIFD(metadata.GetOrCreateDirectory <SonyType1MakernoteDirectory>(), processedIfdOffsets, makernoteOffset + 12, tiffHeaderOffset, metadata, reader);
                    }
                    else
                    {
                        if ("SEMC MS\u0000\u0000\u0000\u0000\u0000".Equals(firstTwelveChars))
                        {
                            // force MM for this directory
                            reader.SetMotorolaByteOrder(true);
                            // skip 12 byte header + 2 for "MM" + 6
                            ProcessIFD(metadata.GetOrCreateDirectory <SonyType6MakernoteDirectory>(), processedIfdOffsets, makernoteOffset + 20, tiffHeaderOffset, metadata, reader);
                        }
                        else
                        {
                            if ("SIGMA\u0000\u0000\u0000".Equals(firstEightChars) || "FOVEON\u0000\u0000".Equals(firstEightChars))
                            {
                                ProcessIFD(metadata.GetOrCreateDirectory <SigmaMakernoteDirectory>(), processedIfdOffsets, makernoteOffset + 10, tiffHeaderOffset, metadata, reader);
                            }
                            else
                            {
                                if ("KDK".Equals(firstThreeChars))
                                {
                                    reader.SetMotorolaByteOrder(firstSevenChars.Equals("KDK INFO"));
                                    ProcessKodakMakernote(metadata.GetOrCreateDirectory <KodakMakernoteDirectory>(), makernoteOffset, reader);
                                }
                                else
                                {
                                    if (Sharpen.Runtime.EqualsIgnoreCase("Canon", cameraMake))
                                    {
                                        ProcessIFD(metadata.GetOrCreateDirectory <CanonMakernoteDirectory>(), processedIfdOffsets, makernoteOffset, tiffHeaderOffset, metadata, reader);
                                    }
                                    else
                                    {
                                        if (cameraMake != null && cameraMake.ToUpper().StartsWith("CASIO"))
                                        {
                                            if ("QVC\u0000\u0000\u0000".Equals(firstSixChars))
                                            {
                                                ProcessIFD(metadata.GetOrCreateDirectory <CasioType2MakernoteDirectory>(), processedIfdOffsets, makernoteOffset + 6, tiffHeaderOffset, metadata, reader);
                                            }
                                            else
                                            {
                                                ProcessIFD(metadata.GetOrCreateDirectory <CasioType1MakernoteDirectory>(), processedIfdOffsets, makernoteOffset, tiffHeaderOffset, metadata, reader);
                                            }
                                        }
                                        else
                                        {
                                            if ("FUJIFILM".Equals(firstEightChars) || Sharpen.Runtime.EqualsIgnoreCase("Fujifilm", cameraMake))
                                            {
                                                // Note that this also applies to certain Leica cameras, such as the Digilux-4.3
                                                reader.SetMotorolaByteOrder(false);
                                                // the 4 bytes after "FUJIFILM" in the makernote point to the start of the makernote
                                                // IFD, though the offset is relative to the start of the makernote, not the TIFF
                                                // header (like everywhere else)
                                                int ifdStart = makernoteOffset + reader.GetInt32(makernoteOffset + 8);
                                                ProcessIFD(metadata.GetOrCreateDirectory <FujifilmMakernoteDirectory>(), processedIfdOffsets, ifdStart, makernoteOffset, metadata, reader);
                                            }
                                            else
                                            {
                                                if (cameraMake != null && cameraMake.ToUpper().StartsWith("MINOLTA"))
                                                {
                                                    // Cases seen with the model starting with MINOLTA in capitals seem to have a valid Olympus makernote
                                                    // area that commences immediately.
                                                    ProcessIFD(metadata.GetOrCreateDirectory <OlympusMakernoteDirectory>(), processedIfdOffsets, makernoteOffset, tiffHeaderOffset, metadata, reader);
                                                }
                                                else
                                                {
                                                    if ("KYOCERA".Equals(firstSevenChars))
                                                    {
                                                        // http://www.ozhiker.com/electronics/pjmt/jpeg_info/kyocera_mn.html
                                                        ProcessIFD(metadata.GetOrCreateDirectory <KyoceraMakernoteDirectory>(), processedIfdOffsets, makernoteOffset + 22, tiffHeaderOffset, metadata, reader);
                                                    }
                                                    else
                                                    {
                                                        if ("LEICA".Equals(firstFiveChars))
                                                        {
                                                            reader.SetMotorolaByteOrder(false);
                                                            if ("Leica Camera AG".Equals(cameraMake))
                                                            {
                                                                ProcessIFD(metadata.GetOrCreateDirectory <LeicaMakernoteDirectory>(), processedIfdOffsets, makernoteOffset + 8, tiffHeaderOffset, metadata, reader);
                                                            }
                                                            else
                                                            {
                                                                if ("LEICA".Equals(cameraMake))
                                                                {
                                                                    // Some Leica cameras use Panasonic makernote tags
                                                                    ProcessIFD(metadata.GetOrCreateDirectory <PanasonicMakernoteDirectory>(), processedIfdOffsets, makernoteOffset + 8, tiffHeaderOffset, metadata, reader);
                                                                }
                                                            }
                                                        }
                                                        else
                                                        {
                                                            if ("Panasonic\u0000\u0000\u0000".Equals(reader.GetString(makernoteOffset, 12)))
                                                            {
                                                                // NON-Standard TIFF IFD Data using Panasonic Tags. There is no Next-IFD pointer after the IFD
                                                                // Offsets are relative to the start of the TIFF header at the beginning of the EXIF segment
                                                                // more information here: http://www.ozhiker.com/electronics/pjmt/jpeg_info/panasonic_mn.html
                                                                ProcessIFD(metadata.GetOrCreateDirectory <PanasonicMakernoteDirectory>(), processedIfdOffsets, makernoteOffset + 12, tiffHeaderOffset, metadata, reader);
                                                            }
                                                            else
                                                            {
                                                                if ("AOC\u0000".Equals(firstFourChars))
                                                                {
                                                                    // NON-Standard TIFF IFD Data using Casio Type 2 Tags
                                                                    // IFD has no Next-IFD pointer at end of IFD, and
                                                                    // Offsets are relative to the start of the current IFD tag, not the TIFF header
                                                                    // Observed for:
                                                                    // - Pentax ist D
                                                                    ProcessIFD(metadata.GetOrCreateDirectory <CasioType2MakernoteDirectory>(), processedIfdOffsets, makernoteOffset + 6, makernoteOffset, metadata, reader);
                                                                }
                                                                else
                                                                {
                                                                    if (cameraMake != null && (cameraMake.ToUpper().StartsWith("PENTAX") || cameraMake.ToUpper().StartsWith("ASAHI")))
                                                                    {
                                                                        // NON-Standard TIFF IFD Data using Pentax Tags
                                                                        // IFD has no Next-IFD pointer at end of IFD, and
                                                                        // Offsets are relative to the start of the current IFD tag, not the TIFF header
                                                                        // Observed for:
                                                                        // - PENTAX Optio 330
                                                                        // - PENTAX Optio 430
                                                                        ProcessIFD(metadata.GetOrCreateDirectory <PentaxMakernoteDirectory>(), processedIfdOffsets, makernoteOffset, makernoteOffset, metadata, reader);
                                                                    }
                                                                    else
                                                                    {
                                                                        //        } else if ("KC".equals(firstTwoChars) || "MINOL".equals(firstFiveChars) || "MLY".equals(firstThreeChars) || "+M+M+M+M".equals(firstEightChars)) {
                                                                        //            // This Konica data is not understood.  Header identified in accordance with information at this site:
                                                                        //            // http://www.ozhiker.com/electronics/pjmt/jpeg_info/minolta_mn.html
                                                                        //            // TODO add support for minolta/konica cameras
                                                                        //            exifDirectory.addError("Unsupported Konica/Minolta data ignored.");
                                                                        if ("SANYO\x0\x1\x0".Equals(firstEightChars))
                                                                        {
                                                                            ProcessIFD(metadata.GetOrCreateDirectory <SanyoMakernoteDirectory>(), processedIfdOffsets, makernoteOffset + 8, makernoteOffset, metadata, reader);
                                                                        }
                                                                    }
                                                                }
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            // The makernote is not comprehended by this library.
            // If you are reading this and believe a particular camera's image should be processed, get in touch.
            reader.SetMotorolaByteOrder(byteOrderBefore);
        }
Example #15
0
        private static void ProcessIFD(Com.Drew.Metadata.Directory directory, ICollection <int> processedIfdOffsets, int ifdOffset, int tiffHeaderOffset, Com.Drew.Metadata.Metadata metadata, RandomAccessReader reader)
        {
            // check for directories we've already visited to avoid stack overflows when recursive/cyclic directory structures exist
            if (processedIfdOffsets.Contains(Sharpen.Extensions.ValueOf(ifdOffset)))
            {
                return;
            }
            // remember that we've visited this directory so that we don't visit it again later
            processedIfdOffsets.Add(ifdOffset);
            if (ifdOffset >= reader.GetLength() || ifdOffset < 0)
            {
                directory.AddError("Ignored IFD marked to start outside data segment");
                return;
            }
            // First two bytes in the IFD are the number of tags in this directory
            int dirTagCount = reader.GetUInt16(ifdOffset);
            int dirLength   = (2 + (12 * dirTagCount) + 4);

            if (dirLength + ifdOffset > reader.GetLength())
            {
                directory.AddError("Illegally sized IFD");
                return;
            }
            // Handle each tag in this directory
            for (int tagNumber = 0; tagNumber < dirTagCount; tagNumber++)
            {
                int tagOffset = CalculateTagOffset(ifdOffset, tagNumber);
                // 2 bytes for the tag type
                int tagType = reader.GetUInt16(tagOffset);
                // 2 bytes for the format code
                int formatCode = reader.GetUInt16(tagOffset + 2);
                if (formatCode < 1 || formatCode > MaxFormatCode)
                {
                    // This error suggests that we are processing at an incorrect index and will generate
                    // rubbish until we go out of bounds (which may be a while).  Exit now.
                    directory.AddError("Invalid TIFF tag format code: " + formatCode);
                    return;
                }
                // 4 bytes dictate the number of components in this tag's data
                int componentCount = reader.GetInt32(tagOffset + 4);
                if (componentCount < 0)
                {
                    directory.AddError("Negative TIFF tag component count");
                    continue;
                }
                // each component may have more than one byte... calculate the total number of bytes
                int byteCount = componentCount * BytesPerFormat[formatCode];
                int tagValueOffset;
                if (byteCount > 4)
                {
                    // If it's bigger than 4 bytes, the dir entry contains an offset.
                    // dirEntryOffset must be passed, as some makernote implementations (e.g. Fujifilm) incorrectly use an
                    // offset relative to the start of the makernote itself, not the TIFF segment.
                    int offsetVal = reader.GetInt32(tagOffset + 8);
                    if (offsetVal + byteCount > reader.GetLength())
                    {
                        // Bogus pointer offset and / or byteCount value
                        directory.AddError("Illegal TIFF tag pointer offset");
                        continue;
                    }
                    tagValueOffset = tiffHeaderOffset + offsetVal;
                }
                else
                {
                    // 4 bytes or less and value is in the dir entry itself
                    tagValueOffset = tagOffset + 8;
                }
                if (tagValueOffset < 0 || tagValueOffset > reader.GetLength())
                {
                    directory.AddError("Illegal TIFF tag pointer offset");
                    continue;
                }
                // Check that this tag isn't going to allocate outside the bounds of the data array.
                // This addresses an uncommon OutOfMemoryError.
                if (byteCount < 0 || tagValueOffset + byteCount > reader.GetLength())
                {
                    directory.AddError("Illegal number of bytes for TIFF tag data: " + byteCount);
                    continue;
                }
                //
                // Special handling for certain known tags that point to or contain other chunks of data to be processed
                //
                if (tagType == ExifIFD0Directory.TagExifSubIfdOffset && directory is ExifIFD0Directory)
                {
                    if (byteCount != 4)
                    {
                        directory.AddError("Exif SubIFD Offset tag should have a component count of four (bytes) for the offset.");
                    }
                    else
                    {
                        int subDirOffset = tiffHeaderOffset + reader.GetInt32(tagValueOffset);
                        ProcessIFD(metadata.GetOrCreateDirectory <ExifSubIFDDirectory>(), processedIfdOffsets, subDirOffset, tiffHeaderOffset, metadata, reader);
                    }
                }
                else
                {
                    if (tagType == ExifSubIFDDirectory.TagInteropOffset && directory is ExifSubIFDDirectory)
                    {
                        if (byteCount != 4)
                        {
                            directory.AddError("Exif Interop Offset tag should have a component count of four (bytes) for the offset.");
                        }
                        else
                        {
                            int subDirOffset = tiffHeaderOffset + reader.GetInt32(tagValueOffset);
                            ProcessIFD(metadata.GetOrCreateDirectory <ExifInteropDirectory>(), processedIfdOffsets, subDirOffset, tiffHeaderOffset, metadata, reader);
                        }
                    }
                    else
                    {
                        if (tagType == ExifIFD0Directory.TagGpsInfoOffset && directory is ExifIFD0Directory)
                        {
                            if (byteCount != 4)
                            {
                                directory.AddError("Exif GPS Info Offset tag should have a component count of four (bytes) for the offset.");
                            }
                            else
                            {
                                int subDirOffset = tiffHeaderOffset + reader.GetInt32(tagValueOffset);
                                ProcessIFD(metadata.GetOrCreateDirectory <GpsDirectory>(), processedIfdOffsets, subDirOffset, tiffHeaderOffset, metadata, reader);
                            }
                        }
                        else
                        {
                            if (tagType == ExifSubIFDDirectory.TagMakernote && directory is ExifSubIFDDirectory)
                            {
                                // The makernote tag contains the encoded makernote data directly.
                                // Pass the offset to this tag's value. Manufacturer/Model-specific logic will be used to
                                // determine the correct offset for further processing.
                                ProcessMakernote(tagValueOffset, processedIfdOffsets, tiffHeaderOffset, metadata, reader);
                            }
                            else
                            {
                                ProcessTag(directory, tagType, tagValueOffset, componentCount, formatCode, reader);
                            }
                        }
                    }
                }
            }
            // at the end of each IFD is an optional link to the next IFD
            int finalTagOffset      = CalculateTagOffset(ifdOffset, dirTagCount);
            int nextDirectoryOffset = reader.GetInt32(finalTagOffset);

            if (nextDirectoryOffset != 0)
            {
                nextDirectoryOffset += tiffHeaderOffset;
                if (nextDirectoryOffset >= reader.GetLength())
                {
                    // Last 4 bytes of IFD reference another IFD with an address that is out of bounds
                    // Note this could have been caused by jhead 1.3 cropping too much
                    return;
                }
                else
                {
                    if (nextDirectoryOffset < ifdOffset)
                    {
                        // Last 4 bytes of IFD reference another IFD with an address that is before the start of this directory
                        return;
                    }
                }
                // TODO in Exif, the only known 'follower' IFD is the thumbnail one, however this may not be the case
                ExifThumbnailDirectory nextDirectory = metadata.GetOrCreateDirectory <ExifThumbnailDirectory>();
                ProcessIFD(nextDirectory, processedIfdOffsets, nextDirectoryOffset, tiffHeaderOffset, metadata, reader);
            }
        }
        public static Com.Drew.Metadata.Metadata ReadMetadata(InputStream inputStream)
        {
            // TODO keep a single static hash of these
            ICollection <PngChunkType> desiredChunkTypes = new HashSet <PngChunkType>();

            desiredChunkTypes.Add(PngChunkType.Ihdr);
            desiredChunkTypes.Add(PngChunkType.Plte);
            desiredChunkTypes.Add(PngChunkType.tRNS);
            desiredChunkTypes.Add(PngChunkType.cHRM);
            desiredChunkTypes.Add(PngChunkType.sRGB);
            desiredChunkTypes.Add(PngChunkType.gAMA);
            desiredChunkTypes.Add(PngChunkType.iCCP);
            desiredChunkTypes.Add(PngChunkType.bKGD);
            desiredChunkTypes.Add(PngChunkType.tEXt);
            desiredChunkTypes.Add(PngChunkType.iTXt);
            desiredChunkTypes.Add(PngChunkType.tIME);
            Iterable <PngChunk> chunks = new PngChunkReader().Extract(new Com.Drew.Lang.StreamReader(inputStream), desiredChunkTypes);

            Com.Drew.Metadata.Metadata metadata  = new Com.Drew.Metadata.Metadata();
            IList <KeyValuePair>       textPairs = new AList <KeyValuePair>();

            foreach (PngChunk chunk in chunks)
            {
                PngChunkType chunkType = chunk.GetChunkType();
                sbyte[]      bytes     = chunk.GetBytes();
                if (chunkType.Equals(PngChunkType.Ihdr))
                {
                    PngHeader    header    = new PngHeader(bytes);
                    PngDirectory directory = metadata.GetOrCreateDirectory <PngDirectory>();
                    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());
                }
                else
                {
                    if (chunkType.Equals(PngChunkType.Plte))
                    {
                        PngDirectory directory = metadata.GetOrCreateDirectory <PngDirectory>();
                        directory.SetInt(PngDirectory.TagPaletteSize, bytes.Length / 3);
                    }
                    else
                    {
                        if (chunkType.Equals(PngChunkType.tRNS))
                        {
                            PngDirectory directory = metadata.GetOrCreateDirectory <PngDirectory>();
                            directory.SetInt(PngDirectory.TagPaletteHasTransparency, 1);
                        }
                        else
                        {
                            if (chunkType.Equals(PngChunkType.sRGB))
                            {
                                int          srgbRenderingIntent = new SequentialByteArrayReader(bytes).GetInt8();
                                PngDirectory directory           = metadata.GetOrCreateDirectory <PngDirectory>();
                                directory.SetInt(PngDirectory.TagSrgbRenderingIntent, srgbRenderingIntent);
                            }
                            else
                            {
                                if (chunkType.Equals(PngChunkType.cHRM))
                                {
                                    PngChromaticities          chromaticities = new PngChromaticities(bytes);
                                    PngChromaticitiesDirectory directory      = metadata.GetOrCreateDirectory <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());
                                }
                                else
                                {
                                    if (chunkType.Equals(PngChunkType.gAMA))
                                    {
                                        int          gammaInt  = new SequentialByteArrayReader(bytes).GetInt32();
                                        PngDirectory directory = metadata.GetOrCreateDirectory <PngDirectory>();
                                        directory.SetDouble(PngDirectory.TagGamma, gammaInt / 100000.0);
                                    }
                                    else
                                    {
                                        if (chunkType.Equals(PngChunkType.iCCP))
                                        {
                                            SequentialReader reader      = new SequentialByteArrayReader(bytes);
                                            string           profileName = reader.GetNullTerminatedString(79);
                                            PngDirectory     directory   = metadata.GetOrCreateDirectory <PngDirectory>();
                                            directory.SetString(PngDirectory.TagProfileName, 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();
                                            }
                                        }
                                        else
                                        {
                                            if (chunkType.Equals(PngChunkType.bKGD))
                                            {
                                                PngDirectory directory = metadata.GetOrCreateDirectory <PngDirectory>();
                                                directory.SetByteArray(PngDirectory.TagBackgroundColor, bytes);
                                            }
                                            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);
                                                    textPairs.Add(new KeyValuePair(keyword, value));
                                                }
                                                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
                                                                {
                                                                    metadata.GetOrCreateDirectory <PngDirectory>().AddError("Invalid compression method value");
                                                                }
                                                            }
                                                            else
                                                            {
                                                                metadata.GetOrCreateDirectory <PngDirectory>().AddError("Invalid compression flag value");
                                                            }
                                                        }
                                                        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
                                                            {
                                                                textPairs.Add(new KeyValuePair(keyword, text));
                                                            }
                                                        }
                                                    }
                                                    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 = metadata.GetOrCreateDirectory <PngDirectory>();
                                                            directory.SetDate(PngDirectory.TagLastModificationTime, calendar.GetTime());
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            if (textPairs.Count != 0)
            {
                PngDirectory directory = metadata.GetOrCreateDirectory <PngDirectory>();
                directory.SetObject(PngDirectory.TagTextualData, textPairs);
            }
            return(metadata);
        }
Example #17
0
 protected internal virtual void PushDirectory <T>() where T : Com.Drew.Metadata.Directory
 {
     System.Diagnostics.Debug.Assert((typeof(T) != _currentDirectory.GetType()));
     _directoryStack.Push(_currentDirectory);
     _currentDirectory = _metadata.GetOrCreateDirectory <T>();
 }
		public static Com.Drew.Metadata.Metadata ReadMetadata(InputStream inputStream)
		{
			// TODO keep a single static hash of these
			ICollection<PngChunkType> desiredChunkTypes = new HashSet<PngChunkType>();
			desiredChunkTypes.Add(PngChunkType.Ihdr);
			desiredChunkTypes.Add(PngChunkType.Plte);
			desiredChunkTypes.Add(PngChunkType.tRNS);
			desiredChunkTypes.Add(PngChunkType.cHRM);
			desiredChunkTypes.Add(PngChunkType.sRGB);
			desiredChunkTypes.Add(PngChunkType.gAMA);
			desiredChunkTypes.Add(PngChunkType.iCCP);
			desiredChunkTypes.Add(PngChunkType.bKGD);
			desiredChunkTypes.Add(PngChunkType.tEXt);
			desiredChunkTypes.Add(PngChunkType.iTXt);
			desiredChunkTypes.Add(PngChunkType.tIME);
			Iterable<PngChunk> chunks = new PngChunkReader().Extract(new Com.Drew.Lang.StreamReader(inputStream), desiredChunkTypes);
			Com.Drew.Metadata.Metadata metadata = new Com.Drew.Metadata.Metadata();
			IList<KeyValuePair> textPairs = new AList<KeyValuePair>();
			foreach (PngChunk chunk in chunks)
			{
				PngChunkType chunkType = chunk.GetChunkType();
				sbyte[] bytes = chunk.GetBytes();
				if (chunkType.Equals(PngChunkType.Ihdr))
				{
					PngHeader header = new PngHeader(bytes);
					PngDirectory directory = metadata.GetOrCreateDirectory<PngDirectory>();
					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());
				}
				else
				{
					if (chunkType.Equals(PngChunkType.Plte))
					{
						PngDirectory directory = metadata.GetOrCreateDirectory<PngDirectory>();
						directory.SetInt(PngDirectory.TagPaletteSize, bytes.Length / 3);
					}
					else
					{
						if (chunkType.Equals(PngChunkType.tRNS))
						{
							PngDirectory directory = metadata.GetOrCreateDirectory<PngDirectory>();
							directory.SetInt(PngDirectory.TagPaletteHasTransparency, 1);
						}
						else
						{
							if (chunkType.Equals(PngChunkType.sRGB))
							{
								int srgbRenderingIntent = new SequentialByteArrayReader(bytes).GetInt8();
								PngDirectory directory = metadata.GetOrCreateDirectory<PngDirectory>();
								directory.SetInt(PngDirectory.TagSrgbRenderingIntent, srgbRenderingIntent);
							}
							else
							{
								if (chunkType.Equals(PngChunkType.cHRM))
								{
									PngChromaticities chromaticities = new PngChromaticities(bytes);
									PngChromaticitiesDirectory directory = metadata.GetOrCreateDirectory<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());
								}
								else
								{
									if (chunkType.Equals(PngChunkType.gAMA))
									{
										int gammaInt = new SequentialByteArrayReader(bytes).GetInt32();
										PngDirectory directory = metadata.GetOrCreateDirectory<PngDirectory>();
										directory.SetDouble(PngDirectory.TagGamma, gammaInt / 100000.0);
									}
									else
									{
										if (chunkType.Equals(PngChunkType.iCCP))
										{
											SequentialReader reader = new SequentialByteArrayReader(bytes);
											string profileName = reader.GetNullTerminatedString(79);
											PngDirectory directory = metadata.GetOrCreateDirectory<PngDirectory>();
											directory.SetString(PngDirectory.TagProfileName, 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();
											}
										}
										else
										{
											if (chunkType.Equals(PngChunkType.bKGD))
											{
												PngDirectory directory = metadata.GetOrCreateDirectory<PngDirectory>();
												directory.SetByteArray(PngDirectory.TagBackgroundColor, bytes);
											}
											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);
													textPairs.Add(new KeyValuePair(keyword, value));
												}
												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
																{
																	metadata.GetOrCreateDirectory<PngDirectory>().AddError("Invalid compression method value");
																}
															}
															else
															{
																metadata.GetOrCreateDirectory<PngDirectory>().AddError("Invalid compression flag value");
															}
														}
														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
															{
																textPairs.Add(new KeyValuePair(keyword, text));
															}
														}
													}
													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 = metadata.GetOrCreateDirectory<PngDirectory>();
															directory.SetDate(PngDirectory.TagLastModificationTime, calendar.GetTime());
														}
													}
												}
											}
										}
									}
								}
							}
						}
					}
				}
			}
			if (textPairs.Count != 0)
			{
				PngDirectory directory = metadata.GetOrCreateDirectory<PngDirectory>();
				directory.SetObject(PngDirectory.TagTextualData, textPairs);
			}
			return metadata;
		}