Esempio n. 1
0
 public override void Completed(RandomAccessReader reader, int tiffHeaderOffset)
 {
     if (_storeThumbnailBytes)
     {
         // after the extraction process, if we have the correct tags, we may be able to store thumbnail information
         ExifThumbnailDirectory thumbnailDirectory = _metadata.GetDirectory <ExifThumbnailDirectory>();
         if (thumbnailDirectory != null && thumbnailDirectory.ContainsTag(ExifThumbnailDirectory.TagThumbnailCompression))
         {
             int?offset = thumbnailDirectory.GetInteger(ExifThumbnailDirectory.TagThumbnailOffset);
             int?length = thumbnailDirectory.GetInteger(ExifThumbnailDirectory.TagThumbnailLength);
             if (offset != null && length != null)
             {
                 try
                 {
                     sbyte[] thumbnailData = reader.GetBytes(tiffHeaderOffset + offset.Value, length.Value);
                     thumbnailDirectory.SetThumbnailData(thumbnailData);
                 }
                 catch (IOException ex)
                 {
                     thumbnailDirectory.AddError("Invalid thumbnail data specification: " + ex.Message);
                 }
             }
         }
     }
 }
Esempio n. 2
0
        public virtual void Extract([NotNull] RandomAccessReader reader, [NotNull] Com.Drew.Metadata.Metadata metadata)
        {
            // TODO review whether the 'tagPtr' values below really do require RandomAccessReader or whether SequentialReader may be used instead
            IccDirectory directory = new IccDirectory();

            try
            {
                int profileByteCount = reader.GetInt32(IccDirectory.TagProfileByteCount);
                directory.SetInt(IccDirectory.TagProfileByteCount, profileByteCount);
                // For these tags, the int value of the tag is in fact it's offset within the buffer.
                Set4ByteString(directory, IccDirectory.TagCmmType, reader);
                SetInt32(directory, IccDirectory.TagProfileVersion, reader);
                Set4ByteString(directory, IccDirectory.TagProfileClass, reader);
                Set4ByteString(directory, IccDirectory.TagColorSpace, reader);
                Set4ByteString(directory, IccDirectory.TagProfileConnectionSpace, reader);
                SetDate(directory, IccDirectory.TagProfileDatetime, reader);
                Set4ByteString(directory, IccDirectory.TagSignature, reader);
                Set4ByteString(directory, IccDirectory.TagPlatform, reader);
                SetInt32(directory, IccDirectory.TagCmmFlags, reader);
                Set4ByteString(directory, IccDirectory.TagDeviceMake, reader);
                int temp = reader.GetInt32(IccDirectory.TagDeviceModel);
                if (temp != 0)
                {
                    if (temp <= unchecked ((int)(0x20202020)))
                    {
                        directory.SetInt(IccDirectory.TagDeviceModel, temp);
                    }
                    else
                    {
                        directory.SetString(IccDirectory.TagDeviceModel, GetStringFromInt32(temp));
                    }
                }
                SetInt32(directory, IccDirectory.TagRenderingIntent, reader);
                SetInt64(directory, IccDirectory.TagDeviceAttr, reader);
                float[] xyz = new float[] { reader.GetS15Fixed16(IccDirectory.TagXyzValues), reader.GetS15Fixed16(IccDirectory.TagXyzValues + 4), reader.GetS15Fixed16(IccDirectory.TagXyzValues + 8) };
                directory.SetObject(IccDirectory.TagXyzValues, xyz);
                // Process 'ICC tags'
                int tagCount = reader.GetInt32(IccDirectory.TagTagCount);
                directory.SetInt(IccDirectory.TagTagCount, tagCount);
                for (int i = 0; i < tagCount; i++)
                {
                    int     pos     = IccDirectory.TagTagCount + 4 + i * 12;
                    int     tagType = reader.GetInt32(pos);
                    int     tagPtr  = reader.GetInt32(pos + 4);
                    int     tagLen  = reader.GetInt32(pos + 8);
                    sbyte[] b       = reader.GetBytes(tagPtr, tagLen);
                    directory.SetByteArray(tagType, b);
                }
            }
            catch (IOException ex)
            {
                directory.AddError("Exception reading ICC profile: " + ex.Message);
            }
            metadata.AddDirectory(directory);
        }
Esempio n. 3
0
        private static void ProcessKodakMakernote(KodakMakernoteDirectory directory, int tagValueOffset, RandomAccessReader reader)
        {
            // Kodak's makernote is not in IFD format. It has values at fixed offsets.
            int dataOffset = tagValueOffset + 8;

            try
            {
                directory.SetString(KodakMakernoteDirectory.TagKodakModel, reader.GetString(dataOffset, 8));
                directory.SetInt(KodakMakernoteDirectory.TagQuality, reader.GetUInt8(dataOffset + 9));
                directory.SetInt(KodakMakernoteDirectory.TagBurstMode, reader.GetUInt8(dataOffset + 10));
                directory.SetInt(KodakMakernoteDirectory.TagImageWidth, reader.GetUInt16(dataOffset + 12));
                directory.SetInt(KodakMakernoteDirectory.TagImageHeight, reader.GetUInt16(dataOffset + 14));
                directory.SetInt(KodakMakernoteDirectory.TagYearCreated, reader.GetUInt16(dataOffset + 16));
                directory.SetByteArray(KodakMakernoteDirectory.TagMonthDayCreated, reader.GetBytes(dataOffset + 18, 2));
                directory.SetByteArray(KodakMakernoteDirectory.TagTimeCreated, reader.GetBytes(dataOffset + 20, 4));
                directory.SetInt(KodakMakernoteDirectory.TagBurstMode2, reader.GetUInt16(dataOffset + 24));
                directory.SetInt(KodakMakernoteDirectory.TagShutterMode, reader.GetUInt8(dataOffset + 27));
                directory.SetInt(KodakMakernoteDirectory.TagMeteringMode, reader.GetUInt8(dataOffset + 28));
                directory.SetInt(KodakMakernoteDirectory.TagSequenceNumber, reader.GetUInt8(dataOffset + 29));
                directory.SetInt(KodakMakernoteDirectory.TagFNumber, reader.GetUInt16(dataOffset + 30));
                directory.SetLong(KodakMakernoteDirectory.TagExposureTime, reader.GetUInt32(dataOffset + 32));
                directory.SetInt(KodakMakernoteDirectory.TagExposureCompensation, reader.GetInt16(dataOffset + 36));
                directory.SetInt(KodakMakernoteDirectory.TagFocusMode, reader.GetUInt8(dataOffset + 56));
                directory.SetInt(KodakMakernoteDirectory.TagWhiteBalance, reader.GetUInt8(dataOffset + 64));
                directory.SetInt(KodakMakernoteDirectory.TagFlashMode, reader.GetUInt8(dataOffset + 92));
                directory.SetInt(KodakMakernoteDirectory.TagFlashFired, reader.GetUInt8(dataOffset + 93));
                directory.SetInt(KodakMakernoteDirectory.TagIsoSetting, reader.GetUInt16(dataOffset + 94));
                directory.SetInt(KodakMakernoteDirectory.TagIso, reader.GetUInt16(dataOffset + 96));
                directory.SetInt(KodakMakernoteDirectory.TagTotalZoom, reader.GetUInt16(dataOffset + 98));
                directory.SetInt(KodakMakernoteDirectory.TagDateTimeStamp, reader.GetUInt16(dataOffset + 100));
                directory.SetInt(KodakMakernoteDirectory.TagColorMode, reader.GetUInt16(dataOffset + 102));
                directory.SetInt(KodakMakernoteDirectory.TagDigitalZoom, reader.GetUInt16(dataOffset + 104));
                directory.SetInt(KodakMakernoteDirectory.TagSharpness, reader.GetInt8(dataOffset + 107));
            }
            catch (IOException ex)
            {
                directory.AddError("Error processing Kodak makernote data: " + ex.Message);
            }
        }
Esempio n. 4
0
 /// <exception cref="System.IO.IOException"/>
 public override bool CustomProcessTag(int tagOffset, [NotNull] ICollection <int?> processedIfdOffsets, int tiffHeaderOffset, [NotNull] RandomAccessReader reader, int tagId, int byteCount)
 {
     // Custom processing for the Makernote tag
     if (tagId == ExifSubIFDDirectory.TagMakernote && _currentDirectory is ExifSubIFDDirectory)
     {
         return(ProcessMakernote(tagOffset, processedIfdOffsets, tiffHeaderOffset, reader));
     }
     // Custom processing for embedded IPTC data
     if (tagId == ExifSubIFDDirectory.TagIptcNaa && _currentDirectory is ExifIFD0Directory)
     {
         // NOTE Adobe sets type 4 for IPTC instead of 7
         if (reader.GetInt8(tagOffset) == unchecked ((int)(0x1c)))
         {
             sbyte[] iptcBytes = reader.GetBytes(tagOffset, byteCount);
             new IptcReader().Extract(new SequentialByteArrayReader(iptcBytes), _metadata, iptcBytes.Length);
             return(true);
         }
         return(false);
     }
     return(false);
 }
        public virtual void Extract(RandomAccessReader reader, Com.Drew.Metadata.Metadata metadata)
        {
            PhotoshopDirectory directory = metadata.GetOrCreateDirectory <PhotoshopDirectory>();
            int pos;

            try
            {
                pos = reader.GetString(0, 13).Equals("Photoshop 3.0") ? 14 : 0;
            }
            catch (IOException)
            {
                directory.AddError("Unable to read header");
                return;
            }
            long length;

            try
            {
                length = reader.GetLength();
            }
            catch (IOException e)
            {
                directory.AddError("Unable to read Photoshop data: " + e.Message);
                return;
            }
            while (pos < length)
            {
                try
                {
                    // 4 bytes for the signature.  Should always be "8BIM".
                    //String signature = new String(data, pos, 4);
                    pos += 4;
                    // 2 bytes for the resource identifier (tag type).
                    int tagType = reader.GetUInt16(pos);
                    // segment type
                    pos += 2;
                    // A variable number of bytes holding a pascal string (two leading bytes for length).
                    int descriptionLength = reader.GetUInt16(pos);
                    pos += 2;
                    // Some basic bounds checking
                    if (descriptionLength < 0 || descriptionLength + pos > length)
                    {
                        return;
                    }
                    //String description = new String(data, pos, descriptionLength);
                    pos += descriptionLength;
                    // The number of bytes is padded with a trailing zero, if needed, to make the size even.
                    if (pos % 2 != 0)
                    {
                        pos++;
                    }
                    // 4 bytes for the size of the resource data that follows.
                    int byteCount = reader.GetInt32(pos);
                    pos += 4;
                    // The resource data.
                    sbyte[] tagBytes = reader.GetBytes(pos, byteCount);
                    pos += byteCount;
                    // The number of bytes is padded with a trailing zero, if needed, to make the size even.
                    if (pos % 2 != 0)
                    {
                        pos++;
                    }
                    directory.SetByteArray(tagType, tagBytes);
                    // TODO allow rebasing the reader with a new zero-point, rather than copying data here
                    if (tagType == PhotoshopDirectory.TagIptc)
                    {
                        new IptcReader().Extract(new SequentialByteArrayReader(tagBytes), metadata, tagBytes.Length);
                    }
                    if (tagType >= unchecked ((int)(0x0fa0)) && tagType <= unchecked ((int)(0x1387)))
                    {
                        PhotoshopDirectory._tagNameMap.Put(tagType, Sharpen.Extensions.StringFormat("Plug-in %d Data", tagType - unchecked ((int)(0x0fa0)) + 1));
                    }
                }
                catch (IOException ex)
                {
                    directory.AddError(ex.Message);
                    return;
                }
            }
        }
        /// <exception cref="System.IO.IOException"/>
        private static void ProcessTag(TiffHandler handler, int tagId, int tagValueOffset, int componentCount, int formatCode, RandomAccessReader reader)
        {
            switch (formatCode)
            {
            case TiffDataFormat.CodeUndefined:
            {
                // this includes exif user comments
                handler.SetByteArray(tagId, reader.GetBytes(tagValueOffset, componentCount));
                break;
            }

            case TiffDataFormat.CodeString:
            {
                handler.SetString(tagId, reader.GetNullTerminatedString(tagValueOffset, componentCount));
                break;
            }

            case TiffDataFormat.CodeRationalS:
            {
                if (componentCount == 1)
                {
                    handler.SetRational(tagId, new Rational(reader.GetInt32(tagValueOffset), reader.GetInt32(tagValueOffset + 4)));
                }
                else
                {
                    if (componentCount > 1)
                    {
                        Rational[] array = new Rational[componentCount];
                        for (int i = 0; i < componentCount; i++)
                        {
                            array[i] = new Rational(reader.GetInt32(tagValueOffset + (8 * i)), reader.GetInt32(tagValueOffset + 4 + (8 * i)));
                        }
                        handler.SetRationalArray(tagId, array);
                    }
                }
                break;
            }

            case TiffDataFormat.CodeRationalU:
            {
                if (componentCount == 1)
                {
                    handler.SetRational(tagId, new Rational(reader.GetUInt32(tagValueOffset), reader.GetUInt32(tagValueOffset + 4)));
                }
                else
                {
                    if (componentCount > 1)
                    {
                        Rational[] array = new Rational[componentCount];
                        for (int i = 0; i < componentCount; i++)
                        {
                            array[i] = new Rational(reader.GetUInt32(tagValueOffset + (8 * i)), reader.GetUInt32(tagValueOffset + 4 + (8 * i)));
                        }
                        handler.SetRationalArray(tagId, array);
                    }
                }
                break;
            }

            case TiffDataFormat.CodeSingle:
            {
                if (componentCount == 1)
                {
                    handler.SetFloat(tagId, reader.GetFloat32(tagValueOffset));
                }
                else
                {
                    float[] array = new float[componentCount];
                    for (int i = 0; i < componentCount; i++)
                    {
                        array[i] = reader.GetFloat32(tagValueOffset + (i * 4));
                    }
                    handler.SetFloatArray(tagId, array);
                }
                break;
            }

            case TiffDataFormat.CodeDouble:
            {
                if (componentCount == 1)
                {
                    handler.SetDouble(tagId, reader.GetDouble64(tagValueOffset));
                }
                else
                {
                    double[] array = new double[componentCount];
                    for (int i = 0; i < componentCount; i++)
                    {
                        array[i] = reader.GetDouble64(tagValueOffset + (i * 4));
                    }
                    handler.SetDoubleArray(tagId, array);
                }
                break;
            }

            case TiffDataFormat.CodeInt8S:
            {
                if (componentCount == 1)
                {
                    handler.SetInt8s(tagId, reader.GetInt8(tagValueOffset));
                }
                else
                {
                    sbyte[] array = new sbyte[componentCount];
                    for (int i = 0; i < componentCount; i++)
                    {
                        array[i] = reader.GetInt8(tagValueOffset + i);
                    }
                    handler.SetInt8sArray(tagId, array);
                }
                break;
            }

            case TiffDataFormat.CodeInt8U:
            {
                if (componentCount == 1)
                {
                    handler.SetInt8u(tagId, reader.GetUInt8(tagValueOffset));
                }
                else
                {
                    short[] array = new short[componentCount];
                    for (int i = 0; i < componentCount; i++)
                    {
                        array[i] = reader.GetUInt8(tagValueOffset + i);
                    }
                    handler.SetInt8uArray(tagId, array);
                }
                break;
            }

            case TiffDataFormat.CodeInt16S:
            {
                if (componentCount == 1)
                {
                    handler.SetInt16s(tagId, (int)reader.GetInt16(tagValueOffset));
                }
                else
                {
                    short[] array = new short[componentCount];
                    for (int i = 0; i < componentCount; i++)
                    {
                        array[i] = reader.GetInt16(tagValueOffset + (i * 2));
                    }
                    handler.SetInt16sArray(tagId, array);
                }
                break;
            }

            case TiffDataFormat.CodeInt16U:
            {
                if (componentCount == 1)
                {
                    handler.SetInt16u(tagId, reader.GetUInt16(tagValueOffset));
                }
                else
                {
                    int[] array = new int[componentCount];
                    for (int i = 0; i < componentCount; i++)
                    {
                        array[i] = reader.GetUInt16(tagValueOffset + (i * 2));
                    }
                    handler.SetInt16uArray(tagId, array);
                }
                break;
            }

            case TiffDataFormat.CodeInt32S:
            {
                // NOTE 'long' in this case means 32 bit, not 64
                if (componentCount == 1)
                {
                    handler.SetInt32s(tagId, reader.GetInt32(tagValueOffset));
                }
                else
                {
                    int[] array = new int[componentCount];
                    for (int i = 0; i < componentCount; i++)
                    {
                        array[i] = reader.GetInt32(tagValueOffset + (i * 4));
                    }
                    handler.SetInt32sArray(tagId, array);
                }
                break;
            }

            case TiffDataFormat.CodeInt32U:
            {
                // NOTE 'long' in this case means 32 bit, not 64
                if (componentCount == 1)
                {
                    handler.SetInt32u(tagId, reader.GetUInt32(tagValueOffset));
                }
                else
                {
                    long[] array = new long[componentCount];
                    for (int i = 0; i < componentCount; i++)
                    {
                        array[i] = reader.GetUInt32(tagValueOffset + (i * 4));
                    }
                    handler.SetInt32uArray(tagId, array);
                }
                break;
            }

            default:
            {
                handler.Error(Sharpen.Extensions.StringFormat("Unknown format code %d for tag %d", formatCode, tagId));
                break;
            }
            }
        }
Esempio n. 7
0
        private static void ProcessTag(Com.Drew.Metadata.Directory directory, int tagType, int tagValueOffset, int componentCount, int formatCode, RandomAccessReader reader)
        {
            switch (formatCode)
            {
            case FmtUndefined:
            {
                // Directory simply stores raw values
                // The display side uses a Descriptor class per directory to turn the raw values into 'pretty' descriptions
                // this includes exif user comments
                directory.SetByteArray(tagType, reader.GetBytes(tagValueOffset, componentCount));
                break;
            }

            case FmtString:
            {
                string @string = reader.GetNullTerminatedString(tagValueOffset, componentCount);
                directory.SetString(tagType, @string);
                break;
            }

            case FmtSrational:
            {
                if (componentCount == 1)
                {
                    directory.SetRational(tagType, new Rational(reader.GetInt32(tagValueOffset), reader.GetInt32(tagValueOffset + 4)));
                }
                else
                {
                    if (componentCount > 1)
                    {
                        Rational[] rationals = new Rational[componentCount];
                        for (int i = 0; i < componentCount; i++)
                        {
                            rationals[i] = new Rational(reader.GetInt32(tagValueOffset + (8 * i)), reader.GetInt32(tagValueOffset + 4 + (8 * i)));
                        }
                        directory.SetRationalArray(tagType, rationals);
                    }
                }
                break;
            }

            case FmtUrational:
            {
                if (componentCount == 1)
                {
                    directory.SetRational(tagType, new Rational(reader.GetUInt32(tagValueOffset), reader.GetUInt32(tagValueOffset + 4)));
                }
                else
                {
                    if (componentCount > 1)
                    {
                        Rational[] rationals = new Rational[componentCount];
                        for (int i = 0; i < componentCount; i++)
                        {
                            rationals[i] = new Rational(reader.GetUInt32(tagValueOffset + (8 * i)), reader.GetUInt32(tagValueOffset + 4 + (8 * i)));
                        }
                        directory.SetRationalArray(tagType, rationals);
                    }
                }
                break;
            }

            case FmtSingle:
            {
                if (componentCount == 1)
                {
                    directory.SetFloat(tagType, reader.GetFloat32(tagValueOffset));
                }
                else
                {
                    float[] floats = new float[componentCount];
                    for (int i = 0; i < componentCount; i++)
                    {
                        floats[i] = reader.GetFloat32(tagValueOffset + (i * 4));
                    }
                    directory.SetFloatArray(tagType, floats);
                }
                break;
            }

            case FmtDouble:
            {
                if (componentCount == 1)
                {
                    directory.SetDouble(tagType, reader.GetDouble64(tagValueOffset));
                }
                else
                {
                    double[] doubles = new double[componentCount];
                    for (int i = 0; i < componentCount; i++)
                    {
                        doubles[i] = reader.GetDouble64(tagValueOffset + (i * 4));
                    }
                    directory.SetDoubleArray(tagType, doubles);
                }
                break;
            }

            case FmtSbyte:
            {
                //
                // Note that all integral types are stored as int32 internally (the largest supported by TIFF)
                //
                if (componentCount == 1)
                {
                    directory.SetInt(tagType, reader.GetInt8(tagValueOffset));
                }
                else
                {
                    int[] bytes = new int[componentCount];
                    for (int i = 0; i < componentCount; i++)
                    {
                        bytes[i] = reader.GetInt8(tagValueOffset + i);
                    }
                    directory.SetIntArray(tagType, bytes);
                }
                break;
            }

            case FmtByte:
            {
                if (componentCount == 1)
                {
                    directory.SetInt(tagType, reader.GetUInt8(tagValueOffset));
                }
                else
                {
                    int[] bytes = new int[componentCount];
                    for (int i = 0; i < componentCount; i++)
                    {
                        bytes[i] = reader.GetUInt8(tagValueOffset + i);
                    }
                    directory.SetIntArray(tagType, bytes);
                }
                break;
            }

            case FmtUshort:
            {
                if (componentCount == 1)
                {
                    int i = reader.GetUInt16(tagValueOffset);
                    directory.SetInt(tagType, i);
                }
                else
                {
                    int[] ints = new int[componentCount];
                    for (int i = 0; i < componentCount; i++)
                    {
                        ints[i] = reader.GetUInt16(tagValueOffset + (i * 2));
                    }
                    directory.SetIntArray(tagType, ints);
                }
                break;
            }

            case FmtSshort:
            {
                if (componentCount == 1)
                {
                    int i = reader.GetInt16(tagValueOffset);
                    directory.SetInt(tagType, i);
                }
                else
                {
                    int[] ints = new int[componentCount];
                    for (int i = 0; i < componentCount; i++)
                    {
                        ints[i] = reader.GetInt16(tagValueOffset + (i * 2));
                    }
                    directory.SetIntArray(tagType, ints);
                }
                break;
            }

            case FmtSlong:
            case FmtUlong:
            {
                // NOTE 'long' in this case means 32 bit, not 64
                if (componentCount == 1)
                {
                    int i = reader.GetInt32(tagValueOffset);
                    directory.SetInt(tagType, i);
                }
                else
                {
                    int[] ints = new int[componentCount];
                    for (int i = 0; i < componentCount; i++)
                    {
                        ints[i] = reader.GetInt32(tagValueOffset + (i * 4));
                    }
                    directory.SetIntArray(tagType, ints);
                }
                break;
            }

            default:
            {
                directory.AddError("Unknown format code " + formatCode + " for tag " + tagType);
                break;
            }
            }
        }
Esempio n. 8
0
        private static void ExtractTiff(RandomAccessReader reader, Com.Drew.Metadata.Metadata metadata, Com.Drew.Metadata.Directory firstDirectory, int tiffHeaderOffset)
        {
            // this should be either "MM" or "II"
            string byteOrderIdentifier = reader.GetString(tiffHeaderOffset, 2);

            if ("MM".Equals(byteOrderIdentifier))
            {
                reader.SetMotorolaByteOrder(true);
            }
            else
            {
                if ("II".Equals(byteOrderIdentifier))
                {
                    reader.SetMotorolaByteOrder(false);
                }
                else
                {
                    firstDirectory.AddError("Unclear distinction between Motorola/Intel byte ordering: " + byteOrderIdentifier);
                    return;
                }
            }
            // Check the next two values for correctness.
            int tiffMarker           = reader.GetUInt16(2 + tiffHeaderOffset);
            int standardTiffMarker   = unchecked ((int)(0x002A));
            int olympusRawTiffMarker = unchecked ((int)(0x4F52));
            // for ORF files
            int panasonicRawTiffMarker = unchecked ((int)(0x0055));

            // for RW2 files
            if (tiffMarker != standardTiffMarker && tiffMarker != olympusRawTiffMarker && tiffMarker != panasonicRawTiffMarker)
            {
                firstDirectory.AddError("Unexpected TIFF marker after byte order identifier: 0x" + Sharpen.Extensions.ToHexString(tiffMarker));
                return;
            }
            int firstIfdOffset = reader.GetInt32(4 + tiffHeaderOffset) + tiffHeaderOffset;

            // David Ekholm sent a digital camera image that has this problem
            // TODO getLength should be avoided as it causes RandomAccessStreamReader to read to the end of the stream
            if (firstIfdOffset >= reader.GetLength() - 1)
            {
                firstDirectory.AddError("First Exif directory offset is beyond end of Exif data segment");
                // First directory normally starts 14 bytes in -- try it here and catch another error in the worst case
                firstIfdOffset = 14;
            }
            ICollection <int> processedIfdOffsets = new HashSet <int>();

            ProcessIFD(firstDirectory, processedIfdOffsets, firstIfdOffset, tiffHeaderOffset, metadata, reader);
            // after the extraction process, if we have the correct tags, we may be able to store thumbnail information
            ExifThumbnailDirectory thumbnailDirectory = metadata.GetDirectory <ExifThumbnailDirectory>();

            if (thumbnailDirectory != null && thumbnailDirectory.ContainsTag(ExifThumbnailDirectory.TagThumbnailCompression))
            {
                int?offset = thumbnailDirectory.GetInteger(ExifThumbnailDirectory.TagThumbnailOffset);
                int?length = thumbnailDirectory.GetInteger(ExifThumbnailDirectory.TagThumbnailLength);
                if (offset != null && length != null)
                {
                    try
                    {
                        sbyte[] thumbnailData = reader.GetBytes(tiffHeaderOffset + offset.Value, length.Value);
                        thumbnailDirectory.SetThumbnailData(thumbnailData);
                    }
                    catch (IOException ex)
                    {
                        firstDirectory.AddError("Invalid thumbnail data specification: " + ex.Message);
                    }
                }
            }
        }