public void WriteSegment(JpegSegment segment) { if (segment == null) { throw new ArgumentNullException(nameof(segment)); } if (segment.Bytes.Length + 2 > 0xFFFF) { throw new ArgumentOutOfRangeException(nameof(segment)); } _writer.Write((byte)0xFF); // segment start _writer.Write((byte)segment.Type); if (HasData(segment.Type)) { // write size as big endian ushort var size = segment.Bytes.Length + 2; // + 2 for the size bytes _writer.Write((byte)(size >> 8)); _writer.Write((byte)(size & 0xFF)); // write data _writer.Write(segment.Bytes, 0, segment.Bytes.Length); } }
public void ReadJpegSegmentWithNoExifData() { var badExifSegment = new JpegSegment(JpegSegmentType.App1, new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, offset: 0); var directories = new ExifReader().ReadJpegSegments(new[] { badExifSegment }); Assert.Equal(0, directories.Count); }
public void Parse_ThumbnailData() { var dir = new ExifThumbnailDirectory(); dir.Set(ExifThumbnailDirectory.TagThumbnailOffset, 0); dir.Set(ExifThumbnailDirectory.TagThumbnailLength, 2); var segment = new JpegSegment(JpegSegmentType.App1, new byte[] { (byte)'E', (byte)'x', (byte)'i', (byte)'f', (byte)'\0', (byte)'\0', 0x12, 0x23, 0x34 }, 0); var metadata = new Mock <IExifMetadata>(); metadata.Setup(mock => mock.Segment).Returns(segment); metadata.Setup(mock => mock.GetDirectoryOfType <ExifDirectoryBase>()).Returns(dir); var parser = new ThumbnaiExifAttributeParser <ExifDirectoryBase>("thumbnail"); var thumbnail = parser.Parse(metadata.Object).Value as ImageValue; CollectionAssert.AreEqual(new byte[] { 0x12, 0x23 }, thumbnail.Value); }
public void ReadJpegSegments_InvalidData() { var app2 = new JpegSegment(JpegSegmentType.App2, File.ReadAllBytes("Data/iccDataInvalid1.jpg.app2"), offset: 0); var directory = new IccReader().ReadJpegSegments(new[] { app2 }); Assert.NotNull(directory); Assert.True(directory.Single().HasError); }
public void MatchSegment_ValidHeaderAndType() { var segment = new JpegSegment(JpegSegmentType.App1, new byte[] { (byte)'T', (byte)'e', (byte)'s', (byte)'t', 0x00, 0x12, 0x34 }, 0); Assert.IsTrue(JpegSegmentUtils.MatchSegment(segment, JpegSegmentType.App1, "Test\0")); }
public void Extract_ProfileDateTime() { var app2 = new JpegSegment(JpegSegmentType.App2, File.ReadAllBytes("Data/withExifAndIptc.jpg.app2"), offset: 0); var directory = new IccReader() .ReadJpegSegments(new[] { app2 }) .OfType <IccDirectory>() .Single(); Assert.NotNull(directory); // Assert.Equal("1998:02:09 06:49:00", directory.GetString(IccDirectory.TagProfileDateTime)); Assert.Equal(new DateTime(1998, 2, 9, 6, 49, 0), directory.GetDateTime(IccDirectory.TagProfileDateTime)); }
public void WriteSegment_SegmentWithoutData() { var output = new MemoryStream(); var writer = new JpegSegmentWriter(new BinaryWriter(output)); var segment = new JpegSegment(JpegSegmentType.Soi, new byte[0], 0); writer.WriteSegment(segment); var result = output.ToArray(); CollectionAssert.AreEqual( new byte[] { 0xFF, 0xD8 }, result); }
public void WriteSegment_SegmentWithData() { var output = new MemoryStream(); var writer = new JpegSegmentWriter(new BinaryWriter(output)); var segment = new JpegSegment(JpegSegmentType.App1, new byte[] { 0x12, 0x34, 0x56 }, 0); writer.WriteSegment(segment); var result = output.ToArray(); CollectionAssert.AreEqual( new byte[] { 0xFF, 0xE1, 0x00, 0x05, 0x12, 0x34, 0x56 }, result); }
/// <summary> /// Check segment type and header. /// </summary> /// <param name="segment">JPEG segment</param> /// <param name="type">Expected type</param> /// <param name="header">Expected header</param> /// <returns>true iff the segment has given type and header</returns> public static bool MatchSegment(JpegSegment segment, JpegSegmentType type, string header) { if (segment.Type != type || segment.Bytes.Length < header.Length) { return(false); } for (int i = 0; i < header.Length; ++i) { if ((char)segment.Bytes[i] != header[i]) { return(false); } } return(true); }
private static bool IsXmpSegment(JpegSegment segment) => segment.Bytes.StartsWith(JpegSegmentPreambleBytes);
private static bool IsExtendedXmpSegment(JpegSegment segment) => segment.Bytes.StartsWith(JpegSegmentPreambleExtensionBytes);
public override FileMetadata AnalyzeFile() { try { this.foundMetadata = new FileMetadata(); IReadOnlyList <MetadataExtractor.Directory> directories = MetadataExtractor.ImageMetadataReader.ReadMetadata(this.fileStream); foreach (MetadataExtractor.Directory currentDir in directories.Where(p => !IgnoredExifDirectories.Contains(p.Name))) { if (currentDir is MetadataExtractor.Formats.Exif.ExifThumbnailDirectory) { try { uint offset = (uint)currentDir.GetObject(MetadataExtractor.Formats.Exif.ExifThumbnailDirectory.TagThumbnailOffset) + (uint)MetadataExtractor.Formats.Exif.ExifReader.JpegSegmentPreamble.Length; uint length = (uint)currentDir.GetObject(MetadataExtractor.Formats.Exif.ExifThumbnailDirectory.TagThumbnailLength); long currentPosition = this.fileStream.Position; this.fileStream.Seek(0, SeekOrigin.Begin); JpegSegment app1Segment = JpegSegmentReader.ReadSegments(new MetadataExtractor.IO.SequentialStreamReader(this.fileStream), new[] { JpegSegmentType.App1 }).FirstOrDefault(); if (app1Segment != null) { byte[] thumb = new byte[length]; Array.Copy(app1Segment.Bytes, offset, thumb, 0, length); this.foundMetadata.Thumbnail = thumb; } this.fileStream.Seek(currentPosition, SeekOrigin.Begin); } catch (Exception) { } } else if (currentDir is MetadataExtractor.Formats.Exif.GpsDirectory gps) { if (this.foundMetadata.GPS == null) { MetadataExtractor.GeoLocation gpsLocation = gps.GetGeoLocation(); if (gpsLocation != null) { this.foundMetadata.GPS = new GeoLocation(gpsLocation.ToDmsString(), gpsLocation.Longitude, gpsLocation.Latitude); if (gps.ContainsTag(MetadataExtractor.Formats.Exif.GpsDirectory.TagAltitude)) { this.foundMetadata.GPS.Altitude = $"{gps.GetDescription(MetadataExtractor.Formats.Exif.GpsDirectory.TagAltitude)} ({gps.GetDescription(MetadataExtractor.Formats.Exif.GpsDirectory.TagAltitudeRef)})";; } } } } else { Dictionary <string, string> dicTags = new Dictionary <string, string>(); foreach (MetadataExtractor.Tag tag in currentDir.Tags) { string lcDescription = tag.Description?.Trim(); string lcName = tag.Name?.Trim(); if (!String.IsNullOrWhiteSpace(lcName) && !String.IsNullOrWhiteSpace(lcDescription) && !lcName.StartsWith("unknown", StringComparison.OrdinalIgnoreCase) && !lcDescription.ToLower().StartsWith("unknown", StringComparison.OrdinalIgnoreCase)) { lcName = Functions.RemoveAccentsWithNormalization(lcName); lcDescription = Functions.RemoveAccentsWithNormalization(lcDescription); switch (tag.Type) { case MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagWinAuthor: case MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagArtist: case MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagCameraOwnerName: this.foundMetadata.Add(new User(lcDescription, false, "EXIF")); break; case MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagSoftware: string strSoftware = Analysis.ApplicationAnalysis.GetApplicationsFromString(lcDescription); if (!String.IsNullOrEmpty(strSoftware)) { this.foundMetadata.Add(new Application(strSoftware)); } break; case MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagDateTime: case MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagDateTimeDigitized: case MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagDateTimeOriginal: if (DateTime.TryParseExact(lcDescription, "yyyy:MM:dd HH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime date) && date > DateTime.MinValue && (!foundMetadata.Dates.CreationDate.HasValue || this.foundMetadata.Dates.CreationDate > date)) { this.foundMetadata.Dates.CreationDate = date; } break; case MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagModel: this.foundMetadata.Model = lcDescription; break; case MetadataExtractor.Formats.Exif.ExifDirectoryBase.TagHostComputer: this.foundMetadata.OperatingSystem = lcDescription; break; default: break; } if (!dicTags.ContainsKey(lcName)) { dicTags.Add(lcName, lcDescription); } } } if (dicTags.Count > 0) { string makerKey = currentDir.Name; int i = 1; while (foundMetadata.Makernotes.ContainsKey(makerKey)) { makerKey = $"{currentDir.Name} ({i})"; i++; } foundMetadata.Makernotes.Add(makerKey, dicTags); } } } } catch (Exception e) { System.Diagnostics.Debug.WriteLine($"Error analizing EXIF metadata ({e.ToString()})"); } return(this.foundMetadata); }
private static string GetExtendedDataGuid(JpegSegment segment) => Encoding.UTF8.GetString(segment.Bytes, JpegSegmentPreambleExtensionBytes.Length, 32);
private bool IsExifSegment(JpegSegment segment) { return(segment.Type == JpegSegmentType.App1 && Encoding.UTF8.GetString(segment.Bytes, 0, ExifHeader.Length) == ExifHeader); }
public JpegReaderTest() { var sof0 = new JpegSegment(JpegSegmentType.Sof0, File.ReadAllBytes("Data/simple.jpg.sof0"), offset: 0); _directory = new JpegReader().Extract(sof0); }
public static IList <Directory> ProcessSegmentBytes(string filePath, JpegSegmentType type) { var segment = new JpegSegment(type, TestDataUtil.GetBytes(filePath), 0); return(new ExifReader().ReadJpegSegments(new[] { segment }).ToList()); }
private static bool IsAttributeSegment(JpegSegment segment) { const string header = AttributeReader.JpegSegmentHeader; return(JpegSegmentUtils.MatchSegment(segment, JpegSegmentType.App1, header)); }
public ExifMetadata(JpegSegment segment, IReadOnlyList <Directory> directories) { Segment = segment; _directories = directories; }
public void MatchSegment_InvalidType() { var segment = new JpegSegment(JpegSegmentType.App0, new byte[0], 0); Assert.IsFalse(JpegSegmentUtils.MatchSegment(segment, JpegSegmentType.App1, "Test\0")); }
public void MatchSegment_ShortHeader() { var segment = new JpegSegment(JpegSegmentType.App1, new byte[0], 0); Assert.IsFalse(JpegSegmentUtils.MatchSegment(segment, JpegSegmentType.App1, "Test\0")); }
public static IEnumerable <JpegSegment> ReadSegments(SequentialStreamReWriter reader, Stream writer , ICollection <IJpegSementMetadataReWriter> segmentProcessors) { if (segmentProcessors == null || segmentProcessors.Count() <= 0) { throw new ArgumentException(nameof(segmentProcessors)); } // Build the union of segment types desired by all readers var segmentTypes = new HashSet <JpegSegmentType>(segmentProcessors.SelectMany(reader => reader.SegmentTypes)); if (!reader.IsMotorolaByteOrder) { throw new JpegProcessingException("Must be big-endian/Motorola byte order."); } // first two bytes should be JPEG magic number var magicTupleData = reader.GetBytes(2); var magicNumber = reader.GetUInt16(magicTupleData); Write(writer, magicTupleData); if (magicNumber != 0xFFD8) { throw new JpegProcessingException($"JPEG data is expected to begin with 0xFFD8 (ÿØ) not 0x{magicNumber:X4}"); } do { // Find the segment marker. Markers are zero or more 0xFF bytes, followed // by a 0xFF and then a byte not equal to 0x00 or 0xFF. var segmentIdentifier = reader.GetByte(); Write(writer, segmentIdentifier); var segmentTypeByte = reader.GetByte(); Write(writer, segmentTypeByte); // Read until we have a 0xFF byte followed by a byte that is not 0xFF or 0x00 while (segmentIdentifier != 0xFF || segmentTypeByte == 0xFF || segmentTypeByte == 0) { segmentIdentifier = segmentTypeByte; segmentTypeByte = reader.GetByte(); Write(writer, segmentTypeByte); } var segmentType = (JpegSegmentType)segmentTypeByte; if (segmentType == JpegSegmentType.Eoi) { // the 'End-Of-Image' segment yield break; } // next 2-bytes are <segment-size>: [high-byte] [low-byte] var segmentLenghtDataData = reader.GetBytes(2); var segmentLenghtData = reader.GetUInt16(segmentLenghtDataData); Write(writer, segmentLenghtDataData); var segmentLength = (int)segmentLenghtData; // segment length includes size bytes, so subtract two segmentLength -= 2; if (segmentLength < 0) { throw new JpegProcessingException("JPEG segment size would be less than zero"); } if (segmentType == JpegSegmentType.Sos) { // The 'Start-Of-Scan' segment's length doesn't include the image data, instead would // have to search for the two bytes: 0xFF 0xD9 (EOI). // It comes last so simply return at this point reader.CopyRemainingBytes(writer); yield break; } // Check whether we are interested in this segment if (segmentTypes.Contains(segmentType)) { var segmentOffset = reader.Position; var segmentBytes = reader.GetBytes(segmentLength); //Write(writer, segmentBytes); // components are responsable for writing themselfs Debug.Assert(segmentLength == segmentBytes.Length); var orignialSegmentContent = new JpegSegment(segmentType, segmentBytes, segmentOffset); var segmentProcessor = segmentProcessors.FirstOrDefault(x => x.SegmentTypes.Contains(segmentType)); // should not happen if (segmentProcessor == null) { throw new InvalidOperationException("Segment processor for {segmentType} not found"); } var result = segmentProcessor.ReadJpegSegments(new JpegSegment[] { orignialSegmentContent }, writer); yield return(orignialSegmentContent); // [sno] todo - remove return here } else { // Any other segment var segmentBytes = reader.GetBytes(segmentLength); Write(writer, segmentBytes); } }while (true); }
public static IList <Directory> ProcessSegmentBytes([NotNull] string filePath, JpegSegmentType type) { var segment = new JpegSegment(type, File.ReadAllBytes(filePath), 0); return(new ExifReader().ReadJpegSegments(new[] { segment }).ToList()); }