private static extern bool GetColorProfileHeader(IntPtr phProfile, out PROFILEHEADER pHeader);
private ColorProfile(IntPtr handle) { var header = new PROFILEHEADER(); if (!GetColorProfileHeader(handle, ref header)) { throw new Win32Exception(Marshal.GetLastWin32Error()); } Size = header.phSize; CmmType = header.phCMMType; Version = header.phVersion; Class = header.phClass; DataColorSpace = header.phDataColorSpace; ConnectionSpace = header.phConnectionSpace; Signature = header.phSignature; Platform = header.phPlatform; IsEmbeddedProfile = (header.phProfileFlags & FLAG_EMBEDDEDPROFILE) != 0; IsDependentOnData = (header.phProfileFlags & FLAG_DEPENDENTONDATA) != 0; Manufacturer = header.phManufacturer; Model = header.phModel; Attributes = header.phAttributes; Creator = header.phCreator; RenderingIntent = header.phRenderingIntent; Illuminant = header.phIlluminant; var size = 0; if (!GetColorProfileFromHandle(handle, null, ref size)) { var gle = Marshal.GetLastWin32Error(); if (gle != ERROR_INSUFFICIENT_BUFFER) { throw new Win32Exception(gle); } } Profile = new byte[size]; if (!GetColorProfileFromHandle(handle, Profile, ref size)) { throw new Win32Exception(Marshal.GetLastWin32Error()); } var localizedStrings = new Dictionary <string, List <string> >(StringComparer.OrdinalIgnoreCase); GetCountColorProfileElements(handle, out var count); for (var i = 0; i < count; i++) { if (GetColorProfileElementTag(handle, i + 1, out var tag)) { size = 0; GetColorProfileElement(handle, tag, 0, ref size, null, out _); if (size > 0) { var bytes = new byte[size]; if (GetColorProfileElement(handle, tag, 0, ref size, bytes, out _)) { int offset; // https://www.color.org/specification/ICC.2-2019.pdf var type = Get4BytesString(BitConverter.ToInt32(bytes, 0)); switch (type) { case "text": offset = 8; switch (tag) { case 0x63707274: Copyright = getAscii(bytes.Length - 8); break; case 0x74617267: RegisteredCharacterization = getAscii(bytes.Length - 8); break; //default: // var s = getAscii(bytes.Length - 8); // Console.WriteLine(tag.ToString("x8") + " => " + s); // break; } break; case "desc": offset = 8; var n = getInt32(); if (n > 0) { switch (tag) { case 0x64657363: Description = getAscii(n); break; case 0x646d6e64: ManufacturerDescription = getAscii(n); break; case 0x646d6464: ModelDescription = getAscii(n); break; } } UnicodeLanguageCode = getInt32(); var m = getInt32(); if (m > 0) { switch (tag) { case 0x64657363: Description = getUnicode(n); break; case 0x646d6464: ModelDescription = getUnicode(n); break; } } break; case "mluc": // multiLocalizedUnicodeType offset = 8; var records = getInt32(); _ = getInt32(); // record size var languageCode = Get2BytesString(BitConverter.ToInt16(bytes, offset)); offset += 2; var countryCode = Get2BytesString(BitConverter.ToInt16(bytes, offset)); var lcid = languageCode + "-" + countryCode; offset += 2; for (var ir = 0; ir < records; ir++) { var sl = getInt32(); var o = offset; // save offset = getInt32(); var s = getBEUnicode(sl / 2); offset = o; // restore if (!localizedStrings.TryGetValue(lcid, out var list)) { list = new List <string>(); localizedStrings.Add(lcid, list); } list.Add(s); } break; } int getInt32() => (bytes[offset++] << 24) | (bytes[offset++] << 16) | (bytes[offset++] << 8) | bytes[offset++]; string getAscii(int len) { var s = TrimTerminatingZeros(Encoding.ASCII.GetString(bytes, offset, len)); offset += len; return(s); } string getBEUnicode(int len) { var s = TrimTerminatingZeros(Encoding.BigEndianUnicode.GetString(bytes, offset, len * 2)); offset += len; return(s); } string getUnicode(int len) { var bom = BitConverter.ToInt16(bytes, offset); if (bom == -2) { offset += 3; len -= 2; } var s = TrimTerminatingZeros(Encoding.Unicode.GetString(bytes, offset, len * 2)); offset += len; return(s); } } } } } LocalizedStrings = localizedStrings.ToDictionary(kv => kv.Key, kv => (IReadOnlyList <string>)kv.Value.AsReadOnly()); }