From 814982e418946e8313f5cdcad4219f11e27787a2 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 26 Apr 2020 16:19:50 +0200 Subject: [PATCH 1/3] Identify now parses zTXt and iTXt chunks --- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 6 ++ .../Formats/Png/PngMetadataTests.cs | 59 +++++++++++-------- 2 files changed, 42 insertions(+), 23 deletions(-) diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 0247dba351..7d9011a55a 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -273,6 +273,12 @@ namespace SixLabors.ImageSharp.Formats.Png 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.End: this.isEndChunkReached = true; break; diff --git a/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs index 5f5d5fd3d7..ee4e4f3c27 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs @@ -56,17 +56,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png using (Image 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); } } @@ -85,17 +75,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png using (Image image = decoder.Decode(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); } } } @@ -178,7 +158,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png IgnoreMetadata = true }; - var testFile = TestFile.Create(TestImages.Png.Blur); + var testFile = TestFile.Create(TestImages.Png.PngWithMetadata); using (Image image = testFile.CreateRgba32Image(options)) { @@ -220,5 +200,38 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png 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); + } + } + + 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")); + } } } From b9307edd9d42bddd1e61cb56fcd65a6db556598b Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 26 Apr 2020 17:32:22 +0200 Subject: [PATCH 2/3] Identify reads now exif data --- src/ImageSharp/Formats/Png/PngDecoderCore.cs | 9 +++ .../Formats/Png/PngMetadataTests.cs | 58 +++++++++++++++++++ tests/Images/Input/Png/PngWithMetaData.png | 4 +- 3 files changed, 69 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 7d9011a55a..a5bcff3b2b 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -278,6 +278,15 @@ namespace SixLabors.ImageSharp.Formats.Png 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; diff --git a/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs index ee4e4f3c27..bf42066002 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngMetadataTests.cs @@ -6,6 +6,7 @@ using System.IO; using System.Linq; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Metadata; +using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; using Xunit; @@ -129,6 +130,40 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png } } + [Theory] + [WithFile(TestImages.Png.PngWithMetadata, PixelTypes.Rgba32)] + public void Decode_ReadsExifData(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + var decoder = new PngDecoder + { + IgnoreMetadata = false + }; + + using (Image 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(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + var decoder = new PngDecoder + { + IgnoreMetadata = true + }; + + using (Image image = provider.GetImage(decoder)) + { + Assert.Null(image.Metadata.ExifProfile); + } + } + [Fact] public void Decode_IgnoreMetadataIsFalse_TextChunkIsRead() { @@ -215,6 +250,29 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png } } + [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 software = exif.GetValue(ExifTag.Software); + Assert.NotNull(software); + Assert.Equal("ImageSharp", software.Value); + } + private static void VerifyTextDataIsPresent(PngMetadata meta) { Assert.NotNull(meta); diff --git a/tests/Images/Input/Png/PngWithMetaData.png b/tests/Images/Input/Png/PngWithMetaData.png index 54c08ca42c..b9404cd8a3 100644 --- a/tests/Images/Input/Png/PngWithMetaData.png +++ b/tests/Images/Input/Png/PngWithMetaData.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c0490f627b22a3487b78e2797ebb65f5741fdbabfd4a3d9db806ca624f62fe8c -size 805 +oid sha256:a2350c200d0d05c18ac70f3d6dcb2adb16a49aca6407dc02bb6a865e0b5b836d +size 751 From 54560b1614d1e773e7b68733ac175ea18b0b727a Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 26 Apr 2020 20:29:30 +0200 Subject: [PATCH 3/3] Fix png test image: Some text chunks unintentional changed to none compressed --- tests/Images/Input/Png/PngWithMetaData.png | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Images/Input/Png/PngWithMetaData.png b/tests/Images/Input/Png/PngWithMetaData.png index b9404cd8a3..16be2b0e85 100644 --- a/tests/Images/Input/Png/PngWithMetaData.png +++ b/tests/Images/Input/Png/PngWithMetaData.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a2350c200d0d05c18ac70f3d6dcb2adb16a49aca6407dc02bb6a865e0b5b836d -size 751 +oid sha256:c92a4ea43f50a7b5f5a492991c0a619ab639c4e802bf4ae0c2433a066e8c05a7 +size 777