public XmpReaderTest() { var jpegSegments = new [] { File.ReadAllBytes("Tests/Data/withXmpAndIptc.jpg.app1.1") }; var directories = new XmpReader().ReadJpegSegments(jpegSegments, JpegSegmentType.App1); _directory = directories.OfType<XmpDirectory>().ToList().Single(); Assert.False(_directory.HasError); }
/// <exception cref="System.IO.IOException"/> public override bool CustomProcessTag(int tagOffset, ICollection<int> processedIfdOffsets, int tiffHeaderOffset, IndexedReader reader, int tagId, int byteCount) { // Custom processing for the Makernote tag if (tagId == ExifDirectoryBase.TagMakernote && CurrentDirectory is ExifSubIfdDirectory) { return ProcessMakernote(tagOffset, processedIfdOffsets, tiffHeaderOffset, 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; } return false; }
public IEnumerable <IDocument> Execute(IReadOnlyList <IDocument> inputs, IExecutionContext context) { return(inputs.Select(x => { MetadataExtractor.Formats.Xmp.XmpDirectory xmpDirectory; try { using (var stream = x.GetStream()) { xmpDirectory = ImageMetadataReader.ReadMetadata(stream).OfType <XmpDirectory>().FirstOrDefault(); } } catch (Exception) { xmpDirectory = null; } if (xmpDirectory == null) // Try to read sidecarfile { if (x.ContainsKey(Keys.SourceFilePath) && System.IO.File.Exists(x[Keys.SourceFilePath] + ".xmp")) { string xmpXml = System.IO.File.ReadAllText(x[Keys.SourceFilePath] + ".xmp"); xmpDirectory = new MetadataExtractor.Formats.Xmp.XmpReader().Extract(xmpXml); } } if (xmpDirectory == null) { if (_toSearch.Any(y => y.IsMandatory)) { context.Trace.Warning($"File doe not contain Metadata or sidecar file ({x.Source})"); if (_skipElementOnMissingData) { return null; } } return x; } Dictionary <string, object> newValues = new Dictionary <string, object>(); TreeDirectory hierarchicalDirectory = TreeDirectory.GetHierarchicalDirectory(xmpDirectory); foreach (var search in _toSearch) { try { TreeDirectory metadata = hierarchicalDirectory.Childrean.FirstOrDefault(y => search.PathWithoutNamespacePrefix == y.ElementName && search.Namespace == y.ElementNameSpace); if (metadata == null) { if (search.IsMandatory) { context.Trace.Error($"Metadata does not Contain {search.XmpPath} ({x.Source})"); if (_skipElementOnMissingData) { return null; } } continue; } object value = GetObjectFromMetadata(metadata, hierarchicalDirectory); if (newValues.ContainsKey(search.MetadataKey) && _errorOnDoubleKeys) { context.Trace.Error($"This Module tries to write same Key multiple times {search.MetadataKey} ({x.Source})"); } else { newValues[search.MetadataKey] = value; } } catch (Exception e) { context.Trace.Error($"An exception occurred : {e} {e.Message}"); if (search.IsMandatory && _skipElementOnMissingData) { return null; } } } return newValues.Any() ? x.Clone(newValues) : x; }).Where(x => x != null)); }
public IEnumerable<IDocument> Execute(IReadOnlyList<IDocument> inputs, IExecutionContext context) { return inputs.Select(input => { MetadataExtractor.Formats.Xmp.XmpDirectory xmpDirectory; try { using (var stream = input.GetStream()) { xmpDirectory = ImageMetadataReader.ReadMetadata(stream).OfType<XmpDirectory>().FirstOrDefault(); } } catch (Exception) { xmpDirectory = null; } if (xmpDirectory == null) // Try to read sidecarfile { if (input.ContainsKey(Keys.SourceFilePath) && System.IO.File.Exists(input[Keys.SourceFilePath] + ".xmp")) { string xmpXml = System.IO.File.ReadAllText(input[Keys.SourceFilePath] + ".xmp"); xmpDirectory = new MetadataExtractor.Formats.Xmp.XmpReader().Extract(xmpXml); } } if (xmpDirectory == null) { if (_toSearch.Any(y => y.IsMandatory)) { Trace.Warning($"File doe not contain Metadata or sidecar file ({input.Source})"); if (_skipElementOnMissingData) return null; } return input; } Dictionary<string, object> newValues = new Dictionary<string, object>(); TreeDirectory hierarchicalDirectory = TreeDirectory.GetHierarchicalDirectory(xmpDirectory); foreach (var search in _toSearch) { try { TreeDirectory metadata = hierarchicalDirectory.Childrean.FirstOrDefault(y => search.PathWithoutNamespacePrefix == y.ElementName && search.Namespace == y.ElementNameSpace); if (metadata == null) { if (search.IsMandatory) { Trace.Error($"Metadata does not Contain {search.XmpPath} ({input.Source})"); if (_skipElementOnMissingData) { return null; } } continue; } object value = GetObjectFromMetadata(metadata, hierarchicalDirectory); if (newValues.ContainsKey(search.MetadataKey) && _errorOnDoubleKeys) { Trace.Error($"This Module tries to write same Key multiple times {search.MetadataKey} ({input.Source})"); } else { newValues[search.MetadataKey] = value; } } catch (Exception e) { Trace.Error($"An exception occurred : {e} {e.Message}"); if (search.IsMandatory && _skipElementOnMissingData) return null; } } return newValues.Any() ? context.GetDocument(input, newValues) : input; }).Where(x => x != null); }
Extract([NotNull] 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); 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; }