/// <summary>
        /// Returns a <see cref="ImageFileDirectory"/> initialized from the given byte data.
        /// </summary>
        /// <param name="data">The data.</param>
        /// <param name="offset">The offset into <paramref name="data"/>.</param>
        /// <param name="byteOrder">The byte order of <paramref name="data"/>.</param>
        /// <returns>A <see cref="ImageFileDirectory"/> initialized from the given byte data.</returns>
        public static ImageFileDirectory FromBytes(byte[] data, uint offset, BitConverterEx.ByteOrder byteOrder)
        {
            ImageFileDirectory ifd  = new ImageFileDirectory();
            BitConverterEx     conv = new BitConverterEx(byteOrder, BitConverterEx.SystemByteOrder);

            List <uint> stripOffsets = new List <uint>();
            List <uint> stripLengths = new List <uint>();

            // Count
            ushort fieldcount = conv.ToUInt16(data, offset);

            // Read fields
            for (uint i = 0; i < fieldcount; i++)
            {
                uint fieldoffset = offset + 2 + 12 * i;
                ImageFileDirectoryEntry field = ImageFileDirectoryEntry.FromBytes(data, fieldoffset, byteOrder);
                ifd.Fields.Add(field);

                // Read strip offsets
                if (field.Tag == 273)
                {
                    int baselen = field.Data.Length / (int)field.Count;
                    for (int j = 0; j < field.Count; j++)
                    {
                        byte[] val = new byte[baselen];
                        Array.Copy(field.Data, j * baselen, val, 0, baselen);
                        uint stripOffset = (field.Type == 3 ? (uint)BitConverter.ToUInt16(val, 0) : BitConverter.ToUInt32(val, 0));
                        stripOffsets.Add(stripOffset);
                    }
                }

                // Read strip lengths
                if (field.Tag == 279)
                {
                    int baselen = field.Data.Length / (int)field.Count;
                    for (int j = 0; j < field.Count; j++)
                    {
                        byte[] val = new byte[baselen];
                        Array.Copy(field.Data, j * baselen, val, 0, baselen);
                        uint stripLength = (field.Type == 3 ? (uint)BitConverter.ToUInt16(val, 0) : BitConverter.ToUInt32(val, 0));
                        stripLengths.Add(stripLength);
                    }
                }
            }

            // Save strips
            if (stripOffsets.Count != stripLengths.Count)
            {
                throw new NotValidTIFFileException();
            }
            for (int i = 0; i < stripOffsets.Count; i++)
            {
                ifd.Strips.Add(new TIFFStrip(data, stripOffsets[i], stripLengths[i]));
            }

            // Offset to next ifd
            ifd.NextIFDOffset = conv.ToUInt32(data, offset + 2 + 12 * fieldcount);

            return(ifd);
        }
Example #2
0
        /// <summary>
        /// Returns a <see cref="ImageFileDirectory"/> initialized from the given byte data.
        /// </summary>
        /// <param name="data">The data.</param>
        /// <param name="offset">The offset into <paramref name="data"/>.</param>
        /// <param name="byteOrder">The byte order of <paramref name="data"/>.</param>
        /// <returns>A <see cref="ImageFileDirectory"/> initialized from the given byte data.</returns>
        public static ImageFileDirectory FromBytes(byte[] data, uint offset, BitConverterEx.ByteOrder byteOrder)
        {
            ImageFileDirectory ifd = new ImageFileDirectory();
            BitConverterEx conv = new BitConverterEx(byteOrder, BitConverterEx.SystemByteOrder);

            List<uint> stripOffsets = new List<uint>();
            List<uint> stripLengths = new List<uint>();

            // Count
            ushort fieldcount = conv.ToUInt16(data, offset);

            // Read fields
            for (uint i = 0; i < fieldcount; i++)
            {
                uint fieldoffset = offset + 2 + 12 * i;
                ImageFileDirectoryEntry field = ImageFileDirectoryEntry.FromBytes(data, fieldoffset, byteOrder);
                ifd.Fields.Add(field);

                // Read strip offsets
                if (field.Tag == 273)
                {
                    int baselen = field.Data.Length / (int)field.Count;
                    for (uint j = 0; j < field.Count; j++)
                    {
                        byte[] val = new byte[baselen];
                        Array.Copy(field.Data, j * baselen, val, 0, baselen);
                        uint stripOffset = (field.Type == 3 ? (uint)BitConverter.ToUInt16(val, 0) : BitConverter.ToUInt32(val, 0));
                        stripOffsets.Add(stripOffset);
                    }
                }

                // Read strip lengths
                if (field.Tag == 279)
                {
                    int baselen = field.Data.Length / (int)field.Count;
                    for (uint j = 0; j < field.Count; j++)
                    {
                        byte[] val = new byte[baselen];
                        Array.Copy(field.Data, j * baselen, val, 0, baselen);
                        uint stripLength = (field.Type == 3 ? (uint)BitConverter.ToUInt16(val, 0) : BitConverter.ToUInt32(val, 0));
                        stripLengths.Add(stripLength);
                    }
                }
            }

            // Save strips
            if (stripOffsets.Count != stripLengths.Count)
                throw new NotValidTIFFileException();
            for (int i = 0; i < stripOffsets.Count; i++)
                ifd.Strips.Add(new TIFFStrip(data, stripOffsets[i], stripLengths[i]));

            // Offset to next ifd
            ifd.NextIFDOffset = conv.ToUInt32(data, offset + 2 + 12 * fieldcount);

            return ifd;
        }
Example #3
0
        /// <summary>
        /// Initializes a new instance of the <see cref="TIFFHeader"/> struct.
        /// </summary>
        /// <param name="byteOrder">The byte order.</param>
        /// <param name="id">The TIFF ID. This value should always be 42.</param>
        /// <param name="ifdOffset">The offset to the first IFD section from the 
        /// start of the TIFF header.</param>
        /// <param name="headerByteOrder">The byte order of the TIFF header itself.</param>
        public TIFFHeader(BitConverterEx.ByteOrder byteOrder, byte id, uint ifdOffset, BitConverterEx.ByteOrder headerByteOrder)
        {
            if (id != 42)
                throw new NotValidTIFFHeader();

            ByteOrder = byteOrder;
            ID = id;
            IFDOffset = ifdOffset;
            TIFFHeaderByteOrder = headerByteOrder;
        }
Example #4
0
        /// <summary>
        /// Initializes a new instance of the <see cref="JPEGFile"/> class from the
        /// specified data stream.
        /// </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 PNGFile(Stream stream, System.Text.Encoding encoding)
        {
            Format   = ImageFileFormat.PNG;
            Chunks   = new List <PNGChunk>();
            Encoding = encoding;
            BitConverterEx conv = BitConverterEx.BigEndian;

            // Skip header
            stream.Seek(8, SeekOrigin.Begin);

            // Read chunks in order until we reach the end of file.
            while (stream.Position != stream.Length)
            {
                // Length of chunk data
                byte[] lengthBytes = new byte[4];
                if (stream.Read(lengthBytes, 0, 4) != 4)
                {
                    throw new NotValidPNGFileException();
                }
                uint length = conv.ToUInt32(lengthBytes, 0);

                // Chunk type
                byte[] typeBytes = new byte[4];
                if (stream.Read(typeBytes, 0, 4) != 4)
                {
                    throw new NotValidPNGFileException();
                }
                string type = Encoding.ASCII.GetString(typeBytes);

                // Chunk data
                byte[] data = Utility.GetStreamBytes(stream, length);

                // CRC of type name and data
                byte[] crcBytes = new byte[4];
                if (stream.Read(crcBytes, 0, 4) != 4)
                {
                    throw new NotValidPNGFileException();
                }
                uint crc = conv.ToUInt32(crcBytes, 0);

                // Add to chunks list
                PNGChunk chunk = new PNGChunk(type, data);
                if (chunk.CRC != crc)
                {
                    throw new NotValidPNGFileException();
                }
                Chunks.Add(chunk);
            }

            ReadPNGMetadata();
        }
Example #5
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)));
        }
Example #6
0
        /// <summary>
        /// Saves the <see cref="ImageFile"/> to the given stream.
        /// </summary>
        /// <param name="stream">The data stream used to save the image.</param>
        public override void Save(Stream stream)
        {
            // Add end chunk if it does not exist
            if (Chunks[Chunks.Count - 1].Type != "IEND")
            {
                Chunks.Add(new PNGChunk("IEND", new byte[0]));
            }

            // Save metadata
            WritePNGMetadata();

            BitConverterEx conv = BitConverterEx.BigEndian;

            // Write header
            stream.WriteByte(0x89);
            stream.WriteByte(0x50);
            stream.WriteByte(0x4E);
            stream.WriteByte(0x47);
            stream.WriteByte(0x0D);
            stream.WriteByte(0x0A);
            stream.WriteByte(0x1A);
            stream.WriteByte(0x0A);

            // Write chunks in order
            foreach (PNGChunk chunk in Chunks)
            {
                // Length of chunk data
                stream.Write(conv.GetBytes((uint)chunk.Data.LongLength), 0, 4);

                // Chunk type
                stream.Write(Encoding.ASCII.GetBytes(chunk.Type), 0, 4);

                // Chunk data
                long   rem    = chunk.Data.LongLength;
                long   offset = 0;
                byte[] b      = new byte[32768];
                while (rem > 0)
                {
                    int len = (int)Math.Min(rem, b.LongLength);
                    Array.Copy(chunk.Data, offset, b, 0, len);
                    stream.Write(b, 0, len);
                    rem    -= len;
                    offset += len;
                }

                // CRC of type name and data
                stream.Write(conv.GetBytes(chunk.CRC), 0, 4);
            }
        }
        /// <summary>
        /// Returns a <see cref="ImageFileDirectoryEntry"/> initialized from the given byte data.
        /// </summary>
        /// <param name="data">The data.</param>
        /// <param name="offset">The offset into <paramref name="data"/>.</param>
        /// <param name="byteOrder">The byte order of <paramref name="data"/>.</param>
        /// <returns>A <see cref="ImageFileDirectoryEntry"/> initialized from the given byte data.</returns>
        public static ImageFileDirectoryEntry FromBytes(byte[] data, uint offset, BitConverterEx.ByteOrder byteOrder)
        {
            // Tag ID
            ushort tag = BitConverterEx.ToUInt16(data, offset, byteOrder, BitConverterEx.SystemByteOrder);

            // Tag Type
            ushort type = BitConverterEx.ToUInt16(data, offset + 2, byteOrder, BitConverterEx.SystemByteOrder);

            // Count of Type
            uint count = BitConverterEx.ToUInt32(data, offset + 4, byteOrder, BitConverterEx.SystemByteOrder);

            // Field value or offset to field data
            byte[] value = new byte[4];
            Array.Copy(data, offset + 8, value, 0, 4);

            // Calculate the bytes we need to read
            uint baselength = GetBaseLength(type);
            uint totallength = count * baselength;

            // If field value does not fit in 4 bytes
            // the value field is an offset to the actual
            // field value
            if (totallength > 4)
            {
                uint dataoffset = BitConverterEx.ToUInt32(value, 0, byteOrder, BitConverterEx.SystemByteOrder);
                value = new byte[totallength];
                Array.Copy(data, dataoffset, value, 0, totallength);
            }

            // Reverse array order if byte orders are different
            if (byteOrder != BitConverterEx.SystemByteOrder)
            {
                for (uint i = 0; i < count; i++)
                {
                    byte[] val = new byte[baselength];
                    Array.Copy(value, i * baselength, val, 0, baselength);
                    Array.Reverse(val);
                    Array.Copy(val, 0, value, i * baselength, baselength);
                }
            }

            return new ImageFileDirectoryEntry(tag, type, count, value);
        }
Example #8
0
        /// <summary>
        /// Returns a <see cref="ImageFileDirectoryEntry"/> initialized from the given byte data.
        /// </summary>
        /// <param name="data">The data.</param>
        /// <param name="offset">The offset into <paramref name="data"/>.</param>
        /// <param name="byteOrder">The byte order of <paramref name="data"/>.</param>
        /// <returns>A <see cref="ImageFileDirectoryEntry"/> initialized from the given byte data.</returns>
        public static ImageFileDirectoryEntry FromBytes(byte[] data, uint offset, BitConverterEx.ByteOrder byteOrder)
        {
            // Tag ID
            ushort tag = BitConverterEx.ToUInt16(data, offset, byteOrder, BitConverterEx.SystemByteOrder);

            // Tag Type
            ushort type = BitConverterEx.ToUInt16(data, offset + 2, byteOrder, BitConverterEx.SystemByteOrder);

            // Count of Type
            uint count = BitConverterEx.ToUInt32(data, offset + 4, byteOrder, BitConverterEx.SystemByteOrder);

            // Field value or offset to field data
            byte[] value = new byte[4];
            Array.Copy(data, offset + 8, value, 0, 4);

            // Calculate the bytes we need to read
            uint baselength  = GetBaseLength(type);
            uint totallength = count * baselength;

            // If field value does not fit in 4 bytes
            // the value field is an offset to the actual
            // field value
            if (totallength > 4)
            {
                uint dataoffset = BitConverterEx.ToUInt32(value, 0, byteOrder, BitConverterEx.SystemByteOrder);
                value = new byte[totallength];
                Array.Copy(data, dataoffset, value, 0, totallength);
            }

            // Reverse array order if byte orders are different
            if (byteOrder != BitConverterEx.SystemByteOrder)
            {
                for (uint i = 0; i < count; i++)
                {
                    byte[] val = new byte[baselength];
                    Array.Copy(value, i * baselength, val, 0, baselength);
                    Array.Reverse(val);
                    Array.Copy(val, 0, value, i * baselength, baselength);
                }
            }

            return(new ImageFileDirectoryEntry(tag, type, count, value));
        }
Example #9
0
        /// <summary>
        /// Returns a <see cref="TIFFHeader"/> initialized from the given byte data.
        /// </summary>
        /// <param name="data">The data.</param>
        /// <param name="offset">The offset into <paramref name="data"/>.</param>
        /// <returns>A <see cref="TIFFHeader"/> initialized from the given byte data.</returns>
        public static TIFFHeader FromBytes(byte[] data, int offset)
        {
            TIFFHeader header = new TIFFHeader();

            // TIFF header
            if (data[offset] == 0x49 && data[offset + 1] == 0x49)
            {
                header.ByteOrder = BitConverterEx.ByteOrder.LittleEndian;
            }
            else if (data[offset] == 0x4D && data[offset + 1] == 0x4D)
            {
                header.ByteOrder = BitConverterEx.ByteOrder.BigEndian;
            }
            else
            {
                throw new NotValidTIFFHeader();
            }

            // TIFF header may have a different byte order
            if (BitConverterEx.LittleEndian.ToUInt16(data, offset + 2) == 42)
            {
                header.TIFFHeaderByteOrder = BitConverterEx.ByteOrder.LittleEndian;
            }
            else if (BitConverterEx.BigEndian.ToUInt16(data, offset + 2) == 42)
            {
                header.TIFFHeaderByteOrder = BitConverterEx.ByteOrder.BigEndian;
            }
            else
            {
                throw new NotValidTIFFHeader();
            }
            header.ID = 42;

            // IFD offset
            header.IFDOffset = BitConverterEx.ToUInt32(data, offset + 4, header.TIFFHeaderByteOrder, BitConverterEx.SystemByteOrder);

            return(header);
        }
Example #10
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);
                        }
                    }
                }
            }
        }
Example #11
0
        /// <summary>
        /// Saves the <see cref="ImageFile"/> to the given stream.
        /// </summary>
        /// <param name="stream">The data stream used to save the image.</param>
        public override void Save(Stream stream)
        {
            BitConverterEx conv = BitConverterEx.SystemEndian;

            // Write TIFF header
            uint ifdoffset = 8;

            // Byte order
            stream.Write((BitConverterEx.SystemByteOrder == BitConverterEx.ByteOrder.LittleEndian ? new byte[] { 0x49, 0x49 } : new byte[] { 0x4D, 0x4D }), 0, 2);
            // TIFF ID
            stream.Write(conv.GetBytes((ushort)42), 0, 2);
            // Offset to 0th IFD, will be corrected below
            stream.Write(conv.GetBytes(ifdoffset), 0, 4);

            // Write IFD sections
            for (int i = 0; i < IFDs.Count; i++)
            {
                ImageFileDirectory ifd = IFDs[i];

                // Save the location of IFD offset
                long ifdLocation = stream.Position - 4;

                // Write strips first
                byte[] stripOffsets = new byte[4 * ifd.Strips.Count];
                byte[] stripLengths = new byte[4 * ifd.Strips.Count];
                uint   stripOffset  = ifdoffset;
                for (int j = 0; j < ifd.Strips.Count; j++)
                {
                    byte[] stripData = ifd.Strips[j].Data;
                    byte[] oBytes    = BitConverter.GetBytes(stripOffset);
                    byte[] lBytes    = BitConverter.GetBytes((uint)stripData.Length);
                    Array.Copy(oBytes, 0, stripOffsets, 4 * j, 4);
                    Array.Copy(lBytes, 0, stripLengths, 4 * j, 4);
                    stream.Write(stripData, 0, stripData.Length);
                    stripOffset += (uint)stripData.Length;
                }

                // Remove old strip tags
                for (int j = ifd.Fields.Count - 1; j > 0; j--)
                {
                    ushort tag = ifd.Fields[j].Tag;
                    if (tag == 273 || tag == 279)
                    {
                        ifd.Fields.RemoveAt(j);
                    }
                }
                // Write new strip tags
                ifd.Fields.Add(new ImageFileDirectoryEntry(273, 4, (uint)ifd.Strips.Count, stripOffsets));
                ifd.Fields.Add(new ImageFileDirectoryEntry(279, 4, (uint)ifd.Strips.Count, stripLengths));

                // Write fields after strips
                ifdoffset = stripOffset;

                // Correct IFD offset
                long currentLocation = stream.Position;
                stream.Seek(ifdLocation, SeekOrigin.Begin);
                stream.Write(conv.GetBytes(ifdoffset), 0, 4);
                stream.Seek(currentLocation, SeekOrigin.Begin);

                // Offset to field data
                uint dataOffset = ifdoffset + 2 + (uint)ifd.Fields.Count * 12 + 4;

                // Field count
                stream.Write(conv.GetBytes((ushort)ifd.Fields.Count), 0, 2);

                // Fields
                foreach (ImageFileDirectoryEntry field in ifd.Fields)
                {
                    // Tag
                    stream.Write(conv.GetBytes(field.Tag), 0, 2);
                    // Type
                    stream.Write(conv.GetBytes(field.Type), 0, 2);
                    // Count
                    stream.Write(conv.GetBytes(field.Count), 0, 4);

                    // Field data
                    byte[] data = field.Data;
                    if (data.Length <= 4)
                    {
                        stream.Write(data, 0, data.Length);
                        for (int j = data.Length; j < 4; j++)
                        {
                            stream.WriteByte(0);
                        }
                    }
                    else
                    {
                        stream.Write(conv.GetBytes(dataOffset), 0, 4);
                        long currentOffset = stream.Position;
                        stream.Seek(dataOffset, SeekOrigin.Begin);
                        stream.Write(data, 0, data.Length);
                        dataOffset += (uint)data.Length;
                        stream.Seek(currentOffset, SeekOrigin.Begin);
                    }
                }

                // Offset to next IFD
                ifdoffset = dataOffset;
                stream.Write(conv.GetBytes(i == IFDs.Count - 1 ? 0 : ifdoffset), 0, 4);
            }
        }
Example #12
0
 /// <summary>
 /// Converts the given array of bytes to a 32-bit unsigned integer.
 /// </summary>
 public uint ToUInt32(byte[] value, int startIndex)
 {
     return(BitConverterEx.ToUInt32(value, startIndex, mFrom, mTo));
 }
Example #13
0
 /// <summary>
 /// Converts the given array of bytes to a 16-bit unsigned integer.
 /// </summary>
 public char ToChar(byte[] value, long startIndex)
 {
     return(BitConverterEx.ToChar(value, startIndex, mFrom, mTo));
 }
Example #14
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);
                        }
                    }
                }
            }
        }
Example #15
0
 /// <summary>
 /// Converts the given array of bytes to a single precision floating number.
 /// </summary>
 public float ToSingle(byte[] value, long startIndex)
 {
     return(BitConverterEx.ToSingle(value, startIndex, mFrom, mTo));
 }
Example #16
0
        /// <summary>
        /// Creates an ExifProperty from the given interoperability parameters.
        /// </summary>
        /// <param name="tag">The tag id of the exif property.</param>
        /// <param name="type">The type id of the exif property.</param>
        /// <param name="count">Byte or component count.</param>
        /// <param name="value">Field data as an array of bytes.</param>
        /// <param name="byteOrder">Byte order of value.</param>
        /// <param name="ifd">IFD section containing this propery.</param>
        /// <returns>an ExifProperty initialized from the interoperability parameters.</returns>
        public static ExifProperty Get(ushort tag, ushort type, uint count, byte[] value, BitConverterEx.ByteOrder byteOrder, IFD ifd)
        {
            BitConverterEx conv = new BitConverterEx(byteOrder, BitConverterEx.ByteOrder.System);

            if (ifd == IFD.Zeroth)
            {
                if (tag == 0x103) // Compression
                {
                    return(new ExifEnumProperty <Compression>(ExifTag.Compression, (Compression)conv.ToUInt16(value, 0)));
                }
                else if (tag == 0x106) // PhotometricInterpretation
                {
                    return(new ExifEnumProperty <PhotometricInterpretation>(ExifTag.PhotometricInterpretation, (PhotometricInterpretation)conv.ToUInt16(value, 0)));
                }
                else if (tag == 0x112) // Orientation
                {
                    return(new ExifEnumProperty <Orientation>(ExifTag.Orientation, (Orientation)conv.ToUInt16(value, 0)));
                }
                else if (tag == 0x11c) // PlanarConfiguration
                {
                    return(new ExifEnumProperty <PlanarConfiguration>(ExifTag.PlanarConfiguration, (PlanarConfiguration)conv.ToUInt16(value, 0)));
                }
                else if (tag == 0x213) // YCbCrPositioning
                {
                    return(new ExifEnumProperty <YCbCrPositioning>(ExifTag.YCbCrPositioning, (YCbCrPositioning)conv.ToUInt16(value, 0)));
                }
                else if (tag == 0x128) // ResolutionUnit
                {
                    return(new ExifEnumProperty <ResolutionUnit>(ExifTag.ResolutionUnit, (ResolutionUnit)conv.ToUInt16(value, 0)));
                }
                else if (tag == 0x132) // DateTime
                {
                    return(new ExifDateTime(ExifTag.DateTime, ExifBitConverter.ToDateTime(value)));
                }
            }
            else if (ifd == IFD.EXIF)
            {
                if (tag == 0x9000) // ExifVersion
                {
                    return(new ExifVersion(ExifTag.ExifVersion, ExifBitConverter.ToAscii(value)));
                }
                else if (tag == 0xa000) // FlashpixVersion
                {
                    return(new ExifVersion(ExifTag.FlashpixVersion, ExifBitConverter.ToAscii(value)));
                }
                else if (tag == 0xa001) // ColorSpace
                {
                    return(new ExifEnumProperty <ColorSpace>(ExifTag.ColorSpace, (ColorSpace)conv.ToUInt16(value, 0)));
                }
                else if (tag == 0x9286) // UserComment
                {
                    byte[] encbytes = new byte[8];
                    byte[] strbytes = new byte[value.Length - 8];
                    Array.Copy(value, encbytes, 8);
                    Array.Copy(value, 8, strbytes, 0, value.Length - 8);
                    Encoding enc    = Encoding.UTF8;
                    string   encstr = enc.GetString(encbytes, 0, encbytes.Length);
                    if (encstr == "ASCII\0\0\0")
                    {
                        enc = Encoding.UTF8;
                    }
                    else if (encstr == "JIS\0\0\0\0\0")
                    {
                        enc = Encoding.GetEncoding("Japanese (JIS 0208-1990 and 0212-1990)");
                    }
                    else if (encstr == "Unicode\0")
                    {
                        enc = Encoding.Unicode;
                    }
                    else
                    {
                        enc = null;
                    }

                    int len = Array.IndexOf(strbytes, (byte)0);
                    if (len == -1)
                    {
                        len = strbytes.Length;
                    }
                    return(new ExifEncodedString(ExifTag.UserComment, (enc == null ? Encoding.UTF8.GetString(strbytes, 0, len) : enc.GetString(strbytes, 0, len)), enc));
                }
                else if (tag == 0x9003) // DateTimeOriginal
                {
                    return(new ExifDateTime(ExifTag.DateTimeOriginal, ExifBitConverter.ToDateTime(value)));
                }
                else if (tag == 0x9004) // DateTimeDigitized
                {
                    return(new ExifDateTime(ExifTag.DateTimeDigitized, ExifBitConverter.ToDateTime(value)));
                }
                else if (tag == 0x8822) // ExposureProgram
                {
                    return(new ExifEnumProperty <ExposureProgram>(ExifTag.ExposureProgram, (ExposureProgram)conv.ToUInt16(value, 0)));
                }
                else if (tag == 0x9207) // MeteringMode
                {
                    return(new ExifEnumProperty <MeteringMode>(ExifTag.MeteringMode, (MeteringMode)conv.ToUInt16(value, 0)));
                }
                else if (tag == 0x9208) // LightSource
                {
                    return(new ExifEnumProperty <LightSource>(ExifTag.LightSource, (LightSource)conv.ToUInt16(value, 0)));
                }
                else if (tag == 0x9209) // Flash
                {
                    return(new ExifEnumProperty <Flash>(ExifTag.Flash, (Flash)conv.ToUInt16(value, 0), true));
                }
                else if (tag == 0x9214) // SubjectArea
                {
                    if (count == 3)
                    {
                        return(new ExifCircularSubjectArea(ExifTag.SubjectArea, ExifBitConverter.ToUShortArray(value, (int)count, byteOrder)));
                    }
                    else if (count == 4)
                    {
                        return(new ExifRectangularSubjectArea(ExifTag.SubjectArea, ExifBitConverter.ToUShortArray(value, (int)count, byteOrder)));
                    }
                    else // count == 2
                    {
                        return(new ExifPointSubjectArea(ExifTag.SubjectArea, ExifBitConverter.ToUShortArray(value, (int)count, byteOrder)));
                    }
                }
                else if (tag == 0xa210) // FocalPlaneResolutionUnit
                {
                    return(new ExifEnumProperty <ResolutionUnit>(ExifTag.FocalPlaneResolutionUnit, (ResolutionUnit)conv.ToUInt16(value, 0), true));
                }
                else if (tag == 0xa214) // SubjectLocation
                {
                    return(new ExifPointSubjectArea(ExifTag.SubjectLocation, ExifBitConverter.ToUShortArray(value, (int)count, byteOrder)));
                }
                else if (tag == 0xa217) // SensingMethod
                {
                    return(new ExifEnumProperty <SensingMethod>(ExifTag.SensingMethod, (SensingMethod)conv.ToUInt16(value, 0), true));
                }
                else if (tag == 0xa300) // FileSource
                {
                    return(new ExifEnumProperty <FileSource>(ExifTag.FileSource, (FileSource)conv.ToUInt16(value, 0), true));
                }
                else if (tag == 0xa301) // SceneType
                {
                    return(new ExifEnumProperty <SceneType>(ExifTag.SceneType, (SceneType)conv.ToUInt16(value, 0), true));
                }
                else if (tag == 0xa401) // CustomRendered
                {
                    return(new ExifEnumProperty <CustomRendered>(ExifTag.CustomRendered, (CustomRendered)conv.ToUInt16(value, 0), true));
                }
                else if (tag == 0xa402) // ExposureMode
                {
                    return(new ExifEnumProperty <ExposureMode>(ExifTag.ExposureMode, (ExposureMode)conv.ToUInt16(value, 0), true));
                }
                else if (tag == 0xa403) // WhiteBalance
                {
                    return(new ExifEnumProperty <WhiteBalance>(ExifTag.WhiteBalance, (WhiteBalance)conv.ToUInt16(value, 0), true));
                }
                else if (tag == 0xa406) // SceneCaptureType
                {
                    return(new ExifEnumProperty <SceneCaptureType>(ExifTag.SceneCaptureType, (SceneCaptureType)conv.ToUInt16(value, 0), true));
                }
                else if (tag == 0xa407) // GainControl
                {
                    return(new ExifEnumProperty <GainControl>(ExifTag.GainControl, (GainControl)conv.ToUInt16(value, 0), true));
                }
                else if (tag == 0xa408) // Contrast
                {
                    return(new ExifEnumProperty <Contrast>(ExifTag.Contrast, (Contrast)conv.ToUInt16(value, 0), true));
                }
                else if (tag == 0xa409) // Saturation
                {
                    return(new ExifEnumProperty <Saturation>(ExifTag.Saturation, (Saturation)conv.ToUInt16(value, 0), true));
                }
                else if (tag == 0xa40a) // Sharpness
                {
                    return(new ExifEnumProperty <Sharpness>(ExifTag.Sharpness, (Sharpness)conv.ToUInt16(value, 0), true));
                }
                else if (tag == 0xa40c) // SubjectDistanceRange
                {
                    return(new ExifEnumProperty <SubjectDistanceRange>(ExifTag.SubjectDistance, (SubjectDistanceRange)conv.ToUInt16(value, 0), true));
                }
            }
            else if (ifd == IFD.GPS)
            {
                if (tag == 0) // GPSVersionID
                {
                    return(new ExifVersion(ExifTag.GPSVersionID, ExifBitConverter.ToString(value)));
                }
                else if (tag == 1) // GPSLatitudeRef
                {
                    return(new ExifEnumProperty <GPSLatitudeRef>(ExifTag.GPSLatitudeRef, (GPSLatitudeRef)value[0]));
                }
                else if (tag == 2) // GPSLatitude
                {
                    return(new GPSLatitudeLongitude(ExifTag.GPSLatitude, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder)));
                }
                else if (tag == 3) // GPSLongitudeRef
                {
                    return(new ExifEnumProperty <GPSLongitudeRef>(ExifTag.GPSLongitudeRef, (GPSLongitudeRef)value[0]));
                }
                else if (tag == 4) // GPSLongitude
                {
                    return(new GPSLatitudeLongitude(ExifTag.GPSLongitude, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder)));
                }
                else if (tag == 5) // GPSAltitudeRef
                {
                    return(new ExifEnumProperty <GPSAltitudeRef>(ExifTag.GPSAltitudeRef, (GPSAltitudeRef)value[0]));
                }
                else if (tag == 7) // GPSTimeStamp
                {
                    return(new GPSTimeStamp(ExifTag.GPSTimeStamp, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder)));
                }
                else if (tag == 9) // GPSStatus
                {
                    return(new ExifEnumProperty <GPSStatus>(ExifTag.GPSStatus, (GPSStatus)value[0]));
                }
                else if (tag == 10) // GPSMeasureMode
                {
                    return(new ExifEnumProperty <GPSMeasureMode>(ExifTag.GPSMeasureMode, (GPSMeasureMode)value[0]));
                }
                else if (tag == 12) // GPSSpeedRef
                {
                    return(new ExifEnumProperty <GPSSpeedRef>(ExifTag.GPSSpeedRef, (GPSSpeedRef)value[0]));
                }
                else if (tag == 14) // GPSTrackRef
                {
                    return(new ExifEnumProperty <GPSDirectionRef>(ExifTag.GPSTrackRef, (GPSDirectionRef)value[0]));
                }
                else if (tag == 16) // GPSImgDirectionRef
                {
                    return(new ExifEnumProperty <GPSDirectionRef>(ExifTag.GPSImgDirectionRef, (GPSDirectionRef)value[0]));
                }
                else if (tag == 19) // GPSDestLatitudeRef
                {
                    return(new ExifEnumProperty <GPSLatitudeRef>(ExifTag.GPSDestLatitudeRef, (GPSLatitudeRef)value[0]));
                }
                else if (tag == 20) // GPSDestLatitude
                {
                    return(new GPSLatitudeLongitude(ExifTag.GPSDestLatitude, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder)));
                }
                else if (tag == 21) // GPSDestLongitudeRef
                {
                    return(new ExifEnumProperty <GPSLongitudeRef>(ExifTag.GPSDestLongitudeRef, (GPSLongitudeRef)value[0]));
                }
                else if (tag == 22) // GPSDestLongitude
                {
                    return(new GPSLatitudeLongitude(ExifTag.GPSDestLongitude, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder)));
                }
                else if (tag == 23) // GPSDestBearingRef
                {
                    return(new ExifEnumProperty <GPSDirectionRef>(ExifTag.GPSDestBearingRef, (GPSDirectionRef)value[0]));
                }
                else if (tag == 25) // GPSDestDistanceRef
                {
                    return(new ExifEnumProperty <GPSDistanceRef>(ExifTag.GPSDestDistanceRef, (GPSDistanceRef)value[0]));
                }
                else if (tag == 29) // GPSDate
                {
                    return(new ExifDateTime(ExifTag.GPSDateStamp, ExifBitConverter.ToDateTime(value, false)));
                }
                else if (tag == 30) // GPSDifferential
                {
                    return(new ExifEnumProperty <GPSDifferential>(ExifTag.GPSDifferential, (GPSDifferential)conv.ToUInt16(value, 0)));
                }
            }
            else if (ifd == IFD.Interop)
            {
                if (tag == 1) // InteroperabilityIndex
                {
                    return(new ExifAscii(ExifTag.InteroperabilityIndex, ExifBitConverter.ToAscii(value)));
                }
                else if (tag == 2) // InteroperabilityVersion
                {
                    return(new ExifVersion(ExifTag.InteroperabilityVersion, ExifBitConverter.ToAscii(value)));
                }
            }
            else if (ifd == IFD.First)
            {
                if (tag == 0x103) // Compression
                {
                    return(new ExifEnumProperty <Compression>(ExifTag.ThumbnailCompression, (Compression)conv.ToUInt16(value, 0)));
                }
                else if (tag == 0x106) // PhotometricInterpretation
                {
                    return(new ExifEnumProperty <PhotometricInterpretation>(ExifTag.ThumbnailPhotometricInterpretation, (PhotometricInterpretation)conv.ToUInt16(value, 0)));
                }
                else if (tag == 0x112) // Orientation
                {
                    return(new ExifEnumProperty <Orientation>(ExifTag.ThumbnailOrientation, (Orientation)conv.ToUInt16(value, 0)));
                }
                else if (tag == 0x11c) // PlanarConfiguration
                {
                    return(new ExifEnumProperty <PlanarConfiguration>(ExifTag.ThumbnailPlanarConfiguration, (PlanarConfiguration)conv.ToUInt16(value, 0)));
                }
                else if (tag == 0x213) // YCbCrPositioning
                {
                    return(new ExifEnumProperty <YCbCrPositioning>(ExifTag.ThumbnailYCbCrPositioning, (YCbCrPositioning)conv.ToUInt16(value, 0)));
                }
                else if (tag == 0x128) // ResolutionUnit
                {
                    return(new ExifEnumProperty <ResolutionUnit>(ExifTag.ThumbnailResolutionUnit, (ResolutionUnit)conv.ToUInt16(value, 0)));
                }
                else if (tag == 0x132) // DateTime
                {
                    return(new ExifDateTime(ExifTag.ThumbnailDateTime, ExifBitConverter.ToDateTime(value)));
                }
            }

            // Find the exif tag corresponding to given tag id
            ExifTag etag = ExifTagFactory.GetExifTag(ifd, tag);

            if (type == 1) // 1 = BYTE An 8-bit unsigned integer.
            {
                if (count == 1)
                {
                    return(new ExifByte(etag, value[0]));
                }
                else
                {
                    return(new ExifByteArray(etag, value));
                }
            }
            else if (type == 2) // 2 = ASCII An 8-bit byte containing one 7-bit ASCII code.
            {
                return(new ExifAscii(etag, ExifBitConverter.ToAscii(value)));
            }
            else if (type == 3) // 3 = SHORT A 16-bit (2-byte) unsigned integer.
            {
                if (count == 1)
                {
                    return(new ExifUShort(etag, conv.ToUInt16(value, 0)));
                }
                else
                {
                    return(new ExifUShortArray(etag, ExifBitConverter.ToUShortArray(value, (int)count, byteOrder)));
                }
            }
            else if (type == 4) // 4 = LONG A 32-bit (4-byte) unsigned integer.
            {
                if (count == 1)
                {
                    return(new ExifUInt(etag, conv.ToUInt32(value, 0)));
                }
                else
                {
                    return(new ExifUIntArray(etag, ExifBitConverter.ToUIntArray(value, (int)count, byteOrder)));
                }
            }
            else if (type == 5) // 5 = RATIONAL Two LONGs. The first LONG is the numerator and the second LONG expresses the denominator.
            {
                if (count == 1)
                {
                    return(new ExifURational(etag, ExifBitConverter.ToURational(value, byteOrder)));
                }
                else
                {
                    return(new ExifURationalArray(etag, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder)));
                }
            }
            else if (type == 7) // 7 = UNDEFINED An 8-bit byte that can take any value depending on the field definition.
            {
                return(new ExifUndefined(etag, value));
            }
            else if (type == 9) // 9 = SLONG A 32-bit (4-byte) signed integer (2's complement notation).
            {
                if (count == 1)
                {
                    return(new ExifSInt(etag, conv.ToInt32(value, 0)));
                }
                else
                {
                    return(new ExifSIntArray(etag, ExifBitConverter.ToSIntArray(value, (int)count, byteOrder)));
                }
            }
            else if (type == 10) // 10 = SRATIONAL Two SLONGs. The first SLONG is the numerator and the second SLONG is the denominator.
            {
                if (count == 1)
                {
                    return(new ExifSRational(etag, ExifBitConverter.ToSRational(value, byteOrder)));
                }
                else
                {
                    return(new ExifSRationalArray(etag, ExifBitConverter.ToSRationalArray(value, (int)count, byteOrder)));
                }
            }
            else
            {
                throw new ArgumentException("Unknown property type.");
            }
        }
Example #17
0
        /// <summary>
        /// Replaces the contents of the APP1 section with the Exif properties.
        /// </summary>
        private void WriteApp1(bool preserveMakerNote)
        {
            // Zero out IFD field offsets. We will fill those as we write the IFD sections
            exifIFDFieldOffset    = 0;
            gpsIFDFieldOffset     = 0;
            interopIFDFieldOffset = 0;
            firstIFDFieldOffset   = 0;
            // We also do not know the location of the embedded thumbnail yet
            thumbOffsetLocation = 0;
            thumbOffsetValue    = 0;
            thumbSizeLocation   = 0;
            thumbSizeValue      = 0;

            // Which IFD sections do we have?
            Dictionary <ExifTag, ExifProperty> ifdzeroth  = new Dictionary <ExifTag, ExifProperty>();
            Dictionary <ExifTag, ExifProperty> ifdexif    = new Dictionary <ExifTag, ExifProperty>();
            Dictionary <ExifTag, ExifProperty> ifdgps     = new Dictionary <ExifTag, ExifProperty>();
            Dictionary <ExifTag, ExifProperty> ifdinterop = new Dictionary <ExifTag, ExifProperty>();
            Dictionary <ExifTag, ExifProperty> ifdfirst   = new Dictionary <ExifTag, ExifProperty>();

            foreach (KeyValuePair <ExifTag, ExifProperty> pair in Properties)
            {
                switch (pair.Value.IFD)
                {
                case IFD.Zeroth:
                    ifdzeroth.Add(pair.Key, pair.Value);
                    break;

                case IFD.EXIF:
                    ifdexif.Add(pair.Key, pair.Value);
                    break;

                case IFD.GPS:
                    ifdgps.Add(pair.Key, pair.Value);
                    break;

                case IFD.Interop:
                    ifdinterop.Add(pair.Key, pair.Value);
                    break;

                case IFD.First:
                    ifdfirst.Add(pair.Key, pair.Value);
                    break;
                }
            }

            // Add IFD pointers if they are missing
            // We will write the pointer values later on
            if (ifdexif.Count != 0 && !ifdzeroth.ContainsKey(ExifTag.EXIFIFDPointer))
            {
                ifdzeroth.Add(ExifTag.EXIFIFDPointer, new ExifUInt(ExifTag.EXIFIFDPointer, 0));
            }
            if (ifdgps.Count != 0 && !ifdzeroth.ContainsKey(ExifTag.GPSIFDPointer))
            {
                ifdzeroth.Add(ExifTag.GPSIFDPointer, new ExifUInt(ExifTag.GPSIFDPointer, 0));
            }
            if (ifdinterop.Count != 0 && !ifdexif.ContainsKey(ExifTag.InteroperabilityIFDPointer))
            {
                ifdexif.Add(ExifTag.InteroperabilityIFDPointer, new ExifUInt(ExifTag.InteroperabilityIFDPointer, 0));
            }

            // Remove IFD pointers if IFD sections are missing
            if (ifdexif.Count == 0 && ifdzeroth.ContainsKey(ExifTag.EXIFIFDPointer))
            {
                ifdzeroth.Remove(ExifTag.EXIFIFDPointer);
            }
            if (ifdgps.Count == 0 && ifdzeroth.ContainsKey(ExifTag.GPSIFDPointer))
            {
                ifdzeroth.Remove(ExifTag.GPSIFDPointer);
            }
            if (ifdinterop.Count == 0 && ifdexif.ContainsKey(ExifTag.InteroperabilityIFDPointer))
            {
                ifdexif.Remove(ExifTag.InteroperabilityIFDPointer);
            }

            if (ifdzeroth.Count == 0)
            {
                throw new IFD0IsEmptyException();
            }

            // We will need these bitconverters to write byte-ordered data
            BitConverterEx bceJPEG = new BitConverterEx(BitConverterEx.ByteOrder.System, BitConverterEx.ByteOrder.BigEndian);
            BitConverterEx bceExif = new BitConverterEx(BitConverterEx.ByteOrder.System, ByteOrder);

            // Create a memory stream to write the APP1 section to
            MemoryStream ms = new MemoryStream();

            // Exif identifer
            ms.Write(Encoding.ASCII.GetBytes("Exif\0\0"), 0, 6);

            // TIFF header
            // Byte order
            long tiffoffset = ms.Position;

            ms.Write((ByteOrder == BitConverterEx.ByteOrder.LittleEndian ? new byte[] { 0x49, 0x49 } : new byte[] { 0x4D, 0x4D }), 0, 2);
            // TIFF ID
            ms.Write(bceExif.GetBytes((ushort)42), 0, 2);
            // Offset to 0th IFD
            ms.Write(bceExif.GetBytes((uint)8), 0, 4);

            // Write IFDs
            WriteIFD(ms, ifdzeroth, IFD.Zeroth, tiffoffset, preserveMakerNote);
            uint exififdrelativeoffset = (uint)(ms.Position - tiffoffset);

            WriteIFD(ms, ifdexif, IFD.EXIF, tiffoffset, preserveMakerNote);
            uint gpsifdrelativeoffset = (uint)(ms.Position - tiffoffset);

            WriteIFD(ms, ifdgps, IFD.GPS, tiffoffset, preserveMakerNote);
            uint interopifdrelativeoffset = (uint)(ms.Position - tiffoffset);

            WriteIFD(ms, ifdinterop, IFD.Interop, tiffoffset, preserveMakerNote);
            uint firstifdrelativeoffset = (uint)(ms.Position - tiffoffset);

            WriteIFD(ms, ifdfirst, IFD.First, tiffoffset, preserveMakerNote);

            // Now that we now the location of IFDs we can go back and write IFD offsets
            if (exifIFDFieldOffset != 0)
            {
                ms.Seek(exifIFDFieldOffset, SeekOrigin.Begin);
                ms.Write(bceExif.GetBytes(exififdrelativeoffset), 0, 4);
            }
            if (gpsIFDFieldOffset != 0)
            {
                ms.Seek(gpsIFDFieldOffset, SeekOrigin.Begin);
                ms.Write(bceExif.GetBytes(gpsifdrelativeoffset), 0, 4);
            }
            if (interopIFDFieldOffset != 0)
            {
                ms.Seek(interopIFDFieldOffset, SeekOrigin.Begin);
                ms.Write(bceExif.GetBytes(interopifdrelativeoffset), 0, 4);
            }
            if (firstIFDFieldOffset != 0)
            {
                ms.Seek(firstIFDFieldOffset, SeekOrigin.Begin);
                ms.Write(bceExif.GetBytes(firstifdrelativeoffset), 0, 4);
            }
            // We can write thumbnail location now
            if (thumbOffsetLocation != 0)
            {
                ms.Seek(thumbOffsetLocation, SeekOrigin.Begin);
                ms.Write(bceExif.GetBytes(thumbOffsetValue), 0, 4);
            }
            if (thumbSizeLocation != 0)
            {
                ms.Seek(thumbSizeLocation, SeekOrigin.Begin);
                ms.Write(bceExif.GetBytes(thumbSizeValue), 0, 4);
            }

            ms.Close();

            // Return APP1 header
            app1.Header = ms.ToArray();
        }
Example #18
0
        /// <summary>
        /// Replaces the contents of the APP1 section with the Exif properties.
        /// </summary>
        private bool WriteExifApp1(bool preserveMakerNote)
        {
            // Zero out IFD field offsets. We will fill those as we write the IFD sections
            exifIFDFieldOffset = 0;
            gpsIFDFieldOffset = 0;
            interopIFDFieldOffset = 0;
            firstIFDFieldOffset = 0;
            // We also do not know the location of the embedded thumbnail yet
            thumbOffsetLocation = 0;
            thumbOffsetValue = 0;
            thumbSizeLocation = 0;
            thumbSizeValue = 0;
            // Write thumbnail tags if they are missing, remove otherwise
            int indexf = -1;
            int indexl = -1;
            for (int i = 0; i < Properties.Count; i++)
            {
                ExifProperty prop = Properties[i];
                if (prop.Tag == ExifTag.ThumbnailJPEGInterchangeFormat) indexf = i;
                if (prop.Tag == ExifTag.ThumbnailJPEGInterchangeFormatLength) indexl = i;
                if (indexf != -1 && indexl != -1) break;
            }
            if (Thumbnail == null)
            {
                if (indexf != -1)
                    Properties.RemoveAt(indexf);
                if (indexl != -1)
                    Properties.RemoveAt(indexl);
            }
            else
            {
                if (indexf == -1)
                    Properties.Add(new ExifUInt(ExifTag.ThumbnailJPEGInterchangeFormat, 0));
                if (indexl == -1)
                    Properties.Add(new ExifUInt(ExifTag.ThumbnailJPEGInterchangeFormatLength, 0));
            }

            // Which IFD sections do we have?
            Dictionary<ExifTag, ExifProperty> ifdzeroth = new Dictionary<ExifTag, ExifProperty>();
            Dictionary<ExifTag, ExifProperty> ifdexif = new Dictionary<ExifTag, ExifProperty>();
            Dictionary<ExifTag, ExifProperty> ifdgps = new Dictionary<ExifTag, ExifProperty>();
            Dictionary<ExifTag, ExifProperty> ifdinterop = new Dictionary<ExifTag, ExifProperty>();
            Dictionary<ExifTag, ExifProperty> ifdfirst = new Dictionary<ExifTag, ExifProperty>();

            foreach (ExifProperty prop in Properties)
            {
                switch (prop.IFD)
                {
                    case IFD.Zeroth:
                        ifdzeroth.Add(prop.Tag, prop);
                        break;
                    case IFD.EXIF:
                        ifdexif.Add(prop.Tag, prop);
                        break;
                    case IFD.GPS:
                        ifdgps.Add(prop.Tag, prop);
                        break;
                    case IFD.Interop:
                        ifdinterop.Add(prop.Tag, prop);
                        break;
                    case IFD.First:
                        ifdfirst.Add(prop.Tag, prop);
                        break;
                }
            }

            // Add IFD pointers if they are missing
            // We will write the pointer values later on
            if (ifdexif.Count != 0 && !ifdzeroth.ContainsKey(ExifTag.EXIFIFDPointer))
                ifdzeroth.Add(ExifTag.EXIFIFDPointer, new ExifUInt(ExifTag.EXIFIFDPointer, 0));
            if (ifdgps.Count != 0 && !ifdzeroth.ContainsKey(ExifTag.GPSIFDPointer))
                ifdzeroth.Add(ExifTag.GPSIFDPointer, new ExifUInt(ExifTag.GPSIFDPointer, 0));
            if (ifdinterop.Count != 0 && !ifdexif.ContainsKey(ExifTag.InteroperabilityIFDPointer))
                ifdexif.Add(ExifTag.InteroperabilityIFDPointer, new ExifUInt(ExifTag.InteroperabilityIFDPointer, 0));

            // Remove IFD pointers if IFD sections are missing
            if (ifdexif.Count == 0 && ifdzeroth.ContainsKey(ExifTag.EXIFIFDPointer))
                ifdzeroth.Remove(ExifTag.EXIFIFDPointer);
            if (ifdgps.Count == 0 && ifdzeroth.ContainsKey(ExifTag.GPSIFDPointer))
                ifdzeroth.Remove(ExifTag.GPSIFDPointer);
            if (ifdinterop.Count == 0 && ifdexif.ContainsKey(ExifTag.InteroperabilityIFDPointer))
                ifdexif.Remove(ExifTag.InteroperabilityIFDPointer);

            if (ifdzeroth.Count == 0 && ifdgps.Count == 0 && ifdinterop.Count == 0 && ifdfirst.Count == 0 && Thumbnail == null)
            {
                // Nothing to write
                return false;
            }

            // We will need these bitconverter to write byte-ordered data
            BitConverterEx bceExif = new BitConverterEx(BitConverterEx.SystemByteOrder, ByteOrder);

            // Create a memory stream to write the APP1 section to
            MemoryStream ms = new MemoryStream();

            // Exif identifer
            ms.Write(Encoding.ASCII.GetBytes("Exif\0\0"), 0, 6);

            // TIFF header
            // Byte order
            long tiffoffset = ms.Position;
            ms.Write((ByteOrder == BitConverterEx.ByteOrder.LittleEndian ? new byte[] { 0x49, 0x49 } : new byte[] { 0x4D, 0x4D }), 0, 2);
            // TIFF ID
            ms.Write(bceExif.GetBytes((ushort)42), 0, 2);
            // Offset to 0th IFD
            ms.Write(bceExif.GetBytes((uint)8), 0, 4);

            // Write IFDs
            WriteIFD(ms, ifdzeroth, IFD.Zeroth, tiffoffset, preserveMakerNote);
            uint exififdrelativeoffset = (uint)(ms.Position - tiffoffset);
            WriteIFD(ms, ifdexif, IFD.EXIF, tiffoffset, preserveMakerNote);
            uint gpsifdrelativeoffset = (uint)(ms.Position - tiffoffset);
            WriteIFD(ms, ifdgps, IFD.GPS, tiffoffset, preserveMakerNote);
            uint interopifdrelativeoffset = (uint)(ms.Position - tiffoffset);
            WriteIFD(ms, ifdinterop, IFD.Interop, tiffoffset, preserveMakerNote);
            uint firstifdrelativeoffset = (uint)(ms.Position - tiffoffset);
            WriteIFD(ms, ifdfirst, IFD.First, tiffoffset, preserveMakerNote);

            // Now that we now the location of IFDs we can go back and write IFD offsets
            if (exifIFDFieldOffset != 0)
            {
                ms.Seek(exifIFDFieldOffset, SeekOrigin.Begin);
                ms.Write(bceExif.GetBytes(exififdrelativeoffset), 0, 4);
            }
            if (gpsIFDFieldOffset != 0)
            {
                ms.Seek(gpsIFDFieldOffset, SeekOrigin.Begin);
                ms.Write(bceExif.GetBytes(gpsifdrelativeoffset), 0, 4);
            }
            if (interopIFDFieldOffset != 0)
            {
                ms.Seek(interopIFDFieldOffset, SeekOrigin.Begin);
                ms.Write(bceExif.GetBytes(interopifdrelativeoffset), 0, 4);
            }
            if (firstIFDFieldOffset != 0)
            {
                ms.Seek(firstIFDFieldOffset, SeekOrigin.Begin);
                ms.Write(bceExif.GetBytes(firstifdrelativeoffset), 0, 4);
            }
            // We can write thumbnail location now
            if (thumbOffsetLocation != 0)
            {
                ms.Seek(thumbOffsetLocation, SeekOrigin.Begin);
                ms.Write(bceExif.GetBytes(thumbOffsetValue), 0, 4);
            }
            if (thumbSizeLocation != 0)
            {
                ms.Seek(thumbSizeLocation, SeekOrigin.Begin);
                ms.Write(bceExif.GetBytes(thumbSizeValue), 0, 4);
            }

            ms.Close();

            // Return APP1 header
            exifApp1.Header = ms.ToArray();
            return true;
        }
Example #19
0
        /// <summary>
        /// Saves the <see cref="ImageFile"/> to the given stream.
        /// </summary>
        /// <param name="stream">The data stream used to save the image.</param>
        protected override void SaveInternal(MemoryStream stream)
        {
            BitConverterEx conv = BitConverterEx.SystemEndian;

            // Write TIFF header
            uint ifdoffset = 8;

            // Byte order
            stream.Write((BitConverterEx.SystemByteOrder == BitConverterEx.ByteOrder.LittleEndian ? new byte[] { 0x49, 0x49 } : new byte[] { 0x4D, 0x4D }), 0, 2);
            // TIFF ID
            stream.Write(conv.GetBytes((ushort)42), 0, 2);
            // Offset to 0th IFD, will be corrected below
            stream.Write(conv.GetBytes(ifdoffset), 0, 4);

            // Write IFD sections
            for (int i = 0; i < IFDs.Count; i++)
            {
                ImageFileDirectory ifd = IFDs[i];

                // Update zeroth IFD fields from image properties
                if (i == 0)
                {
                    var ifdZeroth = new Dictionary <ushort, ImageFileDirectoryEntry>();
                    foreach (var prop in Properties)
                    {
                        if (prop.IFD == IFD.Zeroth)
                        {
                            var interop = prop.Interoperability;
                            if (ifdZeroth.TryGetValue(interop.TagID, out var field))
                            {
                                field.Count = interop.Count;
                                field.Data  = interop.Data;
                            }
                            else
                            {
                                ifdZeroth.Add(interop.TagID, new ImageFileDirectoryEntry(interop.TagID, (ushort)interop.TypeID, interop.Count, interop.Data));
                            }
                        }
                    }

                    ifd.Fields.Clear();
                    ifd.Fields.AddRange(ifdZeroth.Values);
                }

                // Save the location of IFD offset
                long ifdLocation = stream.Position - 4;

                // Write strips first
                byte[] stripOffsets = new byte[4 * ifd.Strips.Count];
                byte[] stripLengths = new byte[4 * ifd.Strips.Count];
                uint   stripOffset  = ifdoffset;
                for (int j = 0; j < ifd.Strips.Count; j++)
                {
                    byte[] stripData = ifd.Strips[j].Data;
                    byte[] oBytes    = BitConverter.GetBytes(stripOffset);
                    byte[] lBytes    = BitConverter.GetBytes((uint)stripData.Length);
                    Array.Copy(oBytes, 0, stripOffsets, 4 * j, 4);
                    Array.Copy(lBytes, 0, stripLengths, 4 * j, 4);
                    stream.Write(stripData, 0, stripData.Length);
                    stripOffset += (uint)stripData.Length;
                }

                // Remove old strip tags
                for (int j = ifd.Fields.Count - 1; j > 0; j--)
                {
                    ushort tag = ifd.Fields[j].Tag;
                    if (tag == 273 || tag == 279)
                    {
                        ifd.Fields.RemoveAt(j);
                    }
                }
                // Write new strip tags
                ifd.Fields.Add(new ImageFileDirectoryEntry(273, 4, (uint)ifd.Strips.Count, stripOffsets));
                ifd.Fields.Add(new ImageFileDirectoryEntry(279, 4, (uint)ifd.Strips.Count, stripLengths));

                // Write fields after strips
                ifdoffset = stripOffset;

                // Correct IFD offset
                long currentLocation = stream.Position;
                stream.Seek(ifdLocation, SeekOrigin.Begin);
                stream.Write(conv.GetBytes(ifdoffset), 0, 4);
                stream.Seek(currentLocation, SeekOrigin.Begin);

                // Offset to field data
                uint dataOffset = ifdoffset + 2 + (uint)ifd.Fields.Count * 12 + 4;

                // Field count
                stream.Write(conv.GetBytes((ushort)ifd.Fields.Count), 0, 2);

                // Fields
                foreach (ImageFileDirectoryEntry field in ifd.Fields)
                {
                    // Tag
                    stream.Write(conv.GetBytes(field.Tag), 0, 2);
                    // Type
                    stream.Write(conv.GetBytes(field.Type), 0, 2);
                    // Count
                    stream.Write(conv.GetBytes(field.Count), 0, 4);

                    // Field data
                    byte[] data = field.Data;
                    if (data.Length <= 4)
                    {
                        stream.Write(data, 0, data.Length);
                        for (int j = data.Length; j < 4; j++)
                        {
                            stream.WriteByte(0);
                        }
                    }
                    else
                    {
                        stream.Write(conv.GetBytes(dataOffset), 0, 4);
                        long currentOffset = stream.Position;
                        stream.Seek(dataOffset, SeekOrigin.Begin);
                        stream.Write(data, 0, data.Length);
                        dataOffset += (uint)data.Length;
                        stream.Seek(currentOffset, SeekOrigin.Begin);
                    }
                }

                // Offset to next IFD
                ifdoffset = dataOffset;
                stream.Write(conv.GetBytes(i == IFDs.Count - 1 ? 0 : ifdoffset), 0, 4);
            }
        }
        /// <summary>
        /// Creates an ExifProperty from the given interoperability parameters.
        /// </summary>
        /// <param name="tag">The tag id of the exif property.</param>
        /// <param name="type">The type id of the exif property.</param>
        /// <param name="count">Byte or component count.</param>
        /// <param name="value">Field data as an array of bytes.</param>
        /// <param name="byteOrder">Byte order of value.</param>
        /// <param name="ifd">IFD section containing this propery.</param>
        /// <returns>an ExifProperty initialized from the interoperability parameters.</returns>
        public static ExifProperty Get(ushort tag, ushort type, uint count, byte[] value, BitConverterEx.ByteOrder byteOrder, IFD ifd)
        {
            BitConverterEx conv = new BitConverterEx(byteOrder, BitConverterEx.ByteOrder.System);

            if (ifd == IFD.Zeroth)
            {
                if (tag == 0x103) // Compression
                    return new ExifEnumProperty<Compression>(ExifTag.Compression, (Compression)conv.ToUInt16(value, 0));
                else if (tag == 0x106) // PhotometricInterpretation
                    return new ExifEnumProperty<PhotometricInterpretation>(ExifTag.PhotometricInterpretation, (PhotometricInterpretation)conv.ToUInt16(value, 0));
                else if (tag == 0x112) // Orientation
                    return new ExifEnumProperty<Orientation>(ExifTag.Orientation, (Orientation)conv.ToUInt16(value, 0));
                else if (tag == 0x11c) // PlanarConfiguration
                    return new ExifEnumProperty<PlanarConfiguration>(ExifTag.PlanarConfiguration, (PlanarConfiguration)conv.ToUInt16(value, 0));
                else if (tag == 0x213) // YCbCrPositioning
                    return new ExifEnumProperty<YCbCrPositioning>(ExifTag.YCbCrPositioning, (YCbCrPositioning)conv.ToUInt16(value, 0));
                else if (tag == 0x128) // ResolutionUnit
                    return new ExifEnumProperty<ResolutionUnit>(ExifTag.ResolutionUnit, (ResolutionUnit)conv.ToUInt16(value, 0));
                else if (tag == 0x132) // DateTime
                    return new ExifDateTime(ExifTag.DateTime, ExifBitConverter.ToDateTime(value));
            }
            else if (ifd == IFD.EXIF)
            {
                if (tag == 0x9000) // ExifVersion
                    return new ExifVersion(ExifTag.ExifVersion, ExifBitConverter.ToAscii(value));
                else if (tag == 0xa000) // FlashpixVersion
                    return new ExifVersion(ExifTag.FlashpixVersion, ExifBitConverter.ToAscii(value));
                else if (tag == 0xa001) // ColorSpace
                    return new ExifEnumProperty<ColorSpace>(ExifTag.ColorSpace, (ColorSpace)conv.ToUInt16(value, 0));
                else if (tag == 0x9286) // UserComment
                {
                    byte[] encbytes = new byte[8];
                    byte[] strbytes = new byte[value.Length - 8];
                    Array.Copy(value, encbytes, 8);
                    Array.Copy(value, 8, strbytes, 0, value.Length - 8);
                    Encoding enc = Encoding.UTF8;
                    string encstr = enc.GetString(encbytes, 0, encbytes.Length);
                    if (encstr == "ASCII\0\0\0")
                        enc = Encoding.UTF8;
                    else if (encstr == "JIS\0\0\0\0\0")
                        enc = Encoding.GetEncoding("Japanese (JIS 0208-1990 and 0212-1990)");
                    else if (encstr == "Unicode\0")
                        enc = Encoding.Unicode;
                    else
                        enc = null;

                    int len = Array.IndexOf(strbytes, (byte)0);
                    if (len == -1) len = strbytes.Length;
                    return new ExifEncodedString(ExifTag.UserComment, (enc == null ? Encoding.UTF8.GetString(strbytes, 0, len) : enc.GetString(strbytes, 0, len)), enc);
                }
                else if (tag == 0x9003) // DateTimeOriginal
                    return new ExifDateTime(ExifTag.DateTimeOriginal, ExifBitConverter.ToDateTime(value));
                else if (tag == 0x9004) // DateTimeDigitized
                    return new ExifDateTime(ExifTag.DateTimeDigitized, ExifBitConverter.ToDateTime(value));
                else if (tag == 0x8822) // ExposureProgram
                    return new ExifEnumProperty<ExposureProgram>(ExifTag.ExposureProgram, (ExposureProgram)conv.ToUInt16(value, 0));
                else if (tag == 0x9207) // MeteringMode
                    return new ExifEnumProperty<MeteringMode>(ExifTag.MeteringMode, (MeteringMode)conv.ToUInt16(value, 0));
                else if (tag == 0x9208) // LightSource
                    return new ExifEnumProperty<LightSource>(ExifTag.LightSource, (LightSource)conv.ToUInt16(value, 0));
                else if (tag == 0x9209) // Flash
                    return new ExifEnumProperty<Flash>(ExifTag.Flash, (Flash)conv.ToUInt16(value, 0), true);
                else if (tag == 0x9214) // SubjectArea
                {
                    if (count == 3)
                        return new ExifCircularSubjectArea(ExifTag.SubjectArea, ExifBitConverter.ToUShortArray(value, (int)count, byteOrder));
                    else if (count == 4)
                        return new ExifRectangularSubjectArea(ExifTag.SubjectArea, ExifBitConverter.ToUShortArray(value, (int)count, byteOrder));
                    else // count == 2
                        return new ExifPointSubjectArea(ExifTag.SubjectArea, ExifBitConverter.ToUShortArray(value, (int)count, byteOrder));
                }
                else if (tag == 0xa210) // FocalPlaneResolutionUnit
                    return new ExifEnumProperty<ResolutionUnit>(ExifTag.FocalPlaneResolutionUnit, (ResolutionUnit)conv.ToUInt16(value, 0), true);
                else if (tag == 0xa214) // SubjectLocation
                    return new ExifPointSubjectArea(ExifTag.SubjectLocation, ExifBitConverter.ToUShortArray(value, (int)count, byteOrder));
                else if (tag == 0xa217) // SensingMethod
                    return new ExifEnumProperty<SensingMethod>(ExifTag.SensingMethod, (SensingMethod)conv.ToUInt16(value, 0), true);
                else if (tag == 0xa300) // FileSource
                    return new ExifEnumProperty<FileSource>(ExifTag.FileSource, (FileSource)conv.ToUInt16(value, 0), true);
                else if (tag == 0xa301) // SceneType
                    return new ExifEnumProperty<SceneType>(ExifTag.SceneType, (SceneType)conv.ToUInt16(value, 0), true);
                else if (tag == 0xa401) // CustomRendered
                    return new ExifEnumProperty<CustomRendered>(ExifTag.CustomRendered, (CustomRendered)conv.ToUInt16(value, 0), true);
                else if (tag == 0xa402) // ExposureMode
                    return new ExifEnumProperty<ExposureMode>(ExifTag.ExposureMode, (ExposureMode)conv.ToUInt16(value, 0), true);
                else if (tag == 0xa403) // WhiteBalance
                    return new ExifEnumProperty<WhiteBalance>(ExifTag.WhiteBalance, (WhiteBalance)conv.ToUInt16(value, 0), true);
                else if (tag == 0xa406) // SceneCaptureType
                    return new ExifEnumProperty<SceneCaptureType>(ExifTag.SceneCaptureType, (SceneCaptureType)conv.ToUInt16(value, 0), true);
                else if (tag == 0xa407) // GainControl
                    return new ExifEnumProperty<GainControl>(ExifTag.GainControl, (GainControl)conv.ToUInt16(value, 0), true);
                else if (tag == 0xa408) // Contrast
                    return new ExifEnumProperty<Contrast>(ExifTag.Contrast, (Contrast)conv.ToUInt16(value, 0), true);
                else if (tag == 0xa409) // Saturation
                    return new ExifEnumProperty<Saturation>(ExifTag.Saturation, (Saturation)conv.ToUInt16(value, 0), true);
                else if (tag == 0xa40a) // Sharpness
                    return new ExifEnumProperty<Sharpness>(ExifTag.Sharpness, (Sharpness)conv.ToUInt16(value, 0), true);
                else if (tag == 0xa40c) // SubjectDistanceRange
                    return new ExifEnumProperty<SubjectDistanceRange>(ExifTag.SubjectDistance, (SubjectDistanceRange)conv.ToUInt16(value, 0), true);
            }
            else if (ifd == IFD.GPS)
            {
                if (tag == 0) // GPSVersionID
                    return new ExifVersion(ExifTag.GPSVersionID, ExifBitConverter.ToString(value));
                else if (tag == 1) // GPSLatitudeRef
                    return new ExifEnumProperty<GPSLatitudeRef>(ExifTag.GPSLatitudeRef, (GPSLatitudeRef)value[0]);
                else if (tag == 2) // GPSLatitude
                    return new GPSLatitudeLongitude(ExifTag.GPSLatitude, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder));
                else if (tag == 3) // GPSLongitudeRef
                    return new ExifEnumProperty<GPSLongitudeRef>(ExifTag.GPSLongitudeRef, (GPSLongitudeRef)value[0]);
                else if (tag == 4) // GPSLongitude
                    return new GPSLatitudeLongitude(ExifTag.GPSLongitude, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder));
                else if (tag == 5) // GPSAltitudeRef
                    return new ExifEnumProperty<GPSAltitudeRef>(ExifTag.GPSAltitudeRef, (GPSAltitudeRef)value[0]);
                else if (tag == 7) // GPSTimeStamp
                    return new GPSTimeStamp(ExifTag.GPSTimeStamp, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder));
                else if (tag == 9) // GPSStatus
                    return new ExifEnumProperty<GPSStatus>(ExifTag.GPSStatus, (GPSStatus)value[0]);
                else if (tag == 10) // GPSMeasureMode
                    return new ExifEnumProperty<GPSMeasureMode>(ExifTag.GPSMeasureMode, (GPSMeasureMode)value[0]);
                else if (tag == 12) // GPSSpeedRef
                    return new ExifEnumProperty<GPSSpeedRef>(ExifTag.GPSSpeedRef, (GPSSpeedRef)value[0]);
                else if (tag == 14) // GPSTrackRef
                    return new ExifEnumProperty<GPSDirectionRef>(ExifTag.GPSTrackRef, (GPSDirectionRef)value[0]);
                else if (tag == 16) // GPSImgDirectionRef
                    return new ExifEnumProperty<GPSDirectionRef>(ExifTag.GPSImgDirectionRef, (GPSDirectionRef)value[0]);
                else if (tag == 19) // GPSDestLatitudeRef
                    return new ExifEnumProperty<GPSLatitudeRef>(ExifTag.GPSDestLatitudeRef, (GPSLatitudeRef)value[0]);
                else if (tag == 20) // GPSDestLatitude
                    return new GPSLatitudeLongitude(ExifTag.GPSDestLatitude, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder));
                else if (tag == 21) // GPSDestLongitudeRef
                    return new ExifEnumProperty<GPSLongitudeRef>(ExifTag.GPSDestLongitudeRef, (GPSLongitudeRef)value[0]);
                else if (tag == 22) // GPSDestLongitude
                    return new GPSLatitudeLongitude(ExifTag.GPSDestLongitude, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder));
                else if (tag == 23) // GPSDestBearingRef
                    return new ExifEnumProperty<GPSDirectionRef>(ExifTag.GPSDestBearingRef, (GPSDirectionRef)value[0]);
                else if (tag == 25) // GPSDestDistanceRef
                    return new ExifEnumProperty<GPSDistanceRef>(ExifTag.GPSDestDistanceRef, (GPSDistanceRef)value[0]);
                else if (tag == 29) // GPSDate
                    return new ExifDateTime(ExifTag.GPSDateStamp, ExifBitConverter.ToDateTime(value, false));
                else if (tag == 30) // GPSDifferential
                    return new ExifEnumProperty<GPSDifferential>(ExifTag.GPSDifferential, (GPSDifferential)conv.ToUInt16(value, 0));
            }
            else if (ifd == IFD.Interop)
            {
                if (tag == 1) // InteroperabilityIndex
                    return new ExifAscii(ExifTag.InteroperabilityIndex, ExifBitConverter.ToAscii(value));
                else if (tag == 2) // InteroperabilityVersion
                    return new ExifVersion(ExifTag.InteroperabilityVersion, ExifBitConverter.ToAscii(value));
            }
            else if (ifd == IFD.First)
            {
                if (tag == 0x103) // Compression
                    return new ExifEnumProperty<Compression>(ExifTag.ThumbnailCompression, (Compression)conv.ToUInt16(value, 0));
                else if (tag == 0x106) // PhotometricInterpretation
                    return new ExifEnumProperty<PhotometricInterpretation>(ExifTag.ThumbnailPhotometricInterpretation, (PhotometricInterpretation)conv.ToUInt16(value, 0));
                else if (tag == 0x112) // Orientation
                    return new ExifEnumProperty<Orientation>(ExifTag.ThumbnailOrientation, (Orientation)conv.ToUInt16(value, 0));
                else if (tag == 0x11c) // PlanarConfiguration
                    return new ExifEnumProperty<PlanarConfiguration>(ExifTag.ThumbnailPlanarConfiguration, (PlanarConfiguration)conv.ToUInt16(value, 0));
                else if (tag == 0x213) // YCbCrPositioning
                    return new ExifEnumProperty<YCbCrPositioning>(ExifTag.ThumbnailYCbCrPositioning, (YCbCrPositioning)conv.ToUInt16(value, 0));
                else if (tag == 0x128) // ResolutionUnit
                    return new ExifEnumProperty<ResolutionUnit>(ExifTag.ThumbnailResolutionUnit, (ResolutionUnit)conv.ToUInt16(value, 0));
                else if (tag == 0x132) // DateTime
                    return new ExifDateTime(ExifTag.ThumbnailDateTime, ExifBitConverter.ToDateTime(value));
            }

            // Find the exif tag corresponding to given tag id
            ExifTag etag = ExifTagFactory.GetExifTag(ifd, tag);

            if (type == 1) // 1 = BYTE An 8-bit unsigned integer.
            {
                if (count == 1)
                    return new ExifByte(etag, value[0]);
                else
                    return new ExifByteArray(etag, value);
            }
            else if (type == 2) // 2 = ASCII An 8-bit byte containing one 7-bit ASCII code.
            {
                return new ExifAscii(etag, ExifBitConverter.ToAscii(value));
            }
            else if (type == 3) // 3 = SHORT A 16-bit (2-byte) unsigned integer.
            {
                if (count == 1)
                    return new ExifUShort(etag, conv.ToUInt16(value, 0));
                else
                    return new ExifUShortArray(etag, ExifBitConverter.ToUShortArray(value, (int)count, byteOrder));
            }
            else if (type == 4) // 4 = LONG A 32-bit (4-byte) unsigned integer.
            {
                if (count == 1)
                    return new ExifUInt(etag, conv.ToUInt32(value, 0));
                else
                    return new ExifUIntArray(etag, ExifBitConverter.ToUIntArray(value, (int)count, byteOrder));
            }
            else if (type == 5) // 5 = RATIONAL Two LONGs. The first LONG is the numerator and the second LONG expresses the denominator.
            {
                if (count == 1)
                    return new ExifURational(etag, ExifBitConverter.ToURational(value, byteOrder));
                else
                    return new ExifURationalArray(etag, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder));
            }
            else if (type == 7) // 7 = UNDEFINED An 8-bit byte that can take any value depending on the field definition.
                return new ExifUndefined(etag, value);
            else if (type == 9) // 9 = SLONG A 32-bit (4-byte) signed integer (2's complement notation).
            {
                if (count == 1)
                    return new ExifSInt(etag, conv.ToInt32(value, 0));
                else
                    return new ExifSIntArray(etag, ExifBitConverter.ToSIntArray(value, (int)count, byteOrder));
            }
            else if (type == 10) // 10 = SRATIONAL Two SLONGs. The first SLONG is the numerator and the second SLONG is the denominator.
            {
                if (count == 1)
                    return new ExifSRational(etag, ExifBitConverter.ToSRational(value, byteOrder));
                else
                    return new ExifSRationalArray(etag, ExifBitConverter.ToSRationalArray(value, (int)count, byteOrder));
            }
            else
                throw new ArgumentException("Unknown property type.");
        }
Example #21
0
 /// <summary>
 /// Converts the given array of bytes to a 32-bit signed integer.
 /// </summary>
 public int ToInt32(byte[] value, long startIndex)
 {
     return(BitConverterEx.ToInt32(value, startIndex, mFrom, mTo));
 }
Example #22
0
 /// <summary>
 /// Converts the given double precision floating-point number to an array of bytes.
 /// </summary>
 public byte[] GetBytes(double value)
 {
     return(BitConverterEx.GetBytes(value, mFrom, mTo));
 }
Example #23
0
 /// <summary>
 /// Converts the given array of bytes to a double precision floating number.
 /// </summary>
 public double ToDouble(byte[] value, long startIndex)
 {
     return(BitConverterEx.ToDouble(value, startIndex, mFrom, mTo));
 }
Example #24
0
        /// <summary>
        /// Replaces the contents of the APP1 section with the Exif properties.
        /// </summary>
        private bool WriteExifApp1(bool preserveMakerNote)
        {
            // Zero out IFD field offsets. We will fill those as we write the IFD sections
            exifIFDFieldOffset    = 0;
            gpsIFDFieldOffset     = 0;
            interopIFDFieldOffset = 0;
            firstIFDFieldOffset   = 0;
            // We also do not know the location of the embedded thumbnail yet
            thumbOffsetLocation = 0;
            thumbOffsetValue    = 0;
            thumbSizeLocation   = 0;
            thumbSizeValue      = 0;
            // Write thumbnail tags if they are missing, remove otherwise
            ExifProperty thumbnailFormatProperty = null;
            ExifProperty thumbnailLengthProperty = null;

            foreach (var prop in Properties)
            {
                if (prop.Tag == ExifTag.ThumbnailJPEGInterchangeFormat)
                {
                    thumbnailFormatProperty = prop;
                }
                if (prop.Tag == ExifTag.ThumbnailJPEGInterchangeFormatLength)
                {
                    thumbnailLengthProperty = prop;
                }
                if (thumbnailFormatProperty != null && thumbnailLengthProperty != null)
                {
                    break;
                }
            }
            if (Thumbnail == null)
            {
                if (thumbnailFormatProperty != null)
                {
                    Properties.Remove(thumbnailFormatProperty);
                }
                if (thumbnailLengthProperty != null)
                {
                    Properties.Remove(thumbnailLengthProperty);
                }
            }
            else
            {
                if (thumbnailFormatProperty == null)
                {
                    Properties.Add(new ExifUInt(ExifTag.ThumbnailJPEGInterchangeFormat, 0));
                }
                if (thumbnailLengthProperty == null)
                {
                    Properties.Add(new ExifUInt(ExifTag.ThumbnailJPEGInterchangeFormatLength, 0));
                }
            }

            // Which IFD sections do we have?
            Dictionary <ExifTag, ExifProperty> ifdzeroth  = new Dictionary <ExifTag, ExifProperty>();
            Dictionary <ExifTag, ExifProperty> ifdexif    = new Dictionary <ExifTag, ExifProperty>();
            Dictionary <ExifTag, ExifProperty> ifdgps     = new Dictionary <ExifTag, ExifProperty>();
            Dictionary <ExifTag, ExifProperty> ifdinterop = new Dictionary <ExifTag, ExifProperty>();
            Dictionary <ExifTag, ExifProperty> ifdfirst   = new Dictionary <ExifTag, ExifProperty>();

            foreach (ExifProperty prop in Properties)
            {
                switch (prop.IFD)
                {
                case IFD.Zeroth:
                    ifdzeroth[prop.Tag] = prop;
                    break;

                case IFD.EXIF:
                    ifdexif[prop.Tag] = prop;
                    break;

                case IFD.GPS:
                    ifdgps[prop.Tag] = prop;
                    break;

                case IFD.Interop:
                    ifdinterop[prop.Tag] = prop;
                    break;

                case IFD.First:
                    ifdfirst[prop.Tag] = prop;
                    break;
                }
            }

            // Add IFD pointers if they are missing
            // We will write the pointer values later on
            if (ifdexif.Count != 0 && !ifdzeroth.ContainsKey(ExifTag.EXIFIFDPointer))
            {
                ifdzeroth[ExifTag.EXIFIFDPointer] = new ExifUInt(ExifTag.EXIFIFDPointer, 0);
            }
            if (ifdgps.Count != 0 && !ifdzeroth.ContainsKey(ExifTag.GPSIFDPointer))
            {
                ifdzeroth[ExifTag.GPSIFDPointer] = new ExifUInt(ExifTag.GPSIFDPointer, 0);
            }
            if (ifdinterop.Count != 0 && !ifdexif.ContainsKey(ExifTag.InteroperabilityIFDPointer))
            {
                ifdexif[ExifTag.InteroperabilityIFDPointer] = new ExifUInt(ExifTag.InteroperabilityIFDPointer, 0);
            }

            // Remove IFD pointers if IFD sections are missing
            if (ifdexif.Count == 0 && ifdzeroth.ContainsKey(ExifTag.EXIFIFDPointer))
            {
                ifdzeroth.Remove(ExifTag.EXIFIFDPointer);
            }
            if (ifdgps.Count == 0 && ifdzeroth.ContainsKey(ExifTag.GPSIFDPointer))
            {
                ifdzeroth.Remove(ExifTag.GPSIFDPointer);
            }
            if (ifdinterop.Count == 0 && ifdexif.ContainsKey(ExifTag.InteroperabilityIFDPointer))
            {
                ifdexif.Remove(ExifTag.InteroperabilityIFDPointer);
            }

            if (ifdzeroth.Count == 0 && ifdgps.Count == 0 && ifdinterop.Count == 0 && ifdfirst.Count == 0 && Thumbnail == null)
            {
                // Nothing to write to App1 section
                exifApp1.Header = new byte[0];
                return(false);
            }

            // We will need these bitconverter to write byte-ordered data
            BitConverterEx bceExif = new BitConverterEx(BitConverterEx.SystemByteOrder, ByteOrder);

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

                // TIFF header
                // Byte order
                long tiffoffset = ms.Position;
                ms.Write((ByteOrder == BitConverterEx.ByteOrder.LittleEndian ? new byte[] { 0x49, 0x49 } : new byte[] { 0x4D, 0x4D }), 0, 2);
                // TIFF ID
                ms.Write(bceExif.GetBytes((ushort)42), 0, 2);
                // Offset to 0th IFD
                ms.Write(bceExif.GetBytes((uint)8), 0, 4);

                // Write IFDs
                WriteIFD(ms, ifdzeroth, IFD.Zeroth, tiffoffset, preserveMakerNote);
                uint exififdrelativeoffset = (uint)(ms.Position - tiffoffset);
                WriteIFD(ms, ifdexif, IFD.EXIF, tiffoffset, preserveMakerNote);
                uint gpsifdrelativeoffset = (uint)(ms.Position - tiffoffset);
                WriteIFD(ms, ifdgps, IFD.GPS, tiffoffset, preserveMakerNote);
                uint interopifdrelativeoffset = (uint)(ms.Position - tiffoffset);
                WriteIFD(ms, ifdinterop, IFD.Interop, tiffoffset, preserveMakerNote);
                uint firstifdrelativeoffset = (uint)(ms.Position - tiffoffset);
                WriteIFD(ms, ifdfirst, IFD.First, tiffoffset, preserveMakerNote);

                // Now that we now the location of IFDs we can go back and write IFD offsets
                if (exifIFDFieldOffset != 0)
                {
                    ms.Seek(exifIFDFieldOffset, SeekOrigin.Begin);
                    ms.Write(bceExif.GetBytes(exififdrelativeoffset), 0, 4);
                }
                if (gpsIFDFieldOffset != 0)
                {
                    ms.Seek(gpsIFDFieldOffset, SeekOrigin.Begin);
                    ms.Write(bceExif.GetBytes(gpsifdrelativeoffset), 0, 4);
                }
                if (interopIFDFieldOffset != 0)
                {
                    ms.Seek(interopIFDFieldOffset, SeekOrigin.Begin);
                    ms.Write(bceExif.GetBytes(interopifdrelativeoffset), 0, 4);
                }
                if (firstIFDFieldOffset != 0)
                {
                    ms.Seek(firstIFDFieldOffset, SeekOrigin.Begin);
                    ms.Write(bceExif.GetBytes(firstifdrelativeoffset), 0, 4);
                }
                // We can write thumbnail location now
                if (thumbOffsetLocation != 0)
                {
                    ms.Seek(thumbOffsetLocation, SeekOrigin.Begin);
                    ms.Write(bceExif.GetBytes(thumbOffsetValue), 0, 4);
                }
                if (thumbSizeLocation != 0)
                {
                    ms.Seek(thumbSizeLocation, SeekOrigin.Begin);
                    ms.Write(bceExif.GetBytes(thumbSizeValue), 0, 4);
                }

                // Return APP1 header
                exifApp1.Header = ms.ToArray();
            }
            return(true);
        }
Example #25
0
 /// <summary>
 /// Converts the given array of bytes to a 16-bit signed integer.
 /// </summary>
 public short ToInt16(byte[] value, long startIndex)
 {
     return(BitConverterEx.ToInt16(value, startIndex, mFrom, mTo));
 }
Example #26
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
                }
            }
        }
Example #27
0
 /// <summary>
 /// Converts the given array of bytes to a 64-bit signed integer.
 /// </summary>
 public long ToInt64(byte[] value, long startIndex)
 {
     return(BitConverterEx.ToInt64(value, startIndex, mFrom, mTo));
 }
Example #28
0
        /// <summary>
        /// Creates an ExifProperty from the given interoperability parameters.
        /// </summary>
        /// <param name="tag">The tag id of the exif property.</param>
        /// <param name="type">The type id of the exif property.</param>
        /// <param name="count">Byte or component count.</param>
        /// <param name="value">Field data as an array of bytes.</param>
        /// <param name="byteOrder">Byte order of value.</param>
        /// <param name="ifd">IFD section containing this propery.</param>
        /// <param name="encoding">The encoding to be used for text metadata when the source encoding is unknown.</param>
        /// <returns>an ExifProperty initialized from the interoperability parameters.</returns>
        public static ExifProperty Get(ushort tag, ushort type, uint count, byte[] value, BitConverterEx.ByteOrder byteOrder, IFD ifd, Encoding encoding)
        {
            BitConverterEx conv = new BitConverterEx(byteOrder, BitConverterEx.SystemByteOrder);
            // Find the exif tag corresponding to given tag id
            ExifTag etag = ExifTagFactory.GetExifTag(ifd, tag);

            if (ifd == IFD.Zeroth)
            {
                if (tag == 0x103) // Compression
                {
                    return(new ExifEnumProperty <Compression>(ExifTag.Compression, (Compression)conv.ToUInt16(value, 0)));
                }
                else if (tag == 0x106) // PhotometricInterpretation
                {
                    return(new ExifEnumProperty <PhotometricInterpretation>(ExifTag.PhotometricInterpretation, (PhotometricInterpretation)conv.ToUInt16(value, 0)));
                }
                else if (tag == 0x112) // Orientation
                {
                    return(new ExifEnumProperty <Orientation>(ExifTag.Orientation, (Orientation)conv.ToUInt16(value, 0)));
                }
                else if (tag == 0x11c) // PlanarConfiguration
                {
                    return(new ExifEnumProperty <PlanarConfiguration>(ExifTag.PlanarConfiguration, (PlanarConfiguration)conv.ToUInt16(value, 0)));
                }
                else if (tag == 0x213) // YCbCrPositioning
                {
                    return(new ExifEnumProperty <YCbCrPositioning>(ExifTag.YCbCrPositioning, (YCbCrPositioning)conv.ToUInt16(value, 0)));
                }
                else if (tag == 0x128) // ResolutionUnit
                {
                    return(new ExifEnumProperty <ResolutionUnit>(ExifTag.ResolutionUnit, (ResolutionUnit)conv.ToUInt16(value, 0)));
                }
                else if (tag == 0x132) // DateTime
                {
                    return(new ExifDateTime(ExifTag.DateTime, ExifBitConverter.ToDateTime(value)));
                }
                else if (tag == 0x9c9b || tag == 0x9c9c ||  // Windows tags
                         tag == 0x9c9d || tag == 0x9c9e || tag == 0x9c9f)
                {
                    return(new WindowsByteString(etag, Encoding.Unicode.GetString(value).TrimEnd('\0')));
                }
            }
            else if (ifd == IFD.EXIF)
            {
                if (tag == 0x9000) // ExifVersion
                {
                    return(new ExifVersion(ExifTag.ExifVersion, ExifBitConverter.ToAscii(value, Encoding.ASCII)));
                }
                else if (tag == 0xa000) // FlashpixVersion
                {
                    return(new ExifVersion(ExifTag.FlashpixVersion, ExifBitConverter.ToAscii(value, Encoding.ASCII)));
                }
                else if (tag == 0xa001) // ColorSpace
                {
                    return(new ExifEnumProperty <ColorSpace>(ExifTag.ColorSpace, (ColorSpace)conv.ToUInt16(value, 0)));
                }
                else if (tag == 0x9286) // UserComment
                {
                    // Default to ASCII
                    Encoding enc = Encoding.ASCII;
                    bool     hasenc;
                    if (value.Length < 8)
                    {
                        hasenc = false;
                    }
                    else
                    {
                        hasenc = true;
                        string encstr = enc.GetString(value, 0, 8);
                        if (string.Compare(encstr, "ASCII\0\0\0", StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            enc = Encoding.ASCII;
                        }
                        else if (string.Compare(encstr, "JIS\0\0\0\0\0", StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            enc = Encoding.GetEncoding("Japanese (JIS 0208-1990 and 0212-1990)");
                        }
                        else if (string.Compare(encstr, "Unicode\0", StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            enc = Encoding.Unicode;
                        }
                        else
                        {
                            hasenc = false;
                        }
                    }

                    string val = (hasenc ? enc.GetString(value, 8, value.Length - 8) : enc.GetString(value)).Trim('\0');

                    return(new ExifEncodedString(ExifTag.UserComment, val, enc));
                }
                else if (tag == 0x9003) // DateTimeOriginal
                {
                    return(new ExifDateTime(ExifTag.DateTimeOriginal, ExifBitConverter.ToDateTime(value)));
                }
                else if (tag == 0x9004) // DateTimeDigitized
                {
                    return(new ExifDateTime(ExifTag.DateTimeDigitized, ExifBitConverter.ToDateTime(value)));
                }
                else if (tag == 0x8822) // ExposureProgram
                {
                    return(new ExifEnumProperty <ExposureProgram>(ExifTag.ExposureProgram, (ExposureProgram)conv.ToUInt16(value, 0)));
                }
                else if (tag == 0x9207) // MeteringMode
                {
                    return(new ExifEnumProperty <MeteringMode>(ExifTag.MeteringMode, (MeteringMode)conv.ToUInt16(value, 0)));
                }
                else if (tag == 0x9208) // LightSource
                {
                    return(new ExifEnumProperty <LightSource>(ExifTag.LightSource, (LightSource)conv.ToUInt16(value, 0)));
                }
                else if (tag == 0x9209) // Flash
                {
                    return(new ExifEnumProperty <Flash>(ExifTag.Flash, (Flash)conv.ToUInt16(value, 0), true));
                }
                else if (tag == 0x9214) // SubjectArea
                {
                    if (count == 3)
                    {
                        return(new ExifCircularSubjectArea(ExifTag.SubjectArea, ExifBitConverter.ToUShortArray(value, (int)count, byteOrder)));
                    }
                    else if (count == 4)
                    {
                        return(new ExifRectangularSubjectArea(ExifTag.SubjectArea, ExifBitConverter.ToUShortArray(value, (int)count, byteOrder)));
                    }
                    else // count == 2
                    {
                        return(new ExifPointSubjectArea(ExifTag.SubjectArea, ExifBitConverter.ToUShortArray(value, (int)count, byteOrder)));
                    }
                }
                else if (tag == 0xa210) // FocalPlaneResolutionUnit
                {
                    return(new ExifEnumProperty <ResolutionUnit>(ExifTag.FocalPlaneResolutionUnit, (ResolutionUnit)conv.ToUInt16(value, 0), true));
                }
                else if (tag == 0xa214) // SubjectLocation
                {
                    return(new ExifPointSubjectArea(ExifTag.SubjectLocation, ExifBitConverter.ToUShortArray(value, (int)count, byteOrder)));
                }
                else if (tag == 0xa217) // SensingMethod
                {
                    return(new ExifEnumProperty <SensingMethod>(ExifTag.SensingMethod, (SensingMethod)conv.ToUInt16(value, 0), true));
                }
                else if (tag == 0xa300) // FileSource
                {
                    return(new ExifEnumProperty <FileSource>(ExifTag.FileSource, (FileSource)conv.ToUInt16(value, 0), true));
                }
                else if (tag == 0xa301) // SceneType
                {
                    return(new ExifEnumProperty <SceneType>(ExifTag.SceneType, (SceneType)conv.ToUInt16(value, 0), true));
                }
                else if (tag == 0xa401) // CustomRendered
                {
                    return(new ExifEnumProperty <CustomRendered>(ExifTag.CustomRendered, (CustomRendered)conv.ToUInt16(value, 0), true));
                }
                else if (tag == 0xa402) // ExposureMode
                {
                    return(new ExifEnumProperty <ExposureMode>(ExifTag.ExposureMode, (ExposureMode)conv.ToUInt16(value, 0), true));
                }
                else if (tag == 0xa403) // WhiteBalance
                {
                    return(new ExifEnumProperty <WhiteBalance>(ExifTag.WhiteBalance, (WhiteBalance)conv.ToUInt16(value, 0), true));
                }
                else if (tag == 0xa406) // SceneCaptureType
                {
                    return(new ExifEnumProperty <SceneCaptureType>(ExifTag.SceneCaptureType, (SceneCaptureType)conv.ToUInt16(value, 0), true));
                }
                else if (tag == 0xa407) // GainControl
                {
                    return(new ExifEnumProperty <GainControl>(ExifTag.GainControl, (GainControl)conv.ToUInt16(value, 0), true));
                }
                else if (tag == 0xa408) // Contrast
                {
                    return(new ExifEnumProperty <Contrast>(ExifTag.Contrast, (Contrast)conv.ToUInt16(value, 0), true));
                }
                else if (tag == 0xa409) // Saturation
                {
                    return(new ExifEnumProperty <Saturation>(ExifTag.Saturation, (Saturation)conv.ToUInt16(value, 0), true));
                }
                else if (tag == 0xa40a) // Sharpness
                {
                    return(new ExifEnumProperty <Sharpness>(ExifTag.Sharpness, (Sharpness)conv.ToUInt16(value, 0), true));
                }
                else if (tag == 0xa40c) // SubjectDistanceRange
                {
                    return(new ExifEnumProperty <SubjectDistanceRange>(ExifTag.SubjectDistanceRange, (SubjectDistanceRange)conv.ToUInt16(value, 0), true));
                }
                else if (tag == 0xa432) // LensSpecification
                {
                    return(new LensSpecification(ExifTag.LensSpecification, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder)));
                }
            }
            else if (ifd == IFD.GPS)
            {
                if (tag == 0) // GPSVersionID
                {
                    return(new ExifVersion(ExifTag.GPSVersionID, ExifBitConverter.ToString(value)));
                }
                else if (tag == 1) // GPSLatitudeRef
                {
                    return(new ExifEnumProperty <GPSLatitudeRef>(ExifTag.GPSLatitudeRef, (GPSLatitudeRef)value[0]));
                }
                else if (tag == 2) // GPSLatitude
                {
                    return(new GPSLatitudeLongitude(ExifTag.GPSLatitude, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder)));
                }
                else if (tag == 3) // GPSLongitudeRef
                {
                    return(new ExifEnumProperty <GPSLongitudeRef>(ExifTag.GPSLongitudeRef, (GPSLongitudeRef)value[0]));
                }
                else if (tag == 4) // GPSLongitude
                {
                    return(new GPSLatitudeLongitude(ExifTag.GPSLongitude, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder)));
                }
                else if (tag == 5) // GPSAltitudeRef
                {
                    return(new ExifEnumProperty <GPSAltitudeRef>(ExifTag.GPSAltitudeRef, (GPSAltitudeRef)value[0]));
                }
                else if (tag == 7) // GPSTimeStamp
                {
                    return(new GPSTimeStamp(ExifTag.GPSTimeStamp, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder)));
                }
                else if (tag == 9) // GPSStatus
                {
                    return(new ExifEnumProperty <GPSStatus>(ExifTag.GPSStatus, (GPSStatus)value[0]));
                }
                else if (tag == 10) // GPSMeasureMode
                {
                    return(new ExifEnumProperty <GPSMeasureMode>(ExifTag.GPSMeasureMode, (GPSMeasureMode)value[0]));
                }
                else if (tag == 12) // GPSSpeedRef
                {
                    return(new ExifEnumProperty <GPSSpeedRef>(ExifTag.GPSSpeedRef, (GPSSpeedRef)value[0]));
                }
                else if (tag == 14) // GPSTrackRef
                {
                    return(new ExifEnumProperty <GPSDirectionRef>(ExifTag.GPSTrackRef, (GPSDirectionRef)value[0]));
                }
                else if (tag == 16) // GPSImgDirectionRef
                {
                    return(new ExifEnumProperty <GPSDirectionRef>(ExifTag.GPSImgDirectionRef, (GPSDirectionRef)value[0]));
                }
                else if (tag == 19) // GPSDestLatitudeRef
                {
                    return(new ExifEnumProperty <GPSLatitudeRef>(ExifTag.GPSDestLatitudeRef, (GPSLatitudeRef)value[0]));
                }
                else if (tag == 20) // GPSDestLatitude
                {
                    return(new GPSLatitudeLongitude(ExifTag.GPSDestLatitude, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder)));
                }
                else if (tag == 21) // GPSDestLongitudeRef
                {
                    return(new ExifEnumProperty <GPSLongitudeRef>(ExifTag.GPSDestLongitudeRef, (GPSLongitudeRef)value[0]));
                }
                else if (tag == 22) // GPSDestLongitude
                {
                    return(new GPSLatitudeLongitude(ExifTag.GPSDestLongitude, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder)));
                }
                else if (tag == 23) // GPSDestBearingRef
                {
                    return(new ExifEnumProperty <GPSDirectionRef>(ExifTag.GPSDestBearingRef, (GPSDirectionRef)value[0]));
                }
                else if (tag == 25) // GPSDestDistanceRef
                {
                    return(new ExifEnumProperty <GPSDistanceRef>(ExifTag.GPSDestDistanceRef, (GPSDistanceRef)value[0]));
                }
                else if (tag == 29) // GPSDateStamp
                {
                    return(new ExifDate(ExifTag.GPSDateStamp, ExifBitConverter.ToDateTime(value, false)));
                }
                else if (tag == 30) // GPSDifferential
                {
                    return(new ExifEnumProperty <GPSDifferential>(ExifTag.GPSDifferential, (GPSDifferential)conv.ToUInt16(value, 0)));
                }
            }
            else if (ifd == IFD.Interop)
            {
                if (tag == 1) // InteroperabilityIndex
                {
                    return(new ExifAscii(ExifTag.InteroperabilityIndex, ExifBitConverter.ToAscii(value, Encoding.ASCII), Encoding.ASCII));
                }
                else if (tag == 2) // InteroperabilityVersion
                {
                    return(new ExifVersion(ExifTag.InteroperabilityVersion, ExifBitConverter.ToAscii(value, Encoding.ASCII)));
                }
            }
            else if (ifd == IFD.First)
            {
                if (tag == 0x103) // Compression
                {
                    return(new ExifEnumProperty <Compression>(ExifTag.ThumbnailCompression, (Compression)conv.ToUInt16(value, 0)));
                }
                else if (tag == 0x106) // PhotometricInterpretation
                {
                    return(new ExifEnumProperty <PhotometricInterpretation>(ExifTag.ThumbnailPhotometricInterpretation, (PhotometricInterpretation)conv.ToUInt16(value, 0)));
                }
                else if (tag == 0x112) // Orientation
                {
                    return(new ExifEnumProperty <Orientation>(ExifTag.ThumbnailOrientation, (Orientation)conv.ToUInt16(value, 0)));
                }
                else if (tag == 0x11c) // PlanarConfiguration
                {
                    return(new ExifEnumProperty <PlanarConfiguration>(ExifTag.ThumbnailPlanarConfiguration, (PlanarConfiguration)conv.ToUInt16(value, 0)));
                }
                else if (tag == 0x213) // YCbCrPositioning
                {
                    return(new ExifEnumProperty <YCbCrPositioning>(ExifTag.ThumbnailYCbCrPositioning, (YCbCrPositioning)conv.ToUInt16(value, 0)));
                }
                else if (tag == 0x128) // ResolutionUnit
                {
                    return(new ExifEnumProperty <ResolutionUnit>(ExifTag.ThumbnailResolutionUnit, (ResolutionUnit)conv.ToUInt16(value, 0)));
                }
                else if (tag == 0x132) // DateTime
                {
                    return(new ExifDateTime(ExifTag.ThumbnailDateTime, ExifBitConverter.ToDateTime(value)));
                }
            }

            if (type == 1) // 1 = BYTE An 8-bit unsigned integer.
            {
                if (count == 1)
                {
                    return(new ExifByte(etag, value[0]));
                }
                else
                {
                    return(new ExifByteArray(etag, value));
                }
            }
            else if (type == 2) // 2 = ASCII An 8-bit byte containing one 7-bit ASCII code.
            {
                return(new ExifAscii(etag, ExifBitConverter.ToAscii(value, encoding), encoding));
            }
            else if (type == 3) // 3 = SHORT A 16-bit (2-byte) unsigned integer.
            {
                if (count == 1)
                {
                    return(new ExifUShort(etag, conv.ToUInt16(value, 0)));
                }
                else
                {
                    return(new ExifUShortArray(etag, ExifBitConverter.ToUShortArray(value, (int)count, byteOrder)));
                }
            }
            else if (type == 4) // 4 = LONG A 32-bit (4-byte) unsigned integer.
            {
                if (count == 1)
                {
                    return(new ExifUInt(etag, conv.ToUInt32(value, 0)));
                }
                else
                {
                    return(new ExifUIntArray(etag, ExifBitConverter.ToUIntArray(value, (int)count, byteOrder)));
                }
            }
            else if (type == 5) // 5 = RATIONAL Two LONGs. The first LONG is the numerator and the second LONG expresses the denominator.
            {
                if (count == 1)
                {
                    return(new ExifURational(etag, ExifBitConverter.ToURational(value, byteOrder)));
                }
                else
                {
                    return(new ExifURationalArray(etag, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder)));
                }
            }
            else if (type == 6) // 1 = SBYTE An 8-bit signed integer.
            {
                if (count == 1)
                {
                    return(new ExifSByte(etag, (sbyte)value[0]));
                }
                else
                {
                    sbyte[] data = new sbyte[count];
                    Buffer.BlockCopy(value, 0, data, 0, (int)count);
                    return(new ExifSByteArray(etag, data));
                }
            }
            else if (type == 7) // 7 = UNDEFINED An 8-bit byte that can take any value depending on the field definition.
            {
                return(new ExifUndefined(etag, value));
            }
            else if (type == 8) // 8 = SSHORT A 16-bit (2-byte) signed integer.
            {
                if (count == 1)
                {
                    return(new ExifSShort(etag, conv.ToInt16(value, 0)));
                }
                else
                {
                    return(new ExifSShortArray(etag, ExifBitConverter.ToSShortArray(value, (int)count, byteOrder)));
                }
            }
            else if (type == 9) // 9 = SLONG A 32-bit (4-byte) signed integer (2's complement notation).
            {
                if (count == 1)
                {
                    return(new ExifSInt(etag, conv.ToInt32(value, 0)));
                }
                else
                {
                    return(new ExifSIntArray(etag, ExifBitConverter.ToSIntArray(value, (int)count, byteOrder)));
                }
            }
            else if (type == 10) // 10 = SRATIONAL Two SLONGs. The first SLONG is the numerator and the second SLONG is the denominator.
            {
                if (count == 1)
                {
                    return(new ExifSRational(etag, ExifBitConverter.ToSRational(value, byteOrder)));
                }
                else
                {
                    return(new ExifSRationalArray(etag, ExifBitConverter.ToSRationalArray(value, (int)count, byteOrder)));
                }
            }
            else if (type == 11) // 11 = FLOAT Single precision (4-byte) IEEE format.
            {
                if (count == 1)
                {
                    return(new ExifFloat(etag, conv.ToSingle(value, 0)));
                }
                else
                {
                    return(new ExifFloatArray(etag, ExifBitConverter.ToSingleArray(value, (int)count, byteOrder)));
                }
            }
            else if (type == 12) // 12 = DOUBLE Double precision (8-byte) IEEE format.
            {
                if (count == 1)
                {
                    return(new ExifDouble(etag, conv.ToDouble(value, 0)));
                }
                else
                {
                    return(new ExifDoubleArray(etag, ExifBitConverter.ToDoubleArray(value, (int)count, byteOrder)));
                }
            }
            else
            {
                throw new ArgumentException("Unknown property type.");
            }
        }
Example #29
0
        /// <summary>
        /// Replaces the contents of the APP1 section with the Exif properties.
        /// </summary>
        private void WriteApp1(bool preserveMakerNote)
        {
            // Zero out IFD field offsets. We will fill those as we write the IFD sections
            exifIFDFieldOffset = 0;
            gpsIFDFieldOffset = 0;
            interopIFDFieldOffset = 0;
            firstIFDFieldOffset = 0;
            // We also do not know the location of the embedded thumbnail yet
            thumbOffsetLocation = 0;
            thumbOffsetValue = 0;
            thumbSizeLocation = 0;
            thumbSizeValue = 0;

            // Which IFD sections do we have?
            Dictionary<ExifTag, ExifProperty> ifdzeroth = new Dictionary<ExifTag, ExifProperty>();
            Dictionary<ExifTag, ExifProperty> ifdexif = new Dictionary<ExifTag, ExifProperty>();
            Dictionary<ExifTag, ExifProperty> ifdgps = new Dictionary<ExifTag, ExifProperty>();
            Dictionary<ExifTag, ExifProperty> ifdinterop = new Dictionary<ExifTag, ExifProperty>();
            Dictionary<ExifTag, ExifProperty> ifdfirst = new Dictionary<ExifTag, ExifProperty>();

            foreach (KeyValuePair<ExifTag, ExifProperty> pair in Properties)
            {
                switch (pair.Value.IFD)
                {
                    case IFD.Zeroth:
                        ifdzeroth.Add(pair.Key, pair.Value);
                        break;
                    case IFD.EXIF:
                        ifdexif.Add(pair.Key, pair.Value);
                        break;
                    case IFD.GPS:
                        ifdgps.Add(pair.Key, pair.Value);
                        break;
                    case IFD.Interop:
                        ifdinterop.Add(pair.Key, pair.Value);
                        break;
                    case IFD.First:
                        ifdfirst.Add(pair.Key, pair.Value);
                        break;
                }
            }

            // Add IFD pointers if they are missing
            // We will write the pointer values later on
            if (ifdexif.Count != 0 && !ifdzeroth.ContainsKey(ExifTag.EXIFIFDPointer))
                ifdzeroth.Add(ExifTag.EXIFIFDPointer, new ExifUInt(ExifTag.EXIFIFDPointer, 0));
            if (ifdgps.Count != 0 && !ifdzeroth.ContainsKey(ExifTag.GPSIFDPointer))
                ifdzeroth.Add(ExifTag.GPSIFDPointer, new ExifUInt(ExifTag.GPSIFDPointer, 0));
            if (ifdinterop.Count != 0 && !ifdexif.ContainsKey(ExifTag.InteroperabilityIFDPointer))
                ifdexif.Add(ExifTag.InteroperabilityIFDPointer, new ExifUInt(ExifTag.InteroperabilityIFDPointer, 0));

            // Remove IFD pointers if IFD sections are missing
            if (ifdexif.Count == 0 && ifdzeroth.ContainsKey(ExifTag.EXIFIFDPointer))
                ifdzeroth.Remove(ExifTag.EXIFIFDPointer);
            if (ifdgps.Count == 0 && ifdzeroth.ContainsKey(ExifTag.GPSIFDPointer))
                ifdzeroth.Remove(ExifTag.GPSIFDPointer);
            if (ifdinterop.Count == 0 && ifdexif.ContainsKey(ExifTag.InteroperabilityIFDPointer))
                ifdexif.Remove(ExifTag.InteroperabilityIFDPointer);

            if (ifdzeroth.Count == 0)
                throw new IFD0IsEmptyException();

            // We will need these bitconverters to write byte-ordered data
            BitConverterEx bceJPEG = new BitConverterEx(BitConverterEx.ByteOrder.System, BitConverterEx.ByteOrder.BigEndian);
            BitConverterEx bceExif = new BitConverterEx(BitConverterEx.ByteOrder.System, ByteOrder);

            // Create a memory stream to write the APP1 section to
            MemoryStream ms = new MemoryStream();

            // Exif identifer
            ms.Write(Encoding.ASCII.GetBytes("Exif\0\0"), 0, 6);

            // TIFF header
            // Byte order
            long tiffoffset = ms.Position;
            ms.Write((ByteOrder == BitConverterEx.ByteOrder.LittleEndian ? new byte[] { 0x49, 0x49 } : new byte[] { 0x4D, 0x4D }), 0, 2);
            // TIFF ID
            ms.Write(bceExif.GetBytes((ushort)42), 0, 2);
            // Offset to 0th IFD
            ms.Write(bceExif.GetBytes((uint)8), 0, 4);

            // Write IFDs
            WriteIFD(ms, ifdzeroth, IFD.Zeroth, tiffoffset, preserveMakerNote);
            uint exififdrelativeoffset = (uint)(ms.Position - tiffoffset);
            WriteIFD(ms, ifdexif, IFD.EXIF, tiffoffset, preserveMakerNote);
            uint gpsifdrelativeoffset = (uint)(ms.Position - tiffoffset);
            WriteIFD(ms, ifdgps, IFD.GPS, tiffoffset, preserveMakerNote);
            uint interopifdrelativeoffset = (uint)(ms.Position - tiffoffset);
            WriteIFD(ms, ifdinterop, IFD.Interop, tiffoffset, preserveMakerNote);
            uint firstifdrelativeoffset = (uint)(ms.Position - tiffoffset);
            WriteIFD(ms, ifdfirst, IFD.First, tiffoffset, preserveMakerNote);

            // Now that we now the location of IFDs we can go back and write IFD offsets
            if (exifIFDFieldOffset != 0)
            {
                ms.Seek(exifIFDFieldOffset, SeekOrigin.Begin);
                ms.Write(bceExif.GetBytes(exififdrelativeoffset), 0, 4);
            }
            if (gpsIFDFieldOffset != 0)
            {
                ms.Seek(gpsIFDFieldOffset, SeekOrigin.Begin);
                ms.Write(bceExif.GetBytes(gpsifdrelativeoffset), 0, 4);
            }
            if (interopIFDFieldOffset != 0)
            {
                ms.Seek(interopIFDFieldOffset, SeekOrigin.Begin);
                ms.Write(bceExif.GetBytes(interopifdrelativeoffset), 0, 4);
            }
            if (firstIFDFieldOffset != 0)
            {
                ms.Seek(firstIFDFieldOffset, SeekOrigin.Begin);
                ms.Write(bceExif.GetBytes(firstifdrelativeoffset), 0, 4);
            }
            // We can write thumbnail location now
            if (thumbOffsetLocation != 0)
            {
                ms.Seek(thumbOffsetLocation, SeekOrigin.Begin);
                ms.Write(bceExif.GetBytes(thumbOffsetValue), 0, 4);
            }
            if (thumbSizeLocation != 0)
            {
                ms.Seek(thumbSizeLocation, SeekOrigin.Begin);
                ms.Write(bceExif.GetBytes(thumbSizeValue), 0, 4);
            }

            ms.Close();

            // Return APP1 header
            app1.Header = ms.ToArray();
        }
 /// <summary>
 /// Creates an ExifProperty from the given interoperability parameters.
 /// </summary>
 /// <param name="interOperability">Property data.</param>
 /// <param name="byteOrder">Byte order of the source data.</param>
 /// <param name="ifd">IFD section containing this propery.</param>
 /// <returns>an ExifProperty initialized from the interoperability parameters.</returns>
 public static ExifProperty Get(ExifInterOperability interOperability, BitConverterEx.ByteOrder byteOrder, IFD ifd)
 {
     return Get(interOperability.TagID, interOperability.TypeID, interOperability.Count, interOperability.Data, byteOrder, ifd);
 }
Example #31
0
        private void WriteIFD(MemoryStream stream, Dictionary<ExifTag, ExifProperty> ifd, IFD ifdtype, long tiffoffset, bool preserveMakerNote)
        {
            BitConverterEx conv = new BitConverterEx(BitConverterEx.ByteOrder.System, ByteOrder);

            // Create a queue of fields to write
            Queue<ExifProperty> fieldqueue = new Queue<ExifProperty>();
            foreach (ExifProperty prop in ifd.Values)
                if (prop.Tag != ExifTag.MakerNote)
                    fieldqueue.Enqueue(prop);
            // Push the maker note data to the end
            if (ifd.ContainsKey(ExifTag.MakerNote))
                fieldqueue.Enqueue(ifd[ExifTag.MakerNote]);

            // Offset to start of field data from start of TIFF header
            uint dataoffset = (uint)(2 + ifd.Count * 12 + 4 + stream.Position - tiffoffset);
            uint currentdataoffset = dataoffset;
            long absolutedataoffset = stream.Position + (2 + ifd.Count * 12 + 4);

            bool makernotewritten = false;
            // Field count
            stream.Write(conv.GetBytes((ushort)ifd.Count), 0, 2);
            // Fields
            while (fieldqueue.Count != 0)
            {
                ExifProperty field = fieldqueue.Dequeue();
                ExifInterOperability interop = field.Interoperability;

                uint fillerbytecount = 0;

                // Try to preserve the makernote data offset
                if (!makernotewritten &&
                    !makerNoteProcessed &&
                    makerNoteOffset != 0 &&
                    ifdtype == IFD.EXIF &&
                    field.Tag != ExifTag.MakerNote &&
                    interop.Data.Length > 4 &&
                    currentdataoffset + interop.Data.Length > makerNoteOffset &&
                    ifd.ContainsKey(ExifTag.MakerNote))
                {
                    // Delay writing this field until we write makernote data
                    fieldqueue.Enqueue(field);
                    continue;
                }
                else if (field.Tag == ExifTag.MakerNote)
                {
                    makernotewritten = true;
                    // We may need to write filler bytes to preserve maker note offset
                    if (preserveMakerNote && !makerNoteProcessed)
                        fillerbytecount = makerNoteOffset - currentdataoffset;
                    else
                        fillerbytecount = 0;
                }

                // Tag
                stream.Write(conv.GetBytes(interop.TagID), 0, 2);
                // Type
                stream.Write(conv.GetBytes(interop.TypeID), 0, 2);
                // Count
                stream.Write(conv.GetBytes(interop.Count), 0, 4);
                // Field data
                byte[] data = interop.Data;
                if (ByteOrder != BitConverterEx.SystemByteOrder)
                {
                    if (interop.TypeID == 1 || interop.TypeID == 3 || interop.TypeID == 4 || interop.TypeID == 9)
                        Array.Reverse(data);
                    else if (interop.TypeID == 5 || interop.TypeID == 10)
                    {
                        Array.Reverse(data, 0, 4);
                        Array.Reverse(data, 4, 4);
                    }
                }

                // Fields containing offsets to other IFDs
                // Just store their offets, we will write the values later on when we know the lengths of IFDs
                if (ifdtype == IFD.Zeroth && interop.TagID == 0x8769)
                    exifIFDFieldOffset = stream.Position;
                else if (ifdtype == IFD.Zeroth && interop.TagID == 0x8825)
                    gpsIFDFieldOffset = stream.Position;
                else if (ifdtype == IFD.EXIF && interop.TagID == 0xa005)
                    interopIFDFieldOffset = stream.Position;
                else if (ifdtype == IFD.First && interop.TagID == 0x201)
                    thumbOffsetLocation = stream.Position;
                else if (ifdtype == IFD.First && interop.TagID == 0x202)
                    thumbSizeLocation = stream.Position;

                // Write 4 byte field value or field data
                if (data.Length <= 4)
                {
                    stream.Write(data, 0, data.Length);
                    for (int i = data.Length; i < 4; i++)
                        stream.WriteByte(0);
                }
                else
                {
                    // Pointer to data area relative to TIFF header
                    stream.Write(conv.GetBytes(currentdataoffset + fillerbytecount), 0, 4);
                    // Actual data
                    long currentoffset = stream.Position;
                    stream.Seek(absolutedataoffset, SeekOrigin.Begin);
                    // Write filler bytes
                    for (int i = 0; i < fillerbytecount; i++)
                        stream.WriteByte(0xFF);
                    stream.Write(data, 0, data.Length);
                    stream.Seek(currentoffset, SeekOrigin.Begin);
                    // Increment pointers
                    currentdataoffset += fillerbytecount + (uint)data.Length;
                    absolutedataoffset += fillerbytecount + data.Length;
                }
            }
            // Offset to 1st IFD
            // We will write zeros for now. This will be filled after we write all IFDs
            if (ifdtype == IFD.Zeroth)
                firstIFDFieldOffset = stream.Position;
            stream.Write(new byte[] { 0, 0, 0, 0 }, 0, 4);

            // Seek to end of IFD
            stream.Seek(absolutedataoffset, SeekOrigin.Begin);

            // Write thumbnail data
            if (ifdtype == IFD.First)
            {
                if (Thumbnail != null)
                {
                    MemoryStream ts = new MemoryStream();
                    Thumbnail.Save(ts);
                    ts.Close();
                    byte[] thumb = ts.ToArray();
                    thumbOffsetValue = (uint)(stream.Position - tiffoffset);
                    thumbSizeValue = (uint)thumb.Length;
                    stream.Write(thumb, 0, thumb.Length);
                    ts.Dispose();
                }
                else
                {
                    thumbOffsetValue = 0;
                    thumbSizeValue = 0;
                }
            }
        }
Example #32
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
                }
            }
        }
        /// <summary>
        /// Creates an ExifProperty from the given interoperability parameters.
        /// </summary>
        /// <param name="tag">The tag id of the exif property.</param>
        /// <param name="type">The type id of the exif property.</param>
        /// <param name="count">Byte or component count.</param>
        /// <param name="value">Field data as an array of bytes.</param>
        /// <param name="byteOrder">Byte order of value.</param>
        /// <param name="ifd">IFD section containing this propery.</param>
        /// <param name="encoding">The encoding to be used for text metadata when the source encoding is unknown.</param>
        /// <returns>an ExifProperty initialized from the interoperability parameters.</returns>
        public static ExifProperty Get(ushort tag, ushort type, uint count, byte[] value, BitConverterEx.ByteOrder byteOrder, IFD ifd, Encoding encoding)
        {
            BitConverterEx conv = new BitConverterEx(byteOrder, BitConverterEx.SystemByteOrder);
            // Find the exif tag corresponding to given tag id
            ExifTag etag = ExifTagFactory.GetExifTag(ifd, tag);

            if (ifd == IFD.Zeroth)
            {
                if (tag == 0x103) // Compression
                    return new ExifEnumProperty<Compression>(ExifTag.Compression, (Compression)conv.ToUInt16(value, 0));
                else if (tag == 0x106) // PhotometricInterpretation
                    return new ExifEnumProperty<PhotometricInterpretation>(ExifTag.PhotometricInterpretation, (PhotometricInterpretation)conv.ToUInt16(value, 0));
                else if (tag == 0x112) // Orientation
                    return new ExifEnumProperty<Orientation>(ExifTag.Orientation, (Orientation)conv.ToUInt16(value, 0));
                else if (tag == 0x11c) // PlanarConfiguration
                    return new ExifEnumProperty<PlanarConfiguration>(ExifTag.PlanarConfiguration, (PlanarConfiguration)conv.ToUInt16(value, 0));
                else if (tag == 0x213) // YCbCrPositioning
                    return new ExifEnumProperty<YCbCrPositioning>(ExifTag.YCbCrPositioning, (YCbCrPositioning)conv.ToUInt16(value, 0));
                else if (tag == 0x128) // ResolutionUnit
                    return new ExifEnumProperty<ResolutionUnit>(ExifTag.ResolutionUnit, (ResolutionUnit)conv.ToUInt16(value, 0));
                else if (tag == 0x132) // DateTime
                    return new ExifDateTime(ExifTag.DateTime, ExifBitConverter.ToDateTime(value));
                else if (tag == 0x9c9b || tag == 0x9c9c ||  // Windows tags
                    tag == 0x9c9d || tag == 0x9c9e || tag == 0x9c9f)
                    return new WindowsByteString(etag, Encoding.Unicode.GetString(value).TrimEnd('\0'));
            }
            else if (ifd == IFD.EXIF)
            {
                if (tag == 0x9000) // ExifVersion
                    return new ExifVersion(ExifTag.ExifVersion, ExifBitConverter.ToAscii(value, Encoding.ASCII));
                else if (tag == 0xa000) // FlashpixVersion
                    return new ExifVersion(ExifTag.FlashpixVersion, ExifBitConverter.ToAscii(value, Encoding.ASCII));
                else if (tag == 0xa001) // ColorSpace
                    return new ExifEnumProperty<ColorSpace>(ExifTag.ColorSpace, (ColorSpace)conv.ToUInt16(value, 0));
                else if (tag == 0x9286) // UserComment
                {
                    // Default to ASCII
                    Encoding enc = Encoding.ASCII;
                    bool hasenc;
                    if (value.Length < 8)
                        hasenc = false;
                    else
                    {
                        hasenc = true;
                        string encstr = enc.GetString(value, 0, 8);
                        if (string.Compare(encstr, "ASCII\0\0\0", StringComparison.OrdinalIgnoreCase) == 0)
                            enc = Encoding.ASCII;
                        else if (string.Compare(encstr, "JIS\0\0\0\0\0", StringComparison.OrdinalIgnoreCase) == 0)
                            enc = Encoding.GetEncoding("Japanese (JIS 0208-1990 and 0212-1990)");
                        else if (string.Compare(encstr, "Unicode\0", StringComparison.OrdinalIgnoreCase) == 0)
                            enc = Encoding.Unicode;
                        else
                            hasenc = false;
                    }

                    string val = (hasenc ? enc.GetString(value, 8, value.Length - 8) : enc.GetString(value)).Trim('\0');

                    return new ExifEncodedString(ExifTag.UserComment, val, enc);
                }
                else if (tag == 0x9003) // DateTimeOriginal
                    return new ExifDateTime(ExifTag.DateTimeOriginal, ExifBitConverter.ToDateTime(value));
                else if (tag == 0x9004) // DateTimeDigitized
                    return new ExifDateTime(ExifTag.DateTimeDigitized, ExifBitConverter.ToDateTime(value));
                else if (tag == 0x8822) // ExposureProgram
                    return new ExifEnumProperty<ExposureProgram>(ExifTag.ExposureProgram, (ExposureProgram)conv.ToUInt16(value, 0));
                else if (tag == 0x9207) // MeteringMode
                    return new ExifEnumProperty<MeteringMode>(ExifTag.MeteringMode, (MeteringMode)conv.ToUInt16(value, 0));
                else if (tag == 0x9208) // LightSource
                    return new ExifEnumProperty<LightSource>(ExifTag.LightSource, (LightSource)conv.ToUInt16(value, 0));
                else if (tag == 0x9209) // Flash
                    return new ExifEnumProperty<Flash>(ExifTag.Flash, (Flash)conv.ToUInt16(value, 0), true);
                else if (tag == 0x9214) // SubjectArea
                {
                    if (count == 3)
                        return new ExifCircularSubjectArea(ExifTag.SubjectArea, ExifBitConverter.ToUShortArray(value, (int)count, byteOrder));
                    else if (count == 4)
                        return new ExifRectangularSubjectArea(ExifTag.SubjectArea, ExifBitConverter.ToUShortArray(value, (int)count, byteOrder));
                    else // count == 2
                        return new ExifPointSubjectArea(ExifTag.SubjectArea, ExifBitConverter.ToUShortArray(value, (int)count, byteOrder));
                }
                else if (tag == 0xa210) // FocalPlaneResolutionUnit
                    return new ExifEnumProperty<ResolutionUnit>(ExifTag.FocalPlaneResolutionUnit, (ResolutionUnit)conv.ToUInt16(value, 0), true);
                else if (tag == 0xa214) // SubjectLocation
                    return new ExifPointSubjectArea(ExifTag.SubjectLocation, ExifBitConverter.ToUShortArray(value, (int)count, byteOrder));
                else if (tag == 0xa217) // SensingMethod
                    return new ExifEnumProperty<SensingMethod>(ExifTag.SensingMethod, (SensingMethod)conv.ToUInt16(value, 0), true);
                else if (tag == 0xa300) // FileSource
                    return new ExifEnumProperty<FileSource>(ExifTag.FileSource, (FileSource)conv.ToUInt16(value, 0), true);
                else if (tag == 0xa301) // SceneType
                    return new ExifEnumProperty<SceneType>(ExifTag.SceneType, (SceneType)conv.ToUInt16(value, 0), true);
                else if (tag == 0xa401) // CustomRendered
                    return new ExifEnumProperty<CustomRendered>(ExifTag.CustomRendered, (CustomRendered)conv.ToUInt16(value, 0), true);
                else if (tag == 0xa402) // ExposureMode
                    return new ExifEnumProperty<ExposureMode>(ExifTag.ExposureMode, (ExposureMode)conv.ToUInt16(value, 0), true);
                else if (tag == 0xa403) // WhiteBalance
                    return new ExifEnumProperty<WhiteBalance>(ExifTag.WhiteBalance, (WhiteBalance)conv.ToUInt16(value, 0), true);
                else if (tag == 0xa406) // SceneCaptureType
                    return new ExifEnumProperty<SceneCaptureType>(ExifTag.SceneCaptureType, (SceneCaptureType)conv.ToUInt16(value, 0), true);
                else if (tag == 0xa407) // GainControl
                    return new ExifEnumProperty<GainControl>(ExifTag.GainControl, (GainControl)conv.ToUInt16(value, 0), true);
                else if (tag == 0xa408) // Contrast
                    return new ExifEnumProperty<Contrast>(ExifTag.Contrast, (Contrast)conv.ToUInt16(value, 0), true);
                else if (tag == 0xa409) // Saturation
                    return new ExifEnumProperty<Saturation>(ExifTag.Saturation, (Saturation)conv.ToUInt16(value, 0), true);
                else if (tag == 0xa40a) // Sharpness
                    return new ExifEnumProperty<Sharpness>(ExifTag.Sharpness, (Sharpness)conv.ToUInt16(value, 0), true);
                else if (tag == 0xa40c) // SubjectDistanceRange
                    return new ExifEnumProperty<SubjectDistanceRange>(ExifTag.SubjectDistanceRange, (SubjectDistanceRange)conv.ToUInt16(value, 0), true);
            }
            else if (ifd == IFD.GPS)
            {
                if (tag == 0) // GPSVersionID
                    return new ExifVersion(ExifTag.GPSVersionID, ExifBitConverter.ToString(value));
                else if (tag == 1) // GPSLatitudeRef
                    return new ExifEnumProperty<GPSLatitudeRef>(ExifTag.GPSLatitudeRef, (GPSLatitudeRef)value[0]);
                else if (tag == 2) // GPSLatitude
                    return new GPSLatitudeLongitude(ExifTag.GPSLatitude, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder));
                else if (tag == 3) // GPSLongitudeRef
                    return new ExifEnumProperty<GPSLongitudeRef>(ExifTag.GPSLongitudeRef, (GPSLongitudeRef)value[0]);
                else if (tag == 4) // GPSLongitude
                    return new GPSLatitudeLongitude(ExifTag.GPSLongitude, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder));
                else if (tag == 5) // GPSAltitudeRef
                    return new ExifEnumProperty<GPSAltitudeRef>(ExifTag.GPSAltitudeRef, (GPSAltitudeRef)value[0]);
                else if (tag == 7) // GPSTimeStamp
                    return new GPSTimeStamp(ExifTag.GPSTimeStamp, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder));
                else if (tag == 9) // GPSStatus
                    return new ExifEnumProperty<GPSStatus>(ExifTag.GPSStatus, (GPSStatus)value[0]);
                else if (tag == 10) // GPSMeasureMode
                    return new ExifEnumProperty<GPSMeasureMode>(ExifTag.GPSMeasureMode, (GPSMeasureMode)value[0]);
                else if (tag == 12) // GPSSpeedRef
                    return new ExifEnumProperty<GPSSpeedRef>(ExifTag.GPSSpeedRef, (GPSSpeedRef)value[0]);
                else if (tag == 14) // GPSTrackRef
                    return new ExifEnumProperty<GPSDirectionRef>(ExifTag.GPSTrackRef, (GPSDirectionRef)value[0]);
                else if (tag == 16) // GPSImgDirectionRef
                    return new ExifEnumProperty<GPSDirectionRef>(ExifTag.GPSImgDirectionRef, (GPSDirectionRef)value[0]);
                else if (tag == 19) // GPSDestLatitudeRef
                    return new ExifEnumProperty<GPSLatitudeRef>(ExifTag.GPSDestLatitudeRef, (GPSLatitudeRef)value[0]);
                else if (tag == 20) // GPSDestLatitude
                    return new GPSLatitudeLongitude(ExifTag.GPSDestLatitude, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder));
                else if (tag == 21) // GPSDestLongitudeRef
                    return new ExifEnumProperty<GPSLongitudeRef>(ExifTag.GPSDestLongitudeRef, (GPSLongitudeRef)value[0]);
                else if (tag == 22) // GPSDestLongitude
                    return new GPSLatitudeLongitude(ExifTag.GPSDestLongitude, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder));
                else if (tag == 23) // GPSDestBearingRef
                    return new ExifEnumProperty<GPSDirectionRef>(ExifTag.GPSDestBearingRef, (GPSDirectionRef)value[0]);
                else if (tag == 25) // GPSDestDistanceRef
                    return new ExifEnumProperty<GPSDistanceRef>(ExifTag.GPSDestDistanceRef, (GPSDistanceRef)value[0]);
                else if (tag == 29) // GPSDate
                    return new ExifDateTime(ExifTag.GPSDateStamp, ExifBitConverter.ToDateTime(value, false));
                else if (tag == 30) // GPSDifferential
                    return new ExifEnumProperty<GPSDifferential>(ExifTag.GPSDifferential, (GPSDifferential)conv.ToUInt16(value, 0));
            }
            else if (ifd == IFD.Interop)
            {
                if (tag == 1) // InteroperabilityIndex
                    return new ExifAscii(ExifTag.InteroperabilityIndex, ExifBitConverter.ToAscii(value, Encoding.ASCII), Encoding.ASCII);
                else if (tag == 2) // InteroperabilityVersion
                    return new ExifVersion(ExifTag.InteroperabilityVersion, ExifBitConverter.ToAscii(value, Encoding.ASCII));
            }
            else if (ifd == IFD.First)
            {
                if (tag == 0x103) // Compression
                    return new ExifEnumProperty<Compression>(ExifTag.ThumbnailCompression, (Compression)conv.ToUInt16(value, 0));
                else if (tag == 0x106) // PhotometricInterpretation
                    return new ExifEnumProperty<PhotometricInterpretation>(ExifTag.ThumbnailPhotometricInterpretation, (PhotometricInterpretation)conv.ToUInt16(value, 0));
                else if (tag == 0x112) // Orientation
                    return new ExifEnumProperty<Orientation>(ExifTag.ThumbnailOrientation, (Orientation)conv.ToUInt16(value, 0));
                else if (tag == 0x11c) // PlanarConfiguration
                    return new ExifEnumProperty<PlanarConfiguration>(ExifTag.ThumbnailPlanarConfiguration, (PlanarConfiguration)conv.ToUInt16(value, 0));
                else if (tag == 0x213) // YCbCrPositioning
                    return new ExifEnumProperty<YCbCrPositioning>(ExifTag.ThumbnailYCbCrPositioning, (YCbCrPositioning)conv.ToUInt16(value, 0));
                else if (tag == 0x128) // ResolutionUnit
                    return new ExifEnumProperty<ResolutionUnit>(ExifTag.ThumbnailResolutionUnit, (ResolutionUnit)conv.ToUInt16(value, 0));
                else if (tag == 0x132) // DateTime
                    return new ExifDateTime(ExifTag.ThumbnailDateTime, ExifBitConverter.ToDateTime(value));
            }

            if (type == 1) // 1 = BYTE An 8-bit unsigned integer.
            {
                if (count == 1)
                    return new ExifByte(etag, value[0]);
                else
                    return new ExifByteArray(etag, value);
            }
            else if (type == 2) // 2 = ASCII An 8-bit byte containing one 7-bit ASCII code.
            {
                return new ExifAscii(etag, ExifBitConverter.ToAscii(value, encoding), encoding);
            }
            else if (type == 3) // 3 = SHORT A 16-bit (2-byte) unsigned integer.
            {
                if (count == 1)
                    return new ExifUShort(etag, conv.ToUInt16(value, 0));
                else
                    return new ExifUShortArray(etag, ExifBitConverter.ToUShortArray(value, (int)count, byteOrder));
            }
            else if (type == 4) // 4 = LONG A 32-bit (4-byte) unsigned integer.
            {
                if (count == 1)
                    return new ExifUInt(etag, conv.ToUInt32(value, 0));
                else
                    return new ExifUIntArray(etag, ExifBitConverter.ToUIntArray(value, (int)count, byteOrder));
            }
            else if (type == 5) // 5 = RATIONAL Two LONGs. The first LONG is the numerator and the second LONG expresses the denominator.
            {
                if (count == 1)
                    return new ExifURational(etag, ExifBitConverter.ToURational(value, byteOrder));
                else
                    return new ExifURationalArray(etag, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder));
            }
            else if (type == 6) // 6 = SBYTE An 8-bit signed (twos-complement) integer.
            {
                if (count == 1)
                    return new ExifSByte(etag, Convert.ToSByte(value[0]));
                else
                    return new ExifSByteArray(etag, Array.ConvertAll(value, b => Convert.ToSByte(b)));
            }
            else if (type == 7) // 7 = UNDEFINED An 8-bit byte that can take any value depending on the field definition.
            {
                return new ExifUndefined(etag, value);
            }
            else if (type == 8) // 8 = SSHORT A 16-bit (2-byte) signed short
            {
                if (count == 1)
                    return new ExifSShort(etag, conv.ToInt16(value, 0));
                else
                    return new ExifSShortArray(etag, ExifBitConverter.ToSShortArray(value, (int)count, byteOrder));
            }
            else if (type == 9) // 9 = SLONG A 32-bit (4-byte) signed integer (2's complement notation).
            {
                if (count == 1)
                    return new ExifSInt(etag, conv.ToInt32(value, 0));
                else
                    return new ExifSIntArray(etag, ExifBitConverter.ToSIntArray(value, (int)count, byteOrder));
            }
            else if (type == 10) // 10 = SRATIONAL Two SLONGs. The first SLONG is the numerator and the second SLONG is the denominator.
            {
                if (count == 1)
                    return new ExifSRational(etag, ExifBitConverter.ToSRational(value, byteOrder));
                else
                    return new ExifSRationalArray(etag, ExifBitConverter.ToSRationalArray(value, (int)count, byteOrder));
            }
            else if (type == 11) // 11 = FLOAT A single precision (4-byte) IEEE format
            {
                if (count == 1)
                    return new ExifFloat(etag, conv.ToSingle(value, 0));
                else
                    return new ExifFloatArray(etag, ExifBitConverter.ToSingleArray(value, (int)count, byteOrder));
            }
            else if (type == 12) // 12 = DOUBLE A double precision (8-byte) IEEE format
            {
                if (count == 1)
                    return new ExifDouble(etag, conv.ToDouble(value, 0));
                else
                    return new ExifDoubleArray(etag, ExifBitConverter.ToDoubleArray(value, (int)count, byteOrder));
            }
            else
                throw new ArgumentException("Unknown property type.");
        }
Example #34
0
        private void WriteIFD(MemoryStream stream, Dictionary <ExifTag, ExifProperty> ifd, IFD ifdtype, long tiffoffset, bool preserveMakerNote)
        {
            BitConverterEx conv = new BitConverterEx(BitConverterEx.ByteOrder.System, ByteOrder);

            // Create a queue of fields to write
            Queue <ExifProperty> fieldqueue = new Queue <ExifProperty>();

            foreach (ExifProperty prop in ifd.Values)
            {
                if (prop.Tag != ExifTag.MakerNote)
                {
                    fieldqueue.Enqueue(prop);
                }
            }
            // Push the maker note data to the end
            if (ifd.ContainsKey(ExifTag.MakerNote))
            {
                fieldqueue.Enqueue(ifd[ExifTag.MakerNote]);
            }

            // Offset to start of field data from start of TIFF header
            uint dataoffset         = (uint)(2 + ifd.Count * 12 + 4 + stream.Position - tiffoffset);
            uint currentdataoffset  = dataoffset;
            long absolutedataoffset = stream.Position + (2 + ifd.Count * 12 + 4);

            bool makernotewritten = false;

            // Field count
            stream.Write(conv.GetBytes((ushort)ifd.Count), 0, 2);
            // Fields
            while (fieldqueue.Count != 0)
            {
                ExifProperty         field   = fieldqueue.Dequeue();
                ExifInterOperability interop = field.Interoperability;

                uint fillerbytecount = 0;

                // Try to preserve the makernote data offset
                if (!makernotewritten &&
                    !makerNoteProcessed &&
                    makerNoteOffset != 0 &&
                    ifdtype == IFD.EXIF &&
                    field.Tag != ExifTag.MakerNote &&
                    interop.Data.Length > 4 &&
                    currentdataoffset + interop.Data.Length > makerNoteOffset &&
                    ifd.ContainsKey(ExifTag.MakerNote))
                {
                    // Delay writing this field until we write makernote data
                    fieldqueue.Enqueue(field);
                    continue;
                }
                else if (field.Tag == ExifTag.MakerNote)
                {
                    makernotewritten = true;
                    // We may need to write filler bytes to preserve maker note offset
                    if (preserveMakerNote && !makerNoteProcessed)
                    {
                        fillerbytecount = makerNoteOffset - currentdataoffset;
                    }
                    else
                    {
                        fillerbytecount = 0;
                    }
                }

                // Tag
                stream.Write(conv.GetBytes(interop.TagID), 0, 2);
                // Type
                stream.Write(conv.GetBytes(interop.TypeID), 0, 2);
                // Count
                stream.Write(conv.GetBytes(interop.Count), 0, 4);
                // Field data
                byte[] data = interop.Data;
                if (ByteOrder != BitConverterEx.SystemByteOrder)
                {
                    if (interop.TypeID == 1 || interop.TypeID == 3 || interop.TypeID == 4 || interop.TypeID == 9)
                    {
                        Array.Reverse(data);
                    }
                    else if (interop.TypeID == 5 || interop.TypeID == 10)
                    {
                        Array.Reverse(data, 0, 4);
                        Array.Reverse(data, 4, 4);
                    }
                }

                // Fields containing offsets to other IFDs
                // Just store their offets, we will write the values later on when we know the lengths of IFDs
                if (ifdtype == IFD.Zeroth && interop.TagID == 0x8769)
                {
                    exifIFDFieldOffset = stream.Position;
                }
                else if (ifdtype == IFD.Zeroth && interop.TagID == 0x8825)
                {
                    gpsIFDFieldOffset = stream.Position;
                }
                else if (ifdtype == IFD.EXIF && interop.TagID == 0xa005)
                {
                    interopIFDFieldOffset = stream.Position;
                }
                else if (ifdtype == IFD.First && interop.TagID == 0x201)
                {
                    thumbOffsetLocation = stream.Position;
                }
                else if (ifdtype == IFD.First && interop.TagID == 0x202)
                {
                    thumbSizeLocation = stream.Position;
                }

                // Write 4 byte field value or field data
                if (data.Length <= 4)
                {
                    stream.Write(data, 0, data.Length);
                    for (int i = data.Length; i < 4; i++)
                    {
                        stream.WriteByte(0);
                    }
                }
                else
                {
                    // Pointer to data area relative to TIFF header
                    stream.Write(conv.GetBytes(currentdataoffset + fillerbytecount), 0, 4);
                    // Actual data
                    long currentoffset = stream.Position;
                    stream.Seek(absolutedataoffset, SeekOrigin.Begin);
                    // Write filler bytes
                    for (int i = 0; i < fillerbytecount; i++)
                    {
                        stream.WriteByte(0xFF);
                    }
                    stream.Write(data, 0, data.Length);
                    stream.Seek(currentoffset, SeekOrigin.Begin);
                    // Increment pointers
                    currentdataoffset  += fillerbytecount + (uint)data.Length;
                    absolutedataoffset += fillerbytecount + data.Length;
                }
            }
            // Offset to 1st IFD
            // We will write zeros for now. This will be filled after we write all IFDs
            if (ifdtype == IFD.Zeroth)
            {
                firstIFDFieldOffset = stream.Position;
            }
            stream.Write(new byte[] { 0, 0, 0, 0 }, 0, 4);

            // Seek to end of IFD
            stream.Seek(absolutedataoffset, SeekOrigin.Begin);

            // Write thumbnail data
            if (ifdtype == IFD.First)
            {
                if (Thumbnail != null)
                {
                    MemoryStream ts = new MemoryStream();
                    Thumbnail.Save(ts);
                    ts.Close();
                    byte[] thumb = ts.ToArray();
                    thumbOffsetValue = (uint)(stream.Position - tiffoffset);
                    thumbSizeValue   = (uint)thumb.Length;
                    stream.Write(thumb, 0, thumb.Length);
                    ts.Dispose();
                }
                else
                {
                    thumbOffsetValue = 0;
                    thumbSizeValue   = 0;
                }
            }
        }
Example #35
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);
                        }
                    }
                }
            }
        }