Esempio n. 1
0
        /// <summary>
        /// Build byte data from IFD  data.
        /// </summary>
        /// <param name="ifd">IFD structure which will be composed to binary data.</param>
        /// <param name="SectionOffset">Offset of this IFD section from TIFF header.</param>
        /// <returns></returns>
        public static byte[] ComposeIfdsection(IfdData ifd, Definitions.Endian MetadataEndian)
        {
            var data = ifd.Entries;

            // calcurate total size of IFD
            var TotalSize = 2; // TIFF HEader +  number of entry
            UInt32 count = 0;
            foreach (Entry entry in data.Values)
            {
                count++;
                TotalSize += 12;

                // if value is more than 4 bytes, need separated section to store all data.
                if (entry.value.Length > 4)
                {
                    TotalSize += entry.value.Length;
                }
            }

            // area for pointer to next IFD section.
            TotalSize += 4;

            var ComposedData = new byte[TotalSize];

            // set data of entry num.
            var EntryNum = Util.ToByte(count, 2, MetadataEndian);
            Array.Copy(EntryNum, ComposedData, EntryNum.Length);

            // set Next IFD pointer
            var ifdPointerValue = Util.ToByte(ifd.NextIfdPointer, 4, MetadataEndian);

            // Debug.WriteLine("Nexf IFD: " + ifd.NextIfdPointer.ToString("X"));

            Array.Copy(ifdPointerValue, 0, ComposedData, 2 + 12 * (int)count, 4);
            // TIFF header, number of entry, each entries, Nexf IFD pointer.
            var ExtraDataSectionOffset = (UInt32)(2 + 12 * (int)count + 4);
            // Debug.WriteLine("ExtraDataSectionOffset: " + ExtraDataSectionOffset.ToString("X"));

            // Debug.WriteLine("ifd.offset: " + ifd.Offset.ToString("X"));

            var keys = data.Keys.ToArray<UInt32>();
            Array.Sort(keys);

            int pointer = 2;
            foreach (UInt32 key in keys)
            {
                // tag in 2 bytes.
                var tag = Util.ToByte(data[key].Tag, 2, MetadataEndian);
                Array.Copy(tag, 0, ComposedData, pointer, 2);
                pointer += 2;
                // Debug.WriteLine("Tag: " + data[key].Tag.ToString("X"));

                // type
                var type = Util.ToByte(Util.ToUInt32(data[key].Type), 2, MetadataEndian);
                Array.Copy(type, 0, ComposedData, pointer, 2);
                pointer += 2;

                // count
                var c = Util.ToByte(data[key].Count, 4, MetadataEndian);
                Array.Copy(c, 0, ComposedData, pointer, 4);
                pointer += 4;

                if (data[key].value.Length <= 4)
                {
                    // upto 4 bytes, copy value directly.
                    Array.Copy(data[key].value, 0, ComposedData, pointer, data[key].value.Length);
                }
                else
                {
                    // save actual data to extra area
                    Array.Copy(data[key].value, 0, ComposedData, (int)ExtraDataSectionOffset, data[key].value.Length);

                    // store pointer for extra area. Origin of pointer should be position of TIFF header.
                    var offset = Util.ToByte(ExtraDataSectionOffset + ifd.Offset, 4, MetadataEndian);
                    Array.Copy(offset, 0, ComposedData, pointer, 4);
                    // Util.DumpFirst16byte(offset);
                    // Util.DumpByteArray(ComposedData, pointer, 4);

                    ExtraDataSectionOffset += (UInt32)data[key].value.Length;

                }
                pointer += 4;

            }
            // Util.DumpByteArrayAll(ComposedData);
            return ComposedData;
        }
Esempio n. 2
0
        /// <summary>
        /// Parse IFD section of Jpeg's header.
        /// </summary>
        /// <param name="App1Data">Raw data of App1 section</param>
        /// <param name="IfdOffset">Offset to the target IFD section from start of App1 data.</param>
        /// <param name="IfdSectionEndian">Alignment of all sections of this IFD data. This value is contained in TIFF header.</param>
        /// <returns>All entries in given IFD section.</returns>
        public static IfdData ParseIfd(byte[] App1Data, UInt32 IfdOffset, Definitions.Endian IfdSectionEndian)
        {
            var ifd = new IfdData();
            ifd.Offset = IfdOffset;
            var entries = new Dictionary<UInt32, Entry>();
            var EntryNum = Util.GetUIntValue(App1Data, (int)IfdOffset, 2, IfdSectionEndian);
            // Debug.WriteLine("Entry num: " + EntryNum);

            ifd.NextIfdPointer = Util.GetUIntValue(App1Data, (int)IfdOffset + 2 + (int)EntryNum * ENTRY_SIZE, 4, IfdSectionEndian);

            // if there's no extra data area, (if all data is 4 bytes or less), this is length of this IFD section.
            ifd.Length = 2 + EntryNum * ENTRY_SIZE + 4; // entry num (2 bytes), each entries (12 bytes each), Next IFD pointer (4 byte)

            for (int i = 0; i < EntryNum; i++)
            {
                // Debug.WriteLine("--- Entry[" + i + "] ---");
                var EntryOrigin = (int)IfdOffset + 2 + i * ENTRY_SIZE;

                var entry = new Entry();

                // tag
                entry.Tag = Util.GetUIntValue(App1Data, EntryOrigin, 2, IfdSectionEndian);

                var tagTypeName = "Unknown";
                if (Util.TagNames.ContainsKey(entry.Tag))
                {
                    tagTypeName = Util.TagNames[entry.Tag];
                }
                // Debug.WriteLine("Tag: " + entry.Tag.ToString("X") + " " + tagTypeName);

                // type
                var typeValue = Util.GetUIntValue(App1Data, EntryOrigin + 2, 2, IfdSectionEndian);
                entry.Type = Util.ToEntryType(typeValue);
                // Debug.WriteLine("Type: " + entry.Type.ToString());

                // count
                entry.Count = Util.GetUIntValue(App1Data, EntryOrigin + 4, 4, IfdSectionEndian);
                // Debug.WriteLine("Count: " + entry.Count);

                var valueSize = 0;
                valueSize = Util.FindDataSize(entry.Type);
                var TotalValueSize = valueSize * (int)entry.Count;
                // Debug.WriteLine("Total value size: " + TotalValueSize);

                var valueBuff = new byte[TotalValueSize];

                if (TotalValueSize <= 4)
                {
                    // in this case, the value is stored directly here.
                    Array.Copy(App1Data, EntryOrigin + 8, valueBuff, 0, TotalValueSize);
                }
                else
                {
                    // other cases, actual value is stored in separated area
                    var EntryValuePointer = (int)Util.GetUIntValue(App1Data, EntryOrigin + 8, 4, IfdSectionEndian);
                    // Debug.WriteLine("Entry pointer: " + EntryValuePointer.ToString("X"));

                    Array.Copy(App1Data, EntryValuePointer, valueBuff, 0, TotalValueSize);

                    // If there's extra data, its length should be added to total length.
                    ifd.Length += (UInt32)TotalValueSize;
                }

                if (IfdSectionEndian != Entry.InternalEndian)
                {
                    // to change endian, each sections should be reversed.
                    var ReversedValue = new byte[valueBuff.Length];
                    var valueLength = Util.FindDataSize(entry.Type);
                    if (valueLength == 8)
                    {
                        // for fraction value, each value should be reversed individually
                        valueLength = 4;
                    }
                    var valueNum = valueBuff.Length / valueLength;
                    for (int j = 0; j < valueNum; j++)
                    {
                        var tempValue = new byte[valueLength];
                        Array.Copy(valueBuff, j * valueLength, tempValue, 0, valueLength);
                        Array.Reverse(tempValue);
                        Array.Copy(tempValue, 0, ReversedValue, j * valueLength, valueLength);
                    }
                    entry.value = ReversedValue;
                }
                else
                {
                    // if internal endian and target metadata's one is same, no need to reverse.
                    entry.value = valueBuff;
                }

                switch (entry.Type)
                {
                    case Entry.EntryType.Ascii:
                        // Debug.WriteLine("value: " + entry.StringValue + Environment.NewLine + Environment.NewLine);
                        // Debug.WriteLine(" ");
                        break;
                    case Entry.EntryType.Byte:
                    case Entry.EntryType.Undefined:
                        if (entry.Tag == 0x927C)
                        {
                            // Debug.WriteLine("Maker note is too long to print.");
                        }
                        else
                        {
                            foreach (int val in entry.UIntValues)
                            {
                                // Debug.WriteLine("value: " + val.ToString("X"));
                            }
                        }
                        break;
                    case Entry.EntryType.Short:
                    case Entry.EntryType.SShort:
                    case Entry.EntryType.Long:
                    case Entry.EntryType.SLong:

                        // Util.DumpByteArrayAll(entry.value);
                        foreach (int val in entry.UIntValues)
                        {
                            // Debug.WriteLine("value: " + val);
                        }
                        break;
                    case Entry.EntryType.Rational:
                    case Entry.EntryType.SRational:
                        // Util.DumpByteArrayAll(entry.value);
                        foreach (double val in entry.DoubleValues)
                        {
                            // Debug.WriteLine("value: " + val);
                        }
                        break;
                    default:
                        break;
                }

                entries[entry.Tag] = entry;
            }

            ifd.Entries = entries;
            return ifd;
        }
        /// <summary>
        /// Create app1 data section in byte array.
        /// Needs many informations....
        /// </summary>
        /// <param name="OriginalMetaData">metadata from original image.</param>
        /// <param name="NewMetaData">metadata which will be recorded in app1 data section</param>
        /// <param name="OriginalApp1Data">Original app1 data. Other than Exif meta data will be copied to new one</param>
        /// <param name="OutputImageMetadataEndian">Endian which will be used in Exif meta data. for WP OS, Big endian is recommended.</param>
        /// <returns></returns>
        private static byte[] CreateApp1Data(JpegMetaData OriginalMetaData, JpegMetaData NewMetaData, byte[] OriginalApp1Data, Definitions.Endian OutputImageMetadataEndian)
        {

            if (NewMetaData.ExifIfd != null && !NewMetaData.PrimaryIfd.Entries.ContainsKey(Definitions.EXIF_IFD_POINTER_TAG))
            {
                // Add Exif IFD section's pointer entry as dummy.
                NewMetaData.PrimaryIfd.Entries.Add(
                    Definitions.EXIF_IFD_POINTER_TAG,
                    new Entry()
                    {
                        Tag = Definitions.EXIF_IFD_POINTER_TAG,
                        Type = Entry.EntryType.Long,
                        Count = 1,
                        value = new byte[] { 0, 0, 0, 0 }
                    });
            }

            if (NewMetaData.GpsIfd != null && !NewMetaData.PrimaryIfd.Entries.ContainsKey(Definitions.GPS_IFD_POINTER_TAG))
            {
                // Add GPS IFD section's pointer entry as dummy
                NewMetaData.PrimaryIfd.Entries.Add(
                    Definitions.GPS_IFD_POINTER_TAG,
                    new Entry()
                    {
                        Tag = Definitions.GPS_IFD_POINTER_TAG,
                        Type = Entry.EntryType.Long,
                        Count = 1,
                        value = new byte[] { 0, 0, 0, 0 }
                    });
            }


            byte[] primaryIfd = IfdComposer.ComposeIfdsection(NewMetaData.PrimaryIfd, OutputImageMetadataEndian);
            byte[] exifIfd = new byte[] { };
            byte[] gpsIfd = new byte[] { };
            if (NewMetaData.ExifIfd != null)
            {
                exifIfd = IfdComposer.ComposeIfdsection(NewMetaData.ExifIfd, OutputImageMetadataEndian);
            }
            if (NewMetaData.GpsIfd != null)
            {
                gpsIfd = IfdComposer.ComposeIfdsection(NewMetaData.GpsIfd, OutputImageMetadataEndian);
            }

            // Debug.WriteLine("Size fixed. Primary: " + primaryIfd.Length.ToString("X") + " exif: " + exifIfd.Length.ToString("X") + " gps: " + gpsIfd.Length.ToString("X"));

            // after size fixed, set each IFD sections' offset.
            NewMetaData.PrimaryIfd.Offset = 8; // fixed value             

            // now it's possible to calcurate pointer to Exif/GPS IFD
            var exifIfdPointer = 8 + primaryIfd.Length;
            var gpsIfdPointer = 8 + primaryIfd.Length + exifIfd.Length;

            if (NewMetaData.PrimaryIfd.Entries.ContainsKey(Definitions.EXIF_IFD_POINTER_TAG))
            {
                NewMetaData.PrimaryIfd.Entries.Remove(Definitions.EXIF_IFD_POINTER_TAG);
            }

            var exifIfdPointerEntry = new Entry()
            {
                Tag = Definitions.EXIF_IFD_POINTER_TAG,
                Type = Entry.EntryType.Long,
                Count = 1,
            };
            exifIfdPointerEntry.UIntValues = new UInt32[] { (UInt32)exifIfdPointer };
            NewMetaData.PrimaryIfd.Entries.Add(Definitions.EXIF_IFD_POINTER_TAG, exifIfdPointerEntry);

            if (NewMetaData.PrimaryIfd.Entries.ContainsKey(Definitions.GPS_IFD_POINTER_TAG))
            {
                NewMetaData.PrimaryIfd.Entries.Remove(Definitions.GPS_IFD_POINTER_TAG);
            }

            var gpsIfdPointerEntry = new Entry()
            {
                Tag = Definitions.GPS_IFD_POINTER_TAG,
                Type = Entry.EntryType.Long,
                Count = 1,
            };
            gpsIfdPointerEntry.UIntValues = new UInt32[] { (UInt32)gpsIfdPointer };
            NewMetaData.PrimaryIfd.Entries.Add(Definitions.GPS_IFD_POINTER_TAG, gpsIfdPointerEntry);

            var nextIfdPointer = 8 + primaryIfd.Length + exifIfd.Length + gpsIfd.Length;
            NewMetaData.PrimaryIfd.NextIfdPointer = (UInt32)nextIfdPointer;

            // finally, create byte array again
            primaryIfd = IfdComposer.ComposeIfdsection(NewMetaData.PrimaryIfd, OutputImageMetadataEndian);


            if (NewMetaData.ExifIfd != null)
            {
                NewMetaData.ExifIfd.Offset = 8 + (UInt32)primaryIfd.Length;
                exifIfd = IfdComposer.ComposeIfdsection(NewMetaData.ExifIfd, OutputImageMetadataEndian);
            }


            if (NewMetaData.GpsIfd != null)
            {
                NewMetaData.GpsIfd.Offset = 8 + (UInt32)primaryIfd.Length + (UInt32)exifIfd.Length;
                gpsIfd = IfdComposer.ComposeIfdsection(NewMetaData.GpsIfd, OutputImageMetadataEndian);
            }

            // 1st IFD data (after primary IFD data) should be kept
            var Original1stIfdData = new byte[OriginalApp1Data.Length - OriginalMetaData.PrimaryIfd.NextIfdPointer];
            Array.Copy(OriginalApp1Data, (int)OriginalMetaData.PrimaryIfd.NextIfdPointer, Original1stIfdData, 0, Original1stIfdData.Length);

            // Build App1 section. From "Exif\0\0" to end of thumbnail data (1st IFD)
            // Exif00 + TIFF header + 3 IFD sections (Primary, Exif, GPS) + 1st IFD data from original data
            // new app1 size may overfrow from 0xFFFF bytes. in this case, it should be limited up to 0xFFFF byte.
            var NewApp1DataSize = Math.Min(0xFFFF, 6 + 8 + primaryIfd.Length + exifIfd.Length + gpsIfd.Length + Original1stIfdData.Length);
            var NewApp1DataOverflowSize = 0;
            if (NewApp1DataSize == 0xFFFF)
            {
                NewApp1DataOverflowSize = (6 + 8 + primaryIfd.Length + exifIfd.Length + gpsIfd.Length + Original1stIfdData.Length) - 0xFFFF;
                Debug.WriteLine("App1 overflow size: " + NewApp1DataOverflowSize);
            }
            var NewApp1Data = new byte[NewApp1DataSize];
            // var NewApp1Data = new byte[6 + 8 + primaryIfd.Length + exifIfd.Length + gpsIfd.Length];
            // Debug.WriteLine("New App1 size: " + NewApp1Data.Length.ToString("X"));

            // Array.Copy(OriginalApp1Data, 0, NewApp1Data, 0, 6 + 8);
            Array.Copy(OriginalApp1Data, 0, NewApp1Data, 0, 6); // only EXIF00 should be copied.

            var endianSection = Util.ToByte(0x4d4d, 2, OutputImageMetadataEndian);
            Array.Copy(endianSection, 0, NewApp1Data, 6, 2);

            var magicNumber = Util.ToByte(0x002A, 2, OutputImageMetadataEndian);
            Array.Copy(magicNumber, 0, NewApp1Data, 8, 2);

            var primaryIfdOffset = Util.ToByte(8, 4, OutputImageMetadataEndian);
            Array.Copy(primaryIfdOffset, 0, NewApp1Data, 10, 4);

            Array.Copy(primaryIfd, 0, NewApp1Data, 6 + 8, primaryIfd.Length);
            Array.Copy(exifIfd, 0, NewApp1Data, 6 + 8 + primaryIfd.Length, exifIfd.Length);
            Array.Copy(gpsIfd, 0, NewApp1Data, 6 + 8 + primaryIfd.Length + exifIfd.Length, gpsIfd.Length);
            Array.Copy(Original1stIfdData, 0, NewApp1Data, 6 + 8 + primaryIfd.Length + exifIfd.Length + gpsIfd.Length, Original1stIfdData.Length - NewApp1DataOverflowSize);

            return NewApp1Data;
        }
Esempio n. 4
0
 public static byte[] ToByte(SignedFraction value, Definitions.Endian endian)
 {
     var ret = new byte[8];
     Array.Copy(Util.ToByte(value.Numerator, 4, endian), 0, ret, 0, 4);
     Array.Copy(Util.ToByte(value.Denominator, 4, endian), 0, ret, 4, 4);
     return ret;
 }
Esempio n. 5
0
        public static byte[] ToByte(Int32 value, int length, Definitions.Endian endian)
        {
            if (length < 1 || length > 4)
            {
                throw new InvalidCastException();
            }

            var max = (Int64)(Math.Pow(2, (length * 8)) / 2 - 1);
            var min =  -(max + 1);
            if (value > max)
            {
                throw new OverflowException();
            }
            else if (value < min)
            {
                throw new InvalidCastException();
            }

            var ByteValue = new byte[length];
            if (value < 0)
            {
                ByteValue = ToByte((UInt32)(-value), length, endian);
                for (int i = 0; i < ByteValue.Length; i++)
                {
                    ByteValue[i] = (byte)~(ByteValue[i]);
                }
                UInt32 temp = Util.GetUIntValue(ByteValue, 0, length, endian) + 1;
                ByteValue = Util.ToByte(temp, length, endian);
            }
            else
            {
                ByteValue = ToByte((UInt32)value, length, endian);
            }
            return ByteValue;
        }
Esempio n. 6
0
        public static byte[] ToByte(UInt32 value, int length, Definitions.Endian endian)
        {
            if (length > 4 || length <= 0)
            {
                throw new InvalidCastException();
            }
            var ret = new byte[length];
            if (endian == Definitions.Endian.Little)
            {
                for (int i = 0; i < length; i++)
                {
                    ret[i] = (byte)(value & 0xFF);
                    value = value >> 8;
                }
            }
            else
            {
                for (int i = length - 1; i >= 0; i--)
                {
                    ret[i] = (byte)(value & 0xFF);
                    value = value >> 8;
                    // Debug.WriteLine("byte: " + ret[i].ToString("X"));
                }
            }
            // after conversion, remaining value must be 0.
            if (value > 0)
            {
                throw new OverflowException();
            }

            return ret;
        }
Esempio n. 7
0
        public static UInt32 GetUIntValue(byte[] data, int address, int length, Definitions.Endian endian)
        {
            // if bigger than 4 bytes, can't set to int type.
            if (length > 4 || length <= 0)
            {
                throw new InvalidCastException("Uint32 type can't store more than 4 bytes");
            }

            UInt32 value = 0;
            for (int i = 0; i < length; i++)
            {
                // Debug.WriteLine(data[address + i].ToString("X"));
                if (endian == Definitions.Endian.Little)
                {
                    value += (UInt32)data[address + i] << (i * 8);
                }
                else
                {
                    value += (UInt32)data[address + i] << ((length - 1 - i) * 8);
                }
            }
            // Debug.WriteLine("Return " + value.ToString("X"));
            return value;
        }
Esempio n. 8
0
        public static Int32 GetSIntValue(byte[] data, int address, int length, Definitions.Endian endian)
        {
            // if bigger than 4 bytes, can't set to int type.
            if (length > 4)
            {
                throw new InvalidCastException("Uint32 type can't store more than 4 bytes");
            }
            if (length < 1)
            {
                throw new InvalidCastException();
            }

            if (address >= data.Length)
            {
                throw new IndexOutOfRangeException();
            }

            Int32 value = 0;
            bool IsNegative = false;
            var TempValue = new byte[length];
            Array.Copy(data, address, TempValue, 0, length);
            // if highest bit is set, it's negative value.
            if (endian == Definitions.Endian.Big)
            {
                if ((TempValue[0] & 0x80) == 0x80)
                {
                    IsNegative = true;
                }
                // Debug.WriteLine("negative number: " + TempValue[length - 1]);
            }
            else
            {
                if ((TempValue[length - 1] & 0x80) == 0x80)
                {
                    IsNegative = true;
                    // Debug.WriteLine("negative number: " + TempValue[length - 1]);
                }
            }

            if (IsNegative)
            {
                for (int i = 0; i < length; i++)
                {
                    TempValue[i] = (byte)~TempValue[i];
                }
            }

            for (int i = 0; i < length; i++)
            {
                // Debug.WriteLine(data[address + i].ToString("X"));
                if (endian == Definitions.Endian.Little)
                {
                    value += (Int32)TempValue[i] << (i * 8);
                    // Debug.WriteLine("value: " + value);
                }
                else
                {
                    value += (Int32)TempValue[i] << ((length - 1 - i) * 8);
                }
            }
            // Debug.WriteLine("Return " + value.ToString("X"));
            if (IsNegative)
            {
                value += 1;
                value = -value;
            }
            return value;
        }