Browse Source

Handle TIFF image with incorrect IPTC data type (long instead of byte)

pull/1570/head
Brian Popow 5 years ago
parent
commit
f0670af045
  1. 57
      src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs
  2. 22
      tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs
  3. 59
      tests/ImageSharp.Tests/Metadata/Profiles/IPTC/IptcProfileTests.cs
  4. 4
      tests/ImageSharp.Tests/TestImages.cs
  5. 3
      tests/Images/Input/Tiff/7324fcaff3aad96f27899da51c1bb5d9.tiff
  6. 3
      tests/Images/Input/Tiff/iptc.tiff

57
src/ImageSharp/Formats/Tiff/TiffDecoderMetadataCreator.cs

@ -1,8 +1,11 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers.Binary;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.Metadata.Profiles.Exif;
using SixLabors.ImageSharp.Metadata.Profiles.Icc; using SixLabors.ImageSharp.Metadata.Profiles.Icc;
@ -56,10 +59,9 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
if (coreMetadata.IptcProfile == null) if (coreMetadata.IptcProfile == null)
{ {
IExifValue<byte[]> val = frame.ExifProfile.GetValue(ExifTag.IPTC); if (TryGetIptc(frame.ExifProfile.Values, out var iptcBytes))
if (val != null)
{ {
coreMetadata.IptcProfile = new IptcProfile(val.Value); coreMetadata.IptcProfile = new IptcProfile(iptcBytes);
} }
} }
@ -77,6 +79,53 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
return coreMetadata; return coreMetadata;
} }
private static bool TryGetIptc(IReadOnlyList<IExifValue> exifValues, out byte[] iptcBytes)
{
iptcBytes = null;
IExifValue iptc = exifValues.FirstOrDefault(f => f.Tag == ExifTag.IPTC);
if (iptc != null)
{
if (iptc.DataType == ExifDataType.Byte)
{
iptcBytes = (byte[])iptc.GetValue();
return true;
}
else if (iptc.DataType == ExifDataType.Long)
{
var iptcValues = (uint[])iptc.GetValue();
iptcBytes = new byte[iptcValues.Length * 4];
Buffer.BlockCopy(iptcValues, 0, iptcBytes, 0, iptcValues.Length * 4);
if (iptcBytes[0] == 0x1c)
{
return true;
}
else if (iptcBytes[3] != 0x1c)
{
return false;
}
// Probably wrong endianess, swap byte order.
Span<byte> iptcBytesSpan = iptcBytes.AsSpan();
Span<byte> buffer = stackalloc byte[4];
for (int i = 0; i < iptcBytes.Length; i += 4)
{
iptcBytesSpan.Slice(i, 4).CopyTo(buffer);
iptcBytes[i] = buffer[3];
iptcBytes[i + 1] = buffer[2];
iptcBytes[i + 2] = buffer[1];
iptcBytes[i + 3] = buffer[0];
}
return true;
}
return false;
}
return false;
}
private static TiffBitsPerPixel GetBitsPerPixel(TiffFrameMetadata firstFrameMetaData) private static TiffBitsPerPixel GetBitsPerPixel(TiffFrameMetadata firstFrameMetaData)
=> (TiffBitsPerPixel)firstFrameMetaData.BitsPerPixel; => (TiffBitsPerPixel)firstFrameMetaData.BitsPerPixel;
} }

22
tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@ -24,6 +25,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
{ {
private readonly Configuration configuration; private readonly Configuration configuration;
private static TiffDecoder TiffDecoder => new TiffDecoder();
public TiffMetadataTests() public TiffMetadataTests()
{ {
this.configuration = new Configuration(); this.configuration = new Configuration();
@ -130,12 +133,25 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
} }
} }
[Theory]
[WithFile(InvalidIptcData, PixelTypes.Rgba32)]
public void CanDecodeImage_WithIptcDataAsLong<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(TiffDecoder);
Assert.NotNull(image.Metadata.IptcProfile);
IptcValue byline = image.Metadata.IptcProfile.Values.FirstOrDefault(data => data.Tag == IptcTag.Byline);
Assert.NotNull(byline);
Assert.Equal("Studio Mantyniemi", byline.Value);
}
[Theory] [Theory]
[WithFile(SampleMetadata, PixelTypes.Rgba32)] [WithFile(SampleMetadata, PixelTypes.Rgba32)]
public void BaselineTags<TPixel>(TestImageProvider<TPixel> provider) public void BaselineTags<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
using (Image<TPixel> image = provider.GetImage(new TiffDecoder())) using (Image<TPixel> image = provider.GetImage(TiffDecoder))
{ {
TiffMetadata meta = image.Metadata.GetTiffMetadata(); TiffMetadata meta = image.Metadata.GetTiffMetadata();
@ -188,7 +204,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
public void SubfileType<TPixel>(TestImageProvider<TPixel> provider) public void SubfileType<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
using (Image<TPixel> image = provider.GetImage(new TiffDecoder())) using (Image<TPixel> image = provider.GetImage(TiffDecoder))
{ {
TiffMetadata meta = image.Metadata.GetTiffMetadata(); TiffMetadata meta = image.Metadata.GetTiffMetadata();
Assert.NotNull(meta); Assert.NotNull(meta);
@ -311,7 +327,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
coreMeta.HorizontalResolution = 4500; coreMeta.HorizontalResolution = 4500;
coreMeta.VerticalResolution = 5400; coreMeta.VerticalResolution = 5400;
var datetime = DateTime.Now.ToString(); var datetime = DateTime.Now.ToString(CultureInfo.InvariantCulture);
frameMeta.ImageDescription = "test ImageDescription"; frameMeta.ImageDescription = "test ImageDescription";
frameMeta.DateTime = datetime; frameMeta.DateTime = datetime;

59
tests/ImageSharp.Tests/Metadata/Profiles/IPTC/IptcProfileTests.cs

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using SixLabors.ImageSharp.Formats.Experimental.Tiff;
using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Metadata.Profiles.Iptc; using SixLabors.ImageSharp.Metadata.Profiles.Iptc;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -16,6 +17,8 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.IPTC
{ {
private static JpegDecoder JpegDecoder => new JpegDecoder() { IgnoreMetadata = false }; private static JpegDecoder JpegDecoder => new JpegDecoder() { IgnoreMetadata = false };
private static TiffDecoder TiffDecoder => new TiffDecoder() { IgnoreMetadata = false };
public static IEnumerable<object[]> AllIptcTags() public static IEnumerable<object[]> AllIptcTags()
{ {
foreach (object tag in Enum.GetValues(typeof(IptcTag))) foreach (object tag in Enum.GetValues(typeof(IptcTag)))
@ -100,34 +103,52 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.IPTC
[Theory] [Theory]
[WithFile(TestImages.Jpeg.Baseline.Iptc, PixelTypes.Rgba32)] [WithFile(TestImages.Jpeg.Baseline.Iptc, PixelTypes.Rgba32)]
public void ReadIptcMetadata_Works<TPixel>(TestImageProvider<TPixel> provider) public void ReadIptcMetadata_FromJpg_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
using (Image<TPixel> image = provider.GetImage(JpegDecoder)) using (Image<TPixel> image = provider.GetImage(JpegDecoder))
{ {
Assert.NotNull(image.Metadata.IptcProfile); Assert.NotNull(image.Metadata.IptcProfile);
var iptcValues = image.Metadata.IptcProfile.Values.ToList(); var iptcValues = image.Metadata.IptcProfile.Values.ToList();
ContainsIptcValue(iptcValues, IptcTag.Caption, "description"); IptcProfileContainsExpectedValues(iptcValues);
ContainsIptcValue(iptcValues, IptcTag.CaptionWriter, "description writer");
ContainsIptcValue(iptcValues, IptcTag.Headline, "headline");
ContainsIptcValue(iptcValues, IptcTag.SpecialInstructions, "special instructions");
ContainsIptcValue(iptcValues, IptcTag.Byline, "author");
ContainsIptcValue(iptcValues, IptcTag.BylineTitle, "author title");
ContainsIptcValue(iptcValues, IptcTag.Credit, "credits");
ContainsIptcValue(iptcValues, IptcTag.Source, "source");
ContainsIptcValue(iptcValues, IptcTag.Name, "title");
ContainsIptcValue(iptcValues, IptcTag.CreatedDate, "20200414");
ContainsIptcValue(iptcValues, IptcTag.City, "city");
ContainsIptcValue(iptcValues, IptcTag.SubLocation, "sublocation");
ContainsIptcValue(iptcValues, IptcTag.ProvinceState, "province-state");
ContainsIptcValue(iptcValues, IptcTag.Country, "country");
ContainsIptcValue(iptcValues, IptcTag.Category, "category");
ContainsIptcValue(iptcValues, IptcTag.Urgency, "1");
ContainsIptcValue(iptcValues, IptcTag.Keywords, "keywords");
ContainsIptcValue(iptcValues, IptcTag.CopyrightNotice, "copyright");
} }
} }
[Theory]
[WithFile(TestImages.Tiff.IptcData, PixelTypes.Rgba32)]
public void ReadIptcMetadata_FromTiff_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(TiffDecoder))
{
Assert.NotNull(image.Metadata.IptcProfile);
var iptcValues = image.Metadata.IptcProfile.Values.ToList();
IptcProfileContainsExpectedValues(iptcValues);
}
}
private static void IptcProfileContainsExpectedValues(List<IptcValue> iptcValues)
{
ContainsIptcValue(iptcValues, IptcTag.Caption, "description");
ContainsIptcValue(iptcValues, IptcTag.CaptionWriter, "description writer");
ContainsIptcValue(iptcValues, IptcTag.Headline, "headline");
ContainsIptcValue(iptcValues, IptcTag.SpecialInstructions, "special instructions");
ContainsIptcValue(iptcValues, IptcTag.Byline, "author");
ContainsIptcValue(iptcValues, IptcTag.BylineTitle, "author title");
ContainsIptcValue(iptcValues, IptcTag.Credit, "credits");
ContainsIptcValue(iptcValues, IptcTag.Source, "source");
ContainsIptcValue(iptcValues, IptcTag.Name, "title");
ContainsIptcValue(iptcValues, IptcTag.CreatedDate, "20200414");
ContainsIptcValue(iptcValues, IptcTag.City, "city");
ContainsIptcValue(iptcValues, IptcTag.SubLocation, "sublocation");
ContainsIptcValue(iptcValues, IptcTag.ProvinceState, "province-state");
ContainsIptcValue(iptcValues, IptcTag.Country, "country");
ContainsIptcValue(iptcValues, IptcTag.Category, "category");
ContainsIptcValue(iptcValues, IptcTag.Urgency, "1");
ContainsIptcValue(iptcValues, IptcTag.Keywords, "keywords");
ContainsIptcValue(iptcValues, IptcTag.CopyrightNotice, "copyright");
}
[Theory] [Theory]
[WithFile(TestImages.Jpeg.Baseline.App13WithEmptyIptc, PixelTypes.Rgba32)] [WithFile(TestImages.Jpeg.Baseline.App13WithEmptyIptc, PixelTypes.Rgba32)]
public void ReadApp13_WithEmptyIptc_Works<TPixel>(TestImageProvider<TPixel> provider) public void ReadApp13_WithEmptyIptc_Works<TPixel>(TestImageProvider<TPixel> provider)

4
tests/ImageSharp.Tests/TestImages.cs

@ -574,6 +574,10 @@ namespace SixLabors.ImageSharp.Tests
public const string SampleMetadata = "Tiff/metadata_sample.tiff"; public const string SampleMetadata = "Tiff/metadata_sample.tiff";
// Iptc data as long[] instead of byte[]
public const string InvalidIptcData = "Tiff/7324fcaff3aad96f27899da51c1bb5d9.tiff";
public const string IptcData = "Tiff/iptc.tiff";
public static readonly string[] Multiframes = { MultiframeDeflateWithPreview, MultiframeLzwPredictor /*, MultiFrameDifferentSize, MultiframeDifferentSizeTiled, MultiFrameDifferentVariants,*/ }; public static readonly string[] Multiframes = { MultiframeDeflateWithPreview, MultiframeLzwPredictor /*, MultiFrameDifferentSize, MultiframeDifferentSizeTiled, MultiFrameDifferentVariants,*/ };
public static readonly string[] Metadata = { SampleMetadata }; public static readonly string[] Metadata = { SampleMetadata };

3
tests/Images/Input/Tiff/7324fcaff3aad96f27899da51c1bb5d9.tiff

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:622d69dba0a8a67aa3b87e384a2b9ea8d29689eaa5cb5d0eee857f98ed660517
size 15154924

3
tests/Images/Input/Tiff/iptc.tiff

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d7a8d89df97b35ab3be9745a345687defd427bf4ca519ad3c5a52208d98f7694
size 15170
Loading…
Cancel
Save