トップ回答者
ICC profile の説明語句の取得について

質問
-
WPF で画像をハンドリングするものを VB.net で書いていますが、
実行環境上の ICC profile 群を取得する上で、ファイル名は拡張子 .icm から判断出来るのですが、
各ファイルに格納された 説明 (Description) が取得出来ずに困っています。
保存されたプロファイル群を扱う上で、ファイル名ではなく、この 説明 (Description) で取り扱いと考えています。
.Netframework 上から、 ファイルに格納された 説明 (Description) を読み込むには、
どの様に 取得部分 を記述すれば良いのか、 お教え戴けないでしょうか。
説明 (Description) は、
例えば、Canon インクジェットプリンタ用の 汎用プロファイル CNBJPRN3.ICM なら、
"Canon IJ Color Printer Profile 2005" と表記される内容です。
内容は メーカー名+モデル名+用紙(+精度)で構成されています。
回答
-
こんな
Class MainWindow Sub New() ' この呼び出しはデザイナーで必要です。 InitializeComponent() ' InitializeComponent() 呼び出しの後で初期化を追加します。 Dim dlg As Microsoft.Win32.OpenFileDialog = New Microsoft.Win32.OpenFileDialog() dlg.Multiselect = True dlg.Filter = "ICC,ICM|*.icc;*.icm" If dlg.ShowDialog() = True Then For Each path As String In dlg.FileNames Dim d = ICC.GetDescription(path) System.Diagnostics.Debug.WriteLine(path + vbTab + d.ASCIIInvariantProfile + vbTab + d.UnicodeLocalizableProfile) Next End If End Sub End Class Friend Class ICC ' http://www.color.org/icc32.pdfを参考 Public Shared Function GetDescription(ByVal ICC_ICM_FileFullPath As String, Optional ByVal isBOM_BighEndianness As Boolean = True) As Description Dim uri As Uri = New Uri("file://" + ICC_ICM_FileFullPath) Dim cc = New System.Windows.Media.ColorContext(uri) Using stream = cc.OpenProfileStream() Dim tags As List(Of Tag) = Tag.GetTags(stream) Dim tagDesc = tags.FirstOrDefault(Function(t) Return String.Equals(t.signature, "desc") End Function) Dim data As Byte() = tagDesc.GetData(stream) Return Description.Decode(data) End Using End Function Private Class BigEndianness Public Shared Function ReadInt32(ByVal stream As System.IO.Stream) As Int32 Dim bs As Byte() = New Byte(3) {} stream.Read(bs, 0, 4) Array.Reverse(bs) Return BitConverter.ToInt32(bs, 0) End Function Public Shared Function ReadInt32(ByVal buff As Byte(), ByVal index As Integer) As Int32 Dim bs As Byte() = New Byte(3) {} Array.Copy(buff, index, bs, 0, 4) Array.Reverse(bs) Return BitConverter.ToInt32(bs, 0) End Function Public Shared Function ReadInt16(ByVal buff As Byte(), ByVal index As Integer) As Int32 Dim bs As Byte() = New Byte(1) {} Array.Copy(buff, index, bs, 0, 2) Array.Reverse(bs) Return BitConverter.ToInt16(bs, 0) End Function Public Shared Function ReadString(ByVal stream As System.IO.Stream, ByVal size As Integer) As String Dim bs As Byte() = New Byte(size - 1) {} stream.Read(bs, 0, size) Return System.Text.Encoding.ASCII.GetString(bs).TrimEnd(ChrW(0)) End Function Public Shared Function ReadString(ByVal buff As Byte(), ByVal index As Integer, ByVal size As Integer) As String Return System.Text.Encoding.ASCII.GetString(buff, index, size).TrimEnd(ChrW(0)) End Function Public Shared Function ReadUnicode(ByVal buff As Byte(), ByVal index As Integer, ByVal size As Integer, Optional ByVal isBOM_BighEndianness As Boolean = True) As String If (size = 0) Then Return String.Empty End If Dim data As Byte() = New Byte(size - 1) {} Array.Copy(buff, index, data, 0, size) If size >= 2 AndAlso data(0) = &HFF AndAlso data(1) = &HFE Then 'BOMが間違っているファイルがある… If (isBOM_BighEndianness) Then data(0) = &HFE data(1) = &HFF End If ElseIf Not (size > 2 AndAlso data(0) = &HFE AndAlso data(1) = &HFF) Then 'BOMがない data = New Byte(size + 2 - 1) {} Array.Copy(buff, index, data, 2, size) If (data(2) <> 0) Then data(1) = &HFE data(0) = &HFF Else data(0) = &HFE data(1) = &HFF End If End If Dim ms = New System.IO.MemoryStream(data) Dim sr = New System.IO.StreamReader(ms, System.Text.Encoding.Unicode, True) Return sr.ReadToEnd().TrimEnd(ChrW(0)) End Function End Class Public Class Tag Public signature As String Public offset As Integer Public size As Integer Public Shared Function GetTags(ByVal stream As System.IO.Stream) As List(Of Tag) Dim buffer As Byte() = New Byte(127) {} stream.Seek(&H80, System.IO.SeekOrigin.Begin) Dim tags As List(Of Tag) = New List(Of Tag)() Dim tagCount As Integer = BigEndianness.ReadInt32(stream) With Nothing Dim i As Integer = 0 For _F_4 As Integer = 0 To 1 Step 0 If Not (i < tagCount) Then Exit For End If Dim t As Tag = New Tag() t.signature = BigEndianness.ReadString(stream, 4) t.offset = BigEndianness.ReadInt32(stream) t.size = BigEndianness.ReadInt32(stream) tags.Add(t) i += 1 Next End With Return tags End Function Public Function GetData(ByVal stream As System.IO.Stream) As Byte() stream.Seek(offset, System.IO.SeekOrigin.Begin) Dim bs As Byte() = New Byte(size) {} stream.Read(bs, 0, bs.Length) Return bs End Function End Class Public Class Description Public ASCIIInvariantProfile As String Public UnicodeLocalizableProfile As String 'public string LocalizableMacintoshProfile; Public Shared Function Decode(ByVal data As Byte(), Optional ByVal isBOM_BighEndianness As Boolean = True) As Description If System.Text.Encoding.ASCII.GetString(data, 0, 4) <> "desc" Then Throw New ArgumentException() End If Dim d As Description = New Description() Dim p As Integer = 8 Dim asciiLength As Integer = BigEndianness.ReadInt32(data, p) p += 4 d.ASCIIInvariantProfile = BigEndianness.ReadString(data, p, asciiLength) p += asciiLength Dim unicodeLanguageCode As Integer = BigEndianness.ReadInt32(data, p) p += 4 Dim unicodeLength As Integer = BigEndianness.ReadInt32(data, p) p += 4 d.UnicodeLocalizableProfile = BigEndianness.ReadUnicode(data, p, unicodeLength * 2, isBOM_BighEndianness) p += unicodeLength * 2 'int scriptcode = data[p]; 'p += 1; 'int scriptcodeLength = BigEndianness.ReadInt16(data, p); 'p += 2; 'd.LocalizableMacintoshProfile = BigEndianness.ReadString(data, p, scriptcodeLength).TrimEnd('\0'); ; Return d End Function End Class End Class
#例示されているICMファイルは手元の環境にないので試せてません個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)
- 回答としてマーク ShiroYuki_Mot 2016年12月18日 3:55
すべての返信
-
こんな
Class MainWindow Sub New() ' この呼び出しはデザイナーで必要です。 InitializeComponent() ' InitializeComponent() 呼び出しの後で初期化を追加します。 Dim dlg As Microsoft.Win32.OpenFileDialog = New Microsoft.Win32.OpenFileDialog() dlg.Multiselect = True dlg.Filter = "ICC,ICM|*.icc;*.icm" If dlg.ShowDialog() = True Then For Each path As String In dlg.FileNames Dim d = ICC.GetDescription(path) System.Diagnostics.Debug.WriteLine(path + vbTab + d.ASCIIInvariantProfile + vbTab + d.UnicodeLocalizableProfile) Next End If End Sub End Class Friend Class ICC ' http://www.color.org/icc32.pdfを参考 Public Shared Function GetDescription(ByVal ICC_ICM_FileFullPath As String, Optional ByVal isBOM_BighEndianness As Boolean = True) As Description Dim uri As Uri = New Uri("file://" + ICC_ICM_FileFullPath) Dim cc = New System.Windows.Media.ColorContext(uri) Using stream = cc.OpenProfileStream() Dim tags As List(Of Tag) = Tag.GetTags(stream) Dim tagDesc = tags.FirstOrDefault(Function(t) Return String.Equals(t.signature, "desc") End Function) Dim data As Byte() = tagDesc.GetData(stream) Return Description.Decode(data) End Using End Function Private Class BigEndianness Public Shared Function ReadInt32(ByVal stream As System.IO.Stream) As Int32 Dim bs As Byte() = New Byte(3) {} stream.Read(bs, 0, 4) Array.Reverse(bs) Return BitConverter.ToInt32(bs, 0) End Function Public Shared Function ReadInt32(ByVal buff As Byte(), ByVal index As Integer) As Int32 Dim bs As Byte() = New Byte(3) {} Array.Copy(buff, index, bs, 0, 4) Array.Reverse(bs) Return BitConverter.ToInt32(bs, 0) End Function Public Shared Function ReadInt16(ByVal buff As Byte(), ByVal index As Integer) As Int32 Dim bs As Byte() = New Byte(1) {} Array.Copy(buff, index, bs, 0, 2) Array.Reverse(bs) Return BitConverter.ToInt16(bs, 0) End Function Public Shared Function ReadString(ByVal stream As System.IO.Stream, ByVal size As Integer) As String Dim bs As Byte() = New Byte(size - 1) {} stream.Read(bs, 0, size) Return System.Text.Encoding.ASCII.GetString(bs).TrimEnd(ChrW(0)) End Function Public Shared Function ReadString(ByVal buff As Byte(), ByVal index As Integer, ByVal size As Integer) As String Return System.Text.Encoding.ASCII.GetString(buff, index, size).TrimEnd(ChrW(0)) End Function Public Shared Function ReadUnicode(ByVal buff As Byte(), ByVal index As Integer, ByVal size As Integer, Optional ByVal isBOM_BighEndianness As Boolean = True) As String If (size = 0) Then Return String.Empty End If Dim data As Byte() = New Byte(size - 1) {} Array.Copy(buff, index, data, 0, size) If size >= 2 AndAlso data(0) = &HFF AndAlso data(1) = &HFE Then 'BOMが間違っているファイルがある… If (isBOM_BighEndianness) Then data(0) = &HFE data(1) = &HFF End If ElseIf Not (size > 2 AndAlso data(0) = &HFE AndAlso data(1) = &HFF) Then 'BOMがない data = New Byte(size + 2 - 1) {} Array.Copy(buff, index, data, 2, size) If (data(2) <> 0) Then data(1) = &HFE data(0) = &HFF Else data(0) = &HFE data(1) = &HFF End If End If Dim ms = New System.IO.MemoryStream(data) Dim sr = New System.IO.StreamReader(ms, System.Text.Encoding.Unicode, True) Return sr.ReadToEnd().TrimEnd(ChrW(0)) End Function End Class Public Class Tag Public signature As String Public offset As Integer Public size As Integer Public Shared Function GetTags(ByVal stream As System.IO.Stream) As List(Of Tag) Dim buffer As Byte() = New Byte(127) {} stream.Seek(&H80, System.IO.SeekOrigin.Begin) Dim tags As List(Of Tag) = New List(Of Tag)() Dim tagCount As Integer = BigEndianness.ReadInt32(stream) With Nothing Dim i As Integer = 0 For _F_4 As Integer = 0 To 1 Step 0 If Not (i < tagCount) Then Exit For End If Dim t As Tag = New Tag() t.signature = BigEndianness.ReadString(stream, 4) t.offset = BigEndianness.ReadInt32(stream) t.size = BigEndianness.ReadInt32(stream) tags.Add(t) i += 1 Next End With Return tags End Function Public Function GetData(ByVal stream As System.IO.Stream) As Byte() stream.Seek(offset, System.IO.SeekOrigin.Begin) Dim bs As Byte() = New Byte(size) {} stream.Read(bs, 0, bs.Length) Return bs End Function End Class Public Class Description Public ASCIIInvariantProfile As String Public UnicodeLocalizableProfile As String 'public string LocalizableMacintoshProfile; Public Shared Function Decode(ByVal data As Byte(), Optional ByVal isBOM_BighEndianness As Boolean = True) As Description If System.Text.Encoding.ASCII.GetString(data, 0, 4) <> "desc" Then Throw New ArgumentException() End If Dim d As Description = New Description() Dim p As Integer = 8 Dim asciiLength As Integer = BigEndianness.ReadInt32(data, p) p += 4 d.ASCIIInvariantProfile = BigEndianness.ReadString(data, p, asciiLength) p += asciiLength Dim unicodeLanguageCode As Integer = BigEndianness.ReadInt32(data, p) p += 4 Dim unicodeLength As Integer = BigEndianness.ReadInt32(data, p) p += 4 d.UnicodeLocalizableProfile = BigEndianness.ReadUnicode(data, p, unicodeLength * 2, isBOM_BighEndianness) p += unicodeLength * 2 'int scriptcode = data[p]; 'p += 1; 'int scriptcodeLength = BigEndianness.ReadInt16(data, p); 'p += 2; 'd.LocalizableMacintoshProfile = BigEndianness.ReadString(data, p, scriptcodeLength).TrimEnd('\0'); ; Return d End Function End Class End Class
#例示されているICMファイルは手元の環境にないので試せてません個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)
- 回答としてマーク ShiroYuki_Mot 2016年12月18日 3:55
-
gekka さま ご回答ありがとうございます。
Code 完璧です。
C:\Windows\System32\DriverStore\FileRepository\ にある某プリンタ用 icm ファイル群を見てみました。
何の問題もなく、取得が出来る様になりました。本当にありがとうございました。
また、
これらは各言語対応を意識してか、全て、Ascii (EBSDIC) で profile descrition が記述されています。
メーカーサイトに情報はあるのですが、手入力を覚悟していました。
でも、お陰様で、プログラムからファイル情報が取得出来ます。
尚、後日、検索で来られる方の為に、
MainWindow に Textbox をひとつ置いた場合の Sub New() の Code を掲載します。 IDE で Textbox 配置です。
結果(表示内容)は
指定フォルダ
指定ファイル数
ファイル名 ASCIIInvariantProfile
です。Sub New() ' この呼び出しはデザイナーで必要です。 InitializeComponent() 'Add by ShiroYuki_Mot Dim d_SB As New Text.StringBuilder Dim f_inf As IO.FileInfo 'End Add ' InitializeComponent() 呼び出しの後で初期化を追加します。 Dim dlg As Microsoft.Win32.OpenFileDialog = New Microsoft.Win32.OpenFileDialog() dlg.Multiselect = True dlg.Filter = "ICC,ICM|*.icc;*.icm" If dlg.ShowDialog() = True Then 'Add by ShiroYuki_Mot If dlg.FileNames.Count = 0 Then Exit Sub Else f_inf = New IO.FileInfo(dlg.FileName) d_SB.Append(f_inf.DirectoryName) d_SB.Append(vbCrLf) d_SB.Append(dlg.FileNames.Count.ToString) d_SB.Append(vbCrLf) End If 'End Add For Each path As String In dlg.FileNames Dim d = ICC.GetDescription(path) System.Diagnostics.Debug.WriteLine(path + vbTab + d.ASCIIInvariantProfile + vbTab + d.UnicodeLocalizableProfile) 'Add by ShiroYuki_Mot f_inf = New IO.FileInfo(path) d_SB.Append(f_inf.Name) d_SB.Append(Space(6)) d_SB.Append(d.ASCIIInvariantProfile) d_SB.Append(vbCrLf) 'End Add Next End If 'Add by ShiroYuki_Mot Me.TextBox1.Text = d_SB.ToString 'End Add End Sub
- 編集済み ShiroYuki_Mot 2016年12月18日 12:53 Code コメント不完全を修正