/// <summary> /// Reads an IFD from file at position <paramref name="offset"/> relative /// to <paramref name="baseOffset"/>. /// </summary> /// <param name="baseOffset"> /// A <see cref="System.Int64"/> with the base offset which every offset /// in IFD is relative to. /// </param> /// <param name="offset"> /// A <see cref="System.UInt32"/> with the offset of the IFD relative to /// <paramref name="baseOffset"/> /// </param> /// <param name="maxOffset"> /// A <see cref="System.UInt32"/> with the maximal offset to consider for /// the IFD. /// </param> /// <returns> /// A <see cref="System.UInt32"/> with the offset of the next IFD, the /// offset is also relative to <paramref name="baseOffset"/> /// </returns> uint ReadIFD(long baseOffset, uint offset, uint maxOffset) { long length = 0; try { length = file.Length; } catch (Exception) { // Use a safety-value of 4 gigabyte. length = 1073741824L * 4; } if (baseOffset + offset > length) { file.MarkAsCorrupt("Invalid IFD offset"); return(0); } var directory = new IFDDirectory(); file.Seek(baseOffset + offset, SeekOrigin.Begin); ushort entry_count = ReadUShort(); if (file.Tell + 12 * entry_count > baseOffset + maxOffset) { file.MarkAsCorrupt("Size of entries exceeds possible data size"); return(0); } ByteVector entry_datas = file.ReadBlock(12 * entry_count); uint next_offset = ReadUInt(); for (int i = 0; i < entry_count; i++) { ByteVector entry_data = entry_datas.Mid(i * 12, 12); ushort entry_tag = entry_data.Mid(0, 2).ToUShort(is_bigendian); ushort type = entry_data.Mid(2, 2).ToUShort(is_bigendian); uint value_count = entry_data.Mid(4, 4).ToUInt(is_bigendian); ByteVector offset_data = entry_data.Mid(8, 4); IFDEntry entry = CreateIFDEntry(entry_tag, type, value_count, baseOffset, offset_data, maxOffset); if (entry == null) { continue; } if (directory.ContainsKey(entry.Tag)) { directory.Remove(entry.Tag); } directory.Add(entry.Tag, entry); } FixupDirectory(baseOffset, directory); structure.directories.Add(directory); return(next_offset); }
/// <summary> /// Renders a complete entry together with the data. The entry itself /// is stored in <paramref name="entry_data"/> and the data of the /// entry is stored in <paramref name="offset_data"/> if it cannot be /// stored in the offset. This method is called for every <see /// cref="IFDEntry"/> of this IFD and can be overwritten in subclasses /// to provide special behavior. /// </summary> /// <param name="entry"> /// A <see cref="IFDEntry"/> with the entry to render. /// </param> /// <param name="entry_data"> /// A <see cref="ByteVector"/> to add the entry to. /// </param> /// <param name="offset_data"> /// A <see cref="ByteVector"/> to add the entry data to if it cannot be /// stored in the offset field. /// </param> /// <param name="data_offset"> /// A <see cref="System.UInt32"/> with the offset, were the data of the /// entries starts. It is needed to adjust the offsets of the entries /// itself. /// </param> protected virtual void RenderEntryData(IFDEntry entry, ByteVector entry_data, ByteVector offset_data, uint data_offset) { ushort tag = (ushort)entry.Tag; uint offset = (uint)(data_offset + offset_data.Count); ushort type; uint count; ByteVector data = entry.Render(is_bigendian, offset, out type, out count); // store data in offset, if it is smaller than 4 byte if (data.Count <= 4) { while (data.Count < 4) { data.Add("\0"); } offset = data.ToUInt(is_bigendian); data = null; } // preserve word boundary of offsets if (data != null && data.Count % 2 != 0) { data.Add("\0"); } RenderEntry(entry_data, tag, type, count, offset); offset_data.Add(data); }
/// <summary> /// Adds an <see cref="IFDEntry"/> to the IFD, if it is not already /// contained in, it fails otherwise. /// </summary> /// <param name="directory"> /// A <see cref="System.Int32"/> value with the directory index that /// should contain the tag that will be added. /// </param> /// <param name="entry"> /// A <see cref="IFDEntry"/> to add to the IFD. /// </param> public void AddEntry(int directory, IFDEntry entry) { while (directory >= directories.Count) directories.Add (new IFDDirectory ()); directories [directory].Add (entry.Tag, entry); }
public void SetEntry(int directory, IFDEntry entry) { if (ContainsTag(directory, entry.Tag)) { RemoveTag(directory, entry.Tag); } AddEntry(directory, entry); }
public void AddEntry(int directory, IFDEntry entry) { while (directory >= directories.Count) { directories.Add(new IFDDirectory()); } directories[directory].Add(entry.Tag, entry); }
protected virtual void RenderEntryData(IFDEntry entry, ByteVector entry_data, ByteVector offset_data, uint data_offset) { ushort tag = (ushort)entry.Tag; uint offset = (uint)(data_offset + offset_data.Count); ushort type; uint count; ByteVector data = entry.Render(is_bigendian, offset, out type, out count); if (data.Count <= 4) { while (data.Count < 4) { data.Add("\0"); } offset = data.ToUInt(is_bigendian); data = null; } if (data != null && data.Count % 2 != 0) { data.Add("\0"); } RenderEntry(entry_data, tag, type, count, offset); offset_data.Add(data); }
/// <summary> /// Renders a complete entry together with the data. The entry itself /// is stored in <paramref name="entry_data"/> and the data of the /// entry is stored in <paramref name="offset_data"/> if it cannot be /// stored in the offset. This method is called for every <see /// cref="IFDEntry"/> of this IFD and can be overwritten in subclasses /// to provide special behavior. /// </summary> /// <param name="entry"> /// A <see cref="IFDEntry"/> with the entry to render. /// </param> /// <param name="entry_data"> /// A <see cref="ByteVector"/> to add the entry to. /// </param> /// <param name="offset_data"> /// A <see cref="ByteVector"/> to add the entry data to if it cannot be /// stored in the offset field. /// </param> /// <param name="data_offset"> /// A <see cref="System.UInt32"/> with the offset, were the data of the /// entries starts. It is needed to adjust the offsets of the entries /// itself. /// </param> protected virtual void RenderEntryData(IFDEntry entry, ByteVector entry_data, ByteVector offset_data, uint data_offset) { ushort tag = (ushort) entry.Tag; uint offset = (uint) (data_offset + offset_data.Count); ushort type; uint count; ByteVector data = entry.Render (is_bigendian, offset, out type, out count); // store data in offset, if it is smaller than 4 byte if (data.Count <= 4) { while (data.Count < 4) data.Add ("\0"); offset = data.ToUInt (is_bigendian); data = null; } // preserve word boundary of offsets if (data != null && data.Count % 2 != 0) data.Add ("\0"); RenderEntry (entry_data, tag, type, count, offset); offset_data.Add (data); }
/// <summary> /// Adds an <see cref="IFDEntry"/> to the IFD. If it is already contained /// in the IFD, it is overwritten. /// </summary> /// <param name="directory"> /// A <see cref="System.Int32"/> value with the directory index that /// contains the tag that will be set. /// </param> /// <param name="entry"> /// A <see cref="IFDEntry"/> to add to the IFD. /// </param> public void SetEntry(int directory, IFDEntry entry) { if (ContainsTag (directory, entry.Tag)) RemoveTag (directory, entry.Tag); AddEntry (directory, entry); }
internal static void WriteTaglibTiffDirEntry(ushort key, IFDEntry entry, string indent) { if (entry is TagLib.IFD.Entries.ShortIFDEntry ifdShort) { Console.WriteLine("{0}TIFF entry = [{1}] = {2}", indent, ifdShort.Tag, ifdShort.Value); } else if (entry is TagLib.IFD.Entries.LongIFDEntry ifdLong) { Console.WriteLine("{0}TIFF entry = [{1}] = {2}", indent, ifdLong.Tag, ifdLong.Value); } else if (entry is TagLib.IFD.Entries.StringIFDEntry ifdString) { Console.WriteLine("{0}TIFF entry = [{1}] = \"{2}\"", indent, ifdString.Tag, ifdString.Value); } else if (entry is TagLib.IFD.Entries.UserCommentIFDEntry ifdUserComment) { Console.WriteLine("{0}TIFF entry = [{1}] = \"{2}\"", indent, ifdUserComment.Tag, ifdUserComment.Value); } else if (entry is TagLib.IFD.Entries.RationalIFDEntry ifdRational) { Console.WriteLine("{0}TIFF entry = [{1}] = {2}/{3}", indent, ifdRational.Tag, ifdRational.Value.Numerator, ifdRational.Value.Denominator); } else if (entry is TagLib.IFD.Entries.SRationalIFDEntry ifdSRational) { Console.WriteLine("{0}TIFF entry = [{1}] = {2}/{3}", indent, ifdSRational.Tag, ifdSRational.Value.Numerator, ifdSRational.Value.Denominator); } else if (entry is TagLib.IFD.Entries.ShortArrayIFDEntry ifdShortArray) { Console.Write("{0}TIFF entry = [{1}] =", indent, ifdShortArray.Tag); foreach (var x in ifdShortArray.Values) { Console.Write(" {0}", x); } Console.WriteLine(); } else if (entry is TagLib.IFD.Entries.RationalArrayIFDEntry ifdRationalArray) { Console.Write("{0}TIFF entry = [{1}] =", indent, ifdRationalArray.Tag); foreach (var x in ifdRationalArray.Values) { Console.Write(" {0}/{1}", x.Numerator, x.Denominator); } Console.WriteLine(); } else if (entry is TagLib.IFD.Entries.ThumbnailDataIFDEntry ifdThumbnailData) { Console.WriteLine("{0}TIFF entry = [{1}] has {2} bytes of thumbnail data", indent, ifdThumbnailData.Tag, ifdThumbnailData.Data?.Count); } else if (entry is TagLib.IFD.Entries.ByteVectorIFDEntry ifdByteVector) { Console.WriteLine("{0}TIFF entry = [{1}] has {2} bytes of byte vector data", indent, ifdByteVector.Tag, ifdByteVector.Data?.Count); } else if (entry is TagLib.IFD.Entries.SubIFDEntry ifdSubEntry) { Console.WriteLine("{0}TIFF entry = [{1}] has {2} subentries in {3} directories", indent, ifdSubEntry.Tag, ifdSubEntry.ChildCount, ifdSubEntry.Count); foreach (var dir in ifdSubEntry.Structure.Directories) { foreach (var subentry in dir) { WriteTaglibTiffDirEntry(subentry.Key, subentry.Value, indent + " "); } } } else if (entry is TagLib.IFD.Entries.UndefinedIFDEntry ifdUndefined) { Console.WriteLine("{0}TIFF entry = [{1}] has {2} bytes of undefined data", indent, ifdUndefined.Tag, ifdUndefined.Data?.Count); } else { Console.WriteLine("{0}TIFF entry = [{1}] is {2}", indent, entry.Tag, entry.GetType()); } }
/// <summary> /// Reads an IFD from file at position <paramref name="offset"/> relative /// to <paramref name="base_offset"/>. /// </summary> /// <param name="base_offset"> /// A <see cref="System.Int64"/> with the base offset which every offset /// in IFD is relative to. /// </param> /// <param name="offset"> /// A <see cref="System.UInt32"/> with the offset of the IFD relative to /// <paramref name="base_offset"/> /// </param> /// <param name="max_offset"> /// A <see cref="System.UInt32"/> with the maximal offset to consider for /// the IFD. /// </param> /// <returns> /// A <see cref="System.UInt32"/> with the offset of the next IFD, the /// offset is also relative to <paramref name="base_offset"/> /// </returns> private uint ReadIFD(long base_offset, uint offset, uint max_offset) { long length = 0; try { length = file.Length; } catch (Exception) { // Use a safety-value of 4 gigabyte. length = 1073741824L * 4; } if (base_offset + offset > length) { file.MarkAsCorrupt("Invalid IFD offset"); return(0); } var directory = new IFDDirectory(); file.Seek(base_offset + offset, SeekOrigin.Begin); ushort entry_count = ReadUShort(); if (file.Tell + 12 * entry_count > base_offset + max_offset) { file.MarkAsCorrupt("Size of entries exceeds possible data size"); return(0); } ByteVector entry_datas = file.ReadBlock(12 * entry_count); uint next_offset = ReadUInt(); for (int i = 0; i < entry_count; i++) { ByteVector entry_data = entry_datas.Mid(i * 12, 12); ushort entry_tag = entry_data.Mid(0, 2).ToUShort(is_bigendian); ushort type = entry_data.Mid(2, 2).ToUShort(is_bigendian); uint value_count = entry_data.Mid(4, 4).ToUInt(is_bigendian); ByteVector offset_data = entry_data.Mid(8, 4); // Even if the value count represents a single bytes it's impossible // for the count plus the base offset to be bigger than the entire file, // ignore this entry and keep going if (value_count + base_offset > max_offset) { file.MarkAsCorrupt("Value count + base offset is greater than the maximum possible offset"); continue; } IFDEntry entry = CreateIFDEntry(entry_tag, type, value_count, base_offset, offset_data, max_offset); if (entry == null) { continue; } if (directory.ContainsKey(entry.Tag)) { directory.Remove(entry.Tag); } directory.Add(entry.Tag, entry); } FixupDirectory(base_offset, directory); structure.directories.Add(directory); return(next_offset); }