Skip to content

Commit bec0b55

Browse files
authored
Merge pull request #1187 from SixLabors/bp/identifyText
PNG Identify reads zTXt, iTXt and exif cunks
2 parents f2f014e + 386d942 commit bec0b55

3 files changed

Lines changed: 109 additions & 23 deletions

File tree

src/ImageSharp/Formats/Png/PngDecoderCore.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,21 @@ public IImageInfo Identify(Stream stream)
272272
break;
273273
case PngChunkType.Text:
274274
this.ReadTextChunk(pngMetadata, chunk.Data.Array.AsSpan(0, chunk.Length));
275+
break;
276+
case PngChunkType.CompressedText:
277+
this.ReadCompressedTextChunk(pngMetadata, chunk.Data.Array.AsSpan(0, chunk.Length));
278+
break;
279+
case PngChunkType.InternationalText:
280+
this.ReadInternationalTextChunk(pngMetadata, chunk.Data.Array.AsSpan(0, chunk.Length));
281+
break;
282+
case PngChunkType.Exif:
283+
if (!this.ignoreMetadata)
284+
{
285+
var exifData = new byte[chunk.Length];
286+
Buffer.BlockCopy(chunk.Data.Array, 0, exifData, 0, chunk.Length);
287+
metadata.ExifProfile = new ExifProfile(exifData);
288+
}
289+
275290
break;
276291
case PngChunkType.End:
277292
this.isEndChunkReached = true;

tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs

Lines changed: 94 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Linq;
77
using SixLabors.ImageSharp.Formats.Png;
88
using SixLabors.ImageSharp.Metadata;
9+
using SixLabors.ImageSharp.Metadata.Profiles.Exif;
910
using SixLabors.ImageSharp.PixelFormats;
1011
using Xunit;
1112

@@ -56,17 +57,7 @@ public void Decoder_CanReadTextData<TPixel>(TestImageProvider<TPixel> provider)
5657
using (Image<TPixel> image = provider.GetImage(new PngDecoder()))
5758
{
5859
PngMetadata meta = image.Metadata.GetFormatMetadata(PngFormat.Instance);
59-
Assert.Contains(meta.TextData, m => m.Keyword.Equals("Comment") && m.Value.Equals("comment"));
60-
Assert.Contains(meta.TextData, m => m.Keyword.Equals("Author") && m.Value.Equals("ImageSharp"));
61-
Assert.Contains(meta.TextData, m => m.Keyword.Equals("Copyright") && m.Value.Equals("ImageSharp"));
62-
Assert.Contains(meta.TextData, m => m.Keyword.Equals("Title") && m.Value.Equals("unittest"));
63-
Assert.Contains(meta.TextData, m => m.Keyword.Equals("Description") && m.Value.Equals("compressed-text"));
64-
Assert.Contains(meta.TextData, m => m.Keyword.Equals("International") && m.Value.Equals("'e', mu'tlheghvam, ghaH yu'") && m.LanguageTag.Equals("x-klingon") && m.TranslatedKeyword.Equals("warning"));
65-
Assert.Contains(meta.TextData, m => m.Keyword.Equals("International2") && m.Value.Equals("ИМАГЕШАРП") && m.LanguageTag.Equals("rus"));
66-
Assert.Contains(meta.TextData, m => m.Keyword.Equals("CompressedInternational") && m.Value.Equals("la plume de la mante") && m.LanguageTag.Equals("fra") && m.TranslatedKeyword.Equals("foobar"));
67-
Assert.Contains(meta.TextData, m => m.Keyword.Equals("CompressedInternational2") && m.Value.Equals("這是一個考驗") && m.LanguageTag.Equals("chinese"));
68-
Assert.Contains(meta.TextData, m => m.Keyword.Equals("NoLang") && m.Value.Equals("this text chunk is missing a language tag"));
69-
Assert.Contains(meta.TextData, m => m.Keyword.Equals("NoTranslatedKeyword") && m.Value.Equals("dieser chunk hat kein übersetztes Schlüßelwort"));
60+
VerifyTextDataIsPresent(meta);
7061
}
7162
}
7263

@@ -85,17 +76,7 @@ public void Encoder_PreservesTextData<TPixel>(TestImageProvider<TPixel> provider
8576
using (Image<Rgba32> image = decoder.Decode<Rgba32>(Configuration.Default, memoryStream))
8677
{
8778
PngMetadata meta = image.Metadata.GetFormatMetadata(PngFormat.Instance);
88-
Assert.Contains(meta.TextData, m => m.Keyword.Equals("Comment") && m.Value.Equals("comment"));
89-
Assert.Contains(meta.TextData, m => m.Keyword.Equals("Author") && m.Value.Equals("ImageSharp"));
90-
Assert.Contains(meta.TextData, m => m.Keyword.Equals("Copyright") && m.Value.Equals("ImageSharp"));
91-
Assert.Contains(meta.TextData, m => m.Keyword.Equals("Title") && m.Value.Equals("unittest"));
92-
Assert.Contains(meta.TextData, m => m.Keyword.Equals("Description") && m.Value.Equals("compressed-text"));
93-
Assert.Contains(meta.TextData, m => m.Keyword.Equals("International") && m.Value.Equals("'e', mu'tlheghvam, ghaH yu'") && m.LanguageTag.Equals("x-klingon") && m.TranslatedKeyword.Equals("warning"));
94-
Assert.Contains(meta.TextData, m => m.Keyword.Equals("International2") && m.Value.Equals("ИМАГЕШАРП") && m.LanguageTag.Equals("rus"));
95-
Assert.Contains(meta.TextData, m => m.Keyword.Equals("CompressedInternational") && m.Value.Equals("la plume de la mante") && m.LanguageTag.Equals("fra") && m.TranslatedKeyword.Equals("foobar"));
96-
Assert.Contains(meta.TextData, m => m.Keyword.Equals("CompressedInternational2") && m.Value.Equals("這是一個考驗") && m.LanguageTag.Equals("chinese"));
97-
Assert.Contains(meta.TextData, m => m.Keyword.Equals("NoLang") && m.Value.Equals("this text chunk is missing a language tag"));
98-
Assert.Contains(meta.TextData, m => m.Keyword.Equals("NoTranslatedKeyword") && m.Value.Equals("dieser chunk hat kein übersetztes Schlüßelwort"));
79+
VerifyTextDataIsPresent(meta);
9980
}
10081
}
10182
}
@@ -149,6 +130,40 @@ public void Encode_UseCompression_WhenTextIsGreaterThenThreshold_Works<TPixel>(T
149130
}
150131
}
151132

133+
[Theory]
134+
[WithFile(TestImages.Png.PngWithMetadata, PixelTypes.Rgba32)]
135+
public void Decode_ReadsExifData<TPixel>(TestImageProvider<TPixel> provider)
136+
where TPixel : unmanaged, IPixel<TPixel>
137+
{
138+
var decoder = new PngDecoder
139+
{
140+
IgnoreMetadata = false
141+
};
142+
143+
using (Image<TPixel> image = provider.GetImage(decoder))
144+
{
145+
Assert.NotNull(image.Metadata.ExifProfile);
146+
ExifProfile exif = image.Metadata.ExifProfile;
147+
VerifyExifDataIsPresent(exif);
148+
}
149+
}
150+
151+
[Theory]
152+
[WithFile(TestImages.Png.PngWithMetadata, PixelTypes.Rgba32)]
153+
public void Decode_IgnoresExifData_WhenIgnoreMetadataIsTrue<TPixel>(TestImageProvider<TPixel> provider)
154+
where TPixel : unmanaged, IPixel<TPixel>
155+
{
156+
var decoder = new PngDecoder
157+
{
158+
IgnoreMetadata = true
159+
};
160+
161+
using (Image<TPixel> image = provider.GetImage(decoder))
162+
{
163+
Assert.Null(image.Metadata.ExifProfile);
164+
}
165+
}
166+
152167
[Fact]
153168
public void Decode_IgnoreMetadataIsFalse_TextChunkIsRead()
154169
{
@@ -178,7 +193,7 @@ public void Decode_IgnoreMetadataIsTrue_TextChunksAreIgnored()
178193
IgnoreMetadata = true
179194
};
180195

181-
var testFile = TestFile.Create(TestImages.Png.Blur);
196+
var testFile = TestFile.Create(TestImages.Png.PngWithMetadata);
182197

183198
using (Image<Rgba32> image = testFile.CreateRgba32Image(options))
184199
{
@@ -220,5 +235,61 @@ public void Identify_VerifyRatio(string imagePath, int xResolution, int yResolut
220235
Assert.Equal(resolutionUnit, meta.ResolutionUnits);
221236
}
222237
}
238+
239+
[Theory]
240+
[InlineData(TestImages.Png.PngWithMetadata)]
241+
public void Identify_ReadsTextData(string imagePath)
242+
{
243+
var testFile = TestFile.Create(imagePath);
244+
using (var stream = new MemoryStream(testFile.Bytes, false))
245+
{
246+
IImageInfo imageInfo = Image.Identify(stream);
247+
Assert.NotNull(imageInfo);
248+
PngMetadata meta = imageInfo.Metadata.GetFormatMetadata(PngFormat.Instance);
249+
VerifyTextDataIsPresent(meta);
250+
}
251+
}
252+
253+
[Theory]
254+
[InlineData(TestImages.Png.PngWithMetadata)]
255+
public void Identify_ReadsExifData(string imagePath)
256+
{
257+
var testFile = TestFile.Create(imagePath);
258+
using (var stream = new MemoryStream(testFile.Bytes, false))
259+
{
260+
IImageInfo imageInfo = Image.Identify(stream);
261+
Assert.NotNull(imageInfo);
262+
Assert.NotNull(imageInfo.Metadata.ExifProfile);
263+
ExifProfile exif = imageInfo.Metadata.ExifProfile;
264+
VerifyExifDataIsPresent(exif);
265+
}
266+
}
267+
268+
private static void VerifyExifDataIsPresent(ExifProfile exif)
269+
{
270+
Assert.Equal(1, exif.Values.Count);
271+
IExifValue<string> software = exif.GetValue(ExifTag.Software);
272+
Assert.NotNull(software);
273+
Assert.Equal("ImageSharp", software.Value);
274+
}
275+
276+
private static void VerifyTextDataIsPresent(PngMetadata meta)
277+
{
278+
Assert.NotNull(meta);
279+
Assert.Contains(meta.TextData, m => m.Keyword.Equals("Comment") && m.Value.Equals("comment"));
280+
Assert.Contains(meta.TextData, m => m.Keyword.Equals("Author") && m.Value.Equals("ImageSharp"));
281+
Assert.Contains(meta.TextData, m => m.Keyword.Equals("Copyright") && m.Value.Equals("ImageSharp"));
282+
Assert.Contains(meta.TextData, m => m.Keyword.Equals("Title") && m.Value.Equals("unittest"));
283+
Assert.Contains(meta.TextData, m => m.Keyword.Equals("Description") && m.Value.Equals("compressed-text"));
284+
Assert.Contains(meta.TextData, m => m.Keyword.Equals("International") && m.Value.Equals("'e', mu'tlheghvam, ghaH yu'") &&
285+
m.LanguageTag.Equals("x-klingon") && m.TranslatedKeyword.Equals("warning"));
286+
Assert.Contains(meta.TextData, m => m.Keyword.Equals("International2") && m.Value.Equals("ИМАГЕШАРП") && m.LanguageTag.Equals("rus"));
287+
Assert.Contains(meta.TextData, m => m.Keyword.Equals("CompressedInternational") && m.Value.Equals("la plume de la mante") &&
288+
m.LanguageTag.Equals("fra") && m.TranslatedKeyword.Equals("foobar"));
289+
Assert.Contains(meta.TextData, m => m.Keyword.Equals("CompressedInternational2") && m.Value.Equals("這是一個考驗") &&
290+
m.LanguageTag.Equals("chinese"));
291+
Assert.Contains(meta.TextData, m => m.Keyword.Equals("NoLang") && m.Value.Equals("this text chunk is missing a language tag"));
292+
Assert.Contains(meta.TextData, m => m.Keyword.Equals("NoTranslatedKeyword") && m.Value.Equals("dieser chunk hat kein übersetztes Schlüßelwort"));
293+
}
223294
}
224295
}
-28 Bytes
Loading

0 commit comments

Comments
 (0)