public override void Completed(RandomAccessReader reader, int tiffHeaderOffset) { if (_storeThumbnailBytes) { // after the extraction process, if we have the correct tags, we may be able to store thumbnail information ExifThumbnailDirectory thumbnailDirectory = _metadata.GetDirectory <ExifThumbnailDirectory>(); if (thumbnailDirectory != null && thumbnailDirectory.ContainsTag(ExifThumbnailDirectory.TagThumbnailCompression)) { int?offset = thumbnailDirectory.GetInteger(ExifThumbnailDirectory.TagThumbnailOffset); int?length = thumbnailDirectory.GetInteger(ExifThumbnailDirectory.TagThumbnailLength); if (offset != null && length != null) { try { sbyte[] thumbnailData = reader.GetBytes(tiffHeaderOffset + offset.Value, length.Value); thumbnailDirectory.SetThumbnailData(thumbnailData); } catch (IOException ex) { thumbnailDirectory.AddError("Invalid thumbnail data specification: " + ex.Message); } } } } }
public virtual void TestThumbnailCompression() { ExifThumbnailDirectory directory = ExifReaderTest.ProcessBytes <ExifThumbnailDirectory>("Tests/Data/manuallyAddedThumbnail.jpg.app1"); // 6 means JPEG compression Sharpen.Tests.AreEqual(6, directory.GetInt(ExifThumbnailDirectory.TagThumbnailCompression)); }
public virtual void SetUp() { Com.Drew.Metadata.Metadata metadata = ExifReaderTest.ProcessBytes("Tests/Data/nikonMakernoteType1.jpg.app1"); _nikonDirectory = metadata.GetFirstDirectoryOfType<NikonType1MakernoteDirectory>(); _exifSubIFDDirectory = metadata.GetFirstDirectoryOfType<ExifSubIFDDirectory>(); _exifIFD0Directory = metadata.GetFirstDirectoryOfType<ExifIFD0Directory>(); _thumbDirectory = metadata.GetFirstDirectoryOfType<ExifThumbnailDirectory>(); }
public virtual void TestThumbnailData() { ExifThumbnailDirectory directory = ExifReaderTest.ProcessBytes <ExifThumbnailDirectory>("Tests/Data/manuallyAddedThumbnail.jpg.app1"); sbyte[] thumbnailData = directory.GetThumbnailData(); NUnit.Framework.Assert.IsNotNull(thumbnailData); Sharpen.Tests.AreEqual(2970, thumbnailData.Length); }
public virtual void SetUp() { Com.Drew.Metadata.Metadata metadata = ExifReaderTest.ProcessBytes("Tests/Data/nikonMakernoteType1.jpg.app1"); _nikonDirectory = metadata.GetDirectory <NikonType1MakernoteDirectory>(); _exifSubIFDDirectory = metadata.GetDirectory <ExifSubIFDDirectory>(); _exifIFD0Directory = metadata.GetDirectory <ExifIFD0Directory>(); _thumbDirectory = metadata.GetDirectory <ExifThumbnailDirectory>(); }
public virtual void TestThumbnailYResolution() { ExifThumbnailDirectory directory = ExifReaderTest.ProcessBytes <ExifThumbnailDirectory>("Tests/Data/manuallyAddedThumbnail.jpg.app1"); Rational rational = directory.GetRational(ExifThumbnailDirectory.TagYResolution); NUnit.Framework.Assert.IsNotNull(rational); Sharpen.Tests.AreEqual(72, rational.GetNumerator()); Sharpen.Tests.AreEqual(1, rational.GetDenominator()); }
public virtual void SetUp() { Com.Drew.Metadata.Metadata metadata = ExifReaderTest.ProcessBytes("Tests/Data/nikonMakernoteType2b.jpg.app1"); _nikonDirectory = metadata.GetFirstDirectoryOfType <NikonType2MakernoteDirectory>(); _exifIFD0Directory = metadata.GetFirstDirectoryOfType <ExifIFD0Directory>(); _exifSubIFDDirectory = metadata.GetFirstDirectoryOfType <ExifSubIFDDirectory>(); _thumbDirectory = metadata.GetFirstDirectoryOfType <ExifThumbnailDirectory>(); NUnit.Framework.Assert.IsNotNull(_nikonDirectory); NUnit.Framework.Assert.IsNotNull(_exifSubIFDDirectory); }
public virtual void SetUp() { Com.Drew.Metadata.Metadata metadata = ExifReaderTest.ProcessBytes("Tests/Data/nikonMakernoteType2b.jpg.app1"); _nikonDirectory = metadata.GetDirectory<NikonType2MakernoteDirectory>(); _exifIFD0Directory = metadata.GetDirectory<ExifIFD0Directory>(); _exifSubIFDDirectory = metadata.GetDirectory<ExifSubIFDDirectory>(); _thumbDirectory = metadata.GetDirectory<ExifThumbnailDirectory>(); NUnit.Framework.Assert.IsNotNull(_nikonDirectory); NUnit.Framework.Assert.IsNotNull(_exifSubIFDDirectory); }
public virtual void TestGetYCbCrSubsamplingDescription() { ExifThumbnailDirectory directory = new ExifThumbnailDirectory(); directory.SetIntArray(ExifThumbnailDirectory.TagYcbcrSubsampling, new int[] { 2, 1 }); ExifThumbnailDescriptor descriptor = new ExifThumbnailDescriptor(directory); Sharpen.Tests.AreEqual("YCbCr4:2:2", descriptor.GetDescription(ExifThumbnailDirectory.TagYcbcrSubsampling)); Sharpen.Tests.AreEqual("YCbCr4:2:2", descriptor.GetYCbCrSubsamplingDescription()); directory.SetIntArray(ExifThumbnailDirectory.TagYcbcrSubsampling, new int[] { 2, 2 }); Sharpen.Tests.AreEqual("YCbCr4:2:0", descriptor.GetDescription(ExifThumbnailDirectory.TagYcbcrSubsampling)); Sharpen.Tests.AreEqual("YCbCr4:2:0", descriptor.GetYCbCrSubsamplingDescription()); }
public virtual void TestGetDirectoryName() { Com.Drew.Metadata.Directory subIFDDirectory = new ExifSubIFDDirectory(); Com.Drew.Metadata.Directory ifd0Directory = new ExifIFD0Directory(); Com.Drew.Metadata.Directory thumbDirectory = new ExifThumbnailDirectory(); Sharpen.Tests.IsFalse(subIFDDirectory.HasErrors()); Sharpen.Tests.IsFalse(ifd0Directory.HasErrors()); Sharpen.Tests.IsFalse(thumbDirectory.HasErrors()); Sharpen.Tests.AreEqual("Exif IFD0", ifd0Directory.GetName()); Sharpen.Tests.AreEqual("Exif SubIFD", subIFDDirectory.GetName()); Sharpen.Tests.AreEqual("Exif Thumbnail", thumbDirectory.GetName()); }
public virtual void TestGetDirectoryName() { Com.Drew.Metadata.Directory subIFDDirectory = new ExifSubIFDDirectory(); Com.Drew.Metadata.Directory ifd0Directory = new ExifIFD0Directory(); Com.Drew.Metadata.Directory thumbDirectory = new ExifThumbnailDirectory(); Sharpen.Tests.IsFalse(subIFDDirectory.HasErrors()); Sharpen.Tests.IsFalse(ifd0Directory.HasErrors()); Sharpen.Tests.IsFalse(thumbDirectory.HasErrors()); Sharpen.Tests.AreEqual("Exif IFD0", ifd0Directory.GetName()); Sharpen.Tests.AreEqual("Exif SubIFD", subIFDDirectory.GetName()); Sharpen.Tests.AreEqual("Exif Thumbnail", thumbDirectory.GetName()); }
public virtual void TestResolution() { Com.Drew.Metadata.Metadata metadata = ExifReaderTest.ProcessBytes("Tests/Data/withUncompressedRGBThumbnail.jpg.app1"); ExifThumbnailDirectory thumbnailDirectory = metadata.GetDirectory <ExifThumbnailDirectory>(); NUnit.Framework.Assert.IsNotNull(thumbnailDirectory); Sharpen.Tests.AreEqual(72, thumbnailDirectory.GetInt(ExifThumbnailDirectory.TagXResolution)); ExifIFD0Directory exifIFD0Directory = metadata.GetDirectory <ExifIFD0Directory>(); NUnit.Framework.Assert.IsNotNull(exifIFD0Directory); Sharpen.Tests.AreEqual(216, exifIFD0Directory.GetInt(ExifIFD0Directory.TagXResolution)); }
public virtual void TestGetYCbCrSubsamplingDescription() { ExifThumbnailDirectory directory = new ExifThumbnailDirectory(); directory.SetIntArray(ExifThumbnailDirectory.TagYcbcrSubsampling, new int[] { 2, 1 }); ExifThumbnailDescriptor descriptor = new ExifThumbnailDescriptor(directory); Sharpen.Tests.AreEqual("YCbCr4:2:2", descriptor.GetDescription(ExifThumbnailDirectory.TagYcbcrSubsampling)); Sharpen.Tests.AreEqual("YCbCr4:2:2", descriptor.GetYCbCrSubsamplingDescription()); directory.SetIntArray(ExifThumbnailDirectory.TagYcbcrSubsampling, new int[] { 2, 2 }); Sharpen.Tests.AreEqual("YCbCr4:2:0", descriptor.GetDescription(ExifThumbnailDirectory.TagYcbcrSubsampling)); Sharpen.Tests.AreEqual("YCbCr4:2:0", descriptor.GetYCbCrSubsamplingDescription()); }
public virtual void TestDifferenceImageAndThumbnailOrientations() { // This metadata contains different orientations for the thumbnail and the main image. // These values used to be merged into a single directory, causing errors. // This unit test demonstrates correct behaviour. Com.Drew.Metadata.Metadata metadata = ProcessBytes("Tests/Data/repeatedOrientationTagWithDifferentValues.jpg.app1"); ExifIFD0Directory ifd0Directory = metadata.GetFirstDirectoryOfType <ExifIFD0Directory>(); ExifThumbnailDirectory thumbnailDirectory = metadata.GetFirstDirectoryOfType <ExifThumbnailDirectory>(); NUnit.Framework.Assert.IsNotNull(ifd0Directory); NUnit.Framework.Assert.IsNotNull(thumbnailDirectory); Sharpen.Tests.AreEqual(1, ifd0Directory.GetInt(ExifIFD0Directory.TagOrientation)); Sharpen.Tests.AreEqual(8, thumbnailDirectory.GetInt(ExifThumbnailDirectory.TagOrientation)); }
public virtual void TestGetThumbnailData() { ExifThumbnailDirectory directory = ExifReaderTest.ProcessBytes <ExifThumbnailDirectory>("Tests/Data/withExif.jpg.app1"); sbyte[] thumbData = directory.GetThumbnailData(); NUnit.Framework.Assert.IsNotNull(thumbData); try { // attempt to read the thumbnail -- it should be a legal Jpeg file JpegSegmentReader.ReadSegments(new SequentialByteArrayReader(thumbData), null); } catch (JpegProcessingException) { NUnit.Framework.Assert.Fail("Unable to construct JpegSegmentReader from thumbnail data"); } }
public virtual void TestWriteThumbnail() { ExifThumbnailDirectory directory = ExifReaderTest.ProcessBytes <ExifThumbnailDirectory>("Tests/Data/manuallyAddedThumbnail.jpg.app1"); Sharpen.Tests.IsTrue(directory.HasThumbnailData()); FilePath thumbnailFile = FilePath.CreateTempFile("thumbnail", ".jpg"); try { directory.WriteThumbnail(thumbnailFile.GetAbsolutePath()); FilePath file = new FilePath(thumbnailFile.GetAbsolutePath()); Sharpen.Tests.AreEqual(2970, file.Length()); Sharpen.Tests.IsTrue(file.Exists()); } finally { if (!thumbnailFile.Delete()) { NUnit.Framework.Assert.Fail("Unable to delete temp thumbnail file."); } } }
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); } } } }
public virtual void TestThumbnailLength() { ExifThumbnailDirectory directory = ExifReaderTest.ProcessBytes <ExifThumbnailDirectory>("Tests/Data/manuallyAddedThumbnail.jpg.app1"); Sharpen.Tests.AreEqual(2970, directory.GetInt(ExifThumbnailDirectory.TagThumbnailLength)); }
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); } }