Пример #1
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;
                }
            }
        }
Пример #2
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];

                // 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);
            }
        }
Пример #3
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();
        }
Пример #4
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));
 }
Пример #5
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);
        }
Пример #6
0
        private void WriteIFD(MemoryStream stream, Dictionary <ExifTag, ExifProperty> ifd, IFD ifdtype, long tiffoffset, bool preserveMakerNote)
        {
            BitConverterEx conv = new BitConverterEx(BitConverterEx.SystemByteOrder, 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 && (makerNoteOffset > currentdataoffset))
                    {
                        fillerbytecount = makerNoteOffset - currentdataoffset;
                    }
                    else
                    {
                        fillerbytecount = 0;
                    }
                }

                // Tag
                stream.Write(conv.GetBytes(interop.TagID), 0, 2);
                // Type
                stream.Write(conv.GetBytes((ushort)interop.TypeID), 0, 2);
                // Count
                stream.Write(conv.GetBytes(interop.Count), 0, 4);
                // Field data
                byte[] data = interop.Data;
                if (ByteOrder != BitConverterEx.SystemByteOrder &&
                    (interop.TypeID == InterOpType.SHORT || interop.TypeID == InterOpType.LONG || interop.TypeID == InterOpType.SLONG ||
                     interop.TypeID == InterOpType.RATIONAL || interop.TypeID == InterOpType.SRATIONAL))
                {
                    int vlen = 4;
                    if (interop.TypeID == InterOpType.SHORT)
                    {
                        vlen = 2;
                    }
                    int n = data.Length / vlen;

                    for (int i = 0; i < n; i++)
                    {
                        Array.Reverse(data, i * vlen, vlen);
                    }
                }

                // 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)
                {
                    thumbOffsetValue = (uint)(stream.Position - tiffoffset);
                    thumbSizeValue   = (uint)Thumbnail.Length;
                    stream.Write(Thumbnail, 0, Thumbnail.Length);
                }
                else
                {
                    thumbOffsetValue = 0;
                    thumbSizeValue   = 0;
                }
            }
        }
Пример #7
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;
        }
Пример #8
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();
        }