diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs
index f9ca8ebc6..de7e51e09 100644
--- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs
+++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs
@@ -398,7 +398,7 @@ namespace SixLabors.ImageSharp.Formats.Png
///
/// Calculates the correct number of bytes per pixel for the given color type.
///
- /// The
+ /// Bytes per pixel
private int CalculateBytesPerPixel()
{
switch (this.pngColorType)
@@ -535,7 +535,7 @@ namespace SixLabors.ImageSharp.Formats.Png
{
if (image.MetaData.ExifProfile?.Values.Count > 0)
{
- this.WriteChunk(stream, PngChunkType.Exif, image.MetaData.ExifProfile.RawData);
+ this.WriteChunk(stream, PngChunkType.Exif, image.MetaData.ExifProfile.ToByteArray(includeExifIdCode: false));
}
}
diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs
index d3f97c46c..5ba194634 100644
--- a/src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs
+++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs
@@ -94,11 +94,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
///
public ExifParts Parts { get; set; }
- ///
- /// Gets the byte data array containing the exif data.
- ///
- public byte[] RawData => this.data;
-
///
/// Gets the tags that where found but contained an invalid value.
///
@@ -237,8 +232,10 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
///
/// Converts this instance to a byte array.
///
+ /// Indicates, if the Exif ID code should be included.
+ /// This Exif ID code should not be included in case of PNG's. Defaults to true.
/// The
- public byte[] ToByteArray()
+ public byte[] ToByteArray(bool includeExifIdCode = true)
{
if (this.values == null)
{
@@ -251,7 +248,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
}
var writer = new ExifWriter(this.values, this.Parts);
- return writer.GetData();
+ return writer.GetData(includeExifIdCode);
}
///
diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs
index 4f28449d6..755d92939 100644
--- a/src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs
+++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs
@@ -78,6 +78,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
if (this.ReadString(4) == "Exif")
{
+ // two zeros are expected to follow the Exif Id code
if (this.ReadUInt16() != 0)
{
return values;
diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs
index f7363ef31..64e4442c3 100644
--- a/src/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs
+++ b/src/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs
@@ -14,11 +14,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
///
internal sealed class ExifWriter
{
- ///
- /// The start index.
- ///
- private const int StartIndex = 6;
-
///
/// Which parts will be written.
///
@@ -46,11 +41,14 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
///
/// Returns the EXIF data.
///
+ /// Indicates, if the Exif ID code should be included.
+ /// This Exif ID code should not be included in case of PNG's. Defaults to true.
///
/// The .
///
- public byte[] GetData()
+ public byte[] GetData(bool includeExifIdCode = true)
{
+ uint startIndex = 6;
uint length;
int exifIndex = -1;
int gpsIndex = -1;
@@ -86,23 +84,51 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
return null;
}
- length += 10 + 4 + 2;
+ if (includeExifIdCode)
+ {
+ // Exif Code (6 bytes) + byte order marker (4 bytes)
+ length += 10;
+ }
+ else
+ {
+ // special case for PNG eXIf Chunk:
+ // two bytes for the byte Order marker 'II', followed by the number 42 (0x2A) and a 0, making 4 bytes total
+ length += 4;
+
+ // if the Exif Code ("Exif00") is not included, the start index is 0 instead of 6
+ startIndex = 0;
+ }
+
+ length += 4 + 2;
byte[] result = new byte[length];
- result[0] = (byte)'E';
- result[1] = (byte)'x';
- result[2] = (byte)'i';
- result[3] = (byte)'f';
- result[4] = 0x00;
- result[5] = 0x00;
- result[6] = (byte)'I';
- result[7] = (byte)'I';
- result[8] = 0x2A;
- result[9] = 0x00;
-
- int i = 10;
- uint ifdOffset = ((uint)i - StartIndex) + 4;
+ int i = 0;
+ if (includeExifIdCode)
+ {
+ result[0] = (byte)'E';
+ result[1] = (byte)'x';
+ result[2] = (byte)'i';
+ result[3] = (byte)'f';
+ result[4] = 0x00;
+ result[5] = 0x00;
+ result[6] = (byte)'I';
+ result[7] = (byte)'I';
+ result[8] = 0x2A;
+ result[9] = 0x00;
+ i = 10;
+ }
+ else
+ {
+ // the byte order marker followed by the number 42 and a 0
+ result[0] = (byte)'I';
+ result[1] = (byte)'I';
+ result[2] = 0x2A;
+ result[3] = 0x00;
+ i = 4;
+ }
+
+ uint ifdOffset = ((uint)i - startIndex) + 4;
uint thumbnailOffset = ifdOffset + ifdLength + exifLength + gpsLength;
if (exifLength > 0)
@@ -118,18 +144,18 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
i = WriteUInt32(ifdOffset, result, i);
i = this.WriteHeaders(this.ifdIndexes, result, i);
i = WriteUInt32(thumbnailOffset, result, i);
- i = this.WriteData(this.ifdIndexes, result, i);
+ i = this.WriteData(startIndex, this.ifdIndexes, result, i);
if (exifLength > 0)
{
i = this.WriteHeaders(this.exifIndexes, result, i);
- i = this.WriteData(this.exifIndexes, result, i);
+ i = this.WriteData(startIndex, this.exifIndexes, result, i);
}
if (gpsLength > 0)
{
i = this.WriteHeaders(this.gpsIndexes, result, i);
- i = this.WriteData(this.gpsIndexes, result, i);
+ i = this.WriteData(startIndex, this.gpsIndexes, result, i);
}
WriteUInt16((ushort)0, result, i);
@@ -266,7 +292,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
return newOffset;
}
- private int WriteData(List indexes, byte[] destination, int offset)
+ private int WriteData(uint startIndex, List indexes, byte[] destination, int offset)
{
if (this.dataOffsets.Count == 0)
{
@@ -281,7 +307,7 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif
ExifValue value = this.values[index];
if (value.Length > 4)
{
- WriteUInt32((uint)(newOffset - StartIndex), destination, this.dataOffsets[i++]);
+ WriteUInt32((uint)(newOffset - startIndex), destination, this.dataOffsets[i++]);
newOffset = this.WriteValue(value, destination, newOffset);
}
}
diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs
index 3dd38b6d2..ddd852295 100644
--- a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs
+++ b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections;
+using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
@@ -17,6 +18,16 @@ namespace SixLabors.ImageSharp.Tests
{
public class ExifProfileTests
{
+ private static readonly Dictionary TestProfileValues = new Dictionary()
+ {
+ { ExifTag.Software, "Software" },
+ { ExifTag.Model, "Model" },
+ { ExifTag.Copyright, "Copyright" },
+ { ExifTag.Orientation, (ushort)5 },
+ { ExifTag.ShutterSpeedValue, new SignedRational(75.55) },
+ { ExifTag.ExposureTime, new Rational(1.0 / 1600.0) },
+ };
+
[Fact]
public void Constructor()
{
@@ -27,7 +38,7 @@ namespace SixLabors.ImageSharp.Tests
image.MetaData.ExifProfile = new ExifProfile();
image.MetaData.ExifProfile.SetValue(ExifTag.Copyright, "Dirk Lemstra");
- image = WriteAndRead(image);
+ image = WriteAndReadJpeg(image);
Assert.NotNull(image.MetaData.ExifProfile);
Assert.Equal(1, image.MetaData.ExifProfile.Values.Count());
@@ -50,7 +61,7 @@ namespace SixLabors.ImageSharp.Tests
ExifProfile profile = GetExifProfile();
- ExifProfile clone = new ExifProfile(profile);
+ var clone = new ExifProfile(profile);
TestProfile(clone);
profile.SetValue(ExifTag.ColorSpace, (ushort)2);
@@ -62,7 +73,7 @@ namespace SixLabors.ImageSharp.Tests
[Fact]
public void WriteFraction()
{
- using (MemoryStream memStream = new MemoryStream())
+ using (var memStream = new MemoryStream())
{
double exposureTime = 1.0 / 1600;
@@ -70,7 +81,7 @@ namespace SixLabors.ImageSharp.Tests
profile.SetValue(ExifTag.ExposureTime, new Rational(exposureTime));
- Image image = new Image(1, 1);
+ var image = new Image(1, 1);
image.MetaData.ExifProfile = profile;
image.SaveAsJpeg(memStream);
@@ -110,21 +121,21 @@ namespace SixLabors.ImageSharp.Tests
Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateImage();
image.MetaData.ExifProfile.SetValue(ExifTag.ExposureBiasValue, new SignedRational(double.PositiveInfinity));
- image = WriteAndRead(image);
+ image = WriteAndReadJpeg(image);
ExifValue value = image.MetaData.ExifProfile.GetValue(ExifTag.ExposureBiasValue);
Assert.NotNull(value);
Assert.Equal(new SignedRational(double.PositiveInfinity), value.Value);
image.MetaData.ExifProfile.SetValue(ExifTag.ExposureBiasValue, new SignedRational(double.NegativeInfinity));
- image = WriteAndRead(image);
+ image = WriteAndReadJpeg(image);
value = image.MetaData.ExifProfile.GetValue(ExifTag.ExposureBiasValue);
Assert.NotNull(value);
Assert.Equal(new SignedRational(double.NegativeInfinity), value.Value);
image.MetaData.ExifProfile.SetValue(ExifTag.FlashEnergy, new Rational(double.NegativeInfinity));
- image = WriteAndRead(image);
+ image = WriteAndReadJpeg(image);
value = image.MetaData.ExifProfile.GetValue(ExifTag.FlashEnergy);
Assert.NotNull(value);
Assert.Equal(new Rational(double.PositiveInfinity), value.Value);
@@ -133,7 +144,7 @@ namespace SixLabors.ImageSharp.Tests
[Fact]
public void SetValue()
{
- Rational[] latitude = new Rational[] { new Rational(12.3), new Rational(4.56), new Rational(789.0) };
+ var latitude = new Rational[] { new Rational(12.3), new Rational(4.56), new Rational(789.0) };
Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateImage();
image.MetaData.ExifProfile.SetValue(ExifTag.Software, "ImageSharp");
@@ -171,7 +182,7 @@ namespace SixLabors.ImageSharp.Tests
value = image.MetaData.ExifProfile.GetValue(ExifTag.GPSLatitude);
TestValue(value, latitude);
- image = WriteAndRead(image);
+ image = WriteAndReadJpeg(image);
Assert.NotNull(image.MetaData.ExifProfile);
Assert.Equal(17, image.MetaData.ExifProfile.Values.Count());
@@ -193,7 +204,7 @@ namespace SixLabors.ImageSharp.Tests
image.MetaData.ExifProfile.Parts = ExifParts.ExifTags;
- image = WriteAndRead(image);
+ image = WriteAndReadJpeg(image);
Assert.NotNull(image.MetaData.ExifProfile);
Assert.Equal(8, image.MetaData.ExifProfile.Values.Count());
@@ -312,17 +323,34 @@ namespace SixLabors.ImageSharp.Tests
public void TestWritingPngPreservesExifProfile()
{
// arrange
- Image image = TestFile.Create(TestImages.Png.Bike).CreateImage();
- ExifProfile expected = GetExifProfile();
+ var image = new Image(1, 1);
+ ExifProfile expected = CreateExifProfile();
image.MetaData.ExifProfile = expected;
// act
- Image reloadedImage = WritePngAndRead(image);
-
+ Image reloadedImage = WriteAndReadPng(image);
+
// assert
ExifProfile actual = reloadedImage.MetaData.ExifProfile;
Assert.NotNull(actual);
- TestProfile(actual);
+ foreach(KeyValuePair expectedProfileValue in TestProfileValues)
+ {
+ ExifValue actualProfileValue = actual.GetValue(expectedProfileValue.Key);
+ Assert.NotNull(actualProfileValue);
+ Assert.Equal(expectedProfileValue.Value, actualProfileValue.Value);
+ }
+ }
+
+ private static ExifProfile CreateExifProfile()
+ {
+ var profile = new ExifProfile();
+
+ foreach(KeyValuePair exifProfileValue in TestProfileValues)
+ {
+ profile.SetValue(exifProfileValue.Key, exifProfileValue.Value);
+ }
+
+ return profile;
}
private static ExifProfile GetExifProfile()
@@ -335,7 +363,7 @@ namespace SixLabors.ImageSharp.Tests
return profile;
}
- private static Image WriteAndRead(Image image)
+ private static Image WriteAndReadJpeg(Image image)
{
using (var memStream = new MemoryStream())
{
@@ -347,7 +375,7 @@ namespace SixLabors.ImageSharp.Tests
}
}
- private static Image WritePngAndRead(Image image)
+ private static Image WriteAndReadPng(Image image)
{
using (var memStream = new MemoryStream())
{
@@ -370,13 +398,19 @@ namespace SixLabors.ImageSharp.Tests
Assert.NotNull(value.Value);
if (value.Tag == ExifTag.Software)
+ {
Assert.Equal("Windows Photo Editor 10.0.10011.16384", value.ToString());
+ }
if (value.Tag == ExifTag.XResolution)
+ {
Assert.Equal(new Rational(300.0), value.Value);
+ }
if (value.Tag == ExifTag.PixelXDimension)
+ {
Assert.Equal(2338U, value.Value);
+ }
}
}