示例#1
0
        /// <summary>
        /// 读取包含的Exif元数据APP1部分
        /// </summary>
        private void ReadAPP1()
        {
            // Find the APP1 section containing Exif metadata
            app1 = file.Sections.Find(a => (a.Marker == JPEGMarker.APP1) &&
                                      (Encoding.ASCII.GetString(a.Header, 0, 6) == "Exif\0\0"));

            // If there is no APP1 section, add a new one after the last APP0 section (if any).
            if (app1 == null)
            {
                int insertionIndex = file.Sections.FindLastIndex(a => a.Marker == JPEGMarker.APP0);
                if (insertionIndex == -1)
                {
                    insertionIndex = 0;
                }
                insertionIndex++;
                ByteOrder = BitConverterEx.ByteOrder.BigEndian;
                app1      = new JPEGSection(JPEGMarker.APP1);
                file.Sections.Insert(insertionIndex, app1);
                return;
            }

            byte[] header = app1.Header;
            SortedList <int, IFD> ifdqueue = new SortedList <int, IFD>();

            makerNoteOffset = 0;

            // TIFF header
            int tiffoffset = 6;

            if (header[tiffoffset] == 0x49)
            {
                ByteOrder = BitConverterEx.ByteOrder.LittleEndian;
            }
            else
            {
                ByteOrder = BitConverterEx.ByteOrder.BigEndian;
            }
            BitConverterEx conv = new BitConverterEx(ByteOrder, BitConverterEx.ByteOrder.System);

            // Offset to 0th IFD
            int ifd0offset = (int)conv.ToUInt32(header, tiffoffset + 4);

            ifdqueue.Add(ifd0offset, IFD.Zeroth);

            int thumboffset = -1;
            int thumblength = 0;
            int thumbtype   = -1;

            // Read IFDs
            while (ifdqueue.Count != 0)
            {
                int ifdoffset  = tiffoffset + ifdqueue.Keys[0];
                IFD currentifd = ifdqueue.Values[0];
                ifdqueue.RemoveAt(0);

                // Field count
                ushort fieldcount = conv.ToUInt16(header, ifdoffset);
                for (short i = 0; i < fieldcount; i++)
                {
                    // Read field info
                    int    fieldoffset = ifdoffset + 2 + 12 * i;
                    ushort tag         = conv.ToUInt16(header, fieldoffset);
                    ushort type        = conv.ToUInt16(header, fieldoffset + 2);
                    uint   count       = conv.ToUInt32(header, fieldoffset + 4);
                    byte[] value       = new byte[4];
                    Array.Copy(header, fieldoffset + 8, value, 0, 4);

                    // Fields containing offsets to other IFDs
                    if (currentifd == IFD.Zeroth && tag == 0x8769)
                    {
                        int exififdpointer = (int)conv.ToUInt32(value, 0);
                        ifdqueue.Add(exififdpointer, IFD.EXIF);
                    }
                    else if (currentifd == IFD.Zeroth && tag == 0x8825)
                    {
                        int gpsifdpointer = (int)conv.ToUInt32(value, 0);
                        ifdqueue.Add(gpsifdpointer, IFD.GPS);
                    }
                    else if (currentifd == IFD.EXIF && tag == 0xa005)
                    {
                        int interopifdpointer = (int)conv.ToUInt32(value, 0);
                        ifdqueue.Add(interopifdpointer, IFD.Interop);
                    }

                    // Save the offset to maker note data
                    if (currentifd == IFD.EXIF && tag == 37500)
                    {
                        makerNoteOffset = conv.ToUInt32(value, 0);
                    }

                    // Calculate the bytes we need to read
                    uint baselength = 0;
                    if (type == 1 || type == 2 || type == 7)
                    {
                        baselength = 1;
                    }
                    else if (type == 3)
                    {
                        baselength = 2;
                    }
                    else if (type == 4 || type == 9)
                    {
                        baselength = 4;
                    }
                    else if (type == 5 || type == 10)
                    {
                        baselength = 8;
                    }
                    uint totallength = count * baselength;

                    // If field value does not fit in 4 bytes
                    // the value field is an offset to the actual
                    // field value
                    int fieldposition = 0;
                    if (totallength > 4)
                    {
                        fieldposition = tiffoffset + (int)conv.ToUInt32(value, 0);
                        value         = new byte[totallength];
                        Array.Copy(header, fieldposition, value, 0, totallength);
                    }

                    // Compressed thumbnail data
                    if (currentifd == IFD.First && tag == 0x201)
                    {
                        thumbtype   = 0;
                        thumboffset = (int)conv.ToUInt32(value, 0);
                    }
                    else if (currentifd == IFD.First && tag == 0x202)
                    {
                        thumblength = (int)conv.ToUInt32(value, 0);
                    }

                    // Uncompressed thumbnail data
                    if (currentifd == IFD.First && tag == 0x111)
                    {
                        thumbtype = 1;
                        // Offset to first strip
                        if (type == 3)
                        {
                            thumboffset = (int)conv.ToUInt16(value, 0);
                        }
                        else
                        {
                            thumboffset = (int)conv.ToUInt32(value, 0);
                        }
                    }
                    else if (currentifd == IFD.First && tag == 0x117)
                    {
                        thumblength = 0;
                        for (int j = 0; j < count; j++)
                        {
                            if (type == 3)
                            {
                                thumblength += (int)conv.ToUInt16(value, 0);
                            }
                            else
                            {
                                thumblength += (int)conv.ToUInt32(value, 0);
                            }
                        }
                    }

                    // Create the exif property from the interop data
                    ExifProperty prop = ExifPropertyFactory.Get(tag, type, count, value, ByteOrder, currentifd);
                    Properties.Add(prop.Tag, prop);
                }

                // 1st IFD pointer
                int firstifdpointer = (int)conv.ToUInt32(header, ifdoffset + 2 + 12 * fieldcount);
                if (firstifdpointer != 0)
                {
                    ifdqueue.Add(firstifdpointer, IFD.First);
                }
                // Read thumbnail
                if (thumboffset != -1 && thumblength != 0 && Thumbnail == null)
                {
                    if (thumbtype == 0)
                    {
                        using (MemoryStream ts = new MemoryStream(header, tiffoffset + thumboffset, thumblength))
                        {
                            Thumbnail = new JPEGFile(ts);
                        }
                    }
                }
            }
        }
示例#2
0
    /// <summary>
    ///     Initializes a new instance of the <see cref="ExifFile" /> class.
    /// </summary>
    /// <param name="stream">A <see cref="Sytem.IO.Stream" /> that contains image data.</param>
    /// <param name="encoding">The encoding to be used for text metadata when the source encoding is unknown.</param>
    protected internal JPEGFile(Stream stream, Encoding encoding)
    {
        Format       = ImageFileFormat.JPEG;
        Sections     = new List <JPEGSection>();
        TrailingData = new byte[0];
        Encoding     = encoding;

        stream.Seek(0, SeekOrigin.Begin);

        // Read the Start of Image (SOI) marker. SOI marker is represented
        // with two bytes: 0xFF, 0xD8.
        var markerbytes = new byte[2];

        if (stream.Read(markerbytes, 0, 2) != 2 || markerbytes[0] != 0xFF || markerbytes[1] != 0xD8)
        {
            throw new NotValidJPEGFileException();
        }

        stream.Seek(0, SeekOrigin.Begin);

        // Search and read sections until we reach the end of file.
        while (stream.Position != stream.Length)
        {
            // Read the next section marker. Section markers are two bytes
            // with values 0xFF, 0x?? where ?? must not be 0x00 or 0xFF.
            if (stream.Read(markerbytes, 0, 2) != 2 || markerbytes[0] != 0xFF || markerbytes[1] == 0x00 ||
                markerbytes[1] == 0xFF)
            {
                throw new NotValidJPEGFileException();
            }

            var marker = (JPEGMarker)markerbytes[1];

            var header = new byte[0];

            // SOI, EOI and RST markers do not contain any header
            if (marker != JPEGMarker.SOI && marker != JPEGMarker.EOI &&
                !(marker >= JPEGMarker.RST0 && marker <= JPEGMarker.RST7))
            {
                // Length of the header including the length bytes.
                // This value is a 16-bit unsigned integer
                // in big endian byte-order.
                var lengthbytes = new byte[2];
                if (stream.Read(lengthbytes, 0, 2) != 2)
                {
                    throw new NotValidJPEGFileException();
                }

                long length = BitConverterEx.BigEndian.ToUInt16(lengthbytes, 0);

                // Read section header.
                header = new byte[length - 2];
                var bytestoread = header.Length;
                while (bytestoread > 0)
                {
                    var count     = Math.Min(bytestoread, 4 * 1024);
                    var bytesread = stream.Read(header, header.Length - bytestoread, count);
                    if (bytesread == 0)
                    {
                        throw new NotValidJPEGFileException();
                    }

                    bytestoread -= bytesread;
                }
            }

            // Start of Scan (SOS) sections and RST sections are immediately
            // followed by entropy coded data. For that, we need to read until
            // the next section marker once we reach a SOS or RST.
            var entropydata = new byte[0];
            if (marker == JPEGMarker.SOS || (marker >= JPEGMarker.RST0 && marker <= JPEGMarker.RST7))
            {
                var position = stream.Position;

                // Search for the next section marker
                while (true)
                {
                    // Search for an 0xFF indicating start of a marker
                    var nextbyte = 0;
                    do
                    {
                        nextbyte = stream.ReadByte();
                        if (nextbyte == -1)
                        {
                            throw new NotValidJPEGFileException();
                        }
                    }while ((byte)nextbyte != 0xFF);

                    // Skip filler bytes (0xFF)
                    do
                    {
                        nextbyte = stream.ReadByte();
                        if (nextbyte == -1)
                        {
                            throw new NotValidJPEGFileException();
                        }
                    }while ((byte)nextbyte == 0xFF);

                    // Looks like a section marker. The next byte must not be 0x00.
                    if ((byte)nextbyte != 0x00)
                    {
                        // We reached a section marker. Calculate the
                        // length of the entropy coded data.
                        stream.Seek(-2, SeekOrigin.Current);
                        var edlength = stream.Position - position;
                        stream.Seek(-edlength, SeekOrigin.Current);

                        // Read entropy coded data
                        entropydata = new byte[edlength];
                        var bytestoread = entropydata.Length;
                        while (bytestoread > 0)
                        {
                            var count     = Math.Min(bytestoread, 4 * 1024);
                            var bytesread = stream.Read(entropydata, entropydata.Length - bytestoread, count);
                            if (bytesread == 0)
                            {
                                throw new NotValidJPEGFileException();
                            }

                            bytestoread -= bytesread;
                        }

                        break;
                    }
                }
            }

            // Store section.
            var section = new JPEGSection(marker, header, entropydata);
            Sections.Add(section);

            // Some propriety formats store data past the EOI marker
            if (marker == JPEGMarker.EOI)
            {
                var bytestoread = (int)(stream.Length - stream.Position);
                TrailingData = new byte[bytestoread];
                while (bytestoread > 0)
                {
                    var count     = Math.Min(bytestoread, 4 * 1024);
                    var bytesread = stream.Read(TrailingData, TrailingData.Length - bytestoread, count);
                    if (bytesread == 0)
                    {
                        throw new NotValidJPEGFileException();
                    }

                    bytestoread -= bytesread;
                }
            }
        }

        // Read metadata sections
        ReadJFIFAPP0();
        ReadJFXXAPP0();
        ReadExifAPP1();

        // Process the maker note
        _makerNoteProcessed = false;
    }