Skip to content

Commit

Permalink
Merge pull request #1187 from SixLabors/bp/identifyText
Browse files Browse the repository at this point in the history
PNG Identify reads zTXt, iTXt and exif cunks
  • Loading branch information
brianpopow committed Apr 26, 2020
2 parents f2f014e + 386d942 commit bec0b55
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 23 deletions.
15 changes: 15 additions & 0 deletions src/ImageSharp/Formats/Png/PngDecoderCore.cs
Expand Up @@ -272,6 +272,21 @@ public IImageInfo Identify(Stream stream)
break;
case PngChunkType.Text:
this.ReadTextChunk(pngMetadata, chunk.Data.Array.AsSpan(0, chunk.Length));
break;
case PngChunkType.CompressedText:
this.ReadCompressedTextChunk(pngMetadata, chunk.Data.Array.AsSpan(0, chunk.Length));
break;
case PngChunkType.InternationalText:
this.ReadInternationalTextChunk(pngMetadata, chunk.Data.Array.AsSpan(0, chunk.Length));
break;
case PngChunkType.Exif:
if (!this.ignoreMetadata)
{
var exifData = new byte[chunk.Length];
Buffer.BlockCopy(chunk.Data.Array, 0, exifData, 0, chunk.Length);
metadata.ExifProfile = new ExifProfile(exifData);
}

break;
case PngChunkType.End:
this.isEndChunkReached = true;
Expand Down
117 changes: 94 additions & 23 deletions tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs
Expand Up @@ -6,6 +6,7 @@
using System.Linq;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.Metadata.Profiles.Exif;
using SixLabors.ImageSharp.PixelFormats;
using Xunit;

Expand Down Expand Up @@ -56,17 +57,7 @@ public void Decoder_CanReadTextData<TPixel>(TestImageProvider<TPixel> provider)
using (Image<TPixel> image = provider.GetImage(new PngDecoder()))
{
PngMetadata meta = image.Metadata.GetFormatMetadata(PngFormat.Instance);
Assert.Contains(meta.TextData, m => m.Keyword.Equals("Comment") && m.Value.Equals("comment"));
Assert.Contains(meta.TextData, m => m.Keyword.Equals("Author") && m.Value.Equals("ImageSharp"));
Assert.Contains(meta.TextData, m => m.Keyword.Equals("Copyright") && m.Value.Equals("ImageSharp"));
Assert.Contains(meta.TextData, m => m.Keyword.Equals("Title") && m.Value.Equals("unittest"));
Assert.Contains(meta.TextData, m => m.Keyword.Equals("Description") && m.Value.Equals("compressed-text"));
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"));
Assert.Contains(meta.TextData, m => m.Keyword.Equals("International2") && m.Value.Equals("ИМАГЕШАРП") && m.LanguageTag.Equals("rus"));
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"));
Assert.Contains(meta.TextData, m => m.Keyword.Equals("CompressedInternational2") && m.Value.Equals("這是一個考驗") && m.LanguageTag.Equals("chinese"));
Assert.Contains(meta.TextData, m => m.Keyword.Equals("NoLang") && m.Value.Equals("this text chunk is missing a language tag"));
Assert.Contains(meta.TextData, m => m.Keyword.Equals("NoTranslatedKeyword") && m.Value.Equals("dieser chunk hat kein übersetztes Schlüßelwort"));
VerifyTextDataIsPresent(meta);
}
}

Expand All @@ -85,17 +76,7 @@ public void Encoder_PreservesTextData<TPixel>(TestImageProvider<TPixel> provider
using (Image<Rgba32> image = decoder.Decode<Rgba32>(Configuration.Default, memoryStream))
{
PngMetadata meta = image.Metadata.GetFormatMetadata(PngFormat.Instance);
Assert.Contains(meta.TextData, m => m.Keyword.Equals("Comment") && m.Value.Equals("comment"));
Assert.Contains(meta.TextData, m => m.Keyword.Equals("Author") && m.Value.Equals("ImageSharp"));
Assert.Contains(meta.TextData, m => m.Keyword.Equals("Copyright") && m.Value.Equals("ImageSharp"));
Assert.Contains(meta.TextData, m => m.Keyword.Equals("Title") && m.Value.Equals("unittest"));
Assert.Contains(meta.TextData, m => m.Keyword.Equals("Description") && m.Value.Equals("compressed-text"));
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"));
Assert.Contains(meta.TextData, m => m.Keyword.Equals("International2") && m.Value.Equals("ИМАГЕШАРП") && m.LanguageTag.Equals("rus"));
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"));
Assert.Contains(meta.TextData, m => m.Keyword.Equals("CompressedInternational2") && m.Value.Equals("這是一個考驗") && m.LanguageTag.Equals("chinese"));
Assert.Contains(meta.TextData, m => m.Keyword.Equals("NoLang") && m.Value.Equals("this text chunk is missing a language tag"));
Assert.Contains(meta.TextData, m => m.Keyword.Equals("NoTranslatedKeyword") && m.Value.Equals("dieser chunk hat kein übersetztes Schlüßelwort"));
VerifyTextDataIsPresent(meta);
}
}
}
Expand Down Expand Up @@ -149,6 +130,40 @@ public void Encode_UseCompression_WhenTextIsGreaterThenThreshold_Works<TPixel>(T
}
}

[Theory]
[WithFile(TestImages.Png.PngWithMetadata, PixelTypes.Rgba32)]
public void Decode_ReadsExifData<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
var decoder = new PngDecoder
{
IgnoreMetadata = false
};

using (Image<TPixel> image = provider.GetImage(decoder))
{
Assert.NotNull(image.Metadata.ExifProfile);
ExifProfile exif = image.Metadata.ExifProfile;
VerifyExifDataIsPresent(exif);
}
}

[Theory]
[WithFile(TestImages.Png.PngWithMetadata, PixelTypes.Rgba32)]
public void Decode_IgnoresExifData_WhenIgnoreMetadataIsTrue<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
var decoder = new PngDecoder
{
IgnoreMetadata = true
};

using (Image<TPixel> image = provider.GetImage(decoder))
{
Assert.Null(image.Metadata.ExifProfile);
}
}

[Fact]
public void Decode_IgnoreMetadataIsFalse_TextChunkIsRead()
{
Expand Down Expand Up @@ -178,7 +193,7 @@ public void Decode_IgnoreMetadataIsTrue_TextChunksAreIgnored()
IgnoreMetadata = true
};

var testFile = TestFile.Create(TestImages.Png.Blur);
var testFile = TestFile.Create(TestImages.Png.PngWithMetadata);

using (Image<Rgba32> image = testFile.CreateRgba32Image(options))
{
Expand Down Expand Up @@ -220,5 +235,61 @@ public void Identify_VerifyRatio(string imagePath, int xResolution, int yResolut
Assert.Equal(resolutionUnit, meta.ResolutionUnits);
}
}

[Theory]
[InlineData(TestImages.Png.PngWithMetadata)]
public void Identify_ReadsTextData(string imagePath)
{
var testFile = TestFile.Create(imagePath);
using (var stream = new MemoryStream(testFile.Bytes, false))
{
IImageInfo imageInfo = Image.Identify(stream);
Assert.NotNull(imageInfo);
PngMetadata meta = imageInfo.Metadata.GetFormatMetadata(PngFormat.Instance);
VerifyTextDataIsPresent(meta);
}
}

[Theory]
[InlineData(TestImages.Png.PngWithMetadata)]
public void Identify_ReadsExifData(string imagePath)
{
var testFile = TestFile.Create(imagePath);
using (var stream = new MemoryStream(testFile.Bytes, false))
{
IImageInfo imageInfo = Image.Identify(stream);
Assert.NotNull(imageInfo);
Assert.NotNull(imageInfo.Metadata.ExifProfile);
ExifProfile exif = imageInfo.Metadata.ExifProfile;
VerifyExifDataIsPresent(exif);
}
}

private static void VerifyExifDataIsPresent(ExifProfile exif)
{
Assert.Equal(1, exif.Values.Count);
IExifValue<string> software = exif.GetValue(ExifTag.Software);
Assert.NotNull(software);
Assert.Equal("ImageSharp", software.Value);
}

private static void VerifyTextDataIsPresent(PngMetadata meta)
{
Assert.NotNull(meta);
Assert.Contains(meta.TextData, m => m.Keyword.Equals("Comment") && m.Value.Equals("comment"));
Assert.Contains(meta.TextData, m => m.Keyword.Equals("Author") && m.Value.Equals("ImageSharp"));
Assert.Contains(meta.TextData, m => m.Keyword.Equals("Copyright") && m.Value.Equals("ImageSharp"));
Assert.Contains(meta.TextData, m => m.Keyword.Equals("Title") && m.Value.Equals("unittest"));
Assert.Contains(meta.TextData, m => m.Keyword.Equals("Description") && m.Value.Equals("compressed-text"));
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"));
Assert.Contains(meta.TextData, m => m.Keyword.Equals("International2") && m.Value.Equals("ИМАГЕШАРП") && m.LanguageTag.Equals("rus"));
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"));
Assert.Contains(meta.TextData, m => m.Keyword.Equals("CompressedInternational2") && m.Value.Equals("這是一個考驗") &&
m.LanguageTag.Equals("chinese"));
Assert.Contains(meta.TextData, m => m.Keyword.Equals("NoLang") && m.Value.Equals("this text chunk is missing a language tag"));
Assert.Contains(meta.TextData, m => m.Keyword.Equals("NoTranslatedKeyword") && m.Value.Equals("dieser chunk hat kein übersetztes Schlüßelwort"));
}
}
}
Binary file modified tests/Images/Input/Png/PngWithMetaData.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit bec0b55

Please sign in to comment.