예제 #1
0
        /// <summary>
        ///    Reads a COM segment to find the JPEG comment.
        /// </summary>
        /// <param name="length">
        ///    The length of the segment that will be read.
        /// </param>
        private void ReadCOMSegment(int length)
        {
            if ((ImageTag.TagTypes & TagLib.TagTypes.JpegComment) != 0x00)
            {
                return;
            }

            long position = Tell;

            JpegCommentTag com_tag;

            if (length == 0)
            {
                com_tag = new JpegCommentTag();
            }
            else
            {
                ByteVector data = ReadBlock(length);

                int terminator = data.Find("\0", 0);

                if (terminator < 0)
                {
                    com_tag = new JpegCommentTag(data.ToString());
                }
                else
                {
                    com_tag = new JpegCommentTag(data.Mid(0, terminator).ToString());
                }
            }

            ImageTag.AddTag(com_tag);
            AddMetadataBlock(position - 4, length + 4);
        }
예제 #2
0
        private void ReadAPP13Segment(ushort length)
        {
            var data            = ReadBlock(length);
            var iptc_iim_length = IPTC_IIM_IDENTIFIER.Length;

            if (length < iptc_iim_length || data.Mid(0, iptc_iim_length) != IPTC_IIM_IDENTIFIER)
            {
                return;
            }
            var headerInfoLen = data.Mid(iptc_iim_length, 1).ToUShort();
            int lenToSkip;

            if (headerInfoLen > 0)
            {
                lenToSkip = 1 + headerInfoLen + 4;
            }
            else
            {
                lenToSkip = 6;
            }
            data.RemoveRange(0, iptc_iim_length + lenToSkip);
            var reader = new IIM.IIMReader(data);
            var tag    = reader.Process();

            if (tag != null)
            {
                ImageTag.AddTag(tag);
            }
        }
예제 #3
0
        /// <summary>
        ///    Reads a Comment Block at the current position. The current position must
        ///    point to the 3rd byte of the comment block. (The other 2 bytes are usually
        ///    read before to identify the comment block)
        /// </summary>
        private void ReadCommentBlock()
        {
            long position = Tell;

            // Comment Extension
            //
            // 1 Byte       Extension Introducer (0x21)
            // 1 Byte       Comment Label (0xFE)
            // N Bytes      Comment Data (Sub Blocks)
            // 1 Byte       Block Terminator (0x00)
            //
            // Note, the first 2 bytes are still read to identify the Comment Block.
            // Therefore, we only need to read the sub blocks and extract the data.

            string comment = ReadSubBlocks();

            // Only add the tag, if no one is still contained.
            if ((TagTypes & TagTypes.GifComment) == 0x00)
            {
                ImageTag.AddTag(new GifCommentTag(comment));

                // 2 bytes where read before
                AddMetadataBlock(position - 2, Tell - position + 2);
            }
        }
예제 #4
0
        private void ReadApplicationExtensionBlock()
        {
            long       position = Tell;
            ByteVector data     = ReadBlock(12);

            if (data.Count != 12)
            {
                throw new CorruptFileException("");
            }
            if (data.Mid(1, 8) == XMP_IDENTIFIER && data.Mid(9, 3) == XMP_AUTH_CODE)
            {
                long data_start        = Tell;
                long xmp_trailer_start = Find(new byte[] { 0x00 }, data_start) - XMP_MAGIC_TRAILER.Length + 2;
                Seek(data_start, SeekOrigin.Begin);
                if (xmp_trailer_start <= data_start)
                {
                    throw new CorruptFileException("No End of XMP data found");
                }
                int        data_length = (int)(xmp_trailer_start - data_start);
                ByteVector xmp_data    = ReadBlock(data_length);
                ImageTag.AddTag(new XmpTag(xmp_data.ToString(StringType.UTF8), this));
                AddMetadataBlock(position - 2, 14 + data_length + XMP_MAGIC_TRAILER.Length);
                Seek(xmp_trailer_start + XMP_MAGIC_TRAILER.Length, SeekOrigin.Begin);
            }
            else
            {
                SkipSubBlocks();
            }
        }
예제 #5
0
파일: File.cs 프로젝트: rygos/iTagger
        /// <summary>
        ///    Reads an zTXt Chunk from file. The current position must be set
        ///    to the start of the Chunk Data. Such a Chunk contains compressed
        ///    keywords.
        /// </summary>
        /// <param name="data_length">
        ///    A <see cref="System.Int32"/> with the length of the Chunk Data.
        /// </param>
        /// <remarks>
        ///    The Chunk may also contain compressed Exif data which is written
        ///    by other tools. But, since the PNG specification does not support
        ///    Exif data, we ignore it here.
        /// </remarks>
        private void ReadzTXtChunk(int data_length)
        {
            long position = Tell;

            // zTXt Chunk
            //
            // N Bytes     Keyword
            // 1 Byte      Null Separator
            // 1 Byte      Compression Method
            // N Bytes     Txt
            //
            // Followed by 4 Bytes CRC data

            ByteVector data = ReadChunkData(data_length);

            CheckCRC(zTXt_CHUNK_TYPE, data, ReadCRC());

            int    terminator_index;
            string keyword = ReadKeyword(data, 0, out terminator_index);

            if (terminator_index + 1 >= data_length)
            {
                throw new CorruptFileException("Compression Method byte expected");
            }

            byte compression_method = data [terminator_index + 1];

            ByteVector plain_data = Decompress(compression_method, data.Mid(terminator_index + 2));

            // ignore unknown compression methods
            if (plain_data == null)
            {
                return;
            }

            string     value      = plain_data.ToString();
            RawProfile rawProfile = null;

            if (keyword.StartsWith("Raw profile type"))
            {
                rawProfile = ProcessRawProfile(value);
                value      = rawProfile.ToString();
            }

            // handle XMP, which has a fixed header
            if (keyword == "xmp" || rawProfile != null && string.Compare(rawProfile.Name, "xmp", StringComparison.InvariantCultureIgnoreCase) == 0)
            {
                ImageTag.AddTag(new XmpTag(string.Join("", rawProfile.Data.ToArray()), this));
            }
            else
            {
                PngTag png_tag = GetTag(TagTypes.Png, true) as PngTag;

                if (png_tag.GetKeyword(keyword) == null)
                {
                    png_tag.SetKeyword(keyword, value);
                }
            }
            AddMetadataBlock(position - 8, data_length + 8 + 4);
        }
예제 #6
0
파일: File.cs 프로젝트: asimshah/Music
        /// <summary>
        ///    Reads the file with a specified read style.
        /// </summary>
        /// <param name="propertiesStyle">
        ///    A <see cref="ReadStyle" /> value specifying at what level
        ///    of accuracy to read the media properties, or <see
        ///    cref="ReadStyle.None" /> to ignore the properties.
        /// </param>
        protected void Read(ReadStyle propertiesStyle)
        {
            Mode = AccessMode.Read;
            try
            {
                uint first_ifd_offset = ReadHeader();
                ReadIFD(first_ifd_offset);

                // Find XMP data
                var xmp_entry = ImageTag.Exif.Structure.GetEntry(0, (ushort)IFDEntryTag.XMP) as ByteVectorIFDEntry;
                if (xmp_entry != null)
                {
                    ImageTag.AddTag(new XmpTag(xmp_entry.Data.ToString(), this));
                }

                if (propertiesStyle == ReadStyle.None)
                {
                    return;
                }

                properties = ExtractProperties();
            }
            finally
            {
                Mode = AccessMode.Closed;
            }
        }
예제 #7
0
        private void ReadCommentBlock()
        {
            long   position = Tell;
            string comment  = ReadSubBlocks();

            if ((TagTypes & TagTypes.GifComment) == 0x00)
            {
                ImageTag.AddTag(new GifCommentTag(comment));
                AddMetadataBlock(position - 2, Tell - position + 2);
            }
        }
예제 #8
0
        /// <summary>
        ///    Reads an APP13 segment to find IPTC-IIM metadata.
        /// </summary>
        /// <param name="length">
        ///    The length of the segment that will be read.
        /// </param>
        /// <remarks>More info and specs for IPTC-IIM:
        /// - Guidelines for Handling Image Metadata (http://www.metadataworkinggroup.org/specs/)
        /// - IPTC Standard Photo Metadata (July 2010) (http://www.iptc.org/std/photometadata/specification/IPTC-PhotoMetadata-201007_1.pdf)
        /// - Extracting IPTC header information from JPEG images (http://www.codeproject.com/KB/graphics/iptc.aspx?fid=2301&amp;df=90&amp;mpp=25&amp;noise=3&amp;prof=False&amp;sort=Position&amp;view=Quick&amp;fr=51#xx0xx)
        /// - Reading IPTC APP14 Segment Header Information from JPEG Images (http://www.codeproject.com/KB/graphics/ReadingIPTCAPP14.aspx?q=iptc)
        /// </remarks>
        private void ReadAPP13Segment(ushort length)
        {
            // TODO: if both IPTC-IIM and XMP metadata is contained in a file, we should read
            // a IPTC-IIM checksum and compare that with the checksum built over the IIM block.
            // Depending on the result we should prefer the information from XMP or IIM.
            // Right now we always prefer XMP.

            var data = ReadBlock(length);

            // The APP13 segment consists of:
            // - the string "Photoshop 3.0\u0000"
            // - followed by "8BIM"
            // - and then the section type "\u0004\u0004".
            // There might be multiple 8BIM sections with different types, but we're following
            // YAGNI for now and only deal with the one we're interested in (and hope that it's
            // the first one).
            var iptc_iim_length = IPTC_IIM_IDENTIFIER.Length;

            if (length < iptc_iim_length || data.Mid(0, iptc_iim_length) != IPTC_IIM_IDENTIFIER)
            {
                return;
            }

            // PS6 introduced a new header with variable length text
            var headerInfoLen = data.Mid(iptc_iim_length, 1).ToUShort();
            int lenToSkip;

            if (headerInfoLen > 0)
            {
                // PS6 header: 1 byte headerinfolen + headerinfo + 2 bytes 00 padding (?) + 2 bytes length
                lenToSkip = 1 + headerInfoLen + 4;
            }
            else
            {
                //old style: 4 bytes 00 padding (?) + 2 bytes length
                lenToSkip = 6;
            }
            data.RemoveRange(0, iptc_iim_length + lenToSkip);

            var reader = new IIM.IIMReader(data);
            var tag    = reader.Process();

            if (tag != null)
            {
                ImageTag.AddTag(tag);
            }
        }
예제 #9
0
        private void ReadAPP1Segment(ushort length)
        {
            long       position           = Tell;
            ByteVector data               = null;
            int        exif_header_length = 14;

            if ((ImageTag.TagTypes & TagLib.TagTypes.TiffIFD) == 0x00 && length >= exif_header_length)
            {
                data = ReadBlock(exif_header_length);
                if (data.Count == exif_header_length && data.Mid(0, 6).ToString().Equals(EXIF_IDENTIFIER))
                {
                    bool   is_bigendian = data.Mid(6, 2).ToString().Equals("MM");
                    ushort magic        = data.Mid(8, 2).ToUShort(is_bigendian);
                    if (magic != 42)
                    {
                        throw new Exception(String.Format("Invalid TIFF magic: {0}", magic));
                    }
                    uint ifd_offset = data.Mid(10, 4).ToUInt(is_bigendian);
                    var  exif       = new IFDTag();
                    var  reader     = new IFDReader(this, is_bigendian, exif.Structure, position + 6, ifd_offset, (uint)(length - 6));
                    reader.Read();
                    ImageTag.AddTag(exif);
                    AddMetadataBlock(position - 4, length + 4);
                    return;
                }
            }
            int xmp_header_length = XmpTag.XAP_NS.Length + 1;

            if ((ImageTag.TagTypes & TagLib.TagTypes.XMP) == 0x00 && length >= xmp_header_length)
            {
                if (data == null)
                {
                    data = ReadBlock(xmp_header_length);
                }
                else
                {
                    data.Add(ReadBlock(xmp_header_length - exif_header_length));
                }
                if (data.ToString().Equals(XmpTag.XAP_NS + "\0"))
                {
                    ByteVector xmp_data = ReadBlock(length - xmp_header_length);
                    ImageTag.AddTag(new XmpTag(xmp_data.ToString(), this));
                    AddMetadataBlock(position - 4, length + 4);
                }
            }
        }
예제 #10
0
        /// <summary>
        ///    Gets a tag of a specified type from the current instance,
        ///    optionally creating a new tag if possible.
        /// </summary>
        /// <param name="type">
        ///    A <see cref="TagLib.TagTypes" /> value indicating the
        ///    type of tag to read.
        /// </param>
        /// <param name="create">
        ///    A <see cref="bool" /> value specifying whether or not to
        ///    try and create the tag if one is not found.
        /// </param>
        /// <returns>
        ///    A <see cref="Tag" /> object containing the tag that was
        ///    found in or added to the current instance. If no
        ///    matching tag was found and none was created, <see
        ///    langword="null" /> is returned.
        /// </returns>
        public override Tag GetTag (TagTypes type, bool create)
        {
            Tag tag = base.GetTag (type, false);
            if (tag != null) {
                return tag;
            }

            if (!create || (type & ImageTag.AllowedTypes) == 0)
                return null;

            if (type != TagTypes.TiffIFD)
                return base.GetTag (type, create);

            ImageTag new_tag = new IFDTag (this);
            ImageTag.AddTag (new_tag);
            return new_tag;
        }
예제 #11
0
        /// <summary>
        ///    Reads an APP13 segment to find IPTC-IIM metadata.
        /// </summary>
        /// <param name="length">
        ///    The length of the segment that will be read.
        /// </param>
        /// <remarks>More info and specs for IPTC-IIM:
        /// - Guidelines for Handling Image Metadata (http://www.metadataworkinggroup.org/specs/)
        /// - IPTC Standard Photo Metadata (July 2010) (http://www.iptc.org/std/photometadata/specification/IPTC-PhotoMetadata-201007_1.pdf)
        /// - Extracting IPTC header information from JPEG images (http://www.codeproject.com/KB/graphics/iptc.aspx?fid=2301&amp;df=90&amp;mpp=25&amp;noise=3&amp;prof=False&amp;sort=Position&amp;view=Quick&amp;fr=51#xx0xx)
        /// - Reading IPTC APP14 Segment Header Information from JPEG Images (http://www.codeproject.com/KB/graphics/ReadingIPTCAPP14.aspx?q=iptc)
        /// </remarks>
        void ReadAPP13Segment(ushort length)
        {
            var data = ReadBlock(length);

            // The APP13 segment consists of:
            // - the string "Photoshop 3.0\u0000"
            // - followed by "8BIM"
            // - and then the section type "\u0004\u0004".
            // There might be multiple 8BIM sections with different types, but we're following
            // YAGNI for now and only deal with the one we're interested in (and hope that it's
            // the first one).
            var iptc_iim_length = IPTC_IIM_IDENTIFIER.Length;

            if (length < iptc_iim_length || data.Mid(0, iptc_iim_length) != IPTC_IIM_IDENTIFIER)
            {
                return;
            }

            // PS6 introduced a new header with variable length text
            var headerInfoLen = data.Mid(iptc_iim_length, 1).ToUShort();
            int lenToSkip;

            if (headerInfoLen > 0)
            {
                // PS6 header: 1 byte headerinfolen + headerinfo + 2 bytes 00 padding (?) + 2 bytes length
                lenToSkip = 1 + headerInfoLen + 4;
            }
            else
            {
                //old style: 4 bytes 00 padding (?) + 2 bytes length
                lenToSkip = 6;
            }
            data.RemoveRange(0, iptc_iim_length + lenToSkip);

            var reader = new IIM.IIMReader(data);
            var tag    = reader.Process();

            if (tag != null)
            {
                ImageTag.AddTag(tag);
            }
        }
예제 #12
0
        private void ReadiTXtChunk(int data_length)
        {
            long       position = Tell;
            ByteVector data     = ReadChunkData(data_length);

            CheckCRC(iTXt_CHUNK_TYPE, data, ReadCRC());
            if (data.StartsWith(XMP_CHUNK_HEADER))
            {
                ImageTag.AddTag(new XmpTag(data.Mid(XMP_CHUNK_HEADER.Length).ToString(StringType.UTF8), this));
                AddMetadataBlock(position - 8, data_length + 8 + 4);
                return;
            }
            int    terminator_index;
            string keyword = ReadKeyword(data, 0, out terminator_index);

            if (terminator_index + 2 >= data_length)
            {
                throw new CorruptFileException("Compression Flag and Compression Method byte expected");
            }
            byte       compression_flag   = data[terminator_index + 1];
            byte       compression_method = data[terminator_index + 2];
            ByteVector txt_data           = data.Mid(terminator_index + 1);

            if (compression_flag != 0x00)
            {
                txt_data = Decompress(compression_method, txt_data);
                if (txt_data == null)
                {
                    return;
                }
            }
            string value   = txt_data.ToString();
            PngTag png_tag = GetTag(TagTypes.Png, true)
                             as PngTag;

            if (png_tag.GetKeyword(keyword) == null)
            {
                png_tag.SetKeyword(keyword, value);
            }
            AddMetadataBlock(position - 8, data_length + 8 + 4);
        }
예제 #13
0
        /// <summary>
        ///    Reads an iTXt Chunk from file. The current position must be set
        ///    to the start of the Chunk Data. Such a Chunk may contain XMP data
        ///    or translated keywords.
        /// </summary>
        /// <param name="data_length">
        ///    A <see cref="System.Int32"/> with the length of the Chunk Data.
        /// </param>
        void ReadiTXtChunk(int data_length)
        {
            long position = Tell;

            // iTXt Chunk
            //
            // N Bytes     Keyword
            // 1 Byte      Null Separator
            // 1 Byte      Compression Flag (0 for uncompressed data)
            // 1 Byte      Compression Method
            // N Bytes     Language Tag
            // 1 Byte      Null Separator
            // N Bytes     Translated Keyword
            // 1 Byte      Null Terminator
            // N Bytes     Txt
            //
            // Followed by 4 Bytes CRC data

            ByteVector data = ReadChunkData(data_length);

            CheckCRC(iTXt_CHUNK_TYPE, data, ReadCRC());

            // handle XMP, which has a fixed header
            if (data.StartsWith(XMP_CHUNK_HEADER))
            {
                ImageTag.AddTag(new XmpTag(data.Mid(XMP_CHUNK_HEADER.Length).ToString(StringType.UTF8), this));

                AddMetadataBlock(position - 8, data_length + 8 + 4);

                return;
            }

            string keyword = ReadKeyword(data, 0, out var terminator_index);

            if (terminator_index + 2 >= data_length)
            {
                throw new CorruptFileException("Compression Flag and Compression Method byte expected");
            }

            byte compression_flag   = data[terminator_index + 1];
            byte compression_method = data[terminator_index + 2];

            //string language = ReadTerminatedString (data, terminator_index + 3, out terminator_index);
            //string translated_keyword = ReadTerminatedString (data, terminator_index + 1, out terminator_index);

            ByteVector txt_data = data.Mid(terminator_index + 1);

            if (compression_flag != 0x00)
            {
                txt_data = Decompress(compression_method, txt_data);

                // ignore unknown compression methods
                if (txt_data == null)
                {
                    return;
                }
            }

            string value   = txt_data.ToString();
            var    png_tag = GetTag(TagTypes.Png, true) as PngTag;

            if (png_tag.GetKeyword(keyword) == null)
            {
                png_tag.SetKeyword(keyword, value);
            }

            AddMetadataBlock(position - 8, data_length + 8 + 4);
        }
예제 #14
0
        /// <summary>
        ///    Reads an APP1 segment to find EXIF or XMP metadata.
        /// </summary>
        /// <param name="length">
        ///    The length of the segment that will be read.
        /// </param>
        private void ReadAPP1Segment(ushort length)
        {
            long       position = Tell;
            ByteVector data     = null;

            // for an Exif segment, the data block consists of 14 bytes of:
            //    * 6 bytes Exif identifier string
            //    * 2 bytes bigendian indication MM (or II)
            //    * 2 bytes Tiff magic number (42)
            //    * 4 bytes offset of the first IFD in this segment
            //
            //    the last two points are alreay encoded according to
            //    big- or littleendian
            int exif_header_length = 14;

            // could be an Exif segment
            if ((ImageTag.TagTypes & TagLib.TagTypes.TiffIFD) == 0x00 && length >= exif_header_length)
            {
                data = ReadBlock(exif_header_length);

                if (data.Count == exif_header_length &&
                    data.Mid(0, 6).ToString().Equals(EXIF_IDENTIFIER))
                {
                    bool is_bigendian = data.Mid(6, 2).ToString().Equals("MM");

                    ushort magic = data.Mid(8, 2).ToUShort(is_bigendian);
                    if (magic != 42)
                    {
                        throw new Exception(String.Format("Invalid TIFF magic: {0}", magic));
                    }

                    uint ifd_offset = data.Mid(10, 4).ToUInt(is_bigendian);

                    var exif   = new IFDTag();
                    var reader = new IFDReader(this, is_bigendian, exif.Structure, position + 6, ifd_offset, (uint)(length - 6));
                    reader.Read();
                    ImageTag.AddTag(exif);

                    AddMetadataBlock(position - 4, length + 4);

                    return;
                }
            }

            int xmp_header_length = XmpTag.XAP_NS.Length + 1;

            // could be an Xmp segment
            if ((ImageTag.TagTypes & TagLib.TagTypes.XMP) == 0x00 && length >= xmp_header_length)
            {
                // if already data is read for determining the Exif segment,
                // just read the remaining bytes.
                // NOTE: that (exif_header_length < xmp_header_length) holds
                if (data == null)
                {
                    data = ReadBlock(xmp_header_length);
                }
                else
                {
                    data.Add(ReadBlock(xmp_header_length - exif_header_length));
                }

                if (data.ToString().Equals(XmpTag.XAP_NS + "\0"))
                {
                    ByteVector xmp_data = ReadBlock(length - xmp_header_length);

                    ImageTag.AddTag(new XmpTag(xmp_data.ToString(), this));

                    AddMetadataBlock(position - 4, length + 4);
                }
            }
        }
예제 #15
0
        /// <summary>
        ///    Reads an Application Extension Block at the current position. The current
        ///    position must point to the 3rd byte of the comment block. (The other 2 bytes
        ///    are usually read before to identify the comment block)
        /// </summary>
        private void ReadApplicationExtensionBlock()
        {
            // Application Extension Block
            //
            // 1 Byte       Extension Introducer (0x21)
            // 1 Byte       Application Extension Label (0xFF)
            // 1 Byte       Block Size (0x0B - 11)
            // 8 Bytes      Application Identifier
            // 3 Bytes      Application Auth. Code
            // N Bytes      Application Data (sub blocks)
            // 1 Byte       Block Terminator (0x00)
            //
            // Note, the first 2 bytes are still read to identify the Comment Block.
            // Therefore, we only need to read the sub blocks and extract the data.
            long       position = Tell;
            ByteVector data     = ReadBlock(12);

            if (data.Count != 12)
            {
                throw new CorruptFileException("");
            }

            // Contains XMP data
            if (data.Mid(1, 8) == XMP_IDENTIFIER &&
                data.Mid(9, 3) == XMP_AUTH_CODE)
            {
                // XMP Data is not organized in sub-blocks

                // start of xmp data
                long data_start = Tell;

                // start of trailer start
                // FIXME: Since File.Find is still buggy, the following call does not work to find the end of the
                // XMP data. Therfore, we use here a different way for now.
                //long xmp_trailer_start = Find (new ByteVector (0x00), data_start);

                // Since searching just one byte is save, we search for the end of the xmp trailer which
                // consists of two 0x00 bytes and compute the expected start.
                long xmp_trailer_start = Find(new byte [] { 0x00 }, data_start) - XMP_MAGIC_TRAILER.Length + 2;

                Seek(data_start, SeekOrigin.Begin);

                if (xmp_trailer_start <= data_start)
                {
                    throw new CorruptFileException("No End of XMP data found");
                }

                // length of xmp data
                int data_length = (int)(xmp_trailer_start - data_start);

                ByteVector xmp_data = ReadBlock(data_length);
                ImageTag.AddTag(new XmpTag(xmp_data.ToString(StringType.UTF8), this));

                // 2 bytes where read before
                AddMetadataBlock(position - 2, 14 + data_length + XMP_MAGIC_TRAILER.Length);

                // set position behind the XMP block
                Seek(xmp_trailer_start + XMP_MAGIC_TRAILER.Length, SeekOrigin.Begin);
            }
            else
            {
                SkipSubBlocks();
            }
        }