/// <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); }