diff --git a/MetadataExtractor/Formats/Tiff/TiffReader.cs b/MetadataExtractor/Formats/Tiff/TiffReader.cs index ce1c776d..91992280 100644 --- a/MetadataExtractor/Formats/Tiff/TiffReader.cs +++ b/MetadataExtractor/Formats/Tiff/TiffReader.cs @@ -41,56 +41,58 @@ public static void ProcessTiff(IndexedReader reader, ITiffHandler handler) { 0x4d4d => reader.WithByteOrder(isMotorolaByteOrder: true), 0x4949 => reader.WithByteOrder(isMotorolaByteOrder: false), - _ => throw new TiffProcessingException("Unclear distinction between Motorola/Intel byte ordering: " + byteOrder), + _ => throw new TiffProcessingException("Unclear distinction between Motorola/Intel byte ordering: " + byteOrder) }; // Check the next two values for correctness. var tiffMarker = reader.GetUInt16(2); - var tiffStandard = handler.ProcessTiffMarker(tiffMarker); + TiffStandard tiffStandard = handler.ProcessTiffMarker(tiffMarker); - bool isBigTiff; - - int firstIfdOffset; - - switch (tiffStandard) + bool? isBigTiff = tiffStandard switch { - case TiffStandard.Tiff: - isBigTiff = false; - firstIfdOffset = checked((int)reader.GetUInt32(4)); + TiffStandard.Tiff => false, + TiffStandard.BigTiff => true, + _ => null + }; - // David Ekholm sent a digital camera image that has this problem - // TODO calling Length should be avoided as it causes IndexedCapturingReader to read to the end of the stream - if (firstIfdOffset >= reader.Length - 1) - { - handler.Warn("First IFD offset is beyond the end of the TIFF data segment -- trying default offset"); - // First directory normally starts immediately after the offset bytes, so try that - firstIfdOffset = 2 + 2 + 4; - } + if (isBigTiff is null) + { + handler.Error($"Unsupported TiffStandard {tiffStandard}."); + return; + } - break; + int firstIfdOffset; - case TiffStandard.BigTiff: - isBigTiff = true; - var offsetByteSize = reader.GetInt16(4); + if (!isBigTiff.Value) + { + firstIfdOffset = checked((int)reader.GetUInt32(4)); - if (offsetByteSize != 8) - { - handler.Error($"Unsupported offset byte size: {offsetByteSize}"); - return; - } + // David Ekholm sent a digital camera image that has this problem + // TODO calling Length should be avoided as it causes IndexedCapturingReader to read to the end of the stream -- add reader.TryValidatePosition(int offset) and have implementations return true if they cannot quickly determine the answer, or just buffer until that position + if (firstIfdOffset >= reader.Length - 1) + { + handler.Warn("First IFD offset is beyond the end of the TIFF data segment -- trying default offset"); + // First directory normally starts immediately after the offset bytes, so try that + firstIfdOffset = 2 + 2 + 4; + } + } + else + { + var offsetByteSize = reader.GetInt16(4); - // There are two reserved bytes at offset 6, which are expected to have zero value. - // We skip without validation for now, but may change this in future. + if (offsetByteSize != 8) + { + handler.Error($"Unsupported offset byte size: {offsetByteSize}"); + return; + } - firstIfdOffset = checked((int)reader.GetUInt64(8)); - break; + // There are two reserved bytes at offset 6, which are expected to have zero value. + // We skip without validation for now, but may change this in future. - default: - handler.Error($"Unsupported TiffStandard {tiffStandard}."); - return; + firstIfdOffset = checked((int)reader.GetUInt64(8)); } - var context = new TiffReaderContext(reader, reader.IsMotorolaByteOrder, isBigTiff); + var context = new TiffReaderContext(reader, reader.IsMotorolaByteOrder, isBigTiff.Value); ProcessIfd(handler, context, firstIfdOffset); } @@ -139,82 +141,99 @@ public static void ProcessIfd(ITiffHandler handler, TiffReaderContext context, i return; } - // The number of tags in this directory - var dirTagCount = context.IsBigTiff - ? checked((int)context.Reader.GetUInt64(ifdOffset)) - : context.Reader.GetUInt16(ifdOffset); + // The number of tags in this directory, and various sizes (determined by BigTIFF). +#pragma warning disable format + (int tagCount, int tagCountLength, int entryLength, int followerPointerLength, int entryValueOffset, uint inlineValueLength) = context.IsBigTiff + ? (checked((int)context.Reader.GetUInt64(ifdOffset)), ByteCounts.TagCount_BigTiff, ByteCounts.Entry_BigTiff, ByteCounts.FollowerIfdPointer_BiffTiff, ByteCounts.EntryValueOffset_BigTiff, 8u) + : (context.Reader.GetUInt16(ifdOffset), ByteCounts.TagCount, ByteCounts.Entry, ByteCounts.FollowerIfdPointer, ByteCounts.EntryValueOffset, 4u); +#pragma warning restore format // Some software modifies the byte order of the file, but misses some IFDs (such as makernotes). // The entire test image repository doesn't contain a single IFD with more than 255 entries. // Here we detect switched bytes that suggest this problem, and temporarily swap the byte order. // This was discussed in GitHub issue #136. - if (!context.IsBigTiff && dirTagCount > 0xFF && (dirTagCount & 0xFF) == 0) + if (!context.IsBigTiff && tagCount > 0xFF && (tagCount & 0xFF) == 0) { - dirTagCount >>= 8; + tagCount >>= 8; context = context.WithByteOrder(!context.Reader.IsMotorolaByteOrder); } - var dirLength = context.IsBigTiff - ? 8 + 20 * dirTagCount + 8 - : 2 + 12 * dirTagCount + 4; + // The IFD starts after the tag count. + var tagTableOffset = ifdOffset + tagCountLength; - if (dirLength + ifdOffset > checked((int)context.Reader.Length)) + // The IFD table is stored using fixed-size elements. + // When a value is larger than the allotted space, a pointer is stored. + // + // Per tag: 12 bytes / BigTIFF 20 bytes (see above docs for breakdown). + // Finally, a pointer to a "follower" IFD (optional) + var tagTableLength = (tagCount * entryLength); + + if (tagTableOffset + tagTableLength > checked((int)context.Reader.Length)) { handler.Error("Illegally sized IFD"); return; } - var inlineValueSize = context.IsBigTiff ? 8u : 4u; + // TODO better approach here? stack overflow? if backed by array, just want to slice without copy? other stuff? what about longer tables? + Span tagTableBytes = stackalloc byte[tagTableLength]; + context.Reader.GetBytes(tagTableOffset, tagTableBytes); + BufferReader reader = new(tagTableBytes, isBigEndian: context.Reader.IsMotorolaByteOrder); + + // We will track how many invalid formats we see, and stop processing when we meet a threshold. + var invalidTiffFormatCodeCount = 0; + const int InvalidTiffFormatCodeCountThreshold = 5; // // Handle each tag in this directory // - var invalidTiffFormatCodeCount = 0; - for (var tagNumber = 0; tagNumber < dirTagCount; tagNumber++) + for (var tagNumber = 0; tagNumber < tagCount; tagNumber++) { - var tagOffset = CalculateTagOffset(ifdOffset, tagNumber, context.IsBigTiff); + Debug.Assert(reader.Position % entryLength == 0, "Misaligned read of IFD entry"); - int tagId = context.Reader.GetUInt16(tagOffset); + // Tag identifier (2 bytes) + ushort tagId = reader.GetUInt16(); - var formatCode = (TiffDataFormatCode)context.Reader.GetUInt16(tagOffset + 2); + // Format code (2 bytes) + var formatCode = (TiffDataFormatCode)reader.GetUInt16(); - var componentCount = context.IsBigTiff - ? context.Reader.GetUInt64(tagOffset + 4) - : context.Reader.GetUInt32(tagOffset + 4); + // Number of components (4 bytes / BigTIFF 8 bytes) + ulong componentCount = context.IsBigTiff + ? reader.GetUInt64() + : reader.GetUInt32(); var format = TiffDataFormat.FromTiffFormatCode(formatCode, context.IsBigTiff); - ulong byteCount; - if (format is null) + ulong valueLength; + if (format is { ComponentSizeBytes: byte componentSize }) { - if (!handler.TryCustomProcessFormat(tagId, formatCode, componentCount, out byteCount)) - { - // This error suggests that we are processing at an incorrect index and will generate - // rubbish until we go out of bounds (which may be a while). Exit now. - handler.Error($"Invalid TIFF tag format code {(int)formatCode} for tag 0x{tagId:X4}"); - // TODO specify threshold as a parameter, or provide some other external control over this behaviour - if (++invalidTiffFormatCodeCount > 5) - { - handler.Error("Stopping processing as too many errors seen in TIFF IFD"); - return; - } - continue; - } + valueLength = checked(componentCount * componentSize); } - else + else if (!handler.TryCustomProcessFormat(tagId, formatCode, componentCount, out valueLength)) { - byteCount = checked(componentCount * format.ComponentSizeBytes); + // This error suggests that we are processing at an incorrect index and will generate + // rubbish until we go out of bounds (which may be a while). Exit now. + handler.Error($"Invalid TIFF tag format code {(int)formatCode} for tag 0x{tagId:X4}"); + + // TODO specify threshold as a parameter, or provide some other external control over this behaviour + if (++invalidTiffFormatCodeCount > InvalidTiffFormatCodeCountThreshold) + { + handler.Error("Stopping processing as too many errors seen in TIFF IFD"); + return; + } + + reader.Skip(checked((int)inlineValueLength)); + continue; } - uint tagValueOffset; - if (byteCount > inlineValueSize) + uint valueOffset; + if (valueLength > inlineValueLength) { // Value(s) are too big to fit inline. Follow the pointer. - tagValueOffset = context.IsBigTiff - ? checked((uint)context.Reader.GetUInt64(tagOffset + 12)) - : context.Reader.GetUInt32(tagOffset + 8); + valueOffset = context.IsBigTiff + ? checked((uint)reader.GetUInt64()) + : reader.GetUInt32(); - if (tagValueOffset + byteCount > checked((ulong)context.Reader.Length)) + if (valueOffset + valueLength > checked((ulong)context.Reader.Length)) { // Bogus pointer offset and/or byteCount value handler.Error("Illegal TIFF tag pointer offset"); @@ -224,76 +243,90 @@ public static void ProcessIfd(ITiffHandler handler, TiffReaderContext context, i else { // Value(s) can fit inline. - tagValueOffset = context.IsBigTiff - ? checked((uint)tagOffset + 12) - : checked((uint)tagOffset + 8); + int tagOffset = CalculateTagOffset(tagNumber); + valueOffset = checked((uint)(tagOffset + entryValueOffset)); + reader.Skip(checked((int)inlineValueLength)); } - if (tagValueOffset > context.Reader.Length) - { - handler.Error("Illegal TIFF tag pointer offset"); - continue; - } + Debug.Assert(checked(valueOffset + valueLength) <= checked((ulong)context.Reader.Length)); - // Check that this tag isn't going to allocate outside the bounds of the data array. - // This addresses an uncommon OutOfMemoryError. - if (tagValueOffset + byteCount > checked((ulong)context.Reader.Length)) - { - handler.Error("Illegal number of bytes for TIFF tag data: " + byteCount); - continue; - } + bool isIfdPointer = false; + + // TODO is the following IFD8 check correct? IFD8 is 8-bytes in length, and the following assumed 4-bytes. + const ulong IfdPointerLength = 4; // Some tags point to one or more additional IFDs to process - var isIfdPointer = false; - if (byteCount == checked(4L * componentCount) || formatCode == TiffDataFormatCode.Ifd8) + if (valueLength == checked(componentCount * IfdPointerLength) || formatCode == TiffDataFormatCode.Ifd8) { + // There may be multiple IFD pointers, so we try to enter and process an IFD for each. + // They will all have the same TagId, and therefore it's likely they'll be the same + // kind of directory. for (ulong i = 0; i < componentCount; i++) { if (handler.TryEnterSubIfd(tagId)) { isIfdPointer = true; - var subDirOffset = context.Reader.GetUInt32(checked((int)(tagValueOffset + i * 4))); + + int subDirOffsetOffset = checked((int)(valueOffset + (i * IfdPointerLength))); + + uint subDirOffset = context.Reader.GetUInt32(subDirOffsetOffset); + ProcessIfd(handler, context, (int)subDirOffset); } + else + { + // There's no point trying to enter the same tag ID again. + break; + } } } // If it wasn't an IFD pointer, allow custom tag processing to occur - if (!isIfdPointer && !handler.CustomProcessTag(context, tagId, (int)tagValueOffset, (int)byteCount)) + if (!isIfdPointer && !handler.CustomProcessTag(context, tagId, (int)valueOffset, (int)valueLength)) { // If no custom processing occurred, process the tag in the standard fashion - ProcessTag(handler, tagId, (int)tagValueOffset, (int)componentCount, formatCode, context.Reader); + ProcessTag(handler, tagId, (int)valueOffset, (int)componentCount, formatCode, context.Reader); } } // at the end of each IFD is an optional link to the next IFD - var finalTagOffset = CalculateTagOffset(ifdOffset, dirTagCount, context.IsBigTiff); + int finalTagOffset = CalculateTagOffset(tagIndex: tagCount); - var nextIfdOffsetLong = context.IsBigTiff - ? context.Reader.GetUInt64(finalTagOffset) - : context.Reader.GetUInt32(finalTagOffset); - - if (nextIfdOffsetLong != 0 && nextIfdOffsetLong <= int.MaxValue) + if ((long)finalTagOffset + followerPointerLength <= context.Reader.Length) { - var nextIfdOffset = (int)nextIfdOffsetLong; + ulong nextIfdOffsetLong = context.IsBigTiff + ? context.Reader.GetUInt64(finalTagOffset) + : context.Reader.GetUInt32(finalTagOffset); - if (nextIfdOffset >= context.Reader.Length) - { - // Last 4 bytes of IFD reference another IFD with an address that is out of bounds - return; - } - else if (nextIfdOffset < ifdOffset) + if (nextIfdOffsetLong != 0 && nextIfdOffsetLong <= int.MaxValue) { - // TODO is this a valid restriction? - // Last 4 bytes of IFD reference another IFD with an address that is before the start of this directory - return; - } + int nextIfdOffset = (int)nextIfdOffsetLong; - if (handler.HasFollowerIfd()) - { - ProcessIfd(handler, context, nextIfdOffset); + if (nextIfdOffset >= context.Reader.Length) + { + // Last 4 bytes of IFD reference another IFD with an address that is out of bounds + return; + } + else if (nextIfdOffset < ifdOffset) + { + // TODO is this a valid restriction? + // Last 4 bytes of IFD reference another IFD with an address that is before the start of this directory + return; + } + + if (handler.HasFollowerIfd()) + { + ProcessIfd(handler, context, nextIfdOffset); + } } } + + return; + + int CalculateTagOffset(int tagIndex) + { + return checked(ifdOffset + tagCountLength + (entryLength * tagIndex)); + } } finally { @@ -302,32 +335,32 @@ public static void ProcessIfd(ITiffHandler handler, TiffReaderContext context, i } /// - private static void ProcessTag(ITiffHandler handler, int tagId, int tagValueOffset, int componentCount, TiffDataFormatCode formatCode, IndexedReader reader) + private static void ProcessTag(ITiffHandler handler, int tagId, in int valueOffset, int componentCount, TiffDataFormatCode formatCode, IndexedReader reader) { switch (formatCode) { case TiffDataFormatCode.Undefined: { // this includes exif user comments - handler.SetByteArray(tagId, reader.GetBytes(tagValueOffset, componentCount)); + handler.SetByteArray(tagId, reader.GetBytes(valueOffset, componentCount)); break; } case TiffDataFormatCode.String: { - handler.SetString(tagId, reader.GetNullTerminatedStringValue(tagValueOffset, componentCount)); + handler.SetString(tagId, reader.GetNullTerminatedStringValue(valueOffset, componentCount)); break; } case TiffDataFormatCode.RationalS: { if (componentCount == 1) { - handler.SetRational(tagId, new Rational(reader.GetInt32(tagValueOffset), reader.GetInt32(tagValueOffset + 4))); + handler.SetRational(tagId, new Rational(reader.GetInt32(valueOffset), reader.GetInt32(valueOffset + 4))); } else if (componentCount > 1) { var array = new Rational[componentCount]; - for (var i = 0; i < componentCount; i++) - array[i] = new Rational(reader.GetInt32(tagValueOffset + 8 * i), reader.GetInt32(tagValueOffset + 4 + 8 * i)); + for (int i = 0, componentValueOffset = valueOffset; i < componentCount; i++, componentValueOffset += 8) + array[i] = new Rational(reader.GetInt32(componentValueOffset), reader.GetInt32(componentValueOffset + 4)); handler.SetRationalArray(tagId, array); } break; @@ -336,13 +369,13 @@ private static void ProcessTag(ITiffHandler handler, int tagId, int tagValueOffs { if (componentCount == 1) { - handler.SetRational(tagId, new Rational(reader.GetUInt32(tagValueOffset), reader.GetUInt32(tagValueOffset + 4))); + handler.SetRational(tagId, new Rational(reader.GetUInt32(valueOffset), reader.GetUInt32(valueOffset + 4))); } else if (componentCount > 1) { var array = new Rational[componentCount]; - for (var i = 0; i < componentCount; i++) - array[i] = new Rational(reader.GetUInt32(tagValueOffset + 8 * i), reader.GetUInt32(tagValueOffset + 4 + 8 * i)); + for (int i = 0, componentValueOffset = valueOffset; i < componentCount; i++, componentValueOffset += 8) + array[i] = new Rational(reader.GetUInt32(componentValueOffset), reader.GetUInt32(componentValueOffset + 4)); handler.SetRationalArray(tagId, array); } break; @@ -351,13 +384,13 @@ private static void ProcessTag(ITiffHandler handler, int tagId, int tagValueOffs { if (componentCount == 1) { - handler.SetFloat(tagId, reader.GetFloat32(tagValueOffset)); + handler.SetFloat(tagId, reader.GetFloat32(valueOffset)); } else { var array = new float[componentCount]; - for (var i = 0; i < componentCount; i++) - array[i] = reader.GetFloat32(tagValueOffset + i * 4); + for (int i = 0; i < componentCount; i++) + array[i] = reader.GetFloat32(valueOffset + i * 4); handler.SetFloatArray(tagId, array); } break; @@ -366,13 +399,13 @@ private static void ProcessTag(ITiffHandler handler, int tagId, int tagValueOffs { if (componentCount == 1) { - handler.SetDouble(tagId, reader.GetDouble64(tagValueOffset)); + handler.SetDouble(tagId, reader.GetDouble64(valueOffset)); } else { var array = new double[componentCount]; - for (var i = 0; i < componentCount; i++) - array[i] = reader.GetDouble64(tagValueOffset + i * 8); + for (int i = 0; i < componentCount; i++) + array[i] = reader.GetDouble64(valueOffset + i * 8); handler.SetDoubleArray(tagId, array); } break; @@ -381,13 +414,13 @@ private static void ProcessTag(ITiffHandler handler, int tagId, int tagValueOffs { if (componentCount == 1) { - handler.SetInt8S(tagId, reader.GetSByte(tagValueOffset)); + handler.SetInt8S(tagId, reader.GetSByte(valueOffset)); } else { var array = new sbyte[componentCount]; var bytes = MemoryMarshal.Cast(array); - reader.GetBytes(tagValueOffset, bytes); + reader.GetBytes(valueOffset, bytes); handler.SetInt8SArray(tagId, array); } break; @@ -396,12 +429,12 @@ private static void ProcessTag(ITiffHandler handler, int tagId, int tagValueOffs { if (componentCount == 1) { - handler.SetInt8U(tagId, reader.GetByte(tagValueOffset)); + handler.SetInt8U(tagId, reader.GetByte(valueOffset)); } else { var array = new byte[componentCount]; - reader.GetBytes(tagValueOffset, array); + reader.GetBytes(valueOffset, array); handler.SetInt8UArray(tagId, array); } break; @@ -410,13 +443,13 @@ private static void ProcessTag(ITiffHandler handler, int tagId, int tagValueOffs { if (componentCount == 1) { - handler.SetInt16S(tagId, reader.GetInt16(tagValueOffset)); + handler.SetInt16S(tagId, reader.GetInt16(valueOffset)); } else { var array = new short[componentCount]; - for (var i = 0; i < componentCount; i++) - array[i] = reader.GetInt16(tagValueOffset + i * 2); + for (int i = 0, componentOffset = valueOffset; i < componentCount; i++, componentOffset += 2) + array[i] = reader.GetInt16(componentOffset); handler.SetInt16SArray(tagId, array); } break; @@ -425,13 +458,13 @@ private static void ProcessTag(ITiffHandler handler, int tagId, int tagValueOffs { if (componentCount == 1) { - handler.SetInt16U(tagId, reader.GetUInt16(tagValueOffset)); + handler.SetInt16U(tagId, reader.GetUInt16(valueOffset)); } else { var array = new ushort[componentCount]; - for (var i = 0; i < componentCount; i++) - array[i] = reader.GetUInt16(tagValueOffset + i * 2); + for (int i = 0, componentOffset = valueOffset; i < componentCount; i++, componentOffset += 2) + array[i] = reader.GetUInt16(componentOffset); handler.SetInt16UArray(tagId, array); } break; @@ -441,13 +474,13 @@ private static void ProcessTag(ITiffHandler handler, int tagId, int tagValueOffs // NOTE 'long' in this case means 32 bit, not 64 if (componentCount == 1) { - handler.SetInt32S(tagId, reader.GetInt32(tagValueOffset)); + handler.SetInt32S(tagId, reader.GetInt32(valueOffset)); } else { var array = new int[componentCount]; - for (var i = 0; i < componentCount; i++) - array[i] = reader.GetInt32(tagValueOffset + i * 4); + for (int i = 0, componentOffset = valueOffset; i < componentCount; i++, componentOffset += 4) + array[i] = reader.GetInt32(componentOffset); handler.SetInt32SArray(tagId, array); } break; @@ -457,13 +490,13 @@ private static void ProcessTag(ITiffHandler handler, int tagId, int tagValueOffs // NOTE 'long' in this case means 32 bit, not 64 if (componentCount == 1) { - handler.SetInt32U(tagId, reader.GetUInt32(tagValueOffset)); + handler.SetInt32U(tagId, reader.GetUInt32(valueOffset)); } else { var array = new uint[componentCount]; - for (var i = 0; i < componentCount; i++) - array[i] = reader.GetUInt32(tagValueOffset + i * 4); + for (int i = 0, componentOffset = valueOffset; i < componentCount; i++, componentOffset += 4) + array[i] = reader.GetUInt32(componentOffset); handler.SetInt32UArray(tagId, array); } break; @@ -472,13 +505,13 @@ private static void ProcessTag(ITiffHandler handler, int tagId, int tagValueOffs { if (componentCount == 1) { - handler.SetInt64S(tagId, reader.GetInt64(tagValueOffset)); + handler.SetInt64S(tagId, reader.GetInt64(valueOffset)); } else { var array = new long[componentCount]; - for (var i = 0; i < componentCount; i++) - array[i] = reader.GetInt64(tagValueOffset + i * 8); + for (int i = 0, componentOffset = valueOffset; i < componentCount; i++, componentOffset += 8) + array[i] = reader.GetInt64(componentOffset); handler.SetInt64SArray(tagId, array); } break; @@ -487,13 +520,13 @@ private static void ProcessTag(ITiffHandler handler, int tagId, int tagValueOffs { if (componentCount == 1) { - handler.SetInt64U(tagId, reader.GetUInt64(tagValueOffset)); + handler.SetInt64U(tagId, reader.GetUInt64(valueOffset)); } else { var array = new ulong[componentCount]; - for (var i = 0; i < componentCount; i++) - array[i] = reader.GetUInt64(tagValueOffset + i * 8); + for (int i = 0, componentOffset = valueOffset; i < componentCount; i++, componentOffset += 8) + array[i] = reader.GetUInt64(componentOffset); handler.SetInt64UArray(tagId, array); } break; @@ -506,19 +539,28 @@ private static void ProcessTag(ITiffHandler handler, int tagId, int tagValueOffs } } - /// Determine the offset of a given tag within the specified IFD. - /// - /// Add 2 bytes for the tag count. - /// Each entry is 12 bytes for regular TIFF, or 20 bytes for BigTIFF. - /// - /// The offset at which the IFD starts. - /// The zero-based entry number. - /// Whether we are using BigTIFF encoding. - private static int CalculateTagOffset(int ifdStartOffset, int entryNumber, bool isBigTiff) + private static class ByteCounts { - return !isBigTiff - ? ifdStartOffset + 2 + 12 * entryNumber - : ifdStartOffset + 8 + 20 * entryNumber; + public const int TagCount = 2; + public const int TagCount_BigTiff = 8; + + public const int TagId = 2; + public const int FormatCode = 2; + + public const int ComponentCount = 4; + public const int ComponentCount_BigTiff = 8; + + public const int Value = 4; + public const int Value_BigTiff = 8; + + public const int EntryValueOffset = TagId + FormatCode + ComponentCount; + public const int EntryValueOffset_BigTiff = TagId + FormatCode + ComponentCount_BigTiff; + + public const int Entry = TagId + FormatCode + ComponentCount + Value; + public const int Entry_BigTiff = TagId + FormatCode + ComponentCount_BigTiff + Value_BigTiff; + + public const int FollowerIfdPointer = 4; + public const int FollowerIfdPointer_BiffTiff = 8; } } }