diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs index 498eec9529..e2ed569548 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs @@ -46,23 +46,17 @@ public ExifWriter(IList values, ExifParts allowedParts) public byte[] GetData() { const uint startIndex = 0; - uint length; IExifValue exifOffset = GetOffsetValue(this.ifdValues, this.exifValues, ExifTag.SubIFDOffset); IExifValue gpsOffset = GetOffsetValue(this.ifdValues, this.gpsValues, ExifTag.GPSIFDOffset); - if (this.ifdValues.Count == 0 && this.exifValues.Count == 0 && this.gpsValues.Count == 0) - { - return Array.Empty(); - } - - uint ifdLength = this.GetLength(this.ifdValues) + 4U; + uint ifdLength = this.GetLength(this.ifdValues); uint exifLength = this.GetLength(this.exifValues); uint gpsLength = this.GetLength(this.gpsValues); - length = ifdLength + exifLength + gpsLength; + uint length = ifdLength + exifLength + gpsLength; - if (length == 4U) + if (length == 0) { return Array.Empty(); } @@ -70,9 +64,10 @@ public byte[] GetData() // two bytes for the byte Order marker 'II' or 'MM', followed by the number 42 (0x2A) and a 0, making 4 bytes total length += (uint)ExifConstants.LittleEndianByteOrderMarker.Length; - length += 4 + 2; + // first IFD offset + length += 4; - var result = new byte[length]; + byte[] result = new byte[length]; int i = 0; @@ -80,15 +75,13 @@ public byte[] GetData() ExifConstants.LittleEndianByteOrderMarker.CopyTo(result.AsSpan(start: i)); i += ExifConstants.LittleEndianByteOrderMarker.Length; - uint ifdOffset = ((uint)i - startIndex) + 4U; - uint thumbnailOffset = ifdOffset + ifdLength + exifLength + gpsLength; + uint ifdOffset = (uint)i - startIndex + 4U; exifOffset?.TrySetValue(ifdOffset + ifdLength); gpsOffset?.TrySetValue(ifdOffset + ifdLength + exifLength); i = WriteUInt32(ifdOffset, result, i); i = this.WriteHeaders(this.ifdValues, result, i); - i = WriteUInt32(thumbnailOffset, result, i); i = this.WriteData(startIndex, this.ifdValues, result, i); if (exifLength > 0) @@ -103,8 +96,6 @@ public byte[] GetData() i = this.WriteData(startIndex, this.gpsValues, result, i); } - WriteUInt16(0, result, i); - return result; } @@ -263,7 +254,7 @@ private uint GetLength(IList values) { uint valueLength = GetLength(value); - length += 2 + 2 + 4 + 4; + length += 12; if (valueLength > 4) { @@ -271,6 +262,9 @@ private uint GetLength(IList values) } } + // next IFD offset + length += 4; + return length; } @@ -361,6 +355,9 @@ private int WriteHeaders(List values, Span destination, int of newOffset += 4; } + // next IFD offset + newOffset = WriteUInt32(0, destination, newOffset); + return newOffset; } diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs index 3ee20cbd1f..7fc3ff6f19 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers.Binary; using System.Collections.Generic; using System.IO; using System.Linq; @@ -86,6 +87,18 @@ public void ConstructorEmpty() new ExifProfile(Array.Empty()); } + [Fact] + public void EmptyWriter() + { + var profile = new ExifProfile() { Parts = ExifParts.GpsTags }; + profile.SetValue(ExifTag.Copyright, "Copyright text"); + + byte[] bytes = profile.ToByteArray(); + + Assert.NotNull(bytes); + Assert.Empty(bytes); + } + [Fact] public void ConstructorCopy() { @@ -420,7 +433,7 @@ public void TestArrayValueWithUnspecifiedSize() Assert.Equal(2, profile.Values.Count(v => (ExifTagValue)(ushort)v.Tag == ExifTagValue.DateTime)); byte[] bytes = profile.ToByteArray(); - Assert.Equal(525, bytes.Length); + Assert.Equal(531, bytes.Length); var profile2 = new ExifProfile(bytes); Assert.Equal(25, profile2.Values.Count); @@ -487,6 +500,22 @@ private static ExifProfile CreateExifProfile() return profile; } + [Fact] + public void IfdStructure() + { + var exif = new ExifProfile(); + exif.SetValue(ExifTag.XPAuthor, Encoding.GetEncoding("UCS-2").GetBytes("Dan Petitt")); + + Span actualBytes = exif.ToByteArray(); + + // Assert + int ifdOffset = ExifConstants.LittleEndianByteOrderMarker.Length; + Assert.Equal(8U, BinaryPrimitives.ReadUInt32LittleEndian(actualBytes.Slice(ifdOffset, 4))); + + int nextIfdPointerOffset = ExifConstants.LittleEndianByteOrderMarker.Length + 4 + 2 + 12; + Assert.Equal(0U, BinaryPrimitives.ReadUInt32LittleEndian(actualBytes.Slice(nextIfdPointerOffset, 4))); + } + internal static ExifProfile GetExifProfile() { using Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateRgba32Image();