/// <summary>
        /// Reads the APP0 section containing JFIF extension metadata.
        /// </summary>
        private void ReadJFXXAPP0()
        {
            // Find the APP0 section containing JFIF metadata
            jfxxApp0 = Sections.Find(a => (a.Marker == JPEGMarker.APP0) &&
                                     a.Header.Length >= 5 &&
                                     (Encoding.ASCII.GetString(a.Header, 0, 5) == "JFXX\0"));

            // If there is no APP0 section, return.
            if (jfxxApp0 == null)
            {
                return;
            }

            byte[] header = jfxxApp0.Header;

            // Version
            JFIFExtension version = (JFIFExtension)header[5];

            Properties.Add(new ExifEnumProperty <JFIFExtension>(ExifTag.JFXXExtensionCode, version));

            // Read thumbnail
            if (version == JFIFExtension.ThumbnailJPEG)
            {
                byte[] data = new byte[header.Length - 6];
                Array.Copy(header, 6, data, 0, data.Length);
                Properties.Add(new JFIFThumbnailProperty(ExifTag.JFXXThumbnail, new JFIFThumbnail(JFIFThumbnail.ImageFormat.JPEG, data)));
            }
            else if (version == JFIFExtension.Thumbnail24BitRGB)
            {
                // Thumbnails pixel count
                byte xthumbnail = header[6];
                Properties.Add(new ExifByte(ExifTag.JFXXXThumbnail, xthumbnail));
                byte ythumbnail = header[7];
                Properties.Add(new ExifByte(ExifTag.JFXXYThumbnail, ythumbnail));
                byte[] data = new byte[3 * xthumbnail * ythumbnail];
                Array.Copy(header, 8, data, 0, data.Length);
                Properties.Add(new JFIFThumbnailProperty(ExifTag.JFXXThumbnail, new JFIFThumbnail(JFIFThumbnail.ImageFormat.BMP24Bit, data)));
            }
            else if (version == JFIFExtension.ThumbnailPaletteRGB)
            {
                // Thumbnails pixel count
                byte xthumbnail = header[6];
                Properties.Add(new ExifByte(ExifTag.JFXXXThumbnail, xthumbnail));
                byte ythumbnail = header[7];
                Properties.Add(new ExifByte(ExifTag.JFXXYThumbnail, ythumbnail));
                byte[] palette = new byte[768];
                Array.Copy(header, 8, palette, 0, palette.Length);
                byte[] data = new byte[xthumbnail * ythumbnail];
                Array.Copy(header, 8 + 768, data, 0, data.Length);
                Properties.Add(new JFIFThumbnailProperty(ExifTag.JFXXThumbnail, new JFIFThumbnail(palette, data)));
            }
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Reads the APP0 section containing JFIF metadata.
        /// </summary>
        private void ReadJFIFAPP0()
        {
            // Find the APP0 section containing JFIF metadata
            jfifApp0 = Sections.Find(a => (a.Marker == JPEGMarker.APP0) &&
                                     a.Header.Length >= 5 &&
                                     (Encoding.ASCII.GetString(a.Header, 0, 5) == "JFIF\0"));

            // If there is no APP0 section, return.
            if (jfifApp0 == null)
            {
                return;
            }

            byte[]         header   = jfifApp0.Header;
            BitConverterEx jfifConv = BitConverterEx.BigEndian;

            // Version
            ushort version = jfifConv.ToUInt16(header, 5);

            Properties.Add(new JFIFVersion(ExifTag.JFIFVersion, version));

            // Units
            byte unit = header[7];

            Properties.Add(new ExifEnumProperty <JFIFDensityUnit>(ExifTag.JFIFUnits, (JFIFDensityUnit)unit));

            // X and Y densities
            ushort xdensity = jfifConv.ToUInt16(header, 8);

            Properties.Add(new ExifUShort(ExifTag.XDensity, xdensity));
            ushort ydensity = jfifConv.ToUInt16(header, 10);

            Properties.Add(new ExifUShort(ExifTag.YDensity, ydensity));

            // Thumbnails pixel count
            byte xthumbnail = header[12];

            Properties.Add(new ExifByte(ExifTag.JFIFXThumbnail, xthumbnail));
            byte ythumbnail = header[13];

            Properties.Add(new ExifByte(ExifTag.JFIFYThumbnail, ythumbnail));

            // Read JFIF thumbnail
            int n = xthumbnail * ythumbnail;

            byte[] jfifThumbnail = new byte[n];
            Array.Copy(header, 14, jfifThumbnail, 0, n);
            Properties.Add(new JFIFThumbnailProperty(ExifTag.JFIFThumbnail, new JFIFThumbnail(JFIFThumbnail.ImageFormat.JPEG, jfifThumbnail)));
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Replaces the contents of the APP0 section with the JFIF extension properties.
        /// </summary>
        private bool WriteJFXXApp0()
        {
            // Which IFD sections do we have?
            List <ExifProperty> ifdjfef = new List <ExifProperty>();

            foreach (ExifProperty prop in Properties)
            {
                if (prop.IFD == IFD.JFXX)
                {
                    ifdjfef.Add(prop);
                }
            }

            if (ifdjfef.Count == 0)
            {
                // Nothing to write
                if (jfxxApp0 != null)
                {
                    Errors.Add(new ImageError(Severity.Info, "Removing unused JFXX APP0 segment."));
                    Sections.Remove(jfxxApp0);
                    jfxxApp0 = null;
                }
                return(false);
            }

            // Create a memory stream to write the APP0 section to
            using (MemoryStream ms = new MemoryStream())
            {
                // JFIF identifer
                ms.Write(Encoding.ASCII.GetBytes("JFXX\0"), 0, 5);

                // Write tags
                foreach (ExifProperty prop in ifdjfef)
                {
                    ExifInterOperability interop = prop.Interoperability;
                    byte[] data = interop.Data;
                    if (BitConverterEx.SystemByteOrder != BitConverterEx.ByteOrder.BigEndian && interop.TypeID == InterOpType.SHORT)
                    {
                        Array.Reverse(data);
                    }
                    ms.Write(data, 0, data.Length);
                }

                // Return APP0 header
                jfxxApp0.Header = ms.ToArray();
            }
            return(true);
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Reads the APP1 section containing Exif metadata.
        /// </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;
            }
//#if DEBUG
//            mMap = new MemoryBinStream();
//            mMap.Seek(0, SeekOrigin.Begin);
//            mMap.Write(new Bin("Exif Marker", 3, 6));
//#endif

            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;

#if DEBUG
            mMap.Write(new Bin("Byte Order: " + ByteOrder.ToString(), 4, 2));
            mMap.Write(new Bin("TIFF ID: 0x00, 0x2A", 4, 2));
            mMap.Write(new Bin("Offset to 0th IFD: " + ifd0offset.ToString(), 4, 4));
#endif
            // 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);
#if DEBUG
                mMap.Seek(ifdoffset, SeekOrigin.Begin);
                mMap.Write(new Bin(currentifd.ToString() + " IFD Field Count: " + fieldcount.ToString(), 5, 2));
#endif
                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);
#if DEBUG
                    mMap.Seek(fieldoffset, SeekOrigin.Begin);
                    mMap.Write(new Bin(ExifTagFactory.GetTagName(currentifd, tag) + " ID: " + tag.ToString(), 6, 2, prop));
                    mMap.Write(new Bin(ExifTagFactory.GetTagName(currentifd, tag) + " Type: " + type.ToString(), 6, 2, prop));
                    mMap.Write(new Bin(ExifTagFactory.GetTagName(currentifd, tag) + " Count: " + count.ToString(), 6, 4, prop));
                    mMap.Write(new Bin(ExifTagFactory.GetTagName(currentifd, tag) + " Value: " + string.Format("[0x{0:x2}, 0x{1:x2}, 0x{2:x2}, 0x{3:x2}]", value[0], value[1], value[2], value[3]), 6, 4, prop));
                    if (totallength > 4)
                    {
                        mMap.Seek(fieldposition, SeekOrigin.Begin);
                        mMap.Write(new Bin(ExifTagFactory.GetTagName(currentifd, tag) + " Data", 7, totallength, prop));
                    }
#endif
                }

                // 1st IFD pointer
                int firstifdpointer = (int)conv.ToUInt32(header, ifdoffset + 2 + 12 * fieldcount);
                if (firstifdpointer != 0)
                {
                    ifdqueue.Add(firstifdpointer, IFD.First);
                }
#if DEBUG
                mMap.Seek(ifdoffset + 2 + 12 * fieldcount, SeekOrigin.Begin);
                mMap.Write(new Bin("1st IFD Pointer: " + firstifdpointer.ToString(), 5, 4));
#endif
                // 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);
                        }
                    }
#if DEBUG
                    mMap.Seek(tiffoffset + thumboffset, SeekOrigin.Begin);
                    mMap.Write(new Bin("Thumbnail", 8, thumblength));
#endif
                }
            }
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Initializes a new instance of the <see cref="JPEGFile"/> class.
        /// </summary>
        /// <param name="stream">A stream that contains image data.</param>
        /// <param name="encoding">The encoding to be used for text metadata when the source encoding is unknown.</param>
        /// <param name="readTrailingData">Whether to read data beyond the EOI (end of image) marker.</param>
        protected internal JPEGFile(MemoryStream stream, Encoding encoding, bool readTrailingData = false)
        {
            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.
            byte[] 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();
                }

                JPEGMarker marker = (JPEGMarker)markerbytes[1];

                byte[] 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.
                    byte[] lengthbytes = new byte[2];
                    if (stream.Read(lengthbytes, 0, 2) != 2)
                    {
                        throw new NotValidJPEGFileException();
                    }
                    long length = (long)BitConverterEx.BigEndian.ToUInt16(lengthbytes, 0);

                    // Read section header.
                    header = Utility.GetStreamBytes(stream, length - 2);
                }

                // 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.
                byte[] entropydata = new byte[0];
                if (marker == JPEGMarker.SOS || (marker >= JPEGMarker.RST0 && marker <= JPEGMarker.RST7))
                {
                    long position = stream.Position;

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

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

                        // We either reached the end of file before a new marker(this would indicate
                        // a corrupt image file) or we are at a section marker. In that case the
                        // next byte must not be 0x00.
                        if (nextbyte != 0)
                        {
                            // If we reached a section marker seek back to just before the marker.
                            if (nextbyte != -1)
                            {
                                stream.Seek(-2, SeekOrigin.Current);
                            }

                            // Calculate the length of the entropy coded data.
                            long edlength = stream.Position - position;
                            stream.Seek(position, SeekOrigin.Begin);

                            // Read entropy coded data
                            entropydata = Utility.GetStreamBytes(stream, edlength);

                            break;
                        }
                    }
                }

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

                // Some propriety formats store data past the EOI marker
                if (marker == JPEGMarker.EOI)
                {
                    if (readTrailingData)
                    {
                        long eoflength = stream.Length - stream.Position;
                        TrailingData = Utility.GetStreamBytes(stream, eoflength);
                    }
                    // stop reading once we are past the EOI marker
                    break;
                }
            }

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

            // Process the maker note
            makerNoteProcessed = false;
        }
Ejemplo n.º 6
0
        /// <summary>
        /// Reads the APP1 section containing Exif metadata.
        /// </summary>
        private void ReadExifAPP1()
        {
            // Find the APP1 section containing Exif metadata
            exifApp1 = Sections.Find(a => (a.Marker == JPEGMarker.APP1) &&
                                     a.Header.Length >= 6 &&
                                     (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 (exifApp1 == null)
            {
                int insertionIndex = Sections.FindLastIndex(a => a.Marker == JPEGMarker.APP0);
                if (insertionIndex == -1)
                {
                    insertionIndex = 0;
                }
                insertionIndex++;
                exifApp1 = new JPEGSection(JPEGMarker.APP1);
                Sections.Insert(insertionIndex, exifApp1);
                if (BitConverterEx.SystemByteOrder == BitConverterEx.ByteOrder.LittleEndian)
                {
                    ByteOrder = BitConverterEx.ByteOrder.LittleEndian;
                }
                else
                {
                    ByteOrder = BitConverterEx.ByteOrder.BigEndian;
                }
                return;
            }

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

            makerNoteOffset = 0;

            // TIFF header
            int tiffoffset = 6;

            if (header[tiffoffset] == 0x49 && header[tiffoffset + 1] == 0x49)
            {
                ByteOrder = BitConverterEx.ByteOrder.LittleEndian;
            }
            else if (header[tiffoffset] == 0x4D && header[tiffoffset + 1] == 0x4D)
            {
                ByteOrder = BitConverterEx.ByteOrder.BigEndian;
            }
            else
            {
                throw new NotValidExifFileException();
            }

            // TIFF header may have a different byte order
            BitConverterEx.ByteOrder tiffByteOrder = ByteOrder;
            if (BitConverterEx.LittleEndian.ToUInt16(header, tiffoffset + 2) == 42)
            {
                tiffByteOrder = BitConverterEx.ByteOrder.LittleEndian;
            }
            else if (BitConverterEx.BigEndian.ToUInt16(header, tiffoffset + 2) == 42)
            {
                tiffByteOrder = BitConverterEx.ByteOrder.BigEndian;
            }
            else
            {
                throw new NotValidExifFileException();
            }

            // Offset to 0th IFD
            if (header.Length - (tiffoffset + 4) >= 4)
            {
                int ifd0offset = (int)BitConverterEx.ToUInt32(header, tiffoffset + 4, tiffByteOrder, BitConverterEx.SystemByteOrder);
                ifdqueue.Add(ifd0offset, IFD.Zeroth);
            }

            BitConverterEx conv        = new BitConverterEx(ByteOrder, BitConverterEx.SystemByteOrder);
            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);
                if (ifdoffset > header.Length - 1 || ifdoffset + 2 > header.Length)
                {
                    Errors.Add(new ImageError(Severity.Warning, $"IFD field count overflow for IFD {currentifd}."));
                    continue;
                }
                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 == 6 || type == 7)
                    {
                        baselength = 1;
                    }
                    else if (type == 3 || type == 8)
                    {
                        baselength = 2;
                    }
                    else if (type == 4 || type == 9)
                    {
                        baselength = 4;
                    }
                    else if (type == 5 || type == 10)
                    {
                        baselength = 8;
                    }
                    else // Unknown or invalid type
                    {
                        continue; // Skip and keep going
                    }
                    int totallength = (int)(count * baselength);
                    if (totallength < 0)
                    {
                        Errors.Add(new ImageError(Severity.Warning, $"Field length overflow for tag {tag}."));
                        continue;
                    }

                    // 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);
                        if (fieldposition < 0)
                        {
                            Errors.Add(new ImageError(Severity.Warning, $"Field offset overflow for tag {tag}."));
                            continue;
                        }
                        else if (fieldposition > header.Length - 1)
                        {
                            Errors.Add(new ImageError(Severity.Warning, $"Field offset for tag {tag} exceeds header length."));
                            continue;
                        }
                        else if (fieldposition + totallength > header.Length)
                        {
                            Errors.Add(new ImageError(Severity.Warning, $"Field length for tag {tag} exceeds header length."));
                            continue;
                        }
                        else if (totallength > int.MaxValue)
                        {
                            Errors.Add(new ImageError(Severity.Warning, $"Field length for tag {tag} exceeds maximum allowed length."));
                            continue;
                        }

                        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, Encoding);
                    Properties.Add(prop);
                }

                // 1st IFD pointer
                if (currentifd == IFD.Zeroth)
                {
                    int firstifdoffset = ifdoffset + 2 + 12 * fieldcount;
                    if (firstifdoffset + 4 <= header.Length)
                    {
                        int firstifdpointer = (int)conv.ToUInt32(header, firstifdoffset);
                        if (firstifdpointer != 0)
                        {
                            if (firstifdpointer + 2 <= header.Length)
                            {
                                ifdqueue.Add(firstifdpointer, IFD.First);
                            }
                            else
                            {
                                Errors.Add(new ImageError(Severity.Warning, $"Invalid first IFD pointer."));
                            }
                        }
                    }
                    else
                    {
                        Errors.Add(new ImageError(Severity.Warning, $"Invalid first IFD offset."));
                    }
                }
                // Read thumbnail
                if (thumboffset != -1 && thumblength != 0 && Thumbnail == null)
                {
                    if (thumbtype == 0)
                    {
                        // Ensure that the thumbnail length does not exceed header length
                        if (thumblength > header.Length - tiffoffset - thumboffset)
                        {
                            Errors.Add(new ImageError(Severity.Warning, $"Thumbnail size exceeds header length."));
                            Thumbnail = null;
                        }
                        else
                        {
                            Thumbnail = new byte[thumblength];
                            Array.Copy(header, tiffoffset + thumboffset, Thumbnail, 0, thumblength);
                        }
                    }
                }
            }
        }
Ejemplo n.º 7
0
        /// <summary>
        /// Reads the given stream.
        /// </summary>
        /// <param name="stream">The data stream used to load the image.</param>
        /// <exception cref="NotValidJPEGFileException"></exception>
        private void Read(Stream stream)
        {
            Sections = new List<JPEGSection>();

            using (stream)
            {
                // Read the Start of Image (SOI) marker. SOI marker is represented
                // with two bytes: 0xFF, 0xD8.
                byte[] 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();

                    JPEGMarker marker = (JPEGMarker)markerbytes[1];

                    byte[] 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.
                        byte[] lengthbytes = new byte[2];
                        if (stream.Read(lengthbytes, 0, 2) != 2)
                            throw new NotValidJPEGFileException();
                        long length = (long)BitConverterEx.BigEndian.ToUInt16(lengthbytes, 0);

                        // Read section header.
                        header = new byte[length - 2];
                        int bytestoread = header.Length;
                        while (bytestoread > 0)
                        {
                            int count = Math.Min(bytestoread, 4 * 1024);
                            int 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.
                    byte[] entropydata = new byte[0];
                    if (marker == JPEGMarker.SOS || (marker >= JPEGMarker.RST0 && marker <= JPEGMarker.RST7))
                    {
                        long position = stream.Position;

                        // Search for the next section marker
                        while (true)
                        {
                            // Search for an 0xFF indicating start of a marker
                            int 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);
                                long edlength = stream.Position - position;
                                stream.Seek(-edlength, SeekOrigin.Current);

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

                                break;
                            }
                        }
                    }

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

                    // Some propriety formats store data past the EOI marker
                    if (marker == JPEGMarker.EOI)
                    {
                        int bytestoread = (int)(stream.Length - stream.Position);
                        TrailingData = new byte[bytestoread];
                        while (bytestoread > 0)
                        {
                            int count = (int)Math.Min(bytestoread, 4 * 1024);
                            int bytesread = stream.Read(TrailingData, TrailingData.Length - bytestoread, count);
                            if (bytesread == 0)
                                throw new NotValidJPEGFileException();
                            bytestoread -= bytesread;
                        }
                    }
                }
                stream.Close();
            }
        }
Ejemplo n.º 8
0
        /// <summary>
        /// Reads the APP1 section containing Exif metadata.
        /// </summary>
        private void ReadExifAPP1()
        {
            // Find the APP1 section containing Exif metadata
            exifApp1 = Sections.Find(a => (a.Marker == JPEGMarker.APP1) &&
                                     a.Header.Length >= 6 &&
                                     (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 (exifApp1 == null)
            {
                int insertionIndex = Sections.FindLastIndex(a => a.Marker == JPEGMarker.APP0);
                if (insertionIndex == -1)
                {
                    insertionIndex = 0;
                }
                insertionIndex++;
                exifApp1 = new JPEGSection(JPEGMarker.APP1);
                Sections.Insert(insertionIndex, exifApp1);
                if (BitConverterEx.SystemByteOrder == BitConverterEx.ByteOrder.LittleEndian)
                {
                    ByteOrder = BitConverterEx.ByteOrder.LittleEndian;
                }
                else
                {
                    ByteOrder = BitConverterEx.ByteOrder.BigEndian;
                }
                return;
            }

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

            makerNoteOffset = 0;

            // TIFF header
            int tiffoffset = 6;

            if (header[tiffoffset] == 0x49 && header[tiffoffset + 1] == 0x49)
            {
                ByteOrder = BitConverterEx.ByteOrder.LittleEndian;
            }
            else if (header[tiffoffset] == 0x4D && header[tiffoffset + 1] == 0x4D)
            {
                ByteOrder = BitConverterEx.ByteOrder.BigEndian;
            }
            else
            {
                throw new NotValidExifFileException();
            }

            // TIFF header may have a different byte order
            BitConverterEx.ByteOrder tiffByteOrder = ByteOrder;
            if (BitConverterEx.LittleEndian.ToUInt16(header, tiffoffset + 2) == 42)
            {
                tiffByteOrder = BitConverterEx.ByteOrder.LittleEndian;
            }
            else if (BitConverterEx.BigEndian.ToUInt16(header, tiffoffset + 2) == 42)
            {
                tiffByteOrder = BitConverterEx.ByteOrder.BigEndian;
            }
            else
            {
                throw new NotValidExifFileException();
            }

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

            ifdqueue.Add(ifd0offset, IFD.Zeroth);

            BitConverterEx conv        = new BitConverterEx(ByteOrder, BitConverterEx.SystemByteOrder);
            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);
                }

                // 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 = ImageFile.FromStream(ts);
                        }
                    }
                }
            }
        }
Ejemplo n.º 9
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>
        protected internal JPEGFile(Stream stream)
        {
            Format       = ImageFileFormat.JPEG;
            Sections     = new List <JPEGSection>();
            TrailingData = new byte[0];

            stream.Seek(0, SeekOrigin.Begin);

            // Read the Start of Image (SOI) marker. SOI marker is represented
            // with two bytes: 0xFF, 0xD8.
            byte[] 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();
                }

                JPEGMarker marker = (JPEGMarker)markerbytes[1];

                byte[] 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.
                    byte[] lengthbytes = new byte[2];
                    if (stream.Read(lengthbytes, 0, 2) != 2)
                    {
                        throw new NotValidJPEGFileException();
                    }
                    long length = (long)BitConverterEx.BigEndian.ToUInt16(lengthbytes, 0);

                    // Read section header.
                    header = new byte[length - 2];
                    int bytestoread = header.Length;
                    while (bytestoread > 0)
                    {
                        int count     = Math.Min(bytestoread, 4 * 1024);
                        int 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.
                byte[] entropydata = new byte[0];
                if (marker == JPEGMarker.SOS || (marker >= JPEGMarker.RST0 && marker <= JPEGMarker.RST7))
                {
                    long position = stream.Position;

                    // Search for the next section marker
                    while (true)
                    {
                        // Search for an 0xFF indicating start of a marker
                        int 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);
                            long edlength = stream.Position - position;
                            stream.Seek(-edlength, SeekOrigin.Current);

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

                            break;
                        }
                    }
                }

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

                // Some propriety formats store data past the EOI marker
                if (marker == JPEGMarker.EOI)
                {
                    int bytestoread = (int)(stream.Length - stream.Position);
                    TrailingData = new byte[bytestoread];
                    while (bytestoread > 0)
                    {
                        int count     = (int)Math.Min(bytestoread, 4 * 1024);
                        int 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;
        }
Ejemplo n.º 10
0
        /// <summary>
        /// Reads the APP1 section containing Exif metadata.
        /// </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;
            }
            #if DEBUG
            mMap = new MemoryBinStream();
            mMap.Seek(0, SeekOrigin.Begin);
            mMap.Write(new Bin("Exif Marker", 3, 6));
            #endif

            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;
            #if DEBUG
            mMap.Write(new Bin("Byte Order: " + ByteOrder.ToString(), 4, 2));
            mMap.Write(new Bin("TIFF ID: 0x00, 0x2A", 4, 2));
            mMap.Write(new Bin("Offset to 0th IFD: " + ifd0offset.ToString(), 4, 4));
            #endif
            // 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);
            #if DEBUG
                mMap.Seek(ifdoffset, SeekOrigin.Begin);
                mMap.Write(new Bin(currentifd.ToString() + " IFD Field Count: " + fieldcount.ToString(), 5, 2));
            #endif
                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);
            #if DEBUG
                    mMap.Seek(fieldoffset, SeekOrigin.Begin);
                    mMap.Write(new Bin(ExifTagFactory.GetTagName(currentifd, tag) + " ID: " + tag.ToString(), 6, 2, prop));
                    mMap.Write(new Bin(ExifTagFactory.GetTagName(currentifd, tag) + " Type: " + type.ToString(), 6, 2, prop));
                    mMap.Write(new Bin(ExifTagFactory.GetTagName(currentifd, tag) + " Count: " + count.ToString(), 6, 4, prop));
                    mMap.Write(new Bin(ExifTagFactory.GetTagName(currentifd, tag) + " Value: " + string.Format("[0x{0:x2}, 0x{1:x2}, 0x{2:x2}, 0x{3:x2}]", value[0], value[1], value[2], value[3]), 6, 4, prop));
                    if (totallength > 4)
                    {
                        mMap.Seek(fieldposition, SeekOrigin.Begin);
                        mMap.Write(new Bin(ExifTagFactory.GetTagName(currentifd, tag) + " Data", 7, totallength, prop));
                    }
            #endif
                }

                // 1st IFD pointer
                int firstifdpointer = (int)conv.ToUInt32(header, ifdoffset + 2 + 12 * fieldcount);
                if (firstifdpointer != 0)
                    ifdqueue.Add(firstifdpointer, IFD.First);
            #if DEBUG
                mMap.Seek(ifdoffset + 2 + 12 * fieldcount, SeekOrigin.Begin);
                mMap.Write(new Bin("1st IFD Pointer: " + firstifdpointer.ToString(), 5, 4));
            #endif
                // 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);
                        }
                    }
            #if DEBUG
                    mMap.Seek(tiffoffset + thumboffset, SeekOrigin.Begin);
                    mMap.Write(new Bin("Thumbnail", 8, thumblength));
            #endif
                }
            }
        }
Ejemplo n.º 11
0
        /// <summary>
        /// Reads the APP0 section containing JFIF extension metadata.
        /// </summary>
        private void ReadJFXXAPP0()
        {
            // Find the APP0 section containing JFIF metadata
            jfxxApp0 = Sections.Find(a => (a.Marker == JPEGMarker.APP0) &&
                a.Header.Length >= 5 &&
                (Encoding.ASCII.GetString(a.Header, 0, 5) == "JFXX\0"));

            // If there is no APP0 section, return.
            if (jfxxApp0 == null)
                return;

            byte[] header = jfxxApp0.Header;

            // Version
            JFIFExtension version = (JFIFExtension)header[5];
            Properties.Add(new ExifEnumProperty<JFIFExtension>(ExifTag.JFXXExtensionCode, version));

            // Read thumbnail
            if (version == JFIFExtension.ThumbnailJPEG)
            {
                byte[] data = new byte[header.Length - 6];
                Array.Copy(header, 6, data, 0, data.Length);
                Properties.Add(new JFIFThumbnailProperty(ExifTag.JFXXThumbnail, new JFIFThumbnail(JFIFThumbnail.ImageFormat.JPEG, data)));
            }
            else if (version == JFIFExtension.Thumbnail24BitRGB)
            {
                // Thumbnails pixel count
                byte xthumbnail = header[6];
                Properties.Add(new ExifByte(ExifTag.JFXXXThumbnail, xthumbnail));
                byte ythumbnail = header[7];
                Properties.Add(new ExifByte(ExifTag.JFXXYThumbnail, ythumbnail));
                byte[] data = new byte[3 * xthumbnail * ythumbnail];
                Array.Copy(header, 8, data, 0, data.Length);
                Properties.Add(new JFIFThumbnailProperty(ExifTag.JFXXThumbnail, new JFIFThumbnail(JFIFThumbnail.ImageFormat.BMP24Bit, data)));
            }
            else if (version == JFIFExtension.ThumbnailPaletteRGB)
            {
                // Thumbnails pixel count
                byte xthumbnail = header[6];
                Properties.Add(new ExifByte(ExifTag.JFXXXThumbnail, xthumbnail));
                byte ythumbnail = header[7];
                Properties.Add(new ExifByte(ExifTag.JFXXYThumbnail, ythumbnail));
                byte[] palette = new byte[768];
                Array.Copy(header, 8, palette, 0, palette.Length);
                byte[] data = new byte[xthumbnail * ythumbnail];
                Array.Copy(header, 8 + 768, data, 0, data.Length);
                Properties.Add(new JFIFThumbnailProperty(ExifTag.JFXXThumbnail, new JFIFThumbnail(palette, data)));
            }
        }
Ejemplo n.º 12
0
        /// <summary>
        /// Reads the APP0 section containing JFIF metadata.
        /// </summary>
        private void ReadJFIFAPP0()
        {
            // Find the APP0 section containing JFIF metadata
            jfifApp0 = Sections.Find(a => (a.Marker == JPEGMarker.APP0) &&
                a.Header.Length >= 5 &&
                (Encoding.ASCII.GetString(a.Header, 0, 5) == "JFIF\0"));

            // If there is no APP0 section, return.
            if (jfifApp0 == null)
                return;

            byte[] header = jfifApp0.Header;
            BitConverterEx jfifConv = BitConverterEx.BigEndian;

            // Version
            ushort version = jfifConv.ToUInt16(header, 5);
            Properties.Add(new JFIFVersion(ExifTag.JFIFVersion, version));

            // Units
            byte unit = header[7];
            Properties.Add(new ExifEnumProperty<JFIFDensityUnit>(ExifTag.JFIFUnits, (JFIFDensityUnit)unit));

            // X and Y densities
            ushort xdensity = jfifConv.ToUInt16(header, 8);
            Properties.Add(new ExifUShort(ExifTag.XDensity, xdensity));
            ushort ydensity = jfifConv.ToUInt16(header, 10);
            Properties.Add(new ExifUShort(ExifTag.YDensity, ydensity));

            // Thumbnails pixel count
            byte xthumbnail = header[12];
            Properties.Add(new ExifByte(ExifTag.JFIFXThumbnail, xthumbnail));
            byte ythumbnail = header[13];
            Properties.Add(new ExifByte(ExifTag.JFIFYThumbnail, ythumbnail));

            // Read JFIF thumbnail
            int n = xthumbnail * ythumbnail;
            byte[] jfifThumbnail = new byte[n];
            Array.Copy(header, 14, jfifThumbnail, 0, n);
            Properties.Add(new JFIFThumbnailProperty(ExifTag.JFIFThumbnail, new JFIFThumbnail(JFIFThumbnail.ImageFormat.JPEG, jfifThumbnail)));
        }
Ejemplo n.º 13
0
        /// <summary>
        /// Reads the APP1 section containing Exif metadata.
        /// </summary>
        private void ReadExifAPP1()
        {
            // Find the APP1 section containing Exif metadata
            exifApp1 = Sections.Find(a => (a.Marker == JPEGMarker.APP1) &&
                a.Header.Length >= 6 &&
                (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 (exifApp1 == null)
            {
                int insertionIndex = Sections.FindLastIndex(a => a.Marker == JPEGMarker.APP0);
                if (insertionIndex == -1) insertionIndex = 0;
                insertionIndex++;
                exifApp1 = new JPEGSection(JPEGMarker.APP1);
                Sections.Insert(insertionIndex, exifApp1);
                if (BitConverterEx.SystemByteOrder == BitConverterEx.ByteOrder.LittleEndian)
                    ByteOrder = BitConverterEx.ByteOrder.LittleEndian;
                else
                    ByteOrder = BitConverterEx.ByteOrder.BigEndian;
                return;
            }

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

            // TIFF header
            int tiffoffset = 6;
            if (header[tiffoffset] == 0x49 && header[tiffoffset + 1] == 0x49)
                ByteOrder = BitConverterEx.ByteOrder.LittleEndian;
            else if (header[tiffoffset] == 0x4D && header[tiffoffset + 1] == 0x4D)
                ByteOrder = BitConverterEx.ByteOrder.BigEndian;
            else
                throw new NotValidExifFileException();

            // TIFF header may have a different byte order
            BitConverterEx.ByteOrder tiffByteOrder = ByteOrder;
            if (BitConverterEx.LittleEndian.ToUInt16(header, tiffoffset + 2) == 42)
                tiffByteOrder = BitConverterEx.ByteOrder.LittleEndian;
            else if (BitConverterEx.BigEndian.ToUInt16(header, tiffoffset + 2) == 42)
                tiffByteOrder = BitConverterEx.ByteOrder.BigEndian;
            else
                throw new NotValidExifFileException();

            // Offset to 0th IFD
            int ifd0offset = (int)BitConverterEx.ToUInt32(header, tiffoffset + 4, tiffByteOrder, BitConverterEx.SystemByteOrder);
            ifdqueue.Add(ifd0offset, IFD.Zeroth);

            BitConverterEx conv = new BitConverterEx(ByteOrder, BitConverterEx.SystemByteOrder);
            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);
                }

                // 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 = ImageFile.FromStream(ts);
                        }
                    }
                }
            }
        }
Ejemplo n.º 14
0
        /// <summary>
        /// Replaces the contents of the APP0 section with the JFIF properties.
        /// </summary>
        private bool WriteJFIFApp0()
        {
            // Which IFD sections do we have?
            Dictionary <ExifTag, ExifProperty> ifdjfefExisting = new Dictionary <ExifTag, ExifProperty>();

            foreach (ExifProperty prop in Properties)
            {
                if (prop.IFD == IFD.JFIF)
                {
                    ifdjfefExisting.Add(prop.Tag, prop);
                }
            }

            if (ifdjfefExisting.Count == 0)
            {
                // Nothing to write
                // It is OK for an Exif image to not have a JFIF APP0 segment
                if (jfifApp0 != null)
                {
                    Errors.Add(new ImageError(Severity.Info, "Removing unused JFIF APP0 segment."));
                    Sections.Remove(jfifApp0);
                    jfifApp0 = null;
                }
                return(false);
            }

            // Check and insert missing tags
            List <ExifProperty> ifdjfef = new List <ExifProperty>();

            // Version
            if (ifdjfefExisting.TryGetValue(ExifTag.JFIFVersion, out ExifProperty version))
            {
                ifdjfef.Add(version);
            }
            else
            {
                // default to JFIF version 1.02
                Errors.Add(new ImageError(Severity.Info, "Adding missing JFIF version tag."));
                ifdjfef.Add(new JFIFVersion(ExifTag.JFIFVersion, 1, 2));
            }

            // Units
            if (ifdjfefExisting.TryGetValue(ExifTag.JFIFUnits, out ExifProperty units))
            {
                ifdjfef.Add(units);
            }
            else
            {
                Errors.Add(new ImageError(Severity.Info, "Adding missing JFIF density unit tag."));
                ifdjfef.Add(new ExifEnumProperty <JFIFDensityUnit>(ExifTag.JFIFUnits, JFIFDensityUnit.None));
            }

            // X and Y densities
            if (ifdjfefExisting.TryGetValue(ExifTag.XDensity, out ExifProperty xdensity))
            {
                ifdjfef.Add(xdensity);
            }
            else
            {
                Errors.Add(new ImageError(Severity.Info, "Adding missing JFIF X density tag."));
                ifdjfef.Add(new ExifUShort(ExifTag.XDensity, 1));
            }
            if (ifdjfefExisting.TryGetValue(ExifTag.YDensity, out ExifProperty ydensity))
            {
                ifdjfef.Add(ydensity);
            }
            else
            {
                Errors.Add(new ImageError(Severity.Info, "Adding missing JFIF Y density tag."));
                ifdjfef.Add(new ExifUShort(ExifTag.YDensity, 1));
            }

            // Thumbnails pixel count
            if (ifdjfefExisting.TryGetValue(ExifTag.JFIFXThumbnail, out ExifProperty xthumbnail))
            {
                ifdjfef.Add(xthumbnail);
            }
            else
            {
                Errors.Add(new ImageError(Severity.Info, "Adding missing JFIF X thumbnail pixel count tag."));
                ifdjfef.Add(new ExifByte(ExifTag.JFIFXThumbnail, 0));
            }
            if (ifdjfefExisting.TryGetValue(ExifTag.JFIFYThumbnail, out ExifProperty ythumbnail))
            {
                ifdjfef.Add(ythumbnail);
            }
            else
            {
                Errors.Add(new ImageError(Severity.Info, "Adding missing JFIF Y thumbnail pixel count tag."));
                ifdjfef.Add(new ExifByte(ExifTag.JFIFYThumbnail, 0));
            }

            // JFIF thumbnail
            if (ifdjfefExisting.TryGetValue(ExifTag.JFIFThumbnail, out ExifProperty jfifThumbnail))
            {
                ifdjfef.Add(jfifThumbnail);
            }
            else
            {
                Errors.Add(new ImageError(Severity.Info, "Adding missing JFIF thumbnail tag."));
                ifdjfef.Add(new JFIFThumbnailProperty(ExifTag.JFIFThumbnail, new JFIFThumbnail(JFIFThumbnail.ImageFormat.JPEG, new byte[0])));
            }

            // Create a memory stream to write the APP0 section to
            using (MemoryStream ms = new MemoryStream())
            {
                // JFIF identifer
                ms.Write(Encoding.ASCII.GetBytes("JFIF\0"), 0, 5);

                // Write tags
                foreach (ExifProperty prop in ifdjfef)
                {
                    ExifInterOperability interop = prop.Interoperability;
                    byte[] data = interop.Data;
                    if (BitConverterEx.SystemByteOrder != BitConverterEx.ByteOrder.BigEndian && interop.TypeID == InterOpType.SHORT)
                    {
                        Array.Reverse(data);
                    }
                    ms.Write(data, 0, data.Length);
                }

                // Write APP0 header
                jfifApp0.Header = ms.ToArray();
            }
            return(true);
        }