none
ICC profile の説明語句の取得について RRS feed

  • 質問

  • WPF で画像をハンドリングするものを VB.net で書いていますが、
    実行環境上の ICC profile 群を取得する上で、ファイル名は拡張子 .icm から判断出来るのですが、
    各ファイルに格納された 説明 (Description) が取得出来ずに困っています。
    保存されたプロファイル群を扱う上で、ファイル名ではなく、この 説明 (Description) で取り扱いと考えています。
    .Netframework 上から、 ファイルに格納された 説明 (Description) を読み込むには、
    どの様に 取得部分 を記述すれば良いのか、 お教え戴けないでしょうか。

      説明 (Description) は、
      例えば、Canon インクジェットプリンタ用の 汎用プロファイル CNBJPRN3.ICM なら、
      "Canon IJ Color Printer Profile 2005" と表記される内容です。
          内容は メーカー名+モデル名+用紙(+精度)で構成されています。
    2016年12月16日 6:10

回答

  • こんな

    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!)

    2016年12月17日 16:31

すべての返信

  • こんな

    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!)

    2016年12月17日 16:31
  • 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 コメント不完全を修正
    2016年12月18日 3:58