diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.TagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.TagDataEntry.cs index 1c130b54d0..711de818b3 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.TagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataReader/IccDataReader.TagDataEntry.cs @@ -464,17 +464,31 @@ namespace ImageSharp int start = this.currentIndex - 8; // 8 is the tag header size uint recordCount = this.ReadUInt32(); - // TODO: Why are we storing variable - uint recordSize = this.ReadUInt32(); + this.ReadUInt32(); // Record size (always 12) IccLocalizedString[] text = new IccLocalizedString[recordCount]; - string[] culture = new string[recordCount]; + CultureInfo[] culture = new CultureInfo[recordCount]; uint[] length = new uint[recordCount]; uint[] offset = new uint[recordCount]; for (int i = 0; i < recordCount; i++) { - culture[i] = $"{this.ReadAsciiString(2)}-{this.ReadAsciiString(2)}"; + string languageCode = this.ReadAsciiString(2); + string countryCode = this.ReadAsciiString(2); + + if (string.IsNullOrWhiteSpace(languageCode)) + { + culture[i] = CultureInfo.InvariantCulture; + } + else if (string.IsNullOrWhiteSpace(countryCode)) + { + culture[i] = new CultureInfo(languageCode); + } + else + { + culture[i] = new CultureInfo($"{languageCode}-{countryCode}"); + } + length[i] = this.ReadUInt32(); offset[i] = this.ReadUInt32(); } @@ -482,7 +496,7 @@ namespace ImageSharp for (int i = 0; i < recordCount; i++) { this.currentIndex = (int)(start + offset[i]); - text[i] = new IccLocalizedString(new CultureInfo(culture[i]), this.ReadUnicodeString((int)length[i])); + text[i] = new IccLocalizedString(culture[i], this.ReadUnicodeString((int)length[i])); } return new IccMultiLocalizedUnicodeTagDataEntry(text); diff --git a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntry.cs b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntry.cs index 8e8065cee8..b74f22054e 100644 --- a/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntry.cs +++ b/src/ImageSharp/MetaData/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntry.cs @@ -5,6 +5,8 @@ namespace ImageSharp { + using System.Linq; + /// /// Provides methods to write ICC data types /// @@ -560,27 +562,45 @@ namespace ImageSharp long tpos = this.dataStream.Position; this.dataStream.Position += cultureCount * 12; - uint[] offset = new uint[cultureCount]; - int[] lengths = new int[cultureCount]; + IGrouping[] texts = value.Texts.GroupBy(t => t.Text).ToArray(); + + uint[] offset = new uint[texts.Length]; + int[] lengths = new int[texts.Length]; - for (int i = 0; i < cultureCount; i++) + for (int i = 0; i < texts.Length; i++) { offset[i] = (uint)(this.dataStream.Position - start); - count += lengths[i] = this.WriteUnicodeString(value.Texts[i].Text); + count += lengths[i] = this.WriteUnicodeString(texts[i].Key); } // Write position table long lpos = this.dataStream.Position; this.dataStream.Position = tpos; - for (int i = 0; i < cultureCount; i++) + for (int i = 0; i < texts.Length; i++) { - string[] code = value.Texts[i].Culture.Name.Split('-'); - - count += this.WriteAsciiString(code[0].ToLower(), 2, false); - count += this.WriteAsciiString(code[1].ToUpper(), 2, false); - - count += this.WriteUInt32((uint)lengths[i]); - count += this.WriteUInt32(offset[i]); + foreach (IccLocalizedString localizedString in texts[i]) + { + string cultureName = localizedString.Culture.Name; + if (string.IsNullOrEmpty(cultureName)) + { + count += this.WriteAsciiString("xx", 2, false); + count += this.WriteAsciiString("\0\0", 2, false); + } + else if (cultureName.Contains("-")) + { + string[] code = cultureName.Split('-'); + count += this.WriteAsciiString(code[0].ToLower(), 2, false); + count += this.WriteAsciiString(code[1].ToUpper(), 2, false); + } + else + { + count += this.WriteAsciiString(cultureName, 2, false); + count += this.WriteAsciiString("\0\0", 2, false); + } + + count += this.WriteUInt32((uint)lengths[i]); + count += this.WriteUInt32(offset[i]); + } } this.dataStream.Position = lpos; diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.TagDataEntryTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.TagDataEntryTests.cs index 6f003cc4d6..76bb1cda19 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.TagDataEntryTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataReader/IccDataReader.TagDataEntryTests.cs @@ -167,7 +167,7 @@ namespace ImageSharp.Tests.Icc [Theory] [MemberData( - nameof(IccTestDataTagDataEntry.MultiLocalizedUnicodeTagDataEntryTestData), + nameof(IccTestDataTagDataEntry.MultiLocalizedUnicodeTagDataEntryTestData_Read), MemberType = typeof(IccTestDataTagDataEntry))] internal void ReadMultiLocalizedUnicodeTagDataEntry(byte[] data, IccMultiLocalizedUnicodeTagDataEntry expected) { diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntryTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntryTests.cs index affe9835e9..ea85bd16df 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntryTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/ICC/DataWriter/IccDataWriter.TagDataEntryTests.cs @@ -154,7 +154,7 @@ namespace ImageSharp.Tests.Icc } [Theory] - [MemberData(nameof(IccTestDataTagDataEntry.MultiLocalizedUnicodeTagDataEntryTestData), MemberType = typeof(IccTestDataTagDataEntry))] + [MemberData(nameof(IccTestDataTagDataEntry.MultiLocalizedUnicodeTagDataEntryTestData_Write), MemberType = typeof(IccTestDataTagDataEntry))] internal void WriteMultiLocalizedUnicodeTagDataEntry(byte[] expected, IccMultiLocalizedUnicodeTagDataEntry data) { IccDataWriter writer = CreateWriter(); diff --git a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataTagDataEntry.cs b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataTagDataEntry.cs index 769ec3a016..05942ab618 100644 --- a/tests/ImageSharp.Tests/TestDataIcc/IccTestDataTagDataEntry.cs +++ b/tests/ImageSharp.Tests/TestDataIcc/IccTestDataTagDataEntry.cs @@ -455,21 +455,29 @@ namespace ImageSharp.Tests #region MultiLocalizedUnicodeTagDataEntry - private static readonly IccLocalizedString LocalizedString_Rand1 = new IccLocalizedString(new CultureInfo("en-US"), IccTestDataPrimitives.Unicode_ValRand2); - private static readonly IccLocalizedString LocalizedString_Rand2 = new IccLocalizedString(new CultureInfo("de-DE"), IccTestDataPrimitives.Unicode_ValRand3); + private static readonly IccLocalizedString LocalizedString_Rand_enUs = new IccLocalizedString(new CultureInfo("en-US"), IccTestDataPrimitives.Unicode_ValRand2); + private static readonly IccLocalizedString LocalizedString_Rand_deDE = new IccLocalizedString(new CultureInfo("de-DE"), IccTestDataPrimitives.Unicode_ValRand3); + private static readonly IccLocalizedString LocalizedString_Rand2_deDE = new IccLocalizedString(new CultureInfo("de-DE"), IccTestDataPrimitives.Unicode_ValRand2); + private static readonly IccLocalizedString LocalizedString_Rand_en = new IccLocalizedString(new CultureInfo("en"), IccTestDataPrimitives.Unicode_ValRand2); + private static readonly IccLocalizedString LocalizedString_Rand_Invariant = new IccLocalizedString(CultureInfo.InvariantCulture, IccTestDataPrimitives.Unicode_ValRand3); - private static readonly IccLocalizedString[] LocalizedString_RandArr1 = new IccLocalizedString[] + private static readonly IccLocalizedString[] LocalizedString_RandArr_enUs_deDE = new IccLocalizedString[] { - LocalizedString_Rand1, - LocalizedString_Rand2, + LocalizedString_Rand_enUs, + LocalizedString_Rand_deDE, }; - private static readonly IccLocalizedString[] LocalizedString_RandArr2 = new IccLocalizedString[] + private static readonly IccLocalizedString[] LocalizedString_RandArr_en_Invariant = new IccLocalizedString[] { - LocalizedString_Rand2, - LocalizedString_Rand1, + LocalizedString_Rand_en, + LocalizedString_Rand_Invariant, + }; + private static readonly IccLocalizedString[] LocalizedString_SameArr_enUs_deDE = new IccLocalizedString[] + { + LocalizedString_Rand_enUs, + LocalizedString_Rand2_deDE }; - public static readonly IccMultiLocalizedUnicodeTagDataEntry MultiLocalizedUnicode_Val = new IccMultiLocalizedUnicodeTagDataEntry(LocalizedString_RandArr1); + public static readonly IccMultiLocalizedUnicodeTagDataEntry MultiLocalizedUnicode_Val = new IccMultiLocalizedUnicodeTagDataEntry(LocalizedString_RandArr_enUs_deDE); public static readonly byte[] MultiLocalizedUnicode_Arr = ArrayHelper.Concat ( IccTestDataPrimitives.UInt32_2, @@ -487,9 +495,70 @@ namespace ImageSharp.Tests IccTestDataPrimitives.Unicode_Rand3 ); - public static readonly object[][] MultiLocalizedUnicodeTagDataEntryTestData = + public static readonly IccMultiLocalizedUnicodeTagDataEntry MultiLocalizedUnicode_Val2 = new IccMultiLocalizedUnicodeTagDataEntry(LocalizedString_RandArr_en_Invariant); + public static readonly byte[] MultiLocalizedUnicode_Arr2_Read = ArrayHelper.Concat + ( + IccTestDataPrimitives.UInt32_2, + new byte[] { 0x00, 0x00, 0x00, 0x0C }, // 12 + + new byte[] { (byte)'e', (byte)'n', 0x00, 0x00 }, + new byte[] { 0x00, 0x00, 0x00, 0x0C }, // 12 + new byte[] { 0x00, 0x00, 0x00, 0x28 }, // 40 + + new byte[] { 0x00, 0x00, 0x00, 0x00 }, + new byte[] { 0x00, 0x00, 0x00, 0x0E }, // 14 + new byte[] { 0x00, 0x00, 0x00, 0x34 }, // 52 + + IccTestDataPrimitives.Unicode_Rand2, + IccTestDataPrimitives.Unicode_Rand3 + ); + + public static readonly byte[] MultiLocalizedUnicode_Arr2_Write = ArrayHelper.Concat + ( + IccTestDataPrimitives.UInt32_2, + new byte[] { 0x00, 0x00, 0x00, 0x0C }, // 12 + + new byte[] { (byte)'e', (byte)'n', 0x00, 0x00 }, + new byte[] { 0x00, 0x00, 0x00, 0x0C }, // 12 + new byte[] { 0x00, 0x00, 0x00, 0x28 }, // 40 + + new byte[] { (byte)'x', (byte)'x', 0x00, 0x00 }, + new byte[] { 0x00, 0x00, 0x00, 0x0E }, // 14 + new byte[] { 0x00, 0x00, 0x00, 0x34 }, // 52 + + IccTestDataPrimitives.Unicode_Rand2, + IccTestDataPrimitives.Unicode_Rand3 + ); + + public static readonly IccMultiLocalizedUnicodeTagDataEntry MultiLocalizedUnicode_Val3 = new IccMultiLocalizedUnicodeTagDataEntry(LocalizedString_SameArr_enUs_deDE); + public static readonly byte[] MultiLocalizedUnicode_Arr3 = ArrayHelper.Concat + ( + IccTestDataPrimitives.UInt32_2, + new byte[] { 0x00, 0x00, 0x00, 0x0C }, // 12 + + new byte[] { (byte)'e', (byte)'n', (byte)'U', (byte)'S' }, + new byte[] { 0x00, 0x00, 0x00, 0x0C }, // 12 + new byte[] { 0x00, 0x00, 0x00, 0x28 }, // 40 + + new byte[] { (byte)'d', (byte)'e', (byte)'D', (byte)'E' }, + new byte[] { 0x00, 0x00, 0x00, 0x0C }, // 12 + new byte[] { 0x00, 0x00, 0x00, 0x28 }, // 40 + + IccTestDataPrimitives.Unicode_Rand2 + ); + + public static readonly object[][] MultiLocalizedUnicodeTagDataEntryTestData_Read = + { + new object[] { MultiLocalizedUnicode_Arr, MultiLocalizedUnicode_Val }, + new object[] { MultiLocalizedUnicode_Arr2_Read, MultiLocalizedUnicode_Val2 }, + new object[] { MultiLocalizedUnicode_Arr3, MultiLocalizedUnicode_Val3 }, + }; + + public static readonly object[][] MultiLocalizedUnicodeTagDataEntryTestData_Write = { new object[] { MultiLocalizedUnicode_Arr, MultiLocalizedUnicode_Val }, + new object[] { MultiLocalizedUnicode_Arr2_Write, MultiLocalizedUnicode_Val2 }, + new object[] { MultiLocalizedUnicode_Arr3, MultiLocalizedUnicode_Val3 }, }; #endregion @@ -601,8 +670,8 @@ namespace ImageSharp.Tests ( new IccProfileSequenceIdentifier[] { - new IccProfileSequenceIdentifier(IccTestDataNonPrimitives.ProfileId_ValRand, LocalizedString_RandArr1), - new IccProfileSequenceIdentifier(IccTestDataNonPrimitives.ProfileId_ValRand, LocalizedString_RandArr1), + new IccProfileSequenceIdentifier(IccTestDataNonPrimitives.ProfileId_ValRand, LocalizedString_RandArr_enUs_deDE), + new IccProfileSequenceIdentifier(IccTestDataNonPrimitives.ProfileId_ValRand, LocalizedString_RandArr_enUs_deDE), } ); public static readonly byte[] ProfileSequenceIdentifier_Arr = ArrayHelper.Concat @@ -951,7 +1020,7 @@ namespace ImageSharp.Tests IccTestDataPrimitives.UInt16_7, IccTestDataPrimitives.UInt16_2, IccTestDataPrimitives.UInt16_5, - + IccTestDataPrimitives.Ascii_Rand, new byte[] { 0 } );