private static List <ParserIFDEntry> ParseDirectories(EndianBinaryReader reader, uint firstIFDOffset) { List <ParserIFDEntry> items = new List <ParserIFDEntry>(); bool foundExif = false; bool foundGps = false; bool foundInterop = false; Queue <MetadataOffset> ifdOffsets = new Queue <MetadataOffset>(); ifdOffsets.Enqueue(new MetadataOffset(MetadataSection.Image, firstIFDOffset)); while (ifdOffsets.Count > 0) { MetadataOffset metadataOffset = ifdOffsets.Dequeue(); MetadataSection section = metadataOffset.Section; uint offset = metadataOffset.Offset; if (offset >= reader.Length) { continue; } reader.Position = offset; ushort count = reader.ReadUInt16(); if (count == 0) { continue; } items.Capacity += count; for (int i = 0; i < count; i++) { ParserIFDEntry entry = new ParserIFDEntry(reader, section); switch (entry.Tag) { case TiffConstants.Tags.ExifIFD: if (!foundExif) { foundExif = true; ifdOffsets.Enqueue(new MetadataOffset(MetadataSection.Exif, entry.Offset)); } break; case TiffConstants.Tags.GpsIFD: if (!foundGps) { foundGps = true; ifdOffsets.Enqueue(new MetadataOffset(MetadataSection.Gps, entry.Offset)); } break; case TiffConstants.Tags.InteropIFD: if (!foundInterop) { foundInterop = true; ifdOffsets.Enqueue(new MetadataOffset(MetadataSection.Interop, entry.Offset)); } break; case TiffConstants.Tags.StripOffsets: case TiffConstants.Tags.RowsPerStrip: case TiffConstants.Tags.StripByteCounts: case TiffConstants.Tags.SubIFDs: case TiffConstants.Tags.ThumbnailOffset: case TiffConstants.Tags.ThumbnailLength: // Skip the thumbnail and/or preview images. // The StripOffsets and StripByteCounts tags are used to store a preview image in some formats. // The SubIFDs tag is used to store thumbnails in TIFF and for storing other data in some camera formats. // // Note that some cameras will also store a thumbnail as part of their private data in the EXIF MakerNote tag. // The EXIF MakerNote tag is treated as an opaque blob, so those thumbnails will be preserved. break; default: items.Add(entry); break; } System.Diagnostics.Debug.WriteLine(entry.ToString()); } } return(items); }
#pragma warning restore IDE0032 // Use auto property public ParserIFDEntry(EndianBinaryReader reader, MetadataSection section) { entry = new IFDEntry(reader); offsetIsBigEndian = reader.Endianess == Endianess.Big; Section = section; }
private static ICollection <MetadataEntry> ConvertIFDEntriesToMetadataEntries(EndianBinaryReader reader, List <ParserIFDEntry> entries) { List <MetadataEntry> metadataEntries = new List <MetadataEntry>(entries.Count); bool swapNumberByteOrder = reader.Endianess == Endianess.Big; for (int i = 0; i < entries.Count; i++) { ParserIFDEntry entry = entries[i]; byte[] propertyData; if (entry.OffsetFieldContainsValue) { propertyData = entry.GetValueBytesFromOffset(); if (propertyData == null) { continue; } } else { long bytesToRead = entry.Count * TagDataTypeUtil.GetSizeInBytes(entry.Type); // Skip any tags that are empty or larger than 2 GB. if (bytesToRead == 0 || bytesToRead > int.MaxValue) { continue; } uint offset = entry.Offset; if ((offset + bytesToRead) > reader.Length) { continue; } reader.Position = offset; propertyData = reader.ReadBytes((int)bytesToRead); if (swapNumberByteOrder) { // Paint.NET converts all multi-byte numbers to little-endian. switch (entry.Type) { case TagDataType.Short: case TagDataType.SShort: propertyData = SwapShortArrayToLittleEndian(propertyData, entry.Count); break; case TagDataType.Long: case TagDataType.SLong: case TagDataType.Float: propertyData = SwapLongArrayToLittleEndian(propertyData, entry.Count); break; case TagDataType.Rational: case TagDataType.SRational: propertyData = SwapRationalArrayToLittleEndian(propertyData, entry.Count); break; case TagDataType.Double: propertyData = SwapDoubleArrayToLittleEndian(propertyData, entry.Count); break; case TagDataType.Byte: case TagDataType.Ascii: case TagDataType.Undefined: case TagDataType.SByte: default: break; } } } metadataEntries.Add(new MetadataEntry(entry.Section, entry.Tag, entry.Type, propertyData)); } return(metadataEntries); }