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; return;
} }
var identifier = new byte[ProfileResolver.AdobePhotoshopApp13Marker.Length]; this.InputStream.Read(this.temp, 0, ProfileResolver.AdobePhotoshopApp13Marker.Length);
this.InputStream.Read(identifier, 0, identifier.Length); remaining -= ProfileResolver.AdobePhotoshopApp13Marker.Length;
remaining -= identifier.Length; if (ProfileResolver.IsProfile(this.temp, ProfileResolver.AdobePhotoshopApp13Marker))
if (ProfileResolver.IsProfile(identifier, ProfileResolver.AdobePhotoshopApp13Marker))
{ {
var resourceBlockData = new byte[remaining]; var resourceBlockData = new byte[remaining];
this.InputStream.Read(resourceBlockData, 0, remaining); this.InputStream.Read(resourceBlockData, 0, remaining);
Span<byte> blockDataSpan = resourceBlockData.AsSpan(); Span<byte> blockDataSpan = resourceBlockData.AsSpan();
while (blockDataSpan.Length > 10) while (blockDataSpan.Length > 12)
{ {
if (!ProfileResolver.IsProfile(blockDataSpan.Slice(0, 4), ProfileResolver.AdobeImageResourceBlockMarker)) if (!ProfileResolver.IsProfile(blockDataSpan.Slice(0, 4), ProfileResolver.AdobeImageResourceBlockMarker))
{ {
return; return;

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

@ -233,7 +233,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
// Write the Start Of Image marker. // Write the Start Of Image marker.
this.WriteApplicationHeader(metadata); this.WriteApplicationHeader(metadata);
// Write Exif and ICC profiles // Write Exif, ICC and IPTC profiles
this.WriteProfiles(metadata); this.WriteProfiles(metadata);
// Write the quantization tables. // Write the quantization tables.
@ -708,18 +708,21 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
} }
iptcProfile.UpdateData(); iptcProfile.UpdateData();
var data = iptcProfile.Data; byte[] data = iptcProfile.Data;
if (data.Length == 0) if (data.Length == 0)
{ {
return; return;
} }
var app13length = data.Length + ProfileResolver.AdobeImageResourceBlockMarker.Length + ProfileResolver.AdobeIptcMarker.Length + 2 + 4; var app13Length = 2 + ProfileResolver.AdobePhotoshopApp13Marker.Length +
this.WriteAppHeader(app13length, JpegConstants.Markers.APP13); ProfileResolver.AdobeImageResourceBlockMarker.Length +
this.outputStream.Write(this.buffer, 0, 4); 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.AdobeImageResourceBlockMarker);
this.outputStream.Write(ProfileResolver.AdobeIptcMarker); 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); this.outputStream.WriteByte(0);
BinaryPrimitives.WriteInt32BigEndian(this.buffer, data.Length); BinaryPrimitives.WriteInt32BigEndian(this.buffer, data.Length);
this.outputStream.Write(this.buffer, 0, 4); 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. // Licensed under the Apache License, Version 2.0.
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Metadata.Profiles.Iptc; using SixLabors.ImageSharp.Metadata.Profiles.Iptc;
@ -16,13 +17,13 @@ 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 ReadIptcProfile<TPixel>(TestImageProvider<TPixel> provider) public void ReadIptcMetadata_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);
IEnumerable<IptcValue> iptcValues = image.Metadata.IptcProfile.Values; var iptcValues = image.Metadata.IptcProfile.Values.ToList();
ContainsIptcValue(iptcValues, IptcTag.Caption, "description"); ContainsIptcValue(iptcValues, IptcTag.Caption, "description");
ContainsIptcValue(iptcValues, IptcTag.CaptionWriter, "description writer"); ContainsIptcValue(iptcValues, IptcTag.CaptionWriter, "description writer");
ContainsIptcValue(iptcValues, IptcTag.Headline, "headline"); 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] [Fact]
public void IptcProfile_CloneIsDeep() public void IptcProfile_CloneIsDeep()
{ {
@ -79,10 +101,44 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.IPTC
Assert.NotEqual(iptcValue.Value, clone.Value); 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) 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.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}'"); 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