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.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.Metadata.Profiles.Exif;
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
@ -56,10 +59,9 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
if (coreMetadata.IptcProfile == null)
{
IExifValue<byte[]> val = frame.ExifProfile.GetValue(ExifTag.IPTC);
if (val != null)
if (TryGetIptc(frame.ExifProfile.Values, out var iptcBytes))
{
coreMetadata.IptcProfile = new IptcProfile(val.Value);
coreMetadata.IptcProfile = new IptcProfile(iptcBytes);
}
}
@ -77,6 +79,53 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
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)
=> (TiffBitsPerPixel)firstFrameMetaData.BitsPerPixel;
}

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

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Globalization;
using System.IO;
using System.Linq;
@ -24,6 +25,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
{
private readonly Configuration configuration;
private static TiffDecoder TiffDecoder => new TiffDecoder();
public TiffMetadataTests()
{
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]
[WithFile(SampleMetadata, PixelTypes.Rgba32)]
public void BaselineTags<TPixel>(TestImageProvider<TPixel> provider)
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();
@ -188,7 +204,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
public void SubfileType<TPixel>(TestImageProvider<TPixel> provider)
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();
Assert.NotNull(meta);
@ -311,7 +327,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
coreMeta.HorizontalResolution = 4500;
coreMeta.VerticalResolution = 5400;
var datetime = DateTime.Now.ToString();
var datetime = DateTime.Now.ToString(CultureInfo.InvariantCulture);
frameMeta.ImageDescription = "test ImageDescription";
frameMeta.DateTime = datetime;

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

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using SixLabors.ImageSharp.Formats.Experimental.Tiff;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Metadata.Profiles.Iptc;
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 TiffDecoder TiffDecoder => new TiffDecoder() { IgnoreMetadata = false };
public static IEnumerable<object[]> AllIptcTags()
{
foreach (object tag in Enum.GetValues(typeof(IptcTag)))
@ -100,34 +103,52 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.IPTC
[Theory]
[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>
{
using (Image<TPixel> image = provider.GetImage(JpegDecoder))
{
Assert.NotNull(image.Metadata.IptcProfile);
var iptcValues = image.Metadata.IptcProfile.Values.ToList();
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");
IptcProfileContainsExpectedValues(iptcValues);
}
}
[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]
[WithFile(TestImages.Jpeg.Baseline.App13WithEmptyIptc, PixelTypes.Rgba32)]
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";
// 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[] 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