diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index 1b3a8e27f..7dac3c283 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -49,6 +49,7 @@ + diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifEncodedStringHelpers.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifEncodedStringHelpers.cs index 6c95780d8..d14c0c47d 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifEncodedStringHelpers.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifEncodedStringHelpers.cs @@ -12,9 +12,9 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif { public const int CharacterCodeBytesLength = 8; - private const ulong AsciiCode = 0x_41_53_43_49_49_00_00_00; - private const ulong JISCode = 0x_4A_49_53_00_00_00_00_00; - private const ulong UnicodeCode = 0x_55_4E_49_43_4F_44_45_00; + private const ulong AsciiCode = 0x_00_00_00_49_49_43_53_41; + private const ulong JISCode = 0x_00_00_00_00_00_53_49_4A; + private const ulong UnicodeCode = 0x_45_44_4F_43_49_4E_55; private const ulong UndefinedCode = 0x_00_00_00_00_00_00_00_00; private static ReadOnlySpan AsciiCodeBytes => new byte[] { 0x41, 0x53, 0x43, 0x49, 0x49, 0, 0, 0 }; @@ -25,7 +25,13 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif private static ReadOnlySpan UndefinedCodeBytes => new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }; - private static Encoding JIS0208Encoding => Encoding.GetEncoding(932); + private static Encoding JIS0208Encoding => CodePagesEncodingProvider.Instance.GetEncoding(932); + + public static bool IsEncodedString(ExifTagValue tag) => tag switch + { + ExifTagValue.UserComment or ExifTagValue.GPSProcessingMethod or ExifTagValue.GPSAreaInformation => true, + _ => false + }; public static ReadOnlySpan GetCodeBytes(CharacterCode code) => code switch { @@ -45,7 +51,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif _ => Encoding.UTF8 }; - public static bool TryCreate(ReadOnlySpan buffer, out EncodedString encodedString) + public static bool TryParse(ReadOnlySpan buffer, out EncodedString encodedString) { if (TryDetect(buffer, out CharacterCode code)) { @@ -61,17 +67,17 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif public static uint GetDataLength(EncodedString encodedString) => (uint)GetEncoding(encodedString.Code).GetByteCount(encodedString.Text) + CharacterCodeBytesLength; - public static bool IsEncodedString(ExifTagValue tag) + public static byte[] GetData(EncodedString encodedString) { - switch (tag) - { - case ExifTagValue.UserComment: - case ExifTagValue.GPSProcessingMethod: - case ExifTagValue.GPSAreaInformation: - return true; - default: - return false; - } + int length = (int)GetDataLength(encodedString); + byte[] buffer = new byte[length]; + + GetCodeBytes(encodedString.Code).CopyTo(buffer); + + string text = encodedString.Text; + GetEncoding(encodedString.Code).GetBytes(text, 0, text.Length, buffer, CharacterCodeBytesLength); + + return buffer; } private static bool TryDetect(ReadOnlySpan buffer, out CharacterCode code) diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs index 323c73c9a..23ead7d66 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs @@ -220,10 +220,6 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif if (this.TryReadSpan(buffer)) { object value = this.ConvertValue(tag.Exif, tag.DataType, buffer, tag.NumberOfComponents > 1 || tag.Exif.IsArray); - if (value is EncodedString) - { - // Console.WriteLine("EncodedString tag: " + (ushort)tag.Exif.Tag); - } this.Add(values, tag.Exif, value); } @@ -248,7 +244,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif private byte ConvertToByte(ReadOnlySpan buffer) => buffer[0]; - private string ConvertToString(ReadOnlySpan buffer) + private string ConvertToString(Encoding encoding, ReadOnlySpan buffer) { int nullCharIndex = buffer.IndexOf((byte)0); @@ -257,7 +253,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif buffer = buffer.Slice(0, nullCharIndex); } - return ExifConstants.DefaultEncoding.GetString(buffer); + return encoding.GetString(buffer); } private object ConvertValue(ExifValue exifValue, ExifDataType dataType, ReadOnlySpan buffer, bool isArray) @@ -267,35 +263,20 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif return null; } - var tagValue = (ExifTagValue)(ushort)exifValue.Tag; - if (ExifUcs2StringHelpers.IsUcs2Tag(tagValue)) - { - return ExifUcs2StringHelpers.ConvertToString(buffer); - } - else if (ExifEncodedStringHelpers.IsEncodedString(tagValue)) - { - if (ExifEncodedStringHelpers.TryCreate(buffer, out EncodedString encodedString)) - { - return encodedString; - } - } - switch (dataType) { case ExifDataType.Unknown: return null; case ExifDataType.Ascii: - return this.ConvertToString(buffer); + return this.ConvertToString(ExifConstants.DefaultEncoding, buffer); case ExifDataType.Byte: case ExifDataType.Undefined: - { if (!isArray) { return this.ConvertToByte(buffer); } - return ExifEncodedStringHelpers.TryCreate(buffer, out EncodedString encodedString) ? encodedString : buffer.ToArray(); - } + return buffer.ToArray(); case ExifDataType.DoubleFloat: if (!isArray) diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifUcs2StringHelpers.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifUcs2StringHelpers.cs index cff6fe612..82ea0eaba 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifUcs2StringHelpers.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifUcs2StringHelpers.cs @@ -10,21 +10,10 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif { public static Encoding Ucs2Encoding => Encoding.GetEncoding("UCS-2"); - public static bool IsUcs2Tag(ExifTagValue tag) + public static bool IsUcs2Tag(ExifTagValue tag) => tag switch { - switch (tag) - { - case ExifTagValue.XPAuthor: - case ExifTagValue.XPComment: - case ExifTagValue.XPKeywords: - case ExifTagValue.XPSubject: - case ExifTagValue.XPTitle: - return true; - default: - return false; - } - } - - public static string ConvertToString(ReadOnlySpan buffer) => Ucs2Encoding.GetString(buffer); + ExifTagValue.XPAuthor or ExifTagValue.XPComment or ExifTagValue.XPKeywords or ExifTagValue.XPSubject or ExifTagValue.XPTitle => true, + _ => false, + }; } } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs index f52d2a314..2f1846a82 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs @@ -274,9 +274,9 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif { object value = exifValue.GetValue(); - if (exifValue.DataType == ExifDataType.Ascii) + if (ExifUcs2StringHelpers.IsUcs2Tag((ExifTagValue)(ushort)exifValue.Tag)) { - return (uint)(ExifUcs2StringHelpers.IsUcs2Tag((ExifTagValue)(ushort)exifValue.Tag) ? ExifUcs2StringHelpers.Ucs2Encoding : ExifConstants.DefaultEncoding).GetByteCount((string)value) + 1; + return (uint)ExifUcs2StringHelpers.Ucs2Encoding.GetByteCount((string)value); } if (value is EncodedString encodedString) @@ -284,6 +284,11 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif return ExifEncodedStringHelpers.GetDataLength(encodedString); } + if (exifValue.DataType == ExifDataType.Ascii) + { + return (uint)ExifConstants.DefaultEncoding.GetByteCount((string)value) + 1; + } + if (value is Array arrayValue) { return (uint)arrayValue.Length; @@ -378,21 +383,14 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif switch (dataType) { case ExifDataType.Ascii: - offset = Write((ExifUcs2StringHelpers.IsUcs2Tag((ExifTagValue)(ushort)exifValue.Tag) ? ExifUcs2StringHelpers.Ucs2Encoding : ExifConstants.DefaultEncoding).GetBytes((string)value), destination, offset); + offset = Write(ExifConstants.DefaultEncoding.GetBytes((string)value), destination, offset); destination[offset] = 0; return offset + 1; case ExifDataType.Byte: case ExifDataType.Undefined: - if (value is EncodedString encodedString) + if (value is byte[] array) { - ReadOnlySpan codeBytes = ExifEncodedStringHelpers.GetCodeBytes(encodedString.Code); - codeBytes.CopyTo(destination.Slice(offset)); - offset += codeBytes.Length; - - ReadOnlySpan dataBytes = ExifEncodedStringHelpers.GetEncoding(encodedString.Code).GetBytes(encodedString.Text); - dataBytes.CopyTo(destination.Slice(offset)); - offset += dataBytes.Length; - + offset = Write(array, destination, offset); return offset; } else @@ -441,14 +439,25 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif } } - internal static int WriteValue(IExifValue value, Span destination, int offset) + internal static int WriteValue(IExifValue exifValue, Span destination, int offset) { - if (value.IsArray) + object value = exifValue.GetValue(); + + if (ExifUcs2StringHelpers.IsUcs2Tag((ExifTagValue)(ushort)exifValue.Tag)) + { + value = ExifUcs2StringHelpers.Ucs2Encoding.GetBytes((string)value); + } + else if (value is EncodedString encodedString) + { + value = ExifEncodedStringHelpers.GetData(encodedString); + } + + if (exifValue.IsArray) { - return WriteArray(value, destination, offset); + return WriteArray(exifValue, destination, offset); } - return WriteValue(value, value.DataType, value.GetValue(), destination, offset); + return WriteValue(exifValue, exifValue.DataType, value, destination, offset); } } } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.String.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.String.cs index 10d50dc6a..3fc353211 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.String.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.String.cs @@ -275,30 +275,5 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif /// Gets the GPSDateStamp exif tag. /// public static ExifTag GPSDateStamp { get; } = new ExifTag(ExifTagValue.GPSDateStamp); - - /// - /// Gets the title tag used by Windows (encoded in UCS2). - /// - public static ExifTag XPTitle => new ExifTag(ExifTagValue.XPTitle); - - /// - /// Gets the comment tag used by Windows (encoded in UCS2). - /// - public static ExifTag XPComment => new ExifTag(ExifTagValue.XPComment); - - /// - /// Gets the author tag used by Windows (encoded in UCS2). - /// - public static ExifTag XPAuthor => new ExifTag(ExifTagValue.XPAuthor); - - /// - /// Gets the keywords tag used by Windows (encoded in UCS2). - /// - public static ExifTag XPKeywords => new ExifTag(ExifTagValue.XPKeywords); - - /// - /// Gets the subject tag used by Windows (encoded in UCS2). - /// - public static ExifTag XPSubject => new ExifTag(ExifTagValue.XPSubject); } } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Ucs2String.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Ucs2String.cs new file mode 100644 index 000000000..a6911d76d --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Ucs2String.cs @@ -0,0 +1,34 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + /// + public abstract partial class ExifTag + { + /// + /// Gets the title tag used by Windows (encoded in UCS2). + /// + public static ExifTag XPTitle => new ExifTag(ExifTagValue.XPTitle); + + /// + /// Gets the comment tag used by Windows (encoded in UCS2). + /// + public static ExifTag XPComment => new ExifTag(ExifTagValue.XPComment); + + /// + /// Gets the author tag used by Windows (encoded in UCS2). + /// + public static ExifTag XPAuthor => new ExifTag(ExifTagValue.XPAuthor); + + /// + /// Gets the keywords tag used by Windows (encoded in UCS2). + /// + public static ExifTag XPKeywords => new ExifTag(ExifTagValue.XPKeywords); + + /// + /// Gets the subject tag used by Windows (encoded in UCS2). + /// + public static ExifTag XPSubject => new ExifTag(ExifTagValue.XPSubject); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/EncodedString.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/EncodedString.cs index 08181fc1f..910a7e06f 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/EncodedString.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/EncodedString.cs @@ -66,6 +66,18 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif /// public string Text { get; } + /// + /// Converts the specified to an instance of this type. + /// + /// The text value. + public static implicit operator EncodedString(string text) => new(text); + + /// + /// Converts the specified to a . + /// + /// The to convert. + public static explicit operator string(EncodedString encodedString) => encodedString.Text; + /// public override bool Equals(object obj) => obj is EncodedString other && this.Equals(other); diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifEncodedString.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifEncodedString.cs index 4055573c5..ba9fca5c8 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifEncodedString.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifEncodedString.cs @@ -38,6 +38,14 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif this.Value = new EncodedString(stringValue); return true; } + else if (value is byte[] buffer) + { + if (ExifEncodedStringHelpers.TryParse(buffer, out EncodedString encodedString)) + { + this.Value = encodedString; + return true; + } + } return false; } diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifUcs2String.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifUcs2String.cs new file mode 100644 index 000000000..42637925c --- /dev/null +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifUcs2String.cs @@ -0,0 +1,47 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Metadata.Profiles.Exif +{ + internal sealed class ExifUcs2String : ExifValue + { + public ExifUcs2String(ExifTag tag) + : base(tag) + { + } + + public ExifUcs2String(ExifTagValue tag) + : base(tag) + { + } + + private ExifUcs2String(ExifUcs2String value) + : base(value) + { + } + + public override ExifDataType DataType => ExifDataType.Byte; + + protected override string StringValue => this.Value; + + public override object GetValue() => this.Value; + + public override bool TrySetValue(object value) + { + if (base.TrySetValue(value)) + { + return true; + } + + if (value is byte[] buffer) + { + this.Value = ExifUcs2StringHelpers.Ucs2Encoding.GetString(buffer); + return true; + } + + return false; + } + + public override IExifValue DeepClone() => new ExifUcs2String(this); + } +} diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValues.cs b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValues.cs index 54e809ba9..3795e47a4 100644 --- a/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValues.cs +++ b/src/ImageSharp/Metadata/Profiles/Exif/Values/ExifValues.cs @@ -280,11 +280,6 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif case ExifTagValue.GPSDestBearingRef: return new ExifString(ExifTag.GPSDestBearingRef); case ExifTagValue.GPSDestDistanceRef: return new ExifString(ExifTag.GPSDestDistanceRef); case ExifTagValue.GPSDateStamp: return new ExifString(ExifTag.GPSDateStamp); - case ExifTagValue.XPTitle: return new ExifString(ExifTag.XPTitle); - case ExifTagValue.XPComment: return new ExifString(ExifTag.XPComment); - case ExifTagValue.XPAuthor: return new ExifString(ExifTag.XPAuthor); - case ExifTagValue.XPKeywords: return new ExifString(ExifTag.XPKeywords); - case ExifTagValue.XPSubject: return new ExifString(ExifTag.XPSubject); case ExifTagValue.FileSource: return new ExifByte(ExifTag.FileSource, ExifDataType.Undefined); case ExifTagValue.SceneType: return new ExifByte(ExifTag.SceneType, ExifDataType.Undefined); @@ -302,6 +297,12 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif case ExifTagValue.DeviceSettingDescription: return new ExifByteArray(ExifTag.DeviceSettingDescription, ExifDataType.Undefined); case ExifTagValue.ImageSourceData: return new ExifByteArray(ExifTag.ImageSourceData, ExifDataType.Undefined); + case ExifTagValue.XPTitle: return new ExifUcs2String(ExifTag.XPTitle); + case ExifTagValue.XPComment: return new ExifUcs2String(ExifTag.XPComment); + case ExifTagValue.XPAuthor: return new ExifUcs2String(ExifTag.XPAuthor); + case ExifTagValue.XPKeywords: return new ExifUcs2String(ExifTag.XPKeywords); + case ExifTagValue.XPSubject: return new ExifUcs2String(ExifTag.XPSubject); + case ExifTagValue.UserComment: return new ExifEncodedString(ExifTag.UserComment); case ExifTagValue.GPSProcessingMethod: return new ExifEncodedString(ExifTag.GPSProcessingMethod); case ExifTagValue.GPSAreaInformation: return new ExifEncodedString(ExifTag.GPSAreaInformation); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs index 6e0a79db0..85e5a54bf 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs @@ -303,32 +303,56 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg } [Fact] - public void ExifIfdStructure() + public void EncodedStringTags() { byte[] exifBytes; using var memoryStream = new MemoryStream(); using (var image = Image.Load(TestFile.GetInputFileFullPath(TestImages.Jpeg.Baseline.Calliphora))) { var exif = new ExifProfile(); - exif.SetValue(ExifTag.XPAuthor, "Dan Petitt"); - - exif.SetValue(ExifTag.XPTitle, "A bit of test metadata for image title"); - exif.SetValue(ExifTag.UserComment, new EncodedString(EncodedString.CharacterCode.ASCII, "A bit of normal comment text")); exif.SetValue(ExifTag.GPSDateStamp, "2022-01-06"); - exif.SetValue(ExifTag.XPKeywords, "Keywords"); - image.Metadata.ExifProfile = exif; + exif.SetValue(ExifTag.XPTitle, "A bit of test metadata for image title"); + exif.SetValue(ExifTag.XPComment, "A bit of test metadata for image comment"); + exif.SetValue(ExifTag.XPAuthor, "Dan Petitt"); + exif.SetValue(ExifTag.XPKeywords, "Keyword1;Keyword2"); + exif.SetValue(ExifTag.XPSubject, "This is a subject"); + + exif.SetValue(ExifTag.UserComment, new EncodedString(EncodedString.CharacterCode.ASCII, "user comment (ASCII)")); + exif.SetValue(ExifTag.GPSProcessingMethod, new EncodedString(EncodedString.CharacterCode.JIS, "GPS processing method (JIS)")); + exif.SetValue(ExifTag.GPSAreaInformation, new EncodedString(EncodedString.CharacterCode.Unicode, "GPS area info (Unicode)")); exifBytes = exif.ToByteArray(); + image.Metadata.ExifProfile = exif; + + image.Save(@"c:\\temp\1.jpeg"); image.Save(memoryStream, new JpegEncoder()); } memoryStream.Seek(0, SeekOrigin.Begin); using (var image = Image.Load(memoryStream)) { - Assert.NotNull(image.Metadata.ExifProfile); + ExifProfile exif = image.Metadata.ExifProfile; + Assert.NotNull(exif); + + Assert.Equal("2022-01-06", exif.GetValue(ExifTag.GPSDateStamp).Value); + + Assert.Equal("A bit of test metadata for image title", exif.GetValue(ExifTag.XPTitle).Value); + Assert.Equal("A bit of test metadata for image comment", exif.GetValue(ExifTag.XPComment).Value); + Assert.Equal("Dan Petitt", exif.GetValue(ExifTag.XPAuthor).Value); + Assert.Equal("Keyword1;Keyword2", exif.GetValue(ExifTag.XPKeywords).Value); + Assert.Equal("This is a subject", exif.GetValue(ExifTag.XPSubject).Value); + + Assert.Equal("user comment (ASCII)", exif.GetValue(ExifTag.UserComment).Value.Text); + Assert.Equal(EncodedString.CharacterCode.ASCII, exif.GetValue(ExifTag.UserComment).Value.Code); + + Assert.Equal("GPS processing method (JIS)", exif.GetValue(ExifTag.GPSProcessingMethod).Value.Text); + Assert.Equal(EncodedString.CharacterCode.JIS, exif.GetValue(ExifTag.GPSProcessingMethod).Value.Code); + + Assert.Equal("GPS area info (Unicode)", exif.GetValue(ExifTag.GPSAreaInformation).Value.Text); + Assert.Equal(EncodedString.CharacterCode.Unicode, exif.GetValue(ExifTag.GPSAreaInformation).Value.Code); } } } diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs index 2b68d2fd1..9b589e58e 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs @@ -591,7 +591,7 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif foreach (IExifValue value in profile.Values) { Assert.NotNull(value.GetValue()); - } + } IExifValue software = profile.GetValue(ExifTag.Software); Assert.Equal("Windows Photo Editor 10.0.10011.16384", software.Value); diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/Values/ExifValuesTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/Values/ExifValuesTests.cs index b668a2a1f..acb614c61 100644 --- a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/Values/ExifValuesTests.cs +++ b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/Values/ExifValuesTests.cs @@ -1,6 +1,8 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System; +using System.Text; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using Xunit; @@ -291,11 +293,6 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif.Values { ExifTag.GPSDestBearingRef }, { ExifTag.GPSDestDistanceRef }, { ExifTag.GPSDateStamp }, - { ExifTag.XPTitle }, - { ExifTag.XPComment }, - { ExifTag.XPAuthor }, - { ExifTag.XPKeywords }, - { ExifTag.XPSubject }, }; public static TheoryData UndefinedTags => new TheoryData @@ -327,6 +324,15 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif.Values { ExifTag.GPSAreaInformation } }; + public static TheoryData Ucs2StringTags => new TheoryData + { + { ExifTag.XPTitle }, + { ExifTag.XPComment }, + { ExifTag.XPAuthor }, + { ExifTag.XPKeywords }, + { ExifTag.XPSubject }, + }; + [Theory] [MemberData(nameof(ByteTags))] public void ExifByteTests(ExifTag tag) @@ -602,13 +608,38 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif.Values [MemberData(nameof(EncodedStringTags))] public void ExifEncodedStringTests(ExifTag tag) { - var expected = new EncodedString(EncodedString.CharacterCode.JIS, "test string"); + foreach (object code in Enum.GetValues(typeof(EncodedString.CharacterCode))) + { + var charCode = (EncodedString.CharacterCode)code; + + const string expectedText = "test string"; + var expected = new EncodedString(charCode, expectedText); + ExifValue value = ExifValues.Create(tag); + + Assert.False(value.TrySetValue(123)); + Assert.True(value.TrySetValue(expected)); + + var typed = (ExifEncodedString)value; + Assert.Equal(expected, typed.Value); + Assert.Equal(expectedText, (string)typed.Value); + Assert.Equal(charCode, typed.Value.Code); + } + } + + [Theory] + [MemberData(nameof(Ucs2StringTags))] + public void ExifUcs2StringTests(ExifTag tag) + { + const string expected = "Dan Petitt"; ExifValue value = ExifValues.Create(tag); Assert.False(value.TrySetValue(123)); Assert.True(value.TrySetValue(expected)); - var typed = (ExifEncodedString)value; + var typed = (ExifUcs2String)value; + Assert.Equal(expected, typed.Value); + + Assert.True(value.TrySetValue(Encoding.GetEncoding("UCS-2").GetBytes(expected))); Assert.Equal(expected, typed.Value); } }