2024/12/10

DXL Step-by-Step:#42)インラインイメージの取得

前回はインラインイメージ(リッチテキストに直接貼り付ける画像)を貼り付ける方法を紹介しました。今回はインラインイメージを取得する方法についてまとめます。


DXL の構造

まずはインラインイメージの DXL の確認です。

前回説明したように picture ノードがリッチテキストに貼り付けられた画像を表します。その配下に gif ノードなど画像データが直接存在すればインラインイメージです(画像がイメージリソースを参照している場合は imageref ノード)。

DXL の読み込みにおいては、注意点があります。

上記 DXL で picture ノードの最初の子ノードは border となっています。これは、イメージリソースに設定された枠の情報です。

border ノードは画像データより前に配置され、標準の設定(枠なし)の場合は出力されません。画像データを取得するには ”最初の子ノード” ではなく、子ノードの中から画像データを探す必要があるということになります。


エージェントの作成

#14 以降、文書の DXL について解説してきました。ただ、これまでずっと文書やリッチテキスト内のオブジェクトを新規作成する方法が中心でした。今回は既存文書を DXL でアクセスする方法となります。

そこで、今回はプログラム全体を確認しながら進めます。まずは、選択した文書に対して実行するエージェントを作成します。エージェントのメインプログラムは次の通りです。

Option Declare
Private xns As NotesSession

Sub Initialize
   Dim ndb As NotesDatabase
   Dim ndc As NotesDocumentCollection
   Dim nd As NotesDocument

   '処理対象の文書を取得
   Set xns = New NotesSession
   Set ndb = xns.CurrentDatabase
   Set ndc = ndb.UnprocessedDocuments
   Set nd = ndc.GetFirstDocument()

   '画像データの取得
   Dim nst As NotesStream '画像データ
   Dim sTag As String '画像の拡張子

   If Not(nd Is Nothing) Then
      sTag = xGetDXL_FirstInlineImage(nd, "Body", nst)

      If sTag <> "" Then
         '取得できたらファイルとして保存
         Call StreamToImageFile(nst, "c:\tmp\test." & sTag)
      End If
   End If
End Sub

StreamToImageFile 関数は #9 でイメージリソースから画像をダウンロードした際に作成した関数をそのまま再利用しています。

xGetDXL_FirstInlineImage が画像データを取得する関数で、今回の主題のプログラムとなります。


画像データの取得

関数の全体は次の通りです。

引数は、画像が含まれる文書 vnd、インラインイメージが存在するフィールド名 vsFld、取得した画像を NotesStream オブジェクトで返す rnst です。戻り値は画像データがあったノード名で、png / gif / jpeg となり、メインプログラムでは保存するファイルの拡張子として使用しています。もし、インラインイメージが見つからない場合には null を返します。

Function xGetDXL_FirstInlineImage(vnd As NotesDocument, ByVal vsFld As String, rnst As NotesStream) As String
   'DXL の準備
   Dim dprs As NotesDOMParser
   Set dprs = xGetDOMParser(vnd)

   'DOM ツリーのルートを取得
   Dim ddn As NotesDOMDocumentNode
   Set ddn = dprs.Document

   'リッチテキストの取得
   Dim denRT As NotesDOMElementNode
   Set denRT = xGetDXL_item(ddn, vsFld)

   '画像の取得
   Dim denPct As NotesDOMElementNode
   Set denPct = xGetDXL_FirstNodeByName(denRT, "picture")

   '画像ファイル取得
   Dim denImg As NotesDOMElementNode
   Dim dnl As NotesDOMNodeList
   Dim dtn As NotesDOMTextNode
   Dim nst As NotesStream
   Dim sB64 As String

   '調査対象ノード
   Dim asTag(2) As String
   asTag(0) = "png"
   asTag(1) = "gif"
   asTag(2) = "jpeg"

   '画像データの捜索
   If Not(denPct Is Nothing) Then
      ForAll sTag In asTag
         Set denImg = xGetDXL_FirstNodeByName(denPct, sTag)
         If Not(denImg Is Nothing) Then
            'エンコードされた画像データ取得
            Set dtn = denImg.FirstChild
            sB64 = dtn.NodeValue

            'デコードしてストリームとして取得
            Set nst = xns.CreateStream()
            Call Base64ToBinary(sB64, nst)

            '戻り値セット
            Set rnst = nst
            xGetDXL_FirstInlineImage = sTag
            Exit ForAll
         End If
      End ForAll
   End If
End Function

DXL の準備では文書を DXL に変換しています。コールしている xGetDOMParser 関数は #3 で紹介したものをそのまま流用しています。

xGetDXL_item 関数でフィールドを表す item ノードを取得、xGetDXL_FirstNodeByName 関数で、item ノードの中から最初に出現する画像のノード picture を取得しています。それぞれの関数は後述します。

picture ノード配下から画像データ(png / gif / jpeg のどれか)を捜索します。見つかったら、画像データであるテキストノードを取得、Base64 でエンコードされているので、デコードして NotesStream オブジェクトに変換します。デコードには #9 で紹介した Base64ToBinary 関数を利用しています。

画像が取得できたら戻り値にセットして、関数を終了しています。


フィールドを取得する関数

引数で指定したフィールドを取得する関数 xGetDXL_item は次の通りです。すべての item ノードを取得して、name 属性の値で判定しています。

Option Declare
Private xns As NotesSession

Function xGetDXL_item(vddn As NotesDOMDocumentNode, ByVal vsFld As String) As NotesDOMElementNode
   '文書のノードの取得
   Dim denDoc As NotesDOMElementNode
   Set denDoc = vddn.DocumentElement

   'itemノードをチェック
   Dim den As NotesDOMElementNode
   Dim dnl As NotesDOMNodeList
   Dim i As Integer
   Dim sFld As String

   Set dnl = denDoc.GetElementsByTagName("item")
   For i = 1 To dnl.NumberOfEntries
      Set den = dnl.GetItem(i)

      'フィールド名をチェック
      sFld = den.GetAttribute("name")
      If LCase(sFld) = LCase(vsFld) Then
         '戻り値セットしてループ脱出
         Set xGetDXL_item = den
         Exit For
      End If
   Next
End Function


配下のノードを取得

最初に紹介した picture ノード配下の画像データを取得しているのが xGetDXL_FirstNodeByName 関数です。この関数は GetElementsByTagName で配下のノードを検索し、最初のノードを取得しています。

フィールドの取得とほぼ同じ構造ですが、name 属性の判定がない分シンプルです。

Function xGetDXL_FirstNodeByName(vdenParent As NotesDOMElementNode, ByVal vsName As String) As NotesDOMElementNode
   Dim dnl As NotesDOMNodeList

   On Error Resume Next

   '配下のノードを検索
   Set dnl = vdenParent.GetElementsByTagName(vsName)
   If dnl.NumberOfEntries > 0 Then
      '最初のノードを戻り値にセット
      Set xGetDXL_FirstNodeByName = dnl.GetItem(1)
   End If
End Function


まとめ

今回は、既存文書内に貼り付けられたインラインイメージを画像ファイルとして取得する方法についてまとめました。処理を簡素化するため、リッチテキスト内の最初の picture ノードがインラインイメージであることを前提としています。

リッチテキストにおいて、picture ノードはイメージリソースを貼り付けた時、添付ファイルのアイコン画像などさまざまな用途で出現します。GetElementsByTagName メソッドで単純に配下すべてを検索すると、想定外の picture ノードを取得することがあります。実際のリッチテキストを処理する場合には、注意が必要です。


前回 DXL Step-by-Step


0 件のコメント:

コメントを投稿