Example #1
0
        /// <summary>
        /// Replaces the contents of the APP0 section with the JFIF extension properties.
        /// </summary>
        private bool WriteJFXXApp0()
        {
            // Which IFD sections do we have?
            List <ExifProperty> ifdjfef = new List <ExifProperty>();

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

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

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

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

                // Return APP0 header
                jfxxApp0.Header = ms.ToArray();
            }
            return(true);
        }
Example #2
0
        /// <summary>
        /// Replaces the contents of the APP0 section with the JFIF properties.
        /// </summary>
        private bool WriteJFIFApp0()
        {
            // Which IFD sections do we have?
            List <ExifProperty> ifdjfef = new List <ExifProperty>();

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

            if (ifdjfef.Count == 0)
            {
                // Nothing to write
                return(false);
            }


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

            // JFIF identifer
            ms.Write(Encoding.ASCII.GetBytes("JFIF\0"), 0, 5);

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

            ms.Close();

            // Return APP0 header
            jfifApp0.Header = ms.ToArray();
            return(true);
        }
Example #3
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;
                }
            }
        }
 /// <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 #5
0
 /// <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 #6
0
        /// <summary>
        /// Replaces the contents of the APP0 section with the JFIF properties.
        /// </summary>
        private bool WriteJFIFApp0()
        {
            // Which IFD sections do we have?
            Dictionary <ExifTag, ExifProperty> ifdjfefExisting = new Dictionary <ExifTag, ExifProperty>();

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

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

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

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

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

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

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

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

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

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

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