/// <summary>
		/// Performs the Jfif data extraction, adding found values to the specified
		/// instance of
		/// <see cref="Com.Drew.Metadata.Metadata"/>
		/// .
		/// </summary>
		public virtual void Extract(RandomAccessReader reader, Com.Drew.Metadata.Metadata metadata)
		{
			JfifDirectory directory = metadata.GetOrCreateDirectory<JfifDirectory>();
			try
			{
				// For JFIF, the tag number is also the offset into the segment
				int ver = reader.GetUInt16(JfifDirectory.TagVersion);
				directory.SetInt(JfifDirectory.TagVersion, ver);
				int units = reader.GetUInt8(JfifDirectory.TagUnits);
				directory.SetInt(JfifDirectory.TagUnits, units);
				int height = reader.GetUInt16(JfifDirectory.TagResx);
				directory.SetInt(JfifDirectory.TagResx, height);
				int width = reader.GetUInt16(JfifDirectory.TagResy);
				directory.SetInt(JfifDirectory.TagResy, width);
			}
			catch (IOException me)
			{
				directory.AddError(me.Message);
			}
		}
 public virtual void TestGetUInt16_OutOfBounds()
 {
     try
     {
         RandomAccessReader reader = CreateReader(new sbyte[2]);
         reader.GetUInt16(1);
         NUnit.Framework.Assert.Fail("Exception expected");
     }
     catch (IOException ex)
     {
         Sharpen.Tests.AreEqual("Attempt to read from beyond end of underlying data source (requested index: 1, requested count: 2, max index: 1)", ex.Message);
     }
 }
		public virtual void Extract(RandomAccessReader reader, Com.Drew.Metadata.Metadata metadata)
		{
			PsdHeaderDirectory directory = metadata.GetOrCreateDirectory<PsdHeaderDirectory>();
			try
			{
				int signature = reader.GetInt32(0);
				if (signature != unchecked((int)(0x38425053)))
				{
					directory.AddError("Invalid PSD file signature");
					return;
				}
				int version = reader.GetUInt16(4);
				if (version != 1 && version != 2)
				{
					directory.AddError("Invalid PSD file version (must be 1 or 2)");
					return;
				}
				// 6 reserved bytes are skipped here.  They should be zero.
				int channelCount = reader.GetUInt16(12);
				directory.SetInt(PsdHeaderDirectory.TagChannelCount, channelCount);
				// even though this is probably an unsigned int, the max height in practice is 300,000
				int imageHeight = reader.GetInt32(14);
				directory.SetInt(PsdHeaderDirectory.TagImageHeight, imageHeight);
				// even though this is probably an unsigned int, the max width in practice is 300,000
				int imageWidth = reader.GetInt32(18);
				directory.SetInt(PsdHeaderDirectory.TagImageWidth, imageWidth);
				int bitsPerChannel = reader.GetUInt16(22);
				directory.SetInt(PsdHeaderDirectory.TagBitsPerChannel, bitsPerChannel);
				int colorMode = reader.GetUInt16(24);
				directory.SetInt(PsdHeaderDirectory.TagColorMode, colorMode);
			}
			catch (IOException)
			{
				directory.AddError("Unable to read PSD header");
			}
		}
        public virtual void TestGetUInt16()
        {
            sbyte[]            buffer = new sbyte[] { unchecked ((int)(0x00)), unchecked ((int)(0x01)), unchecked ((sbyte)unchecked ((int)(0x7F))), unchecked ((sbyte)unchecked ((int)(0xFF))) };
            RandomAccessReader reader = CreateReader(buffer);

            Sharpen.Tests.AreEqual(unchecked ((int)(0x0001)), reader.GetUInt16(0));
            Sharpen.Tests.AreEqual(unchecked ((int)(0x017F)), reader.GetUInt16(1));
            Sharpen.Tests.AreEqual(unchecked ((int)(0x7FFF)), reader.GetUInt16(2));
            reader.SetMotorolaByteOrder(false);
            Sharpen.Tests.AreEqual(unchecked ((int)(0x0100)), reader.GetUInt16(0));
            Sharpen.Tests.AreEqual(unchecked ((int)(0x7F01)), reader.GetUInt16(1));
            Sharpen.Tests.AreEqual(unchecked ((int)(0xFF7F)), reader.GetUInt16(2));
        }
		/// <summary>Processes a TIFF data sequence.</summary>
		/// <param name="reader">
		/// the
		/// <see cref="Com.Drew.Lang.RandomAccessReader"/>
		/// from which the data should be read
		/// </param>
		/// <param name="handler">
		/// the
		/// <see cref="TiffHandler"/>
		/// that will coordinate processing and accept read values
		/// </param>
		/// <param name="tiffHeaderOffset">the offset within <code>reader</code> at which the TIFF header starts</param>
		/// <exception cref="TiffProcessingException">
		/// if an error occurred during the processing of TIFF data that could not be
		/// ignored or recovered from
		/// </exception>
		/// <exception cref="System.IO.IOException">an error occurred while accessing the required data</exception>
		/// <exception cref="Com.Drew.Imaging.Tiff.TiffProcessingException"/>
		public virtual void ProcessTiff(RandomAccessReader reader, TiffHandler handler, int tiffHeaderOffset)
		{
			// This must be either "MM" or "II".
			short byteOrderIdentifier = reader.GetInt16(tiffHeaderOffset);
			if (byteOrderIdentifier == unchecked((int)(0x4d4d)))
			{
				// "MM"
				reader.SetMotorolaByteOrder(true);
			}
			else
			{
				if (byteOrderIdentifier == unchecked((int)(0x4949)))
				{
					// "II"
					reader.SetMotorolaByteOrder(false);
				}
				else
				{
					throw new TiffProcessingException("Unclear distinction between Motorola/Intel byte ordering: " + byteOrderIdentifier);
				}
			}
			// Check the next two values for correctness.
			int tiffMarker = reader.GetUInt16(2 + tiffHeaderOffset);
			handler.SetTiffMarker(tiffMarker);
			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)
			{
				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 = tiffHeaderOffset + 2 + 2 + 4;
			}
			ICollection<int> processedIfdOffsets = new HashSet<int>();
			ProcessIfd(handler, reader, processedIfdOffsets, firstIfdOffset, tiffHeaderOffset);
			handler.Completed(reader, tiffHeaderOffset);
		}
		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;
				}
			}
		}
		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;
				}
			}
		}
		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);
			}
		}
		private static void ProcessIFD(Com.Drew.Metadata.Directory directory, ICollection<int> processedIfdOffsets, int ifdOffset, int tiffHeaderOffset, Com.Drew.Metadata.Metadata metadata, RandomAccessReader reader)
		{
			// check for directories we've already visited to avoid stack overflows when recursive/cyclic directory structures exist
			if (processedIfdOffsets.Contains(Sharpen.Extensions.ValueOf(ifdOffset)))
			{
				return;
			}
			// remember that we've visited this directory so that we don't visit it again later
			processedIfdOffsets.Add(ifdOffset);
			if (ifdOffset >= reader.GetLength() || ifdOffset < 0)
			{
				directory.AddError("Ignored IFD marked to start outside data segment");
				return;
			}
			// First two bytes in the IFD are the number of tags in this directory
			int dirTagCount = reader.GetUInt16(ifdOffset);
			int dirLength = (2 + (12 * dirTagCount) + 4);
			if (dirLength + ifdOffset > reader.GetLength())
			{
				directory.AddError("Illegally sized IFD");
				return;
			}
			// Handle each tag in this directory
			for (int tagNumber = 0; tagNumber < dirTagCount; tagNumber++)
			{
				int tagOffset = CalculateTagOffset(ifdOffset, tagNumber);
				// 2 bytes for the tag type
				int tagType = reader.GetUInt16(tagOffset);
				// 2 bytes for the format code
				int formatCode = reader.GetUInt16(tagOffset + 2);
				if (formatCode < 1 || formatCode > MaxFormatCode)
				{
					// 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.
					directory.AddError("Invalid TIFF tag format code: " + formatCode);
					return;
				}
				// 4 bytes dictate the number of components in this tag's data
				int componentCount = reader.GetInt32(tagOffset + 4);
				if (componentCount < 0)
				{
					directory.AddError("Negative TIFF tag component count");
					continue;
				}
				// each component may have more than one byte... calculate the total number of bytes
				int byteCount = componentCount * BytesPerFormat[formatCode];
				int tagValueOffset;
				if (byteCount > 4)
				{
					// If it's bigger than 4 bytes, the dir entry contains an offset.
					// dirEntryOffset must be passed, as some makernote implementations (e.g. Fujifilm) incorrectly use an
					// offset relative to the start of the makernote itself, not the TIFF segment.
					int offsetVal = reader.GetInt32(tagOffset + 8);
					if (offsetVal + byteCount > reader.GetLength())
					{
						// Bogus pointer offset and / or byteCount value
						directory.AddError("Illegal TIFF tag pointer offset");
						continue;
					}
					tagValueOffset = tiffHeaderOffset + offsetVal;
				}
				else
				{
					// 4 bytes or less and value is in the dir entry itself
					tagValueOffset = tagOffset + 8;
				}
				if (tagValueOffset < 0 || tagValueOffset > reader.GetLength())
				{
					directory.AddError("Illegal TIFF tag pointer offset");
					continue;
				}
				// Check that this tag isn't going to allocate outside the bounds of the data array.
				// This addresses an uncommon OutOfMemoryError.
				if (byteCount < 0 || tagValueOffset + byteCount > reader.GetLength())
				{
					directory.AddError("Illegal number of bytes for TIFF tag data: " + byteCount);
					continue;
				}
				//
				// Special handling for certain known tags that point to or contain other chunks of data to be processed
				//
				if (tagType == ExifIFD0Directory.TagExifSubIfdOffset && directory is ExifIFD0Directory)
				{
					if (byteCount != 4)
					{
						directory.AddError("Exif SubIFD Offset tag should have a component count of four (bytes) for the offset.");
					}
					else
					{
						int subDirOffset = tiffHeaderOffset + reader.GetInt32(tagValueOffset);
						ProcessIFD(metadata.GetOrCreateDirectory<ExifSubIFDDirectory>(), processedIfdOffsets, subDirOffset, tiffHeaderOffset, metadata, reader);
					}
				}
				else
				{
					if (tagType == ExifSubIFDDirectory.TagInteropOffset && directory is ExifSubIFDDirectory)
					{
						if (byteCount != 4)
						{
							directory.AddError("Exif Interop Offset tag should have a component count of four (bytes) for the offset.");
						}
						else
						{
							int subDirOffset = tiffHeaderOffset + reader.GetInt32(tagValueOffset);
							ProcessIFD(metadata.GetOrCreateDirectory<ExifInteropDirectory>(), processedIfdOffsets, subDirOffset, tiffHeaderOffset, metadata, reader);
						}
					}
					else
					{
						if (tagType == ExifIFD0Directory.TagGpsInfoOffset && directory is ExifIFD0Directory)
						{
							if (byteCount != 4)
							{
								directory.AddError("Exif GPS Info Offset tag should have a component count of four (bytes) for the offset.");
							}
							else
							{
								int subDirOffset = tiffHeaderOffset + reader.GetInt32(tagValueOffset);
								ProcessIFD(metadata.GetOrCreateDirectory<GpsDirectory>(), processedIfdOffsets, subDirOffset, tiffHeaderOffset, metadata, reader);
							}
						}
						else
						{
							if (tagType == ExifSubIFDDirectory.TagMakernote && directory is ExifSubIFDDirectory)
							{
								// The makernote tag contains the encoded makernote data directly.
								// Pass the offset to this tag's value. Manufacturer/Model-specific logic will be used to
								// determine the correct offset for further processing.
								ProcessMakernote(tagValueOffset, processedIfdOffsets, tiffHeaderOffset, metadata, reader);
							}
							else
							{
								ProcessTag(directory, tagType, tagValueOffset, componentCount, formatCode, reader);
							}
						}
					}
				}
			}
			// at the end of each IFD is an optional link to the next IFD
			int finalTagOffset = CalculateTagOffset(ifdOffset, dirTagCount);
			int nextDirectoryOffset = reader.GetInt32(finalTagOffset);
			if (nextDirectoryOffset != 0)
			{
				nextDirectoryOffset += tiffHeaderOffset;
				if (nextDirectoryOffset >= reader.GetLength())
				{
					// Last 4 bytes of IFD reference another IFD with an address that is out of bounds
					// Note this could have been caused by jhead 1.3 cropping too much
					return;
				}
				else
				{
					if (nextDirectoryOffset < ifdOffset)
					{
						// Last 4 bytes of IFD reference another IFD with an address that is before the start of this directory
						return;
					}
				}
				// TODO in Exif, the only known 'follower' IFD is the thumbnail one, however this may not be the case
				ExifThumbnailDirectory nextDirectory = metadata.GetOrCreateDirectory<ExifThumbnailDirectory>();
				ProcessIFD(nextDirectory, processedIfdOffsets, nextDirectoryOffset, tiffHeaderOffset, metadata, reader);
			}
		}
		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);
					}
				}
			}
		}
		/// <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;
				}
			}
		}
		/// <summary>Processes a TIFF IFD.</summary>
		/// <remarks>
		/// Processes a TIFF IFD.
		/// <p/>
		/// IFD Header:
		/// <ul>
		/// <li><b>2 bytes</b> number of tags</li>
		/// </ul>
		/// Tag structure:
		/// <ul>
		/// <li><b>2 bytes</b> tag type</li>
		/// <li><b>2 bytes</b> format code (values 1 to 12, inclusive)</li>
		/// <li><b>4 bytes</b> component count</li>
		/// <li><b>4 bytes</b> inline value, or offset pointer if too large to fit in four bytes</li>
		/// </ul>
		/// </remarks>
		/// <param name="handler">
		/// the
		/// <see cref="TiffHandler"/>
		/// that will coordinate processing and accept read values
		/// </param>
		/// <param name="reader">
		/// the
		/// <see cref="Com.Drew.Lang.RandomAccessReader"/>
		/// from which the data should be read
		/// </param>
		/// <param name="processedIfdOffsets">the set of visited IFD offsets, to avoid revisiting the same IFD in an endless loop</param>
		/// <param name="ifdOffset">the offset within <code>reader</code> at which the IFD data starts</param>
		/// <param name="tiffHeaderOffset">the offset within <code>reader</code> at which the TIFF header starts</param>
		/// <exception cref="System.IO.IOException">an error occurred while accessing the required data</exception>
		public static void ProcessIfd(TiffHandler handler, RandomAccessReader reader, ICollection<int> processedIfdOffsets, int ifdOffset, int tiffHeaderOffset)
		{
			try
			{
				// check for directories we've already visited to avoid stack overflows when recursive/cyclic directory structures exist
				if (processedIfdOffsets.Contains(Sharpen.Extensions.ValueOf(ifdOffset)))
				{
					return;
				}
				// remember that we've visited this directory so that we don't visit it again later
				processedIfdOffsets.Add(ifdOffset);
				if (ifdOffset >= reader.GetLength() || ifdOffset < 0)
				{
					handler.Error("Ignored IFD marked to start outside data segment");
					return;
				}
				// First two bytes in the IFD are the number of tags in this directory
				int dirTagCount = reader.GetUInt16(ifdOffset);
				int dirLength = (2 + (12 * dirTagCount) + 4);
				if (dirLength + ifdOffset > reader.GetLength())
				{
					handler.Error("Illegally sized IFD");
					return;
				}
				//
				// Handle each tag in this directory
				//
				for (int tagNumber = 0; tagNumber < dirTagCount; tagNumber++)
				{
					int tagOffset = CalculateTagOffset(ifdOffset, tagNumber);
					// 2 bytes for the tag id
					int tagId = reader.GetUInt16(tagOffset);
					// 2 bytes for the format code
					int formatCode = reader.GetUInt16(tagOffset + 2);
					TiffDataFormat format = TiffDataFormat.FromTiffFormatCode(formatCode);
					if (format == null)
					{
						// 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: " + formatCode);
						return;
					}
					// 4 bytes dictate the number of components in this tag's data
					int componentCount = reader.GetInt32(tagOffset + 4);
					if (componentCount < 0)
					{
						handler.Error("Negative TIFF tag component count");
						continue;
					}
					int byteCount = componentCount * format.GetComponentSizeBytes();
					int tagValueOffset;
					if (byteCount > 4)
					{
						// If it's bigger than 4 bytes, the dir entry contains an offset.
						int offsetVal = reader.GetInt32(tagOffset + 8);
						if (offsetVal + byteCount > reader.GetLength())
						{
							// Bogus pointer offset and / or byteCount value
							handler.Error("Illegal TIFF tag pointer offset");
							continue;
						}
						tagValueOffset = tiffHeaderOffset + offsetVal;
					}
					else
					{
						// 4 bytes or less and value is in the dir entry itself.
						tagValueOffset = tagOffset + 8;
					}
					if (tagValueOffset < 0 || tagValueOffset > reader.GetLength())
					{
						handler.Error("Illegal TIFF tag pointer offset");
						continue;
					}
					// Check that this tag isn't going to allocate outside the bounds of the data array.
					// This addresses an uncommon OutOfMemoryError.
					if (byteCount < 0 || tagValueOffset + byteCount > reader.GetLength())
					{
						handler.Error("Illegal number of bytes for TIFF tag data: " + byteCount);
						continue;
					}
					//
					// Special handling for tags that point to other IFDs
					//
					if (byteCount == 4 && handler.IsTagIfdPointer(tagId))
					{
						int subDirOffset = tiffHeaderOffset + reader.GetInt32(tagValueOffset);
						ProcessIfd(handler, reader, processedIfdOffsets, subDirOffset, tiffHeaderOffset);
					}
					else
					{
						if (!handler.CustomProcessTag(tagValueOffset, processedIfdOffsets, tiffHeaderOffset, reader, tagId, byteCount))
						{
							ProcessTag(handler, tagId, tagValueOffset, componentCount, formatCode, reader);
						}
					}
				}
				// at the end of each IFD is an optional link to the next IFD
				int finalTagOffset = CalculateTagOffset(ifdOffset, dirTagCount);
				int nextIfdOffset = reader.GetInt32(finalTagOffset);
				if (nextIfdOffset != 0)
				{
					nextIfdOffset += tiffHeaderOffset;
					if (nextIfdOffset >= reader.GetLength())
					{
						// Last 4 bytes of IFD reference another IFD with an address that is out of bounds
						// Note this could have been caused by jhead 1.3 cropping too much
						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, reader, processedIfdOffsets, nextIfdOffset, tiffHeaderOffset);
					}
				}
			}
			finally
			{
				handler.EndingIFD();
			}
		}
		/// <exception cref="System.IO.IOException"/>
		private void SetDate(IccDirectory directory, int tagType, RandomAccessReader reader)
		{
			int y = reader.GetUInt16(tagType);
			int m = reader.GetUInt16(tagType + 2);
			int d = reader.GetUInt16(tagType + 4);
			int h = reader.GetUInt16(tagType + 6);
			int M = reader.GetUInt16(tagType + 8);
			int s = reader.GetUInt16(tagType + 10);
			//        final Date value = new Date(Date.UTC(y - 1900, m - 1, d, h, M, s));
			Sharpen.Calendar calendar = Sharpen.Calendar.GetInstance(Sharpen.Extensions.GetTimeZone("UTC"));
			calendar.Set(y, m, d, h, M, s);
			DateTime value = calendar.GetTime();
			directory.SetDate(tagType, value);
		}