Browse Source

Add tests for writing IPTC

pull/1574/head
Brian Popow 6 years ago
parent
commit
44d0657f8b
  1. 11
      src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
  2. 15
      src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
  3. 60
      tests/ImageSharp.Tests/Metadata/Profiles/IPTC/IptcProfileTests.cs

11
src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs

@ -623,17 +623,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
return;
}
var identifier = new byte[ProfileResolver.AdobePhotoshopApp13Marker.Length];
this.InputStream.Read(identifier, 0, identifier.Length);
remaining -= identifier.Length;
if (ProfileResolver.IsProfile(identifier, ProfileResolver.AdobePhotoshopApp13Marker))
this.InputStream.Read(this.temp, 0, ProfileResolver.AdobePhotoshopApp13Marker.Length);
remaining -= ProfileResolver.AdobePhotoshopApp13Marker.Length;
if (ProfileResolver.IsProfile(this.temp, ProfileResolver.AdobePhotoshopApp13Marker))
{
var resourceBlockData = new byte[remaining];
this.InputStream.Read(resourceBlockData, 0, remaining);
Span<byte> blockDataSpan = resourceBlockData.AsSpan();
while (blockDataSpan.Length > 10)
{
while (blockDataSpan.Length > 12)
{
if (!ProfileResolver.IsProfile(blockDataSpan.Slice(0, 4), ProfileResolver.AdobeImageResourceBlockMarker))
{
return;

15
src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs

@ -233,7 +233,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
// Write the Start Of Image marker.
this.WriteApplicationHeader(metadata);
// Write Exif and ICC profiles
// Write Exif, ICC and IPTC profiles
this.WriteProfiles(metadata);
// Write the quantization tables.
@ -708,18 +708,21 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
}
iptcProfile.UpdateData();
var data = iptcProfile.Data;
byte[] data = iptcProfile.Data;
if (data.Length == 0)
{
return;
}
var app13length = data.Length + ProfileResolver.AdobeImageResourceBlockMarker.Length + ProfileResolver.AdobeIptcMarker.Length + 2 + 4;
this.WriteAppHeader(app13length, JpegConstants.Markers.APP13);
this.outputStream.Write(this.buffer, 0, 4);
var app13Length = 2 + ProfileResolver.AdobePhotoshopApp13Marker.Length +
ProfileResolver.AdobeImageResourceBlockMarker.Length +
ProfileResolver.AdobeIptcMarker.Length +
2 + 4 + data.Length;
this.WriteAppHeader(app13Length, JpegConstants.Markers.APP13);
this.outputStream.Write(ProfileResolver.AdobePhotoshopApp13Marker);
this.outputStream.Write(ProfileResolver.AdobeImageResourceBlockMarker);
this.outputStream.Write(ProfileResolver.AdobeIptcMarker);
this.outputStream.WriteByte(0); // a null name consists of two bytes of 0.
this.outputStream.WriteByte(0); // a empty pascal string (padded to make size even)
this.outputStream.WriteByte(0);
BinaryPrimitives.WriteInt32BigEndian(this.buffer, data.Length);
this.outputStream.Write(this.buffer, 0, 4);

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

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System.Collections.Generic;
using System.IO;
using System.Linq;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Metadata.Profiles.Iptc;
@ -16,13 +17,13 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.IPTC
[Theory]
[WithFile(TestImages.Jpeg.Baseline.Iptc, PixelTypes.Rgba32)]
public void ReadIptcProfile<TPixel>(TestImageProvider<TPixel> provider)
public void ReadIptcMetadata_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(JpegDecoder))
{
Assert.NotNull(image.Metadata.IptcProfile);
IEnumerable<IptcValue> iptcValues = image.Metadata.IptcProfile.Values;
var iptcValues = image.Metadata.IptcProfile.Values.ToList();
ContainsIptcValue(iptcValues, IptcTag.Caption, "description");
ContainsIptcValue(iptcValues, IptcTag.CaptionWriter, "description writer");
ContainsIptcValue(iptcValues, IptcTag.Headline, "headline");
@ -44,6 +45,27 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.IPTC
}
}
[Fact]
public void IptcProfile_ToAndFromByteArray_Works()
{
// arrange
var profile = new IptcProfile();
var expectedCaptionWriter = "unittest";
var expectedCaption = "test";
profile.SetValue(IptcTag.CaptionWriter, expectedCaptionWriter);
profile.SetValue(IptcTag.Caption, expectedCaption);
// act
profile.UpdateData();
byte[] profileBytes = profile.Data;
var profileFromBytes = new IptcProfile(profileBytes);
// assert
var iptcValues = profileFromBytes.Values.ToList();
ContainsIptcValue(iptcValues, IptcTag.CaptionWriter, expectedCaptionWriter);
ContainsIptcValue(iptcValues, IptcTag.Caption, expectedCaption);
}
[Fact]
public void IptcProfile_CloneIsDeep()
{
@ -79,10 +101,44 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.IPTC
Assert.NotEqual(iptcValue.Value, clone.Value);
}
[Fact]
public void WritingImage_PreservesIptcProfile()
{
// arrange
var image = new Image<Rgba32>(1, 1);
image.Metadata.IptcProfile = new IptcProfile();
var expectedCaptionWriter = "unittest";
var expectedCaption = "test";
image.Metadata.IptcProfile.SetValue(IptcTag.CaptionWriter, expectedCaptionWriter);
image.Metadata.IptcProfile.SetValue(IptcTag.Caption, expectedCaption);
// act
Image<Rgba32> reloadedImage = WriteAndReadJpeg(image);
// assert
IptcProfile actual = reloadedImage.Metadata.IptcProfile;
Assert.NotNull(actual);
var iptcValues = actual.Values.ToList();
ContainsIptcValue(iptcValues, IptcTag.CaptionWriter, expectedCaptionWriter);
ContainsIptcValue(iptcValues, IptcTag.Caption, expectedCaption);
}
private static void ContainsIptcValue(IEnumerable<IptcValue> values, IptcTag tag, string value)
{
Assert.True(values.Any(val => val.Tag == tag), $"Missing iptc tag {tag}");
Assert.True(values.Contains(new IptcValue(tag, System.Text.Encoding.UTF8.GetBytes(value))), $"expected iptc value '{value}' was not found for tag '{tag}'");
}
private static Image<Rgba32> WriteAndReadJpeg(Image<Rgba32> image)
{
using (var memStream = new MemoryStream())
{
image.SaveAsJpeg(memStream);
image.Dispose();
memStream.Position = 0;
return Image.Load<Rgba32>(memStream);
}
}
}
}

Loading…
Cancel
Save