diff --git a/src/ImageSharp/Metadata/Profiles/Exif/DC-008-Translation-2019-E.pdf b/src/ImageSharp/Metadata/Profiles/Exif/DC-008-Translation-2019-E.pdf
new file mode 100644
index 000000000..9be0c8402
Binary files /dev/null and b/src/ImageSharp/Metadata/Profiles/Exif/DC-008-Translation-2019-E.pdf differ
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs
index 630ea6e0d..f98a1f3c7 100644
--- a/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs
+++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs
@@ -143,15 +143,8 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
/// The data type of the tag.
public IExifValue GetValue(ExifTag tag)
{
- foreach (IExifValue exifValue in this.Values)
- {
- if (exifValue.Tag == tag)
- {
- return (IExifValue)exifValue;
- }
- }
-
- return null;
+ IExifValue value = this.GetValueInternal(tag);
+ return value is null ? null : (IExifValue)value;
}
///
@@ -184,25 +177,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
/// The value.
/// The data type of the tag.
public void SetValue(ExifTag tag, TValueType value)
- {
- foreach (IExifValue exifValue in this.Values)
- {
- if (exifValue.Tag == tag)
- {
- exifValue.TrySetValue(value);
- return;
- }
- }
-
- ExifValue newExifValue = ExifValues.Create(tag);
- if (newExifValue is null)
- {
- throw new NotSupportedException();
- }
-
- newExifValue.TrySetValue(value);
- this.values.Add(newExifValue);
- }
+ => this.SetValueInternal(tag, value);
///
/// Converts this instance to a byte array.
@@ -227,6 +202,50 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
///
public ExifProfile DeepClone() => new ExifProfile(this);
+ ///
+ /// Returns the value with the specified tag.
+ ///
+ /// The tag of the exif value.
+ /// The value with the specified tag.
+ internal IExifValue GetValueInternal(ExifTag tag)
+ {
+ foreach (IExifValue exifValue in this.Values)
+ {
+ if (exifValue.Tag == tag)
+ {
+ return exifValue;
+ }
+ }
+
+ return null;
+ }
+
+ ///
+ /// Sets the value of the specified tag.
+ ///
+ /// The tag of the exif value.
+ /// The value.
+ internal void SetValueInternal(ExifTag tag, object value)
+ {
+ foreach (IExifValue exifValue in this.Values)
+ {
+ if (exifValue.Tag == tag)
+ {
+ exifValue.TrySetValue(value);
+ return;
+ }
+ }
+
+ ExifValue newExifValue = ExifValues.Create(tag);
+ if (newExifValue is null)
+ {
+ throw new NotSupportedException();
+ }
+
+ newExifValue.TrySetValue(value);
+ this.values.Add(newExifValue);
+ }
+
///
/// Synchronizes the profiles with the specified metadata.
///
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/README.md b/src/ImageSharp/Metadata/Profiles/Exif/README.md
index b6e27b70c..7901527e1 100644
--- a/src/ImageSharp/Metadata/Profiles/Exif/README.md
+++ b/src/ImageSharp/Metadata/Profiles/Exif/README.md
@@ -1,3 +1,3 @@
-Adapted from Magick.NET:
+Adapted from Magick.NET:
-https://github.com/dlemstra/Magick.NET/tree/784e23b1f5c824fc03d4b95d3387b3efe1ed510b/Magick.NET/Core/Profiles/Exif
\ No newline at end of file
+https://github.com/dlemstra/Magick.NET
diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs
index 8bf44d764..fbd705957 100644
--- a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs
+++ b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs
@@ -154,83 +154,90 @@ namespace SixLabors.ImageSharp.Tests
Assert.Equal(new Rational(double.PositiveInfinity), value2.Value);
}
- //[Theory]
- //[InlineData(TestImageWriteFormat.Jpeg)]
- //[InlineData(TestImageWriteFormat.Png)]
- //public void SetValue(TestImageWriteFormat imageFormat)
- //{
- // var latitude = new Rational[] { new Rational(12.3), new Rational(4.56), new Rational(789.0) };
-
- // Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateRgba32Image();
- // image.Metadata.ExifProfile.SetValue(ExifTag.Software, "ImageSharp");
+ [Theory]
+ [InlineData(TestImageWriteFormat.Jpeg)]
+ [InlineData(TestImageWriteFormat.Png)]
+ public void SetValue(TestImageWriteFormat imageFormat)
+ {
+ Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateRgba32Image();
+ image.Metadata.ExifProfile.SetValue(ExifTag.Software, "ImageSharp");
- // IExifValue value = image.Metadata.ExifProfile.GetValue(ExifTag.Software);
- // TestValue(value, "ImageSharp");
+ IExifValue software = image.Metadata.ExifProfile.GetValue(ExifTag.Software);
+ Assert.Equal("ImageSharp", software.Value);
- // Assert.Throws(() => { value.SetValue(15); });
+ // ExifString can set integer values.
+ Assert.True(software.TrySetValue(15));
+ Assert.False(software.TrySetValue(15F));
- // image.Metadata.ExifProfile.SetValue(ExifTag.ShutterSpeedValue, new SignedRational(75.55));
+ image.Metadata.ExifProfile.SetValue(ExifTag.ShutterSpeedValue, new SignedRational(75.55));
- // value = image.Metadata.ExifProfile.GetValue(ExifTag.ShutterSpeedValue);
+ IExifValue shutterSpeed = image.Metadata.ExifProfile.GetValue(ExifTag.ShutterSpeedValue);
- // TestValue(value, new SignedRational(7555, 100));
+ Assert.Equal(new SignedRational(7555, 100), shutterSpeed.Value);
+ Assert.False(shutterSpeed.TrySetValue(75));
- // Assert.Throws(() => { value.WithValue(75); });
+ image.Metadata.ExifProfile.SetValue(ExifTag.XResolution, new Rational(150.0));
- // image.Metadata.ExifProfile.SetValue(ExifTag.XResolution, new Rational(150.0));
+ // We also need to change this value because this overrides XResolution when the image is written.
+ image.Metadata.HorizontalResolution = 150.0;
- // // We also need to change this value because this overrides XResolution when the image is written.
- // image.Metadata.HorizontalResolution = 150.0;
+ IExifValue xResolution = image.Metadata.ExifProfile.GetValue(ExifTag.XResolution);
+ Assert.Equal(new Rational(150, 1), xResolution.Value);
- // value = image.Metadata.ExifProfile.GetValue(ExifTag.XResolution);
- // TestValue(value, new Rational(150, 1));
+ Assert.False(xResolution.TrySetValue("ImageSharp"));
- // Assert.Throws(() => { value.WithValue("ImageSharp"); });
+ image.Metadata.ExifProfile.SetValue(ExifTag.ReferenceBlackWhite, null);
- // image.Metadata.ExifProfile.SetValue(ExifTag.ReferenceBlackWhite, null);
+ IExifValue referenceBlackWhite = image.Metadata.ExifProfile.GetValue(ExifTag.ReferenceBlackWhite);
+ Assert.Null(referenceBlackWhite.Value);
- // value = image.Metadata.ExifProfile.GetValue(ExifTag.ReferenceBlackWhite);
- // TestValue(value, (string)null);
+ var expectedLatitude = new Rational[] { new Rational(12.3), new Rational(4.56), new Rational(789.0) };
+ image.Metadata.ExifProfile.SetValue(ExifTag.GPSLatitude, expectedLatitude);
- // image.Metadata.ExifProfile.SetValue(ExifTag.GPSLatitude, latitude);
+ IExifValue latitude = image.Metadata.ExifProfile.GetValue(ExifTag.GPSLatitude);
+ Assert.Equal(expectedLatitude, latitude.Value);
- // value = image.Metadata.ExifProfile.GetValue(ExifTag.GPSLatitude);
- // TestValue(value, latitude);
+ int profileCount = image.Metadata.ExifProfile.Values.Count;
+ image = WriteAndRead(image, imageFormat);
- // image = WriteAndRead(image, imageFormat);
+ Assert.NotNull(image.Metadata.ExifProfile);
- // Assert.NotNull(image.Metadata.ExifProfile);
- // Assert.Equal(17, image.Metadata.ExifProfile.Values.Count());
+ // Should be 3 less.
+ // 1 x due to setting of null "ReferenceBlackWhite" value.
+ // 2 x due to use of non-standard padding tag 0xEA1C listed in EXIF Tool. We can read those values but adhere
+ // strictly to the 2.3.1 specification when writing. (TODO: Support 2.3.2)
+ // https://exiftool.org/TagNames/EXIF.html
+ Assert.Equal(profileCount - 3, image.Metadata.ExifProfile.Values.Count);
- // value = image.Metadata.ExifProfile.GetValue(ExifTag.Software);
- // TestValue(value, "ImageSharp");
+ software = image.Metadata.ExifProfile.GetValue(ExifTag.Software);
+ Assert.Equal("15", software.Value);
- // value = image.Metadata.ExifProfile.GetValue(ExifTag.ShutterSpeedValue);
- // TestValue(value, new SignedRational(75.55));
+ shutterSpeed = image.Metadata.ExifProfile.GetValue(ExifTag.ShutterSpeedValue);
+ Assert.Equal(new SignedRational(75.55), shutterSpeed.Value);
- // value = image.Metadata.ExifProfile.GetValue(ExifTag.XResolution);
- // TestValue(value, new Rational(150.0));
+ xResolution = image.Metadata.ExifProfile.GetValue(ExifTag.XResolution);
+ Assert.Equal(new Rational(150.0), xResolution.Value);
- // value = image.Metadata.ExifProfile.GetValue(ExifTag.ReferenceBlackWhite);
- // Assert.Null(value);
+ referenceBlackWhite = image.Metadata.ExifProfile.GetValue(ExifTag.ReferenceBlackWhite);
+ Assert.Null(referenceBlackWhite);
- // value = image.Metadata.ExifProfile.GetValue(ExifTag.GPSLatitude);
- // TestValue(value, latitude);
+ latitude = image.Metadata.ExifProfile.GetValue(ExifTag.GPSLatitude);
+ Assert.Equal(expectedLatitude, latitude.Value);
- // image.Metadata.ExifProfile.Parts = ExifParts.ExifTags;
+ image.Metadata.ExifProfile.Parts = ExifParts.ExifTags;
- // image = WriteAndRead(image, imageFormat);
+ image = WriteAndRead(image, imageFormat);
- // Assert.NotNull(image.Metadata.ExifProfile);
- // Assert.Equal(8, image.Metadata.ExifProfile.Values.Count());
+ Assert.NotNull(image.Metadata.ExifProfile);
+ Assert.Equal(8, image.Metadata.ExifProfile.Values.Count);
- // Assert.NotNull(image.Metadata.ExifProfile.GetValue(ExifTag.ColorSpace));
- // Assert.True(image.Metadata.ExifProfile.RemoveValue(ExifTag.ColorSpace));
- // Assert.False(image.Metadata.ExifProfile.RemoveValue(ExifTag.ColorSpace));
- // Assert.Null(image.Metadata.ExifProfile.GetValue(ExifTag.ColorSpace));
+ Assert.NotNull(image.Metadata.ExifProfile.GetValue(ExifTag.ColorSpace));
+ Assert.True(image.Metadata.ExifProfile.RemoveValue(ExifTag.ColorSpace));
+ Assert.False(image.Metadata.ExifProfile.RemoveValue(ExifTag.ColorSpace));
+ Assert.Null(image.Metadata.ExifProfile.GetValue(ExifTag.ColorSpace));
- // Assert.Equal(7, image.Metadata.ExifProfile.Values.Count());
- //}
+ Assert.Equal(7, image.Metadata.ExifProfile.Values.Count);
+ }
[Fact]
public void Syncs()
@@ -308,8 +315,8 @@ namespace SixLabors.ImageSharp.Tests
foreach (ExifTag expectedProfileTag in expectedProfileTags)
{
- IExifValue actualProfileValue = actualProfile.Values.First(x => x.Tag == expectedProfileTag);
- IExifValue expectedProfileValue = expectedProfile.Values.First(x => x.Tag == expectedProfileTag);
+ IExifValue actualProfileValue = actualProfile.GetValueInternal(expectedProfileTag);
+ IExifValue expectedProfileValue = expectedProfile.GetValueInternal(expectedProfileTag);
Assert.Equal(expectedProfileValue.GetValue(), actualProfileValue.GetValue());
}
@@ -324,7 +331,6 @@ namespace SixLabors.ImageSharp.Tests
{
// This image contains an 802 byte EXIF profile
// It has a tag with an index offset of 18,481,152 bytes (overrunning the data)
-
Image image = TestFile.Create(TestImages.Jpeg.Progressive.Bad.ExifUndefType).CreateRgba32Image();
Assert.NotNull(image);
@@ -357,53 +363,52 @@ namespace SixLabors.ImageSharp.Tests
Assert.Equal(525, bytes.Length);
}
- //[Theory]
- //[InlineData(TestImageWriteFormat.Jpeg)]
- //[InlineData(TestImageWriteFormat.Png)]
- //public void WritingImagePreservesExifProfile(TestImageWriteFormat imageFormat)
- //{
- // // arrange
- // var image = new Image(1, 1);
- // ExifProfile expected = CreateExifProfile();
- // image.Metadata.ExifProfile = expected;
-
- // // act
- // Image reloadedImage = WriteAndRead(image, imageFormat);
-
- // // assert
- // ExifProfile actual = reloadedImage.Metadata.ExifProfile;
- // Assert.NotNull(actual);
- // foreach (KeyValuePair expectedProfileValue in TestProfileValues)
- // {
- // ExifValue actualProfileValue = actual.GetValue(expectedProfileValue.Key);
- // Assert.NotNull(actualProfileValue);
- // Assert.Equal(expectedProfileValue.Value, actualProfileValue.Value);
- // }
- //}
-
- //[Fact]
- //public void ProfileToByteArray()
- //{
- // // arrange
- // byte[] exifBytesWithoutExifCode = ExifConstants.LittleEndianByteOrderMarker;
- // ExifProfile expectedProfile = CreateExifProfile();
- // var expectedProfileTags = expectedProfile.Values.Select(x => x.Tag).ToList();
-
- // // act
- // byte[] actualBytes = expectedProfile.ToByteArray();
- // var actualProfile = new ExifProfile(actualBytes);
-
- // // assert
- // Assert.NotNull(actualBytes);
- // Assert.NotEmpty(actualBytes);
- // Assert.Equal(exifBytesWithoutExifCode, actualBytes.Take(exifBytesWithoutExifCode.Length).ToArray());
- // foreach (ExifTag expectedProfileTag in expectedProfileTags)
- // {
- // ExifValue actualProfileValue = actualProfile.GetValue(expectedProfileTag);
- // ExifValue expectedProfileValue = expectedProfile.GetValue(expectedProfileTag);
- // Assert.Equal(expectedProfileValue.Value, actualProfileValue.Value);
- // }
- //}
+ [Theory]
+ [InlineData(TestImageWriteFormat.Jpeg)]
+ [InlineData(TestImageWriteFormat.Png)]
+ public void WritingImagePreservesExifProfile(TestImageWriteFormat imageFormat)
+ {
+ // Arrange
+ var image = new Image(1, 1);
+ image.Metadata.ExifProfile = CreateExifProfile();
+
+ // Act
+ Image reloadedImage = WriteAndRead(image, imageFormat);
+
+ // Assert
+ ExifProfile actual = reloadedImage.Metadata.ExifProfile;
+ Assert.NotNull(actual);
+ foreach (KeyValuePair expectedProfileValue in TestProfileValues)
+ {
+ IExifValue actualProfileValue = actual.GetValueInternal(expectedProfileValue.Key);
+ Assert.NotNull(actualProfileValue);
+ Assert.Equal(expectedProfileValue.Value, actualProfileValue.GetValue());
+ }
+ }
+
+ [Fact]
+ public void ProfileToByteArray()
+ {
+ // Arrange
+ byte[] exifBytesWithoutExifCode = ExifConstants.LittleEndianByteOrderMarker;
+ ExifProfile expectedProfile = CreateExifProfile();
+ var expectedProfileTags = expectedProfile.Values.Select(x => x.Tag).ToList();
+
+ // Act
+ byte[] actualBytes = expectedProfile.ToByteArray();
+ var actualProfile = new ExifProfile(actualBytes);
+
+ // Assert
+ Assert.NotNull(actualBytes);
+ Assert.NotEmpty(actualBytes);
+ Assert.Equal(exifBytesWithoutExifCode, actualBytes.Take(exifBytesWithoutExifCode.Length).ToArray());
+ foreach (ExifTag expectedProfileTag in expectedProfileTags)
+ {
+ IExifValue actualProfileValue = actualProfile.GetValueInternal(expectedProfileTag);
+ IExifValue expectedProfileValue = expectedProfile.GetValueInternal(expectedProfileTag);
+ Assert.Equal(expectedProfileValue.GetValue(), actualProfileValue.GetValue());
+ }
+ }
private static ExifProfile CreateExifProfile()
{
@@ -411,12 +416,7 @@ namespace SixLabors.ImageSharp.Tests
foreach (KeyValuePair exifProfileValue in TestProfileValues)
{
- // Manky - Answers on a postcard for a nicer way to do this.
- MethodInfo method = typeof(ExifProfile)
- .GetMethod("SetValue")
- .MakeGenericMethod(new[] { exifProfileValue.Key.GetType().GenericTypeArguments[0] });
-
- method.Invoke(profile, new[] { exifProfileValue.Key, exifProfileValue.Value });
+ profile.SetValueInternal(exifProfileValue.Key, exifProfileValue.Value);
}
return profile;
@@ -434,12 +434,15 @@ namespace SixLabors.ImageSharp.Tests
private static Image WriteAndRead(Image image, TestImageWriteFormat imageFormat)
{
- return imageFormat switch
+ switch (imageFormat)
{
- TestImageWriteFormat.Jpeg => WriteAndReadJpeg(image),
- TestImageWriteFormat.Png => WriteAndReadPng(image),
- _ => throw new ArgumentException("Unexpected test image format, only Jpeg and Png are allowed"),
- };
+ case TestImageWriteFormat.Jpeg:
+ return WriteAndReadJpeg(image);
+ case TestImageWriteFormat.Png:
+ return WriteAndReadPng(image);
+ default:
+ throw new ArgumentException("Unexpected test image format, only Jpeg and Png are allowed");
+ }
}
private static Image WriteAndReadJpeg(Image image)
@@ -486,43 +489,5 @@ namespace SixLabors.ImageSharp.Tests
IExifValue xDimension = profile.GetValue(ExifTag.PixelXDimension);
Assert.Equal(2338U, xDimension.Value);
}
-
- private static void TestValue(IExifValue value, string expected)
- {
- Assert.NotNull(value);
- Assert.Equal(expected, value.GetValue());
- }
-
- private static void TestValue(IExifValue value, Rational expected)
- {
- Assert.NotNull(value);
- Assert.Equal(expected, value.GetValue());
- }
-
- private static void TestValue(IExifValue value, SignedRational expected)
- {
- Assert.NotNull(value);
- Assert.Equal(expected, value.GetValue());
- }
-
- private static void TestValue(IExifValue value, Rational[] expected)
- {
- Assert.NotNull(value);
-
- Assert.Equal(expected, (ICollection)value.GetValue());
- }
-
- private static void TestValue(ExifValue value, double expected)
- {
- Assert.NotNull(value);
- Assert.Equal(expected, value.GetValue());
- }
-
- private static void TestValue(ExifValue value, double[] expected)
- {
- Assert.NotNull(value);
-
- Assert.Equal(expected, (ICollection)value.GetValue());
- }
}
}