context: using System;
  using System.Collections.Generic;
  using System.Globalization;
  using System.IO;
  using System.Text;
  using System.Text.RegularExpressions;
  
  namespace ExifLib
  {
      /// <summary>
      /// A class for reading Exif data from a JPEG file. The file will be open for reading for as long as the class exists.
      /// <seealso cref="http://gvsoft.homedns.org/exif/Exif-explanation.html"/>
      /// </summary>
      public sealed class ExifReader : IDisposable
      {
          private readonly Stream _stream;
          private readonly BinaryReader _reader;
          /// <summary>
          /// If set, the underlying stream will not be closed when the reader is disposed
          /// </summary>
          private readonly bool _leaveOpen;
  
          private static readonly Regex _nullDateTimeMatcher = new Regex(@"^[\s0]{4}[:\s][\s0]{2}[:\s][\s0]{5}[:\s][\s0]{2}[:\s][\s0]{2}$");
  
          /// <summary>
          /// The primary tag catalogue (absolute file offsets to tag data, indexed by tag ID)
          /// </summary>
          private Dictionary<ushort, long> _ifd0PrimaryCatalogue;
  
          /// <summary>
          /// The EXIF tag catalogue (absolute file offsets to tag data, indexed by tag ID)
          /// </summary>
          private Dictionary<ushort, long> _ifdExifCatalogue;
  
          /// <summary>
          /// The GPS tag catalogue (absolute file offsets to tag data, indexed by tag ID)
          /// </summary>
          private Dictionary<ushort, long> _ifdGPSCatalogue;
  
          /// <summary>
          /// The thumbnail tag catalogue (absolute file offsets to tag data, indexed by tag ID)
          /// </summary>
          /// <remarks>JPEG images contain 2 main sections - one for the main image (which contains most of the useful EXIF data), and one for the thumbnail
          /// image (which contains little more than the thumbnail itself). This catalogue is only used by <see cref="GetJpegThumbnailBytes"/>.</remarks>
          private Dictionary<ushort, long> _ifd1Catalogue;
  
          /// <summary>
          /// Indicates whether to read data using big or little endian byte aligns
          /// </summary>
          private bool _isLittleEndian;
  
          /// <summary>
          /// The position in the filestream at which the TIFF header starts
          /// </summary>
          private long _tiffHeaderStart;
  
          // Windows 8 store apps don't support the FileStream class
  #if !NETFX_CORE
          public ExifReader(string fileName)
              : this(new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite), false, true)
          {
          }
  #endif
  
          public ExifReader(Stream stream)
              : this(stream, false, false)
          {
          }
  
          // Framework 4.5 gives us the option of leaving the stream open (with the new constructor for BinaryReader). For this framework, we make a new constructor available
  #if NET_45_OR_HIGHER
          public ExifReader(Stream stream, bool leaveOpen) : this(stream, leaveOpen, false)
          {
          }
  #endif
  
          /// <summary>
          /// 
          /// </summary>
          /// <param name="stream"></param>
          /// <param name="leaveOpen">Indicates whether <see cref="stream"/> should be closed when <see cref="Dispose"/> is called</param>
          /// <param name="internalStream">Indicates whether <see cref="stream"/> was instantiated by this reader</param>
          private ExifReader(Stream stream, bool leaveOpen, bool internalStream)
          {
              _stream = stream;
              _leaveOpen = leaveOpen;
              long initialPosition = 0;
  
              try
              {
                  if (stream == null)
                      throw new ArgumentNullException("stream");
  
                  if (!stream.CanSeek)
                      throw new ExifLibException("ExifLib requires a seekable stream");
  
                  // JPEG encoding uses big endian (i.e. Motorola) byte aligns. The TIFF encoding
                  // found later in the document will specify the byte aligns used for the rest of the document.
                  _isLittleEndian = false;
  
                  // The initial stream position is cached so it can be restored in the case of an exception within this constructor
                  initialPosition = stream.Position;
  
  #if NET_45_OR_HIGHER
                  // Note that we always tell the reader to leave the stream open. This means that in cases
                  // where an exception is thrown during construction, the reader won't close the stream.
                  _reader = new BinaryReader(_stream, new UTF8Encoding(), true);
  #else
                  _reader = new BinaryReader(_stream);
  #endif
                  // Make sure the file's a JPEG. If the file length is less than 2 bytes, an EndOfStreamException will be thrown.
                  if (ReadUShort() != 0xFFD8)
                      throw new ExifLibException("File is not a valid JPEG");
  
                  // Scan to the start of the Exif content
                  try
                  {
                      ReadToExifStart();
                  }
                  catch (Exception ex)
                  {
                      throw new ExifLibException("Unable to locate EXIF content", ex);
                  }
  
                  // Create an index of all Exif tags found within the document
                  try
                  {
                      CreateTagIndex();
                  }
                  catch (Exception ex)
                  {
                      throw new ExifLibException("Error indexing EXIF tags", ex);
                  }
              }
              catch
              {
                  // Cleanup. Note that the stream is not closed unless it was created internally
                  try
                  {
                      if (_reader != null)
                      {
  #if NETFX_CORE
                          _reader.Dispose();
  #else
                          _reader.Close();
  #endif
                      }
  
                      if (_stream != null)
                      {
                          if (internalStream)
                              _stream.Dispose();
                          else if (_stream.CanSeek)
                          {
                              // Try to restore the stream to its initial position
                              _stream.Position = initialPosition;
                          }
                      }
                  }
                  catch
                  {
                  }
  
                  throw;
              }
          }
  
          #region TIFF methods
  
          /// <summary>
          /// Returns the length (in bytes) per component of the specified TIFF data type
          /// </summary>
          /// <returns></returns>
          private byte GetTIFFFieldLength(ushort tiffDataType)
          {
              switch (tiffDataType)
              {
                  case 0:
                      // Unknown datatype, therefore it can't be interpreted reliably
                      return 0;
                  case 1:
                  case 2:
                  case 7:
                  case 6:
                      return 1;
                  case 3:
                  case 8:
                      return 2;
                  case 4:
                  case 9:
                  case 11:
                      return 4;
                  case 5:
                  case 10:
                  case 12:
                      return 8;
                  default:
                      throw new ExifLibException(string.Format("Unknown TIFF datatype: {0}", tiffDataType));
              }
          }
  
          #endregion
  
          #region Methods for reading data directly from the filestream
  
          /// <summary>
          /// Gets a 2 byte unsigned integer from the file
          /// </summary>
          /// <returns></returns>
          private ushort ReadUShort()
          {
              return ToUShort(ReadBytes(2));
          }
  
          /// <summary>
          /// Gets a 4 byte unsigned integer from the file
          /// </summary>
          /// <returns></returns>
          private uint ReadUint()
          {
              return ToUint(ReadBytes(4));
          }
  
          private string ReadString(int chars)
          {
              var bytes = ReadBytes(chars);
              return Encoding.UTF8.GetString(bytes, 0, bytes.Length);
          }
  
          private byte[] ReadBytes(int byteCount)
          {
              var bytes = _reader.ReadBytes(byteCount);
  
              // ReadBytes may return less than the bytes requested if the end of the stream is reached
              if (bytes.Length != byteCount)
                  throw new EndOfStreamException();
  
              return bytes;
          }
  
          /// <summary>
          /// Reads some bytes from the specified TIFF offset
          /// </summary>
          /// <param name="tiffOffset"></param>
          /// <param name="byteCount"></param>
          /// <returns></returns>
          private byte[] ReadBytes(ushort tiffOffset, int byteCount)
          {
              // Keep the current file offset
              long originalOffset = _stream.Position;
  
              // Move to the TIFF offset and retrieve the data
              _stream.Seek(tiffOffset + _tiffHeaderStart, SeekOrigin.Begin);
  
              byte[] data = _reader.ReadBytes(byteCount);
  
              // Restore the file offset
              _stream.Position = originalOffset;
  
              return data;
          }
  
          #endregion
  
          #region Data conversion methods for interpreting datatypes from a byte array
  
          /// <summary>
          /// Converts 2 bytes to a ushort using the current byte aligns
          /// </summary>
          /// <returns></returns>
          private ushort ToUShort(byte[] data)
          {
              if (_isLittleEndian != BitConverter.IsLittleEndian)
                  Array.Reverse(data);
  
              return BitConverter.ToUInt16(data, 0);
          }
  
          /// <summary>
          /// Converts 8 bytes to the numerator and denominator
          /// components of an unsigned rational using the current byte aligns
          /// </summary>
          private uint[] ToURationalFraction(byte[] data)
          {
              var numeratorData = new byte[4];
              var denominatorData = new byte[4];
  
  
gt:             Array.Copy(data, numeratorData, 4);