Browse Source

Some IPTC tags can now be added multiple times, some are restricted from doing so

pull/1174/head
Brian Popow 6 years ago
parent
commit
9822c3a7eb
  1. 61
      src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs
  2. 107
      src/ImageSharp/Metadata/Profiles/IPTC/IptcTag.cs
  3. 89
      tests/ImageSharp.Tests/Metadata/Profiles/IPTC/IptcProfileTests.cs

61
src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs

@ -34,6 +34,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc
public IptcProfile(byte[] data) public IptcProfile(byte[] data)
{ {
this.Data = data; this.Data = data;
this.Initialize();
} }
private IptcProfile(IptcProfile other) private IptcProfile(IptcProfile other)
@ -99,7 +100,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc
/// Removes the value with the specified tag. /// Removes the value with the specified tag.
/// </summary> /// </summary>
/// <param name="tag">The tag of the iptc value.</param> /// <param name="tag">The tag of the iptc value.</param>
/// <returns>True when the value was fount and removed.</returns> /// <returns>True when the value was found and removed.</returns>
public bool RemoveValue(IptcTag tag) public bool RemoveValue(IptcTag tag)
{ {
this.Initialize(); this.Initialize();
@ -140,13 +141,16 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc
{ {
Guard.NotNull(encoding, nameof(encoding)); Guard.NotNull(encoding, nameof(encoding));
foreach (IptcValue iptcValue in this.Values) if (!this.IsRepeatable(tag))
{ {
if (iptcValue.Tag == tag) foreach (IptcValue iptcValue in this.Values)
{ {
iptcValue.Encoding = encoding; if (iptcValue.Tag == tag)
iptcValue.Value = value; {
return; iptcValue.Encoding = encoding;
iptcValue.Value = value;
return;
}
} }
} }
@ -228,5 +232,50 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc
i += count; i += count;
} }
} }
private bool IsRepeatable(IptcTag tag)
{
switch (tag)
{
case IptcTag.RecordVersion:
case IptcTag.ObjectType:
case IptcTag.Name:
case IptcTag.EditStatus:
case IptcTag.EditorialUpdate:
case IptcTag.Urgency:
case IptcTag.Category:
case IptcTag.FixtureIdentifier:
case IptcTag.ReleaseDate:
case IptcTag.ReleaseTime:
case IptcTag.ExpirationDate:
case IptcTag.ExpirationTime:
case IptcTag.SpecialInstructions:
case IptcTag.ActionAdvised:
case IptcTag.CreatedDate:
case IptcTag.CreatedTime:
case IptcTag.DigitalCreationDate:
case IptcTag.DigitalCreationTime:
case IptcTag.OriginatingProgram:
case IptcTag.ProgramVersion:
case IptcTag.ObjectCycle:
case IptcTag.City:
case IptcTag.SubLocation:
case IptcTag.ProvinceState:
case IptcTag.CountryCode:
case IptcTag.Country:
case IptcTag.OriginalTransmissionReference:
case IptcTag.Headline:
case IptcTag.Credit:
case IptcTag.Source:
case IptcTag.CopyrightNotice:
case IptcTag.Caption:
case IptcTag.ImageType:
case IptcTag.ImageOrientation:
return false;
default:
return true;
}
}
} }
} }

107
src/ImageSharp/Metadata/Profiles/IPTC/IptcTag.cs

@ -9,242 +9,247 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc
public enum IptcTag public enum IptcTag
{ {
/// <summary> /// <summary>
/// Unknown /// Unknown.
/// </summary> /// </summary>
Unknown = -1, Unknown = -1,
/// <summary> /// <summary>
/// Record version /// Record version, not repeatable.
/// </summary> /// </summary>
RecordVersion = 0, RecordVersion = 0,
/// <summary> /// <summary>
/// Object type /// Object type, not repeatable.
/// </summary> /// </summary>
ObjectType = 3, ObjectType = 3,
/// <summary> /// <summary>
/// Object attribute /// Object attribute.
/// </summary> /// </summary>
ObjectAttribute = 4, ObjectAttribute = 4,
/// <summary> /// <summary>
/// Title /// Object Name, not repeatable.
/// </summary> /// </summary>
Title = 5, Name = 5,
/// <summary> /// <summary>
/// Edit status /// Edit status, not repeatable.
/// </summary> /// </summary>
EditStatus = 7, EditStatus = 7,
/// <summary> /// <summary>
/// Editorial update /// Editorial update, not repeatable.
/// </summary> /// </summary>
EditorialUpdate = 8, EditorialUpdate = 8,
/// <summary> /// <summary>
/// Priority /// Urgency, not repeatable.
/// </summary> /// </summary>
Priority = 10, Urgency = 10,
/// <summary> /// <summary>
/// Category /// Subject Reference.
/// </summary>
SubjectReference = 12,
/// <summary>
/// Category, not repeatable.
/// </summary> /// </summary>
Category = 15, Category = 15,
/// <summary> /// <summary>
/// Supplemental categories /// Supplemental categories.
/// </summary> /// </summary>
SupplementalCategories = 20, SupplementalCategories = 20,
/// <summary> /// <summary>
/// Fixture identifier /// Fixture identifier, not repeatable.
/// </summary> /// </summary>
FixtureIdentifier = 22, FixtureIdentifier = 22,
/// <summary> /// <summary>
/// Keyword /// Keywords.
/// </summary> /// </summary>
Keyword = 25, Keywords = 25,
/// <summary> /// <summary>
/// Location code /// Location code.
/// </summary> /// </summary>
LocationCode = 26, LocationCode = 26,
/// <summary> /// <summary>
/// Location name /// Location name.
/// </summary> /// </summary>
LocationName = 27, LocationName = 27,
/// <summary> /// <summary>
/// Release date /// Release date, not repeatable.
/// </summary> /// </summary>
ReleaseDate = 30, ReleaseDate = 30,
/// <summary> /// <summary>
/// Release time /// Release time, not repeatable.
/// </summary> /// </summary>
ReleaseTime = 35, ReleaseTime = 35,
/// <summary> /// <summary>
/// Expiration date /// Expiration date, not repeatable.
/// </summary> /// </summary>
ExpirationDate = 37, ExpirationDate = 37,
/// <summary> /// <summary>
/// Expiration time /// Expiration time, not repeatable.
/// </summary> /// </summary>
ExpirationTime = 38, ExpirationTime = 38,
/// <summary> /// <summary>
/// Special instructions /// Special instructions, not repeatable.
/// </summary> /// </summary>
SpecialInstructions = 40, SpecialInstructions = 40,
/// <summary> /// <summary>
/// Action advised /// Action advised, not repeatable.
/// </summary> /// </summary>
ActionAdvised = 42, ActionAdvised = 42,
/// <summary> /// <summary>
/// Reference service /// Reference service.
/// </summary> /// </summary>
ReferenceService = 45, ReferenceService = 45,
/// <summary> /// <summary>
/// Reference date /// Reference date.
/// </summary> /// </summary>
ReferenceDate = 47, ReferenceDate = 47,
/// <summary> /// <summary>
/// ReferenceNumber /// ReferenceNumber.
/// </summary> /// </summary>
ReferenceNumber = 50, ReferenceNumber = 50,
/// <summary> /// <summary>
/// Created date /// Created date, not repeatable.
/// </summary> /// </summary>
CreatedDate = 55, CreatedDate = 55,
/// <summary> /// <summary>
/// Created time /// Created time, not repeatable.
/// </summary> /// </summary>
CreatedTime = 60, CreatedTime = 60,
/// <summary> /// <summary>
/// Digital creation date /// Digital creation date, not repeatable.
/// </summary> /// </summary>
DigitalCreationDate = 62, DigitalCreationDate = 62,
/// <summary> /// <summary>
/// Digital creation time /// Digital creation time, not repeatable.
/// </summary> /// </summary>
DigitalCreationTime = 63, DigitalCreationTime = 63,
/// <summary> /// <summary>
/// Originating program /// Originating program, not repeatable.
/// </summary> /// </summary>
OriginatingProgram = 65, OriginatingProgram = 65,
/// <summary> /// <summary>
/// Program version /// Program version, not repeatable.
/// </summary> /// </summary>
ProgramVersion = 70, ProgramVersion = 70,
/// <summary> /// <summary>
/// Object cycle /// Object cycle, not repeatable.
/// </summary> /// </summary>
ObjectCycle = 75, ObjectCycle = 75,
/// <summary> /// <summary>
/// Byline /// Byline.
/// </summary> /// </summary>
Byline = 80, Byline = 80,
/// <summary> /// <summary>
/// Byline title /// Byline title.
/// </summary> /// </summary>
BylineTitle = 85, BylineTitle = 85,
/// <summary> /// <summary>
/// City /// City, not repeatable.
/// </summary> /// </summary>
City = 90, City = 90,
/// <summary> /// <summary>
/// Sub location /// Sub location, not repeatable.
/// </summary> /// </summary>
SubLocation = 92, SubLocation = 92,
/// <summary> /// <summary>
/// Province/State /// Province/State, not repeatable.
/// </summary> /// </summary>
ProvinceState = 95, ProvinceState = 95,
/// <summary> /// <summary>
/// Country code /// Country code, not repeatable.
/// </summary> /// </summary>
CountryCode = 100, CountryCode = 100,
/// <summary> /// <summary>
/// Country /// Country, not repeatable.
/// </summary> /// </summary>
Country = 101, Country = 101,
/// <summary> /// <summary>
/// Original transmission reference /// Original transmission reference, not repeatable.
/// </summary> /// </summary>
OriginalTransmissionReference = 103, OriginalTransmissionReference = 103,
/// <summary> /// <summary>
/// Headline /// Headline, not repeatable.
/// </summary> /// </summary>
Headline = 105, Headline = 105,
/// <summary> /// <summary>
/// Credit /// Credit, not repeatable.
/// </summary> /// </summary>
Credit = 110, Credit = 110,
/// <summary> /// <summary>
/// Source /// Source, not repeatable.
/// </summary> /// </summary>
Source = 115, Source = 115,
/// <summary> /// <summary>
/// Copyright notice /// Copyright notice, not repeatable.
/// </summary> /// </summary>
CopyrightNotice = 116, CopyrightNotice = 116,
/// <summary> /// <summary>
/// Contact /// Contact.
/// </summary> /// </summary>
Contact = 118, Contact = 118,
/// <summary> /// <summary>
/// Caption /// Caption, not repeatable.
/// </summary> /// </summary>
Caption = 120, Caption = 120,
/// <summary> /// <summary>
/// Local caption /// Local caption.
/// </summary> /// </summary>
LocalCaption = 121, LocalCaption = 121,
/// <summary> /// <summary>
/// Caption writer /// Caption writer.
/// </summary> /// </summary>
CaptionWriter = 122, CaptionWriter = 122,
/// <summary> /// <summary>
/// Image type /// Image type, not repeatable.
/// </summary> /// </summary>
ImageType = 130, ImageType = 130,
/// <summary> /// <summary>
/// Image orientation /// Image orientation, not repeatable.
/// </summary> /// </summary>
ImageOrientation = 131, ImageOrientation = 131,

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

@ -32,15 +32,15 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.IPTC
ContainsIptcValue(iptcValues, IptcTag.BylineTitle, "author title"); ContainsIptcValue(iptcValues, IptcTag.BylineTitle, "author title");
ContainsIptcValue(iptcValues, IptcTag.Credit, "credits"); ContainsIptcValue(iptcValues, IptcTag.Credit, "credits");
ContainsIptcValue(iptcValues, IptcTag.Source, "source"); ContainsIptcValue(iptcValues, IptcTag.Source, "source");
ContainsIptcValue(iptcValues, IptcTag.Title, "title"); ContainsIptcValue(iptcValues, IptcTag.Name, "title");
ContainsIptcValue(iptcValues, IptcTag.CreatedDate, "20200414"); ContainsIptcValue(iptcValues, IptcTag.CreatedDate, "20200414");
ContainsIptcValue(iptcValues, IptcTag.City, "city"); ContainsIptcValue(iptcValues, IptcTag.City, "city");
ContainsIptcValue(iptcValues, IptcTag.SubLocation, "sublocation"); ContainsIptcValue(iptcValues, IptcTag.SubLocation, "sublocation");
ContainsIptcValue(iptcValues, IptcTag.ProvinceState, "province-state"); ContainsIptcValue(iptcValues, IptcTag.ProvinceState, "province-state");
ContainsIptcValue(iptcValues, IptcTag.Country, "country"); ContainsIptcValue(iptcValues, IptcTag.Country, "country");
ContainsIptcValue(iptcValues, IptcTag.Category, "category"); ContainsIptcValue(iptcValues, IptcTag.Category, "category");
ContainsIptcValue(iptcValues, IptcTag.Priority, "1"); ContainsIptcValue(iptcValues, IptcTag.Urgency, "1");
ContainsIptcValue(iptcValues, IptcTag.Keyword, "keywords"); ContainsIptcValue(iptcValues, IptcTag.Keywords, "keywords");
ContainsIptcValue(iptcValues, IptcTag.CopyrightNotice, "copyright"); ContainsIptcValue(iptcValues, IptcTag.CopyrightNotice, "copyright");
} }
} }
@ -132,6 +132,89 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.IPTC
ContainsIptcValue(iptcValues, IptcTag.Caption, expectedCaption); ContainsIptcValue(iptcValues, IptcTag.Caption, expectedCaption);
} }
[Theory]
[InlineData(IptcTag.ObjectAttribute)]
[InlineData(IptcTag.SubjectReference)]
[InlineData(IptcTag.SupplementalCategories)]
[InlineData(IptcTag.Keywords)]
[InlineData(IptcTag.LocationCode)]
[InlineData(IptcTag.LocationName)]
[InlineData(IptcTag.ReferenceService)]
[InlineData(IptcTag.ReferenceDate)]
[InlineData(IptcTag.ReferenceNumber)]
[InlineData(IptcTag.Byline)]
[InlineData(IptcTag.BylineTitle)]
[InlineData(IptcTag.Contact)]
[InlineData(IptcTag.LocalCaption)]
[InlineData(IptcTag.CaptionWriter)]
public void IptcProfile_AddRepeatable_Works(IptcTag tag)
{
// arrange
var profile = new IptcProfile();
var expectedValue1 = "test";
var expectedValue2 = "another one";
profile.SetValue(tag, expectedValue1);
// act
profile.SetValue(tag, expectedValue2);
// assert
var values = profile.Values.ToList();
Assert.Equal(2, values.Count);
ContainsIptcValue(values, tag, expectedValue1);
ContainsIptcValue(values, tag, expectedValue2);
}
[Theory]
[InlineData(IptcTag.RecordVersion)]
[InlineData(IptcTag.ObjectType)]
[InlineData(IptcTag.Name)]
[InlineData(IptcTag.EditStatus)]
[InlineData(IptcTag.EditorialUpdate)]
[InlineData(IptcTag.Urgency)]
[InlineData(IptcTag.Category)]
[InlineData(IptcTag.FixtureIdentifier)]
[InlineData(IptcTag.ReleaseDate)]
[InlineData(IptcTag.ReleaseTime)]
[InlineData(IptcTag.ExpirationDate)]
[InlineData(IptcTag.ExpirationTime)]
[InlineData(IptcTag.SpecialInstructions)]
[InlineData(IptcTag.ActionAdvised)]
[InlineData(IptcTag.CreatedDate)]
[InlineData(IptcTag.CreatedTime)]
[InlineData(IptcTag.DigitalCreationDate)]
[InlineData(IptcTag.DigitalCreationTime)]
[InlineData(IptcTag.OriginatingProgram)]
[InlineData(IptcTag.ProgramVersion)]
[InlineData(IptcTag.ObjectCycle)]
[InlineData(IptcTag.City)]
[InlineData(IptcTag.SubLocation)]
[InlineData(IptcTag.ProvinceState)]
[InlineData(IptcTag.CountryCode)]
[InlineData(IptcTag.Country)]
[InlineData(IptcTag.OriginalTransmissionReference)]
[InlineData(IptcTag.Headline)]
[InlineData(IptcTag.Credit)]
[InlineData(IptcTag.CopyrightNotice)]
[InlineData(IptcTag.Caption)]
[InlineData(IptcTag.ImageType)]
[InlineData(IptcTag.ImageOrientation)]
public void IptcProfile_AddNoneRepeatable_DoesOverrideOldValue(IptcTag tag)
{
// arrange
var profile = new IptcProfile();
var expectedValue = "another one";
profile.SetValue(tag, "test");
// act
profile.SetValue(tag, expectedValue);
// assert
var values = profile.Values.ToList();
Assert.Equal(1, values.Count);
ContainsIptcValue(values, tag, expectedValue);
}
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}");

Loading…
Cancel
Save