-
-
Notifications
You must be signed in to change notification settings - Fork 852
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
EXIF Support for PNG's #616
Changes from 6 commits
cd44c22
39e3122
89f60fc
4ed1df8
820f30c
34e2443
9e6c3e5
fd02990
5594177
e26ea49
8a2c8d9
bc9ba35
48178b8
5a7926d
a0a0142
88e41ea
9d81e7c
53336ea
7e688ee
976aad3
4b484f9
caf7fb3
565d885
c228f94
f32e68a
700fde4
94da0b3
f544e18
e6bcb2b
685b5c5
ff160a7
a5d576e
35aaaee
cb6f4a0
7d7e518
c4e81de
a995021
8834f15
efd0288
7f21da4
7e8b464
9aba104
21a7c78
659bc7c
6c777d4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,7 +3,6 @@ | |
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Collections.ObjectModel; | ||
using System.IO; | ||
using SixLabors.ImageSharp.PixelFormats; | ||
using SixLabors.ImageSharp.Primitives; | ||
|
@@ -233,8 +232,10 @@ public void SetValue(ExifTag tag, object value) | |
/// <summary> | ||
/// Converts this instance to a byte array. | ||
/// </summary> | ||
/// <param name="includeExifIdCode">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.</param> | ||
/// <returns>The <see cref="T:byte[]"/></returns> | ||
public byte[] ToByteArray() | ||
public byte[] ToByteArray(bool includeExifIdCode = true) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I want to move the Exif identifier out of the http://www.exif.org/Exif2-2.PDF Ideally we should pass the marker to the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok, i will do that on the weekend. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fantastic, thanks! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pass through the code as a |
||
{ | ||
if (this.values == null) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should also check if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. good point, i missed that one. I have added a unit test for that case. |
||
{ | ||
|
@@ -247,7 +248,7 @@ public byte[] ToByteArray() | |
} | ||
|
||
var writer = new ExifWriter(this.values, this.Parts); | ||
return writer.GetData(); | ||
return writer.GetData(includeExifIdCode); | ||
} | ||
|
||
/// <summary> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -78,6 +78,7 @@ public List<ExifValue> ReadValues() | |
|
||
if (this.ReadString(4) == "Exif") | ||
{ | ||
// two zeros are expected to follow the Exif Id code | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we really need this comment? |
||
if (this.ReadUInt16() != 0) | ||
{ | ||
return values; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,11 +14,6 @@ namespace SixLabors.ImageSharp.MetaData.Profiles.Exif | |
/// </summary> | ||
internal sealed class ExifWriter | ||
{ | ||
/// <summary> | ||
/// The start index. | ||
/// </summary> | ||
private const int StartIndex = 6; | ||
|
||
/// <summary> | ||
/// Which parts will be written. | ||
/// </summary> | ||
|
@@ -46,11 +41,14 @@ public ExifWriter(IList<ExifValue> values, ExifParts allowedParts) | |
/// <summary> | ||
/// Returns the EXIF data. | ||
/// </summary> | ||
/// <param name="includeExifIdCode">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.</param> | ||
/// <returns> | ||
/// The <see cref="T:byte[]"/>. | ||
/// </returns> | ||
public byte[] GetData() | ||
public byte[] GetData(bool includeExifIdCode = true) | ||
{ | ||
uint startIndex = 6; | ||
uint length; | ||
int exifIndex = -1; | ||
int gpsIndex = -1; | ||
|
@@ -86,23 +84,51 @@ public byte[] GetData() | |
return null; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. im not sure about this one, i think this was supposed to return null, if only the Exif Id code is present. Is that correct? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @dlemstra What do you think? |
||
} | ||
|
||
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'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could also increment There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i have changed that |
||
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 @@ public byte[] GetData() | |
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 @@ private int WriteArray(ExifValue value, byte[] destination, int offset) | |
return newOffset; | ||
} | ||
|
||
private int WriteData(List<int> indexes, byte[] destination, int offset) | ||
private int WriteData(uint startIndex, List<int> indexes, byte[] destination, int offset) | ||
{ | ||
if (this.dataOffsets.Count == 0) | ||
{ | ||
|
@@ -281,7 +307,7 @@ private int WriteData(List<int> indexes, byte[] destination, int offset) | |
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); | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to alter this slightly as there's extra checks and balances.
image.MetaData.SyncProfiles();
is required before writing according to the jpeg encoder and we need additional length checks as the spec dictates it's length should match jpeg limitations.ImageSharp/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
Line 608 in d171acb
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i have added the SyncProfile before writing the chunk.
For the length check: I could add a check, if the length exceeds 2^16, like the JpegEncoder does, and throw an exception, but that feels wrong to me. This restriction is Jpeg specific not PNG specific.
In the recommendations they state, when writing PNG Exif to Jpeg:
But how to decide what to cut off here is not clear to me. I really would like to hear @dlemstra opinion on that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at this it appears we should simply the split data into 64k chunks. http://dev.exiv2.org/issues/1232
That means we need an extend method, like the ICC readeron decoding.
ImageSharp/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs
Line 508 in d7bd82b