public virtual void Extract(RandomAccessReader reader, Com.Drew.Metadata.Metadata metadata)
		{
			// TODO review whether the 'tagPtr' values below really do require ICC processing to work with a RandomAccessReader
			IccDirectory directory = metadata.GetOrCreateDirectory<IccDirectory>();
			try
			{
				directory.SetInt(IccDirectory.TagProfileByteCount, reader.GetInt32(IccDirectory.TagProfileByteCount));
				// 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);
			}
		}
		/// <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 Extract(SequentialReader reader, Com.Drew.Metadata.Metadata metadata)
		{
			Com.Drew.Metadata.Directory directory = metadata.GetOrCreateDirectory<AdobeJpegDirectory>();
			try
			{
				reader.SetMotorolaByteOrder(false);
				if (!reader.GetString(5).Equals("Adobe"))
				{
					directory.AddError("Invalid Adobe JPEG data header.");
					return;
				}
				directory.SetInt(AdobeJpegDirectory.TagDctEncodeVersion, reader.GetUInt16());
				directory.SetInt(AdobeJpegDirectory.TagApp14Flags0, reader.GetUInt16());
				directory.SetInt(AdobeJpegDirectory.TagApp14Flags1, reader.GetUInt16());
				directory.SetInt(AdobeJpegDirectory.TagColorTransform, reader.GetInt8());
			}
			catch (IOException ex)
			{
				directory.AddError("IO exception processing data: " + ex.Message);
			}
		}
		public virtual void Extract(sbyte[] segmentBytes, Com.Drew.Metadata.Metadata metadata, JpegSegmentType segmentType)
		{
            if (metadata.ContainsDirectory<JpegDirectory>())
			{
				// If this directory is already present, discontinue this operation.
				// We only store metadata for the *first* matching SOFn segment.
				return;
			}
			JpegDirectory directory = metadata.GetOrCreateDirectory<JpegDirectory>();
			// The value of TAG_COMPRESSION_TYPE is determined by the segment type found
			directory.SetInt(JpegDirectory.TagCompressionType, segmentType.byteValue - JpegSegmentType.Sof0.byteValue);
			SequentialReader reader = new SequentialByteArrayReader(segmentBytes);
			try
			{
				directory.SetInt(JpegDirectory.TagDataPrecision, reader.GetUInt8());
				directory.SetInt(JpegDirectory.TagImageHeight, reader.GetUInt16());
				directory.SetInt(JpegDirectory.TagImageWidth, reader.GetUInt16());
				short componentCount = reader.GetUInt8();
				directory.SetInt(JpegDirectory.TagNumberOfComponents, componentCount);
				// for each component, there are three bytes of data:
				// 1 - Component ID: 1 = Y, 2 = Cb, 3 = Cr, 4 = I, 5 = Q
				// 2 - Sampling factors: bit 0-3 vertical, 4-7 horizontal
				// 3 - Quantization table number
				for (int i = 0; i < (int)componentCount; i++)
				{
					int componentId = reader.GetUInt8();
					int samplingFactorByte = reader.GetUInt8();
					int quantizationTableNumber = reader.GetUInt8();
					JpegComponent component = new JpegComponent(componentId, samplingFactorByte, quantizationTableNumber);
					directory.SetObject(JpegDirectory.TagComponentData1 + i, component);
				}
			}
			catch (IOException ex)
			{
				directory.AddError(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 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 ProcessMakernote(int makernoteOffset, ICollection<int> processedIfdOffsets, int tiffHeaderOffset, Com.Drew.Metadata.Metadata metadata, RandomAccessReader reader)
		{
			// Determine the camera model and makernote format
			Com.Drew.Metadata.Directory ifd0Directory = metadata.GetDirectory<ExifIFD0Directory>();
			if (ifd0Directory == null)
			{
				return;
			}
			string cameraMake = ifd0Directory.GetString(ExifIFD0Directory.TagMake);
			string firstThreeChars = reader.GetString(makernoteOffset, 3);
			string firstFourChars = reader.GetString(makernoteOffset, 4);
			string firstFiveChars = reader.GetString(makernoteOffset, 5);
			string firstSixChars = reader.GetString(makernoteOffset, 6);
			string firstSevenChars = reader.GetString(makernoteOffset, 7);
			string firstEightChars = reader.GetString(makernoteOffset, 8);
			string firstTwelveChars = reader.GetString(makernoteOffset, 12);
			bool byteOrderBefore = reader.IsMotorolaByteOrder();
			if ("OLYMP".Equals(firstFiveChars) || "EPSON".Equals(firstFiveChars) || "AGFA".Equals(firstFourChars))
			{
				// Olympus Makernote
				// Epson and Agfa use Olympus makernote standard: http://www.ozhiker.com/electronics/pjmt/jpeg_info/
				ProcessIFD(metadata.GetOrCreateDirectory<OlympusMakernoteDirectory>(), processedIfdOffsets, makernoteOffset + 8, tiffHeaderOffset, metadata, reader);
			}
			else
			{
				if (cameraMake != null && Sharpen.Extensions.Trim(cameraMake).ToUpper().StartsWith("NIKON"))
				{
					if ("Nikon".Equals(firstFiveChars))
					{
						switch (reader.GetUInt8(makernoteOffset + 6))
						{
							case 1:
							{
								/* There are two scenarios here:
                 * Type 1:                  **
                 * :0000: 4E 69 6B 6F 6E 00 01 00-05 00 02 00 02 00 06 00 Nikon...........
                 * :0010: 00 00 EC 02 00 00 03 00-03 00 01 00 00 00 06 00 ................
                 * Type 3:                  **
                 * :0000: 4E 69 6B 6F 6E 00 02 00-00 00 4D 4D 00 2A 00 00 Nikon....MM.*...
                 * :0010: 00 08 00 1E 00 01 00 07-00 00 00 04 30 32 30 30 ............0200
                 */
								ProcessIFD(metadata.GetOrCreateDirectory<NikonType1MakernoteDirectory>(), processedIfdOffsets, makernoteOffset + 8, tiffHeaderOffset, metadata, reader);
								break;
							}

							case 2:
							{
								ProcessIFD(metadata.GetOrCreateDirectory<NikonType2MakernoteDirectory>(), processedIfdOffsets, makernoteOffset + 18, makernoteOffset + 10, metadata, reader);
								break;
							}

							default:
							{
								ifd0Directory.AddError("Unsupported Nikon makernote data ignored.");
								break;
							}
						}
					}
					else
					{
						// The IFD begins with the first Makernote byte (no ASCII name).  This occurs with CoolPix 775, E990 and D1 models.
						ProcessIFD(metadata.GetOrCreateDirectory<NikonType2MakernoteDirectory>(), processedIfdOffsets, makernoteOffset, tiffHeaderOffset, metadata, reader);
					}
				}
				else
				{
					if ("SONY CAM".Equals(firstEightChars) || "SONY DSC".Equals(firstEightChars))
					{
						ProcessIFD(metadata.GetOrCreateDirectory<SonyType1MakernoteDirectory>(), processedIfdOffsets, makernoteOffset + 12, tiffHeaderOffset, metadata, reader);
					}
					else
					{
						if ("SEMC MS\u0000\u0000\u0000\u0000\u0000".Equals(firstTwelveChars))
						{
							// force MM for this directory
							reader.SetMotorolaByteOrder(true);
							// skip 12 byte header + 2 for "MM" + 6
							ProcessIFD(metadata.GetOrCreateDirectory<SonyType6MakernoteDirectory>(), processedIfdOffsets, makernoteOffset + 20, tiffHeaderOffset, metadata, reader);
						}
						else
						{
							if ("SIGMA\u0000\u0000\u0000".Equals(firstEightChars) || "FOVEON\u0000\u0000".Equals(firstEightChars))
							{
								ProcessIFD(metadata.GetOrCreateDirectory<SigmaMakernoteDirectory>(), processedIfdOffsets, makernoteOffset + 10, tiffHeaderOffset, metadata, reader);
							}
							else
							{
								if ("KDK".Equals(firstThreeChars))
								{
									reader.SetMotorolaByteOrder(firstSevenChars.Equals("KDK INFO"));
									ProcessKodakMakernote(metadata.GetOrCreateDirectory<KodakMakernoteDirectory>(), makernoteOffset, reader);
								}
								else
								{
									if (Sharpen.Runtime.EqualsIgnoreCase("Canon", cameraMake))
									{
										ProcessIFD(metadata.GetOrCreateDirectory<CanonMakernoteDirectory>(), processedIfdOffsets, makernoteOffset, tiffHeaderOffset, metadata, reader);
									}
									else
									{
										if (cameraMake != null && cameraMake.ToUpper().StartsWith("CASIO"))
										{
											if ("QVC\u0000\u0000\u0000".Equals(firstSixChars))
											{
												ProcessIFD(metadata.GetOrCreateDirectory<CasioType2MakernoteDirectory>(), processedIfdOffsets, makernoteOffset + 6, tiffHeaderOffset, metadata, reader);
											}
											else
											{
												ProcessIFD(metadata.GetOrCreateDirectory<CasioType1MakernoteDirectory>(), processedIfdOffsets, makernoteOffset, tiffHeaderOffset, metadata, reader);
											}
										}
										else
										{
											if ("FUJIFILM".Equals(firstEightChars) || Sharpen.Runtime.EqualsIgnoreCase("Fujifilm", cameraMake))
											{
												// Note that this also applies to certain Leica cameras, such as the Digilux-4.3
												reader.SetMotorolaByteOrder(false);
												// the 4 bytes after "FUJIFILM" in the makernote point to the start of the makernote
												// IFD, though the offset is relative to the start of the makernote, not the TIFF
												// header (like everywhere else)
												int ifdStart = makernoteOffset + reader.GetInt32(makernoteOffset + 8);
												ProcessIFD(metadata.GetOrCreateDirectory<FujifilmMakernoteDirectory>(), processedIfdOffsets, ifdStart, makernoteOffset, metadata, reader);
											}
											else
											{
												if (cameraMake != null && cameraMake.ToUpper().StartsWith("MINOLTA"))
												{
													// Cases seen with the model starting with MINOLTA in capitals seem to have a valid Olympus makernote
													// area that commences immediately.
													ProcessIFD(metadata.GetOrCreateDirectory<OlympusMakernoteDirectory>(), processedIfdOffsets, makernoteOffset, tiffHeaderOffset, metadata, reader);
												}
												else
												{
													if ("KYOCERA".Equals(firstSevenChars))
													{
														// http://www.ozhiker.com/electronics/pjmt/jpeg_info/kyocera_mn.html
														ProcessIFD(metadata.GetOrCreateDirectory<KyoceraMakernoteDirectory>(), processedIfdOffsets, makernoteOffset + 22, tiffHeaderOffset, metadata, reader);
													}
													else
													{
														if ("LEICA".Equals(firstFiveChars))
														{
															reader.SetMotorolaByteOrder(false);
															if ("Leica Camera AG".Equals(cameraMake))
															{
																ProcessIFD(metadata.GetOrCreateDirectory<LeicaMakernoteDirectory>(), processedIfdOffsets, makernoteOffset + 8, tiffHeaderOffset, metadata, reader);
															}
															else
															{
																if ("LEICA".Equals(cameraMake))
																{
																	// Some Leica cameras use Panasonic makernote tags
																	ProcessIFD(metadata.GetOrCreateDirectory<PanasonicMakernoteDirectory>(), processedIfdOffsets, makernoteOffset + 8, tiffHeaderOffset, metadata, reader);
																}
															}
														}
														else
														{
															if ("Panasonic\u0000\u0000\u0000".Equals(reader.GetString(makernoteOffset, 12)))
															{
																// NON-Standard TIFF IFD Data using Panasonic Tags. There is no Next-IFD pointer after the IFD
																// Offsets are relative to the start of the TIFF header at the beginning of the EXIF segment
																// more information here: http://www.ozhiker.com/electronics/pjmt/jpeg_info/panasonic_mn.html
																ProcessIFD(metadata.GetOrCreateDirectory<PanasonicMakernoteDirectory>(), processedIfdOffsets, makernoteOffset + 12, tiffHeaderOffset, metadata, reader);
															}
															else
															{
																if ("AOC\u0000".Equals(firstFourChars))
																{
																	// NON-Standard TIFF IFD Data using Casio Type 2 Tags
																	// IFD has no Next-IFD pointer at end of IFD, and
																	// Offsets are relative to the start of the current IFD tag, not the TIFF header
																	// Observed for:
																	// - Pentax ist D
																	ProcessIFD(metadata.GetOrCreateDirectory<CasioType2MakernoteDirectory>(), processedIfdOffsets, makernoteOffset + 6, makernoteOffset, metadata, reader);
																}
																else
																{
																	if (cameraMake != null && (cameraMake.ToUpper().StartsWith("PENTAX") || cameraMake.ToUpper().StartsWith("ASAHI")))
																	{
																		// NON-Standard TIFF IFD Data using Pentax Tags
																		// IFD has no Next-IFD pointer at end of IFD, and
																		// Offsets are relative to the start of the current IFD tag, not the TIFF header
																		// Observed for:
																		// - PENTAX Optio 330
																		// - PENTAX Optio 430
																		ProcessIFD(metadata.GetOrCreateDirectory<PentaxMakernoteDirectory>(), processedIfdOffsets, makernoteOffset, makernoteOffset, metadata, reader);
																	}
																	else
																	{
																		//        } else if ("KC".equals(firstTwoChars) || "MINOL".equals(firstFiveChars) || "MLY".equals(firstThreeChars) || "+M+M+M+M".equals(firstEightChars)) {
																		//            // This Konica data is not understood.  Header identified in accordance with information at this site:
																		//            // http://www.ozhiker.com/electronics/pjmt/jpeg_info/minolta_mn.html
																		//            // TODO add support for minolta/konica cameras
																		//            exifDirectory.addError("Unsupported Konica/Minolta data ignored.");
																		if ("SANYO\x0\x1\x0".Equals(firstEightChars))
																		{
																			ProcessIFD(metadata.GetOrCreateDirectory<SanyoMakernoteDirectory>(), processedIfdOffsets, makernoteOffset + 8, makernoteOffset, metadata, reader);
																		}
																	}
																}
															}
														}
													}
												}
											}
										}
									}
								}
							}
						}
					}
				}
			}
			// The makernote is not comprehended by this library.
			// If you are reading this and believe a particular camera's image should be processed, get in touch.
			reader.SetMotorolaByteOrder(byteOrderBefore);
		}
		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);
			}
		}
		public virtual void ExtractTiff(RandomAccessReader reader, Com.Drew.Metadata.Metadata metadata)
		{
			ExifIFD0Directory directory = metadata.GetOrCreateDirectory<ExifIFD0Directory>();
			try
			{
				ExtractTiff(reader, metadata, directory, 0);
			}
			catch (IOException e)
			{
				directory.AddError("IO problem: " + e.Message);
			}
		}
示例#10
0
		public virtual void Extract(SequentialReader reader, Com.Drew.Metadata.Metadata metadata)
		{
			GifHeaderDirectory directory = metadata.GetOrCreateDirectory<GifHeaderDirectory>();
			// FILE HEADER
			//
			// 3 - signature: "GIF"
			// 3 - version: either "87a" or "89a"
			//
			// LOGICAL SCREEN DESCRIPTOR
			//
			// 2 - pixel width
			// 2 - pixel height
			// 1 - screen and color map information flags (0 is LSB)
			//       0-2  Size of the global color table
			//       3    Color table sort flag (89a only)
			//       4-6  Color resolution
			//       7    Global color table flag
			// 1 - background color index
			// 1 - pixel aspect ratio
			reader.SetMotorolaByteOrder(false);
			try
			{
				string signature = reader.GetString(3);
				if (!signature.Equals("GIF"))
				{
					directory.AddError("Invalid GIF file signature");
					return;
				}
				string version = reader.GetString(3);
				if (!version.Equals(Gif87aVersionIdentifier) && !version.Equals(Gif89aVersionIdentifier))
				{
					directory.AddError("Unexpected GIF version");
					return;
				}
				directory.SetString(GifHeaderDirectory.TagGifFormatVersion, version);
				directory.SetInt(GifHeaderDirectory.TagImageWidth, reader.GetUInt16());
				directory.SetInt(GifHeaderDirectory.TagImageHeight, reader.GetUInt16());
				short flags = reader.GetUInt8();
				// First three bits = (BPP - 1)
				int colorTableSize = 1 << ((flags & 7) + 1);
				directory.SetInt(GifHeaderDirectory.TagColorTableSize, colorTableSize);
				if (version.Equals(Gif89aVersionIdentifier))
				{
					bool isColorTableSorted = (flags & 8) != 0;
					directory.SetBoolean(GifHeaderDirectory.TagIsColorTableSorted, isColorTableSorted);
				}
				int bitsPerPixel = ((flags & unchecked((int)(0x70))) >> 4) + 1;
				directory.SetInt(GifHeaderDirectory.TagBitsPerPixel, bitsPerPixel);
				bool hasGlobalColorTable = (flags & unchecked((int)(0xf))) != 0;
				directory.SetBoolean(GifHeaderDirectory.TagHasGlobalColorTable, hasGlobalColorTable);
				directory.SetInt(GifHeaderDirectory.TagTransparentColorIndex, reader.GetUInt8());
				int aspectRatioByte = reader.GetUInt8();
				if (aspectRatioByte != 0)
				{
					float pixelAspectRatio = (float)((aspectRatioByte + 15d) / 64d);
					directory.SetFloat(GifHeaderDirectory.TagPixelAspectRatio, pixelAspectRatio);
				}
			}
			catch (IOException)
			{
				directory.AddError("Unable to read BMP header");
			}
		}
示例#11
0
		public virtual void Extract(SequentialReader reader, Com.Drew.Metadata.Metadata metadata)
		{
			BmpHeaderDirectory directory = metadata.GetOrCreateDirectory<BmpHeaderDirectory>();
			// FILE HEADER
			//
			// 2 - magic number (0x42 0x4D = "BM")
			// 4 - size of BMP file in bytes
			// 2 - reserved
			// 2 - reserved
			// 4 - the offset of the pixel array
			//
			// BITMAP INFORMATION HEADER
			//
			// The first four bytes of the header give the size, which is a discriminator of the actual header format.
			// See this for more information http://en.wikipedia.org/wiki/BMP_file_format
			//
			// BITMAPINFOHEADER (size = 40)
			//
			// 4 - size of header
			// 4 - pixel width (signed)
			// 4 - pixel height (signed)
			// 2 - number of colour planes (must be set to 1)
			// 2 - number of bits per pixel
			// 4 - compression being used (needs decoding)
			// 4 - pixel data length (not total file size, just pixel array)
			// 4 - horizontal resolution, pixels/meter (signed)
			// 4 - vertical resolution, pixels/meter (signed)
			// 4 - number of colours in the palette (0 means no palette)
			// 4 - number of important colours (generally ignored)
			//
			// BITMAPCOREHEADER (size = 12)
			//
			// 4 - size of header
			// 2 - pixel width
			// 2 - pixel height
			// 2 - number of colour planes (must be set to 1)
			// 2 - number of bits per pixel
			//
			// COMPRESSION VALUES
			//
			// 0 = None
			// 1 = RLE 8-bit/pixel
			// 2 = RLE 4-bit/pixel
			// 3 = Bit field (or Huffman 1D if BITMAPCOREHEADER2 (size 64))
			// 4 = JPEG (or RLE-24 if BITMAPCOREHEADER2 (size 64))
			// 5 = PNG
			// 6 = Bit field
			reader.SetMotorolaByteOrder(false);
			try
			{
				int magicNumber = reader.GetUInt16();
				if (magicNumber != unchecked((int)(0x4D42)))
				{
					directory.AddError("Invalid BMP magic number");
					return;
				}
				// skip past the rest of the file header
				reader.Skip(4 + 2 + 2 + 4);
				int headerSize = reader.GetInt32();
				directory.SetInt(BmpHeaderDirectory.TagHeaderSize, headerSize);
				// We expect the header size to be either 40 (BITMAPINFOHEADER) or 12 (BITMAPCOREHEADER)
				if (headerSize == 40)
				{
					// BITMAPINFOHEADER
					directory.SetInt(BmpHeaderDirectory.TagImageWidth, reader.GetInt32());
					directory.SetInt(BmpHeaderDirectory.TagImageHeight, reader.GetInt32());
					directory.SetInt(BmpHeaderDirectory.TagColourPlanes, reader.GetInt16());
					directory.SetInt(BmpHeaderDirectory.TagBitsPerPixel, reader.GetInt16());
					directory.SetInt(BmpHeaderDirectory.TagCompression, reader.GetInt32());
					// skip the pixel data length
					reader.Skip(4);
					directory.SetInt(BmpHeaderDirectory.TagXPixelsPerMeter, reader.GetInt32());
					directory.SetInt(BmpHeaderDirectory.TagYPixelsPerMeter, reader.GetInt32());
					directory.SetInt(BmpHeaderDirectory.TagPaletteColourCount, reader.GetInt32());
					directory.SetInt(BmpHeaderDirectory.TagImportantColourCount, reader.GetInt32());
				}
				else
				{
					if (headerSize == 12)
					{
						directory.SetInt(BmpHeaderDirectory.TagImageWidth, reader.GetInt16());
						directory.SetInt(BmpHeaderDirectory.TagImageHeight, reader.GetInt16());
						directory.SetInt(BmpHeaderDirectory.TagColourPlanes, reader.GetInt16());
						directory.SetInt(BmpHeaderDirectory.TagBitsPerPixel, reader.GetInt16());
					}
					else
					{
						directory.AddError("Unexpected DIB header size: " + headerSize);
					}
				}
			}
			catch (IOException)
			{
				directory.AddError("Unable to read BMP header");
			}
		}
		public virtual void Extract(sbyte[] segmentBytes, Com.Drew.Metadata.Metadata metadata, JpegSegmentType segmentType)
		{
			JpegCommentDirectory directory = metadata.GetOrCreateDirectory<JpegCommentDirectory>();
			// The entire contents of the directory are the comment
			directory.SetString(JpegCommentDirectory.TagComment, Sharpen.Runtime.GetStringForBytes(segmentBytes));
		}
示例#13
0
		/// <summary>
		/// Performs the IPTC data extraction, adding found values to the specified instance of
		/// <see cref="Com.Drew.Metadata.Metadata"/>
		/// .
		/// </summary>
		public virtual void Extract(SequentialReader reader, Com.Drew.Metadata.Metadata metadata, long length)
		{
			IptcDirectory directory = metadata.GetOrCreateDirectory<IptcDirectory>();
			int offset = 0;
			// for each tag
			while (offset < length)
			{
				// identifies start of a tag
				short startByte;
				try
				{
					startByte = reader.GetUInt8();
					offset++;
				}
				catch (IOException)
				{
					directory.AddError("Unable to read starting byte of IPTC tag");
					return;
				}
				if (startByte != unchecked((int)(0x1c)))
				{
					directory.AddError("Invalid start to IPTC tag");
					return;
				}
				// we need at least five bytes left to read a tag
				if (offset + 5 >= length)
				{
					directory.AddError("Too few bytes remain for a valid IPTC tag");
					return;
				}
				int directoryType;
				int tagType;
				int tagByteCount;
				try
				{
					directoryType = reader.GetUInt8();
					tagType = reader.GetUInt8();
					tagByteCount = reader.GetUInt16();
					offset += 4;
				}
				catch (IOException)
				{
					directory.AddError("IPTC data segment ended mid-way through tag descriptor");
					return;
				}
				if (offset + tagByteCount > length)
				{
					directory.AddError("Data for tag extends beyond end of IPTC segment");
					return;
				}
				try
				{
					ProcessTag(reader, directory, directoryType, tagType, tagByteCount);
				}
				catch (IOException)
				{
					directory.AddError("Error processing IPTC tag");
					return;
				}
				offset += tagByteCount;
			}
		}
示例#14
0
		/// <summary>Version specifically for dealing with XMP found in JPEG segments.</summary>
		/// <remarks>
		/// Version specifically for dealing with XMP found in JPEG segments. This form of XMP has a peculiar preamble, which
		/// must be removed before parsing the XML.
		/// </remarks>
		/// <param name="segmentBytes">The byte array from which the metadata should be extracted.</param>
		/// <param name="metadata">
		/// The
		/// <see cref="Com.Drew.Metadata.Metadata"/>
		/// object into which extracted values should be merged.
		/// </param>
		/// <param name="segmentType">
		/// The
		/// <see cref="Com.Drew.Imaging.Jpeg.JpegSegmentType"/>
		/// being read.
		/// </param>
		public virtual void Extract(sbyte[] segmentBytes, Com.Drew.Metadata.Metadata metadata, JpegSegmentType segmentType)
		{
			XmpDirectory directory = metadata.GetOrCreateDirectory<XmpDirectory>();
			// XMP in a JPEG file has a 29 byte preamble which is not valid XML.
			int preambleLength = 29;
			// check for the header length
			if (segmentBytes.Length <= preambleLength + 1)
			{
				directory.AddError(Sharpen.Extensions.StringFormat("Xmp data segment must contain at least %d bytes", preambleLength + 1));
				return;
			}
			ByteArrayReader reader = new ByteArrayReader(segmentBytes);
			string preamble = Sharpen.Runtime.GetStringForBytes(segmentBytes, 0, preambleLength);
			if (!"http://ns.adobe.com/xap/1.0/\x0".Equals(preamble))
			{
				directory.AddError("XMP data segment doesn't begin with 'http://ns.adobe.com/xap/1.0/'");
				return;
			}
			sbyte[] xmlBytes = new sbyte[segmentBytes.Length - preambleLength];
			System.Array.Copy(segmentBytes, 29, xmlBytes, 0, xmlBytes.Length);
			Extract(xmlBytes, metadata);
		}
示例#15
0
		/// <summary>
		/// Performs the XMP data extraction, adding found values to the specified instance of
		/// <see cref="Com.Drew.Metadata.Metadata"/>
		/// .
		/// <p/>
		/// The extraction is done with Adobe's XMPCore library.
		/// </summary>
		public virtual void Extract(string xmpString, Com.Drew.Metadata.Metadata metadata)
		{
			XmpDirectory directory = metadata.GetOrCreateDirectory<XmpDirectory>();
			try
			{
				XMPMeta xmpMeta = XMPMetaFactory.ParseFromString(xmpString);
				ProcessXmpTags(directory, xmpMeta);
			}
			catch (XMPException e)
			{
				directory.AddError("Error processing XMP data: " + e.Message);
			}
		}