From 4c0d8842e239c047c2873b14fd8f0da48a8356f2 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sat, 18 Apr 2020 18:03:01 +0200 Subject: [PATCH] Add SetDateTimeValue which makes sure the formatting will be as specified in the spec --- .../Metadata/Profiles/IPTC/IptcProfile.cs | 22 ++++++++++ .../Metadata/Profiles/IPTC/IptcTag.cs | 22 ++++------ .../Profiles/IPTC/IptcTagExtensions.cs | 43 ++++++++++++++++++- .../Metadata/Profiles/IPTC/IptcValue.cs | 6 +++ .../Profiles/IPTC/IptcProfileTests.cs | 43 ++++++++++++++++++- 5 files changed, 120 insertions(+), 16 deletions(-) diff --git a/src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs b/src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs index b86f6dff2..cd3c8eb93 100644 --- a/src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs +++ b/src/ImageSharp/Metadata/Profiles/IPTC/IptcProfile.cs @@ -174,6 +174,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc public void SetValue(IptcTag tag, Encoding encoding, string value, bool strict = true) { Guard.NotNull(encoding, nameof(encoding)); + Guard.NotNull(value, nameof(value)); if (!tag.IsRepeatable()) { @@ -192,6 +193,27 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc this.values.Add(new IptcValue(tag, encoding, value, strict)); } + /// + /// Makes sure the datetime is formatted according to the iptc specification. + /// A date will be formatted as CCYYMMDD. + /// A time value will be formatted as HHMMSS±HHMM. + /// + /// The tag of the iptc value. + /// The datetime. + public void SetDateTimeValue(IptcTag tag, DateTime dateTime) + { + if (!tag.IsDate() && !tag.IsTime()) + { + throw new ArgumentException("iptc tag is not a time or date type"); + } + + var formattedDate = tag.IsDate() + ? dateTime.ToString("yyyyMMdd", System.Globalization.CultureInfo.InvariantCulture) + : dateTime.ToString("HHmmsszzzz", System.Globalization.CultureInfo.InvariantCulture).Replace(":", string.Empty); + + this.SetValue(tag, Encoding.UTF8, formattedDate); + } + /// /// Sets the value of the specified tag. /// diff --git a/src/ImageSharp/Metadata/Profiles/IPTC/IptcTag.cs b/src/ImageSharp/Metadata/Profiles/IPTC/IptcTag.cs index 135c41e51..62f74386e 100644 --- a/src/ImageSharp/Metadata/Profiles/IPTC/IptcTag.cs +++ b/src/ImageSharp/Metadata/Profiles/IPTC/IptcTag.cs @@ -86,30 +86,28 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc /// /// Release date. Format should be CCYYMMDD, - /// e.g. "19890317" indicates data for release on 17 March 1989. + /// e.g. "19890317" for the 17 March 1989. /// Not repeatable, max length is 8. /// ReleaseDate = 30, /// /// Release time. Format should be HHMMSS±HHMM, - /// e.g. "090000-0500" indicates object for use after 0900 in - /// New York (five hours behind UTC) + /// e.g. "090000-0500" for 9 o'clock New York time (five hours behind UTC). /// Not repeatable, max length is 11. /// ReleaseTime = 35, /// /// Expiration date. Format should be CCYYMMDD, - /// e.g. "19890317" indicates data for release on 17 March 1989. + /// e.g. "19890317" for the 17 March 1989. /// Not repeatable, max length is 8. /// ExpirationDate = 37, /// /// Expiration time. Format should be HHMMSS±HHMM, - /// e.g. "090000-0500" indicates object for use after 0900 in - /// New York (five hours behind UTC) + /// e.g. "090000-0500" for 9 o'clock New York time (five hours behind UTC). /// Not repeatable, max length is 11. /// ExpirationTime = 38, @@ -131,7 +129,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc /// /// Reference date. Format should be CCYYMMDD, - /// e.g. "19890317" indicates data for release on 17 March 1989. + /// e.g. "19890317" for the 17 March 1989. /// Not repeatable, max length is 8. /// ReferenceDate = 47, @@ -143,30 +141,28 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc /// /// Created date. Format should be CCYYMMDD, - /// e.g. "19890317" indicates data for release on 17 March 1989. + /// e.g. "19890317" for the 17 March 1989. /// Not repeatable, max length is 8. /// CreatedDate = 55, /// /// Created time. Format should be HHMMSS±HHMM, - /// e.g. "090000-0500" indicates object for use after 0900 in - /// New York (five hours behind UTC) + /// e.g. "090000-0500" for 9 o'clock New York time (five hours behind UTC). /// Not repeatable, max length is 11. /// CreatedTime = 60, /// /// Digital creation date. Format should be CCYYMMDD, - /// e.g. "19890317" indicates data for release on 17 March 1989. + /// e.g. "19890317" for the 17 March 1989. /// Not repeatable, max length is 8. /// DigitalCreationDate = 62, /// /// Digital creation time. Format should be HHMMSS±HHMM, - /// e.g. "090000-0500" indicates object for use after 0900 in - /// New York (five hours behind UTC) + /// e.g. "090000-0500" for 9 o'clock New York time (five hours behind UTC). /// Not repeatable, max length is 11. /// DigitalCreationTime = 63, diff --git a/src/ImageSharp/Metadata/Profiles/IPTC/IptcTagExtensions.cs b/src/ImageSharp/Metadata/Profiles/IPTC/IptcTagExtensions.cs index 88d463767..6b39769a7 100644 --- a/src/ImageSharp/Metadata/Profiles/IPTC/IptcTagExtensions.cs +++ b/src/ImageSharp/Metadata/Profiles/IPTC/IptcTagExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc @@ -117,5 +117,46 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc return true; } } + + /// + /// Determines if the tag is a datetime tag which needs to be formatted as CCYYMMDD. + /// + /// The tag to check. + /// True, if its a datetime tag. + public static bool IsDate(this IptcTag tag) + { + switch (tag) + { + case IptcTag.CreatedDate: + case IptcTag.DigitalCreationDate: + case IptcTag.ExpirationDate: + case IptcTag.ReferenceDate: + case IptcTag.ReleaseDate: + return true; + + default: + return false; + } + } + + /// + /// Determines if the tag is a time tag which need to be formatted as HHMMSS±HHMM. + /// + /// The tag to check. + /// True, if its a time tag. + public static bool IsTime(this IptcTag tag) + { + switch (tag) + { + case IptcTag.CreatedTime: + case IptcTag.DigitalCreationTime: + case IptcTag.ExpirationTime: + case IptcTag.ReleaseTime: + return true; + + default: + return false; + } + } } } diff --git a/src/ImageSharp/Metadata/Profiles/IPTC/IptcValue.cs b/src/ImageSharp/Metadata/Profiles/IPTC/IptcValue.cs index 2c2cf5995..8e804353c 100644 --- a/src/ImageSharp/Metadata/Profiles/IPTC/IptcValue.cs +++ b/src/ImageSharp/Metadata/Profiles/IPTC/IptcValue.cs @@ -103,6 +103,12 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Iptc { var cappedValue = value.Substring(0, maxLength); valueBytes = this.encoding.GetBytes(cappedValue); + + // It is still possible that the bytes of the string exceed the limit. + if (valueBytes.Length > maxLength) + { + throw new ArgumentException($"The iptc value exceeds the limit of {maxLength} bytes for the tag {this.Tag}"); + } } else { diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/IPTC/IptcProfileTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/IPTC/IptcProfileTests.cs index 321c7fe5c..9d5db439a 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/IPTC/IptcProfileTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/IPTC/IptcProfileTests.cs @@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.IPTC { private static JpegDecoder JpegDecoder => new JpegDecoder() { IgnoreMetadata = false }; - public static IEnumerable allIptcTags() + public static IEnumerable AllIptcTags() { foreach (object tag in Enum.GetValues(typeof(IptcTag))) { @@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.IPTC } [Theory] - [MemberData("allIptcTags")] + [MemberData("AllIptcTags")] public void IptcProfile_SetValue_WithStrictOption_Works(IptcTag tag) { // arrange @@ -41,6 +41,45 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.IPTC Assert.Equal(expectedLength, actual.Value.Length); } + [Theory] + [InlineData(IptcTag.DigitalCreationDate)] + [InlineData(IptcTag.ExpirationDate)] + [InlineData(IptcTag.CreatedDate)] + [InlineData(IptcTag.ReferenceDate)] + [InlineData(IptcTag.ReleaseDate)] + public void IptcProfile_SetDateValue_Works(IptcTag tag) + { + // arrange + var profile = new IptcProfile(); + var datetime = new DateTime(1994, 3, 17); + + // act + profile.SetDateTimeValue(tag, datetime); + + // assert + IptcValue actual = profile.GetValues(tag).First(); + Assert.Equal("19940317", actual.Value); + } + + [Theory] + [InlineData(IptcTag.CreatedTime)] + [InlineData(IptcTag.DigitalCreationTime)] + [InlineData(IptcTag.ExpirationTime)] + [InlineData(IptcTag.ReleaseTime)] + public void IptcProfile_SetTimeValue_Works(IptcTag tag) + { + // arrange + var profile = new IptcProfile(); + DateTime datetime = new DateTimeOffset(new DateTime(1994, 3, 17, 14, 15, 16), new TimeSpan(1, 0, 0)).DateTime; + + // act + profile.SetDateTimeValue(tag, datetime); + + // assert + IptcValue actual = profile.GetValues(tag).First(); + Assert.Equal("141516+0100", actual.Value); + } + [Theory] [WithFile(TestImages.Jpeg.Baseline.Iptc, PixelTypes.Rgba32)] public void ReadIptcMetadata_Works(TestImageProvider provider)