/// <summary> /// Extracts and caches the metadata for an image. /// </summary> /// <param name="pictureFileInfo">The FileInfo to the image.</param> /// <returns>The MetaData for the image.</returns> internal static Metadata GetImageData(FileInfo pictureFileInfo) { try { string cacheKey = "data(" + pictureFileInfo.FullName + ")"; Cache cache = HttpContext.Current.Cache; object cached = cache[cacheKey]; if (cached == null) { Metadata data = new Metadata(); ExifReader exif = new ExifReader(pictureFileInfo); exif.Extract(data); IptcReader iptc = new IptcReader(pictureFileInfo); iptc.Extract(data); JpegReader jpeg = new JpegReader(pictureFileInfo); jpeg.Extract(data); cache.Insert(cacheKey, data, new CacheDependency(pictureFileInfo.FullName)); return(data); } return((Metadata)cached); } catch { return(new Metadata()); } }
public static IptcDirectory ProcessBytes([NotNull] string filePath) { var bytes = File.ReadAllBytes(filePath); var directory = new IptcReader().Extract(new SequentialByteArrayReader(bytes), bytes.Length); Assert.NotNull(directory); return(directory); }
private static IptcDirectory ProcessBytes([NotNull] string filePath) { var bytes = TestDataUtil.GetBytes(filePath); var directory = new IptcReader().Extract(new SequentialByteArrayReader(bytes), bytes.Length); Assert.NotNull(directory); return(directory); }
public DirectoryList Extract(SequentialReader reader, int length) { var directory = new PhotoshopDirectory(); var directories = new List <Directory> { directory }; // Data contains a sequence of Image Resource Blocks (IRBs): // // 4 bytes - Signature; mostly "8BIM" but "PHUT", "AgHg" and "DCSR" are also found // 2 bytes - Resource identifier // String - Pascal string, padded to make length even // 4 bytes - Size of resource data which follows // Data - The resource data, padded to make size even // // http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577409_pgfId-1037504 var pos = 0; int clippingPathCount = 0; while (pos < length) { try { // 4 bytes for the signature ("8BIM", "PHUT", etc.) var signature = reader.GetString(4, Encoding.UTF8); pos += 4; // 2 bytes for the resource identifier (tag type). var tagType = reader.GetUInt16(); pos += 2; // A variable number of bytes holding a pascal string (two leading bytes for length). var descriptionLength = reader.GetByte(); pos += 1; // Some basic bounds checking if (descriptionLength + pos > length) { throw new ImageProcessingException("Invalid string length"); } // Get name (important for paths) var description = new StringBuilder(); // Loop through each byte and append to string while (descriptionLength > 0) { description.Append((char)reader.GetByte()); pos++; descriptionLength--; } // The number of bytes is padded with a trailing zero, if needed, to make the size even. if (pos % 2 != 0) { reader.Skip(1); pos++; } // 4 bytes for the size of the resource data that follows. var byteCount = reader.GetInt32(); pos += 4; // The resource data. var tagBytes = reader.GetBytes(byteCount); pos += byteCount; // The number of bytes is padded with a trailing zero, if needed, to make the size even. if (pos % 2 != 0) { reader.Skip(1); pos++; } // Skip any unsupported IRBs if (signature != "8BIM") { continue; } switch (tagType) { case PhotoshopDirectory.TagIptc: var iptcDirectory = new IptcReader().Extract(new SequentialByteArrayReader(tagBytes), tagBytes.Length); iptcDirectory.Parent = directory; directories.Add(iptcDirectory); break; case PhotoshopDirectory.TagIccProfileBytes: var iccDirectory = new IccReader().Extract(new ByteArrayReader(tagBytes)); iccDirectory.Parent = directory; directories.Add(iccDirectory); break; case PhotoshopDirectory.TagExifData1: case PhotoshopDirectory.TagExifData3: var exifDirectories = new ExifReader().Extract(new ByteArrayReader(tagBytes)); foreach (var exifDirectory in exifDirectories.Where(d => d.Parent == null)) { exifDirectory.Parent = directory; } directories.AddRange(exifDirectories); break; case PhotoshopDirectory.TagXmpData: var xmpDirectory = new XmpReader().Extract(tagBytes); xmpDirectory.Parent = directory; directories.Add(xmpDirectory); break; default: if (tagType >= PhotoshopDirectory.TagClippingPathBlockStart && tagType <= PhotoshopDirectory.TagClippingPathBlockEnd) { clippingPathCount++; Array.Resize(ref tagBytes, tagBytes.Length + description.Length + 1); // Append description(name) to end of byte array with 1 byte before the description representing the length for (int i = tagBytes.Length - description.Length - 1; i < tagBytes.Length; i++) { if (i % (tagBytes.Length - description.Length - 1 + description.Length) == 0) { tagBytes[i] = (byte)description.Length; } else { tagBytes[i] = (byte)description[i - (tagBytes.Length - description.Length - 1)]; } } PhotoshopDirectory.TagNameMap[PhotoshopDirectory.TagClippingPathBlockStart + clippingPathCount - 1] = "Path Info " + clippingPathCount; directory.Set(PhotoshopDirectory.TagClippingPathBlockStart + clippingPathCount - 1, tagBytes); } else { directory.Set(tagType, tagBytes); } break; } if (tagType >= 0x0fa0 && tagType <= 0x1387) { PhotoshopDirectory.TagNameMap[tagType] = $"Plug-in {tagType - 0x0fa0 + 1} Data"; } } catch (Exception ex) { directory.AddError(ex.Message); break; } } return(directories); }
public DirectoryList Extract(SequentialReader reader, int length) { var directory = new PhotoshopDirectory(); var directories = new List <Directory> { directory }; // Data contains a sequence of Image Resource Blocks (IRBs): // // 4 bytes - Signature; mostly "8BIM" but "PHUT", "AgHg" and "DCSR" are also found // 2 bytes - Resource identifier // String - Pascal string, padded to make length even // 4 bytes - Size of resource data which follows // Data - The resource data, padded to make size even // // http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577409_pgfId-1037504 var pos = 0; while (pos < length) { try { // 4 bytes for the signature ("8BIM", "PHUT", etc.) var signature = reader.GetString(4, Encoding.UTF8); pos += 4; // 2 bytes for the resource identifier (tag type). var tagType = reader.GetUInt16(); pos += 2; // A variable number of bytes holding a pascal string (two leading bytes for length). var descriptionLength = reader.GetByte(); pos += 1; // Some basic bounds checking if (descriptionLength + pos > length) { throw new ImageProcessingException("Invalid string length"); } // We don't use the string value here reader.Skip(descriptionLength); pos += descriptionLength; // The number of bytes is padded with a trailing zero, if needed, to make the size even. if (pos % 2 != 0) { reader.Skip(1); pos++; } // 4 bytes for the size of the resource data that follows. var byteCount = reader.GetInt32(); pos += 4; // The resource data. var tagBytes = reader.GetBytes(byteCount); pos += byteCount; // The number of bytes is padded with a trailing zero, if needed, to make the size even. if (pos % 2 != 0) { reader.Skip(1); pos++; } // Skip any unsupported IRBs if (signature != "8BIM") { continue; } switch (tagType) { case PhotoshopDirectory.TagIptc: var iptcDirectory = new IptcReader().Extract(new SequentialByteArrayReader(tagBytes), tagBytes.Length); iptcDirectory.Parent = directory; directories.Add(iptcDirectory); break; case PhotoshopDirectory.TagIccProfileBytes: var iccDirectory = new IccReader().Extract(new ByteArrayReader(tagBytes)); iccDirectory.Parent = directory; directories.Add(iccDirectory); break; case PhotoshopDirectory.TagExifData1: case PhotoshopDirectory.TagExifData3: var exifDirectories = new ExifReader().Extract(new ByteArrayReader(tagBytes)); foreach (var exifDirectory in exifDirectories.Where(d => d.Parent == null)) { exifDirectory.Parent = directory; } directories.AddRange(exifDirectories); break; case PhotoshopDirectory.TagXmpData: var xmpDirectory = new XmpReader().Extract(tagBytes); xmpDirectory.Parent = directory; directories.Add(xmpDirectory); break; default: directory.Set(tagType, tagBytes); break; } if (tagType >= 0x0fa0 && tagType <= 0x1387) { PhotoshopDirectory.TagNameMap[tagType] = $"Plug-in {tagType - 0x0fa0 + 1} Data"; } } catch (Exception ex) { directory.AddError(ex.Message); break; } } return(directories); }
public override bool CustomProcessTag(int tagOffset, ICollection <int> processedIfdOffsets, IndexedReader reader, int tagId, int byteCount) { // Some 0x0000 tags have a 0 byteCount. Determine whether it's bad. if (tagId == 0) { if (CurrentDirectory.ContainsTag(tagId)) { // Let it go through for now. Some directories handle it, some don't. return(false); } // Skip over 0x0000 tags that don't have any associated bytes. No idea what it contains in this case, if anything. if (byteCount == 0) { return(true); } } // Custom processing for the Makernote tag if (tagId == ExifDirectoryBase.TagMakernote && CurrentDirectory is ExifSubIfdDirectory) { return(ProcessMakernote(tagOffset, processedIfdOffsets, reader)); } // Custom processing for embedded IPTC data if (tagId == ExifDirectoryBase.TagIptcNaa && CurrentDirectory is ExifIfd0Directory) { // NOTE Adobe sets type 4 for IPTC instead of 7 if (reader.GetSByte(tagOffset) == 0x1c) { var iptcBytes = reader.GetBytes(tagOffset, byteCount); var iptcDirectory = new IptcReader().Extract(new SequentialByteArrayReader(iptcBytes), iptcBytes.Length); iptcDirectory.Parent = CurrentDirectory; Directories.Add(iptcDirectory); return(true); } return(false); } // Custom processing for embedded XMP data if (tagId == ExifDirectoryBase.TagApplicationNotes && CurrentDirectory is ExifIfd0Directory) { var xmpDirectory = new XmpReader().Extract(reader.GetNullTerminatedBytes(tagOffset, byteCount)); xmpDirectory.Parent = CurrentDirectory; Directories.Add(xmpDirectory); return(true); } if (HandlePrintIM(CurrentDirectory, tagId)) { var dirPrintIm = new PrintIMDirectory(); dirPrintIm.Parent = CurrentDirectory; Directories.Add(dirPrintIm); ProcessPrintIM(dirPrintIm, tagOffset, reader, byteCount); return(true); } // Note: these also appear in TryEnterSubIfd because some are IFD pointers while others begin immediately // for the same directories if (CurrentDirectory is OlympusMakernoteDirectory) { switch (tagId) { case OlympusMakernoteDirectory.TagEquipment: PushDirectory(new OlympusEquipmentMakernoteDirectory()); TiffReader.ProcessIfd(this, reader, processedIfdOffsets, tagOffset); return(true); case OlympusMakernoteDirectory.TagCameraSettings: PushDirectory(new OlympusCameraSettingsMakernoteDirectory()); TiffReader.ProcessIfd(this, reader, processedIfdOffsets, tagOffset); return(true); case OlympusMakernoteDirectory.TagRawDevelopment: PushDirectory(new OlympusRawDevelopmentMakernoteDirectory()); TiffReader.ProcessIfd(this, reader, processedIfdOffsets, tagOffset); return(true); case OlympusMakernoteDirectory.TagRawDevelopment2: PushDirectory(new OlympusRawDevelopment2MakernoteDirectory()); TiffReader.ProcessIfd(this, reader, processedIfdOffsets, tagOffset); return(true); case OlympusMakernoteDirectory.TagImageProcessing: PushDirectory(new OlympusImageProcessingMakernoteDirectory()); TiffReader.ProcessIfd(this, reader, processedIfdOffsets, tagOffset); return(true); case OlympusMakernoteDirectory.TagFocusInfo: PushDirectory(new OlympusFocusInfoMakernoteDirectory()); TiffReader.ProcessIfd(this, reader, processedIfdOffsets, tagOffset); return(true); case OlympusMakernoteDirectory.TagRawInfo: PushDirectory(new OlympusRawInfoMakernoteDirectory()); TiffReader.ProcessIfd(this, reader, processedIfdOffsets, tagOffset); return(true); case OlympusMakernoteDirectory.TagMainInfo: PushDirectory(new OlympusMakernoteDirectory()); TiffReader.ProcessIfd(this, reader, processedIfdOffsets, tagOffset); return(true); } } if (CurrentDirectory is PanasonicRawIfd0Directory) { // these contain binary data with specific offsets, and can't be processed as regular ifd's. // The binary data is broken into 'fake' tags and there is a pattern. switch (tagId) { case PanasonicRawIfd0Directory.TagWbInfo: var dirWbInfo = new PanasonicRawWbInfoDirectory(); dirWbInfo.Parent = CurrentDirectory; Directories.Add(dirWbInfo); ProcessBinary(dirWbInfo, tagOffset, reader, byteCount, false, 2); return(true); case PanasonicRawIfd0Directory.TagWbInfo2: var dirWbInfo2 = new PanasonicRawWbInfo2Directory(); dirWbInfo2.Parent = CurrentDirectory; Directories.Add(dirWbInfo2); ProcessBinary(dirWbInfo2, tagOffset, reader, byteCount, false, 3); return(true); case PanasonicRawIfd0Directory.TagDistortionInfo: var dirDistort = new PanasonicRawDistortionDirectory(); dirDistort.Parent = CurrentDirectory; Directories.Add(dirDistort); ProcessBinary(dirDistort, tagOffset, reader, byteCount); return(true); } } // Panasonic RAW sometimes contains an embedded version of the data as a JPG file. if (tagId == PanasonicRawIfd0Directory.TagJpgFromRaw && CurrentDirectory is PanasonicRawIfd0Directory) { var jpegrawbytes = reader.GetBytes(tagOffset, byteCount); // Extract information from embedded image since it is metadata-rich var jpegmem = new MemoryStream(jpegrawbytes); var jpegDirectory = Jpeg.JpegMetadataReader.ReadMetadata(jpegmem); foreach (var dir in jpegDirectory) { dir.Parent = CurrentDirectory; Directories.Add(dir); } return(true); } return(false); }