2024/03/03

DXL Step-by-Step:#30)イメージリソースで QR コードを書いてみよう ②

前回に引き続き DXL を使って QR コードを作画に挑戦する話の後編です。今回はメインの部分である作画ルーチンを紹介します。


画像の準備

まず、作画に利用する画像を準備します。

QR コードの 1 マスは白か黒で構成されています。それぞれの画像をリッチテキストに並べれば QR コードができます。ただそれだとプログラミングとして芸がないので、2 x 2 マスのパターン 16 種の画像を準備します。

ファイル名は、左上、右上、左下、右下の順で 2進数としてつなぎ、16 進数に変換しています。例えば次のような感じです。

作成した画像はイメージリソースに登録しておきます。


作画ルーチンのサンプル

前回紹介できなかった xSetDXL_QR の全体像を先に紹介します。作画ルーチンはこの関数だけで構成されています。ポイントとなる点は後述のセクションで解説します。

Function xSetDXL_QR(vddn As NotesDOMDocumentNode, vdenPar As NotesDOMElementNode, vabQR() As Boolean)
   Dim denPic As NotesDOMElementNode
   Dim den As NotesDOMElementNode
   Dim x As Integer
   Dim y As Integer
   Dim by As Byte

   'QR コードをイメージリソースに変換
   For y = 0 To Int(UBound(vabQR, 2)/2)
      For x = 0 To Int(UBound(vabQR, 1)/2)
         '2 x 2 エリアのリソース番号を算出
         by = 0
         On Error Resume Next
         If vabQR(x*2, y*2) Then by = by + (2^0)
         If vabQR(x*2+1, y*2) Then by = by + (2^1)
         If vabQR(x*2, y*2+1) Then by = by + (2^2)
         If vabQR(x*2+1, y*2+1) Then by = by + (2^3)
         On Error GoTo 0

         '画像の作成
         Set den = vddn.CreateElementNode("picture")
         Set denPic = vdenPar.AppendChild(den)

         'イメージリソースの指定
         Set den = vddn.CreateElementNode("imageref")
         Call den.SetAttribute("name", Hex$(by) & ".png")
         Call denPic.AppendChild(den)
      Next

      '改行の追加
      Set den = vddn.CreateElementNode("break")
      Set denPic = vdenPar.AppendChild(den)
   Next
End Function


ループの決定

事前に作成した画像の通り、QR コードの配列を 2 x 2 で抽出しながら処理しています。よってループ数は QR コードのサイズの半分となります。これを実現しているのか以下の For ループです。

   For y = 0 To Int(UBound(vabQR, 2)/2)
      For x = 0 To Int(UBound(vabQR, 1)/2)

UBound の 2 つ目の引数は多次元配列の次元数を指定します。それを利用して、縦と横の要素数の半分でループを回しています。半分にしているのは 2 x 2 単位で処理するからですね。また、端数をなくすために Int 関数を使用して整数部だけを取得しています。

配列 vabQR 要素数が 6 x 4 だったとすると、ループ変数 x と y は以下のようになるということです(実際の QR コードではもっと大きなサイズとなります)。


リソース番号の算出

2 x 2 のエリアに該当するリソース番号を算出しているのは以下の部分です。

         '2 x 2 エリアのリソース番号を算出
         by = 0
         On Error Resume Next
         If vabQR(x*2, y*2) Then by = by + (2^0)
         If vabQR(x*2+1, y*2) Then by = by + (2^1)
         If vabQR(x*2, y*2+1) Then by = by + (2^2)
         If vabQR(x*2+1, y*2+1) Then by = by + (2^3)
         On Error GoTo 0

ループ変数 x と y を使用して vabQR の要素にアクセスします。2 倍して 1 を加算するかどうかで、上下左右の要素にアクセスしています(オレンジ)。その結果が True の場合、その位置に応じた値を加算しています())。


ところで、If 文に "=" がないので、不自然に感じる方がいるかもしれません。

         If vabQR(x*2, y*2) Then by = by + (2^0)

これは配列 vabQR が Boolean 型であるからです。要素の値が True の場合 True = True のような判定をしても意味がありません。要素の値そのものが判定結果として使えるので、省略しています。


また、今回の演算の範囲に限り On Error Resume Next でエラーを無視するようにしています。これは、配列 vabQR の要素数が奇数だった時の対策で、範囲外の要素にアクセスしたエラーを無視するためです。QR コードの場合ではありえないパターンかもしれませんが...


イメージリソースの配置

イメージリソースの配置自体は単純です。picture ノードを作成し、その下に imageref ノードを追加し、リソース名を name 属性で設定します。リソース名は、事前に計算したリソース番号を 16 進数に変換して決定しています。

         '画像の作成
         Set den = vddn.CreateElementNode("picture")
         Set denPic = vdenPar.AppendChild(den)

         'イメージリソースの指定
         Set den = vddn.CreateElementNode("imageref")
         Call den.SetAttribute("name", Hex$(by) & ".png")
         Call denPic.AppendChild(den)

ループ変数 x のループが終わると break ノードを追加しています。このノードは段落内での改行を表します。これで 2 回目のループ以降も行を変え左端から画像が配置されることになります。

      Set den = vddn.CreateElementNode("break")
      Set denPic = vdenPar.AppendChild(den)


生成される DXL

今回のサンプルで生成される DXLは次の通りです(リッチテキスト部分のみ抜粋し一部省略)。

段落 par の下にイメージリソースのノードが並びます。このような構造にすることで、イメージが連続して配置され、左から右に順に表示されます。また、1行分の出力が終わると break ノードが出力されています。これで次の行の画像を表示する準備をしているということですね。


実行結果

今回のサンプルと連載『ノーツで QR コード』のプログラムで出力した結果を比較してみました。課題だったセル間の隙間がなくなりずいぶん QR コードらしくなりました。また、サイズが小さくなったので、より大きな QR コードにも対応できそうですね。

ちなみに、今回はセルのサイズは 5 x 5 ピクセルの画像を使用しました。まだまだ小さくできる余地はありそうです...


前回 DXL Step-by-Step


2024/03/01

DXL Step-by-Step:#29)イメージリソースで QR コードを書いてみよう ①

DXL 活用の調査・検証で、実現できたことや発見したことご紹介する『DXL Step-by-Step』シリーズの第 29 回です。リッチテキストにイメージリソースを表示できるようになったので、それを利用してサンプルプログラムを作成します。

材料にするのは QR コードです。

別の連載『ノーツで QR コード』で作成したライブラリです。この連載では、入力した文字列を QR コードに変換するサンプルるプログラムを紹介しており、すべて LotusScript で構築されています。ただ、連載では QR コードの表示に文字の ”■” を使用していたのですが、文字間に隙間があるためか認識率が良くありませんでした。

この問題点の改善を DXL で挑戦します。今回はその前編となります。


サンプルエージェントと QR コード生成部の組み込み

前々回の 第 27 回 のエージェントをコピペして作業を開始します。

まずは、そこに QR コード生成ライブラリを組み込みます。スクリプトライブラリ lsQRcode をコピペした上で (Options) セクションに追加します。

Option Declare

Use "lsDXL"
Use "lsQRcode"


続いて、QR コードを生成する部分を移植します。

連載終了時のメインルーチンから必要な部分をコピペします。多少修正していますが次のような感じです。なお、bc_2Dms_New 関数はライブラリ lsQRcode の外側(エージェント内)に定義していました。この関数もそのまま利用するので、今回のエージェントにコピペしています。

Sub Initialize
   Dim sSrc As String
   Dim sEnc As String
   Dim abQR() As Boolean

   'QR コード生成
   '---------------------------

   sSrc = Inputbox("QRコードに変換する文字列を入力してください。", "QRコード作成")

   If Msgbox("『" & sSrc & "』の QR コードを作成しますか?", 36, "") <> 6 Then Exit Sub

   sEnc = EncodeBarcode(sSrc, 3)    '3 = 誤り訂正レベル
   Call bc_2Dms_New(sEnc, abQR)    'abQR 配列にQRコードがセット

   Call CreateQRcodeDoc(sSrc, abQR)    'QRコードを文書として保存
    ・・・

bc_2Dms_New までの処理で、入力した文字列を QR コード に変換して、Boolean 型の 2 次元配列 abQR にセットしています。

最後の関数 CreateQRcodeDoc でその配列を文書化して保存していました。今回は、この部分を DXL で作り替えるということになります。


DXL で QR コードを作画

DXL で文書を作成するメインルーチンの構造は、これまでとほとんど同じです。先のメインルーチン Initialize の続きは次の通りです。唯一違うのは、DXL を構築する関数 xSetDXL に引数が追加されており、QR コードに変換前の文字列と変換後の配列を関数に渡すようにしています。

   Call bc_2Dms_New(sEnc, abQR)    'abQR 配列にQRコードがセット

   'DXL で QR コードを文書に保存
   '---------------------------

   Dim dprs As NotesDOMParser
   Dim ndb As NotesDatabase

   Set xns = New NotesSession
   Set ndb = xns.CurrentDatabase

   Set dprs = xns.CreateDOMParser()
   'DXL の構築
   Call xSetDXL(dprs, sSrc, abQR)

   '保存(インポート)
   If Not DXL_Import(dprs, DXLIMPORTOPTION_CREATE, False) Then
      'エラーの場合 DXL を出力
      Call Debug_SaveDXL(dprs)
   End If
End Sub


文書の生成と DXL の構造

今回作成する文書の DXL のイメージは次のようになります。

フォーム QR_DXL にフィールドが 2 つあり、入力した文字列が保存されるテキストフィールド StrSrc と QR コードがセットされるリッチテキスト Body です。

この構造に従い、各ノードをを作成し、必要な属性をセットしていきます。

Function xSetDXL(vdprs As NotesDOMParser, ByVal vsSrc As String, vabQR() As Boolean)
   Dim ddn As NotesDOMDocumentNode
   Dim den As NotesDOMElementNode
   Dim denDoc As NotesDOMElementNode
   Dim denItem As NotesDOMElementNode
   Dim denRT As NotesDOMElementNode
   Dim denPar As NotesDOMElementNode

   Set ddn = vdprs.Document

   '文書作成
   Set den = ddn.CreateElementNode("document")
   Call den.SetAttribute("form", "QR_DXL")
   Set denDoc = ddn.AppendChild(den)

   '変換前の文字列をフィールドにセット
   Call DXL_AppendField(ddn, "StrSrc", vsSrc)

   'リッチテキストフィールド作成
   Set den = ddn.CreateElementNode("item")
   Call den.SetAttribute("name", "Body")
   Set denItem = denDoc.AppendChild(den)

   'リッチテキスト作成
   Set den = ddn.CreateElementNode("richtext")
   Set denRT = denItem.AppendChild(den)

   '段落定義 id='1'
   Set den = ddn.CreateElementNode("pardef")
   Call den.SetAttribute("id", "1")
   Call denRT.AppendChild(den)

   '段落の作成
   Set den = ddn.CreateElementNode("par")
   Call den.SetAttribute("def", "1")
   Set denPar = denRT.AppendChild(den)

   'イメージリソースで QR コードを作画
   Call xSetDXL_QR(ddn, denPar, vabQR)
End Function

最終行の xSetDXL_QR が 2 次元配列から QR コードを作画している関数です。この関数の詳細は次回解説します。


なお、テキストフィールド StrSrc に値をセットする部分については連載で作成した関数を流用しています。第 10 回 で xAppendField として紹介した関数を DXL_AppendField としてライブラリ lsDXL に追加しています。

修正箇所は次の赤字の部分です。それ以外は変更がないので省略しています。ご了承ください。

Public Function DXL_AppendField( _
         vddn As NotesDOMDocumentNode, _
         ByVal vsFldName As String, _
         ByVal vvValue As Variant) As Boolean

    ・・・以下、同じなので省略・・・


前回 DXL Step-by-Step


2024/02/28

設計引継ぎの動作 ③:アプリケーションの設計の置換

テンプレートからアプリケーションに対して設計を反映する機能(操作)の最終回です。今回は『設計の置換』を紹介します。


設計の置き換えの操作

アプリケーションアイコンの右クリックメニューから [アプリケーション] - [再設計] を選択します。

続いて、テンプレートを選択する画面が表示されますので、サーバとテンプレートを選択します。

【置換】ボタンをクリックすると、アプリケーションの設計が選択したテンプレートに置き換わります。実行が終了すると、アイコンが更新されました。処理内容はステータスバーに表示されますので、通知(鈴のアイコン)から詳細が確認できます。

アプリケーションを開くとフォームやビューなど設計が選択したテンプレートに置き換えられたことが確認できます。


設計の置き換えの機能

設計が置き換えられるのは前述の通りですが、その結果を確認しましょう。

プロパティを開きテンプレートを確認すると引き継ぐテンプレート名が変更されています。設計の置換では、テンプレートから設計を反映するだけでなく、引き継ぐテンプレートの設定まで行ってくれるのが特徴です。以降は、再設計や Design タスクで設計が反映できるということですね。


テンプレートを引き継いで設計

新規アプリケーション作成時や設計の置き換え画面には『テンプレートを引き継いで設計』というチェックボックスが存在します。

このチェックを外した場合は、アプリケーションのプロパティからテンプレートの設定が解除されます。再設計や Design タスクで移行設計を更新したくない場合に使用できますね。

もちろん、アプリケーションのプロパティから上記設定を手動で削除するだけでもテンプレートとの関係は解除できます。


設計の置き換えと引継ぎ

設計の置き換えはテンプレートの設計に入れ替える機能でした。この機能は、一般的にはアプリケーションのバージョンアップの場合に利用します。

例えば次のような手順です。

  1. 運用中のアプリをローカルなどにコピーする
  2. デザイナーで設計を変更する
  3. テンプレートを作る(設計を ntf としてコピー)
  4. 運用中のアプリの設計を置き換える

設計の置換後もテンプレートから設計を反映したい場合は、テンプレートの配置とテンプレート名の設定を別途行い、Design タスクに任せることになります。

設計の置き換えと設計の引継ぎは別機能として考えるとわかりやすいと思います。


ところで、最初の事例のように、全く違う設計に変更することもできます。ただ、文書はそのままなので、機能と文書が合致せずアプリとしては成立しなくなります。”設計の置換” 操作としては正しい動作なのでエラーは発生しません。

『見たことのない画面になってる!』と突然ユーザから言われる。長年ノーツ担当すると何度かは経験する『ノーツあるある』です...

ご注意ください。


2024/02/26

設計引継ぎの動作 ②:アプリケーションの再設計

前回は、テンプレートからアプリケーションに対して設計を反映する Design タスクを紹介しました。今回はノーツクライアントから実行できる操作『アプリケーションの再設計』についてまとめます。


アプリケーションの再設計

アプリケーションアイコンの右クリックメニューから [アプリケーション] - [再設計] を選択します。

するとサーバの選択が求められますので、テンプレートが存在するサーバを指定します。

再設計を実行すると、ステータスバーに結果が表示されます。通知を開き、ステータス タブから詳細が確認できます。


指定したサーバにテンプレートが見つからない場合には、エラーメッセージが表示されます。


タスクと再設計の違い

Design タスクと再設計はどちらもテンプレートから設計を反映するための機能です。それぞれの違いについてまとめると次のようになります。


再設計 Design タスク
実行場所 クライアント サーバ
引継ぎ元 別のサーバやローカルのテンプレートも利用可能 サーバ内のテンプレートのみ
対象 単一のアプリケーション サーバ内のすべてのアプリケーション
タイミング 手動 自動(手動実行も可能)

繰り返しになりますが、どちらの機能であっても、アプリケーションとテンプレート名が合致している必要があります。この関係が成立していない場合には設計は反映されません。


2024/02/24

設計引継ぎの動作 ①:デザインタスク

テンプレートからアプリケーションに対して設計を反映する動作の一つである『デザインタスク』の仕組みについて整理します。


デザインタスクとは?

デザインタスク(Design)は ”タスク” ですので Domino サーバで稼働するプログラムです。実行するとサーバ内のすべての DB を対象に実行され、テンプレートとの設計の相違を修正する動作を行います。

処理対象となるアプリケーションは、テンプレートに設定されている名称と合致している必要があります。


Design タスクが実行されるとサーバにログが記載されます。まず、開始と終了は次のようなメッセージが表示されます。

2024/02/23 09:02:04 Database Designer started
  ・・・
2024/02/23 09:02:06 Database Designer shutdown

さらにログには設計要素を修正した記録が出力されます。サンプルとして、更新 / 追加 / 削除 した場合のログを抜き出すと次の通りとなります。

2024/02/23 09:02:05 (コメント\IDB) をデータベース 新規アプリケーション へテンプレート Domino ブログ (12) から更新します。
2024/02/23 09:02:05 (コメント\ID) をデータベース 新規アプリケーション へテンプレート Domino ブログ (12) から追加します。
2024/02/23 09:02:05 (コメント\IDB_add) をデータベース 新規アプリケーション から削除します。

アプリケーションとテンプレートともアプリケーション名称で記録されています。ファイル名やパス、テンプレート名で記載してくれた方が明確になるんですけどね...


エラーの表示

例えば、アプリケーションのプロパティでテンプレート名を間違えたとします。

この状態でデザインタスクを実行すると、テンプレートを発見できないことから警告メッセージが表示されます。

2024/02/23 09:19:38 警告:データベース 'C:\Lotus\Domino\data\NewApp.nsf' で使用されている設計テンプレ ート 'xxxStdDominoBlog/ja' を見つけることができません

エラーの場合は、問題となったテンプレート名やアプリケーションのファイルが明確に表示されます。これなら対応がしやすいですね。


タスクの定期的な実行

Domino サーバのデフォルトでは、午前 1 時に Design タスクが実行されます。これは、サーバの notes.ini に以下の行が記述されているからです。

ServerTasksAt1=Catalog,Design

ServerTasksAt1 は午前 1 時に実行するタスクを設定するエントリです。上記設定では、Catalog タスクと Design タスクの 2 つを実行するということですね。

実行時間を午前 2 時に変更したい場合には ServerTasksAt2 にタスク名のDesign を記述すればよいということになります。


タスクの手動実行

サーバに対して次のコマンドを入力すると即座に Design タスクを実行します。必要の応じて手動でタスクを実行できます。

> load design


2024/02/22

テンプレートの使用と設計の引継ぎ

テンプレートを使用して新規のアプリケーションを作成する画面では、『テンプレートを引き継いで設計』というオプションがあります。今回はこの設定を材料にテンプレートとアプリケーションの関係について確認します。


テンプレートの引継ぎとプロパティ

アプリケーション作成時に『テンプレートを引き継いで設計』にチェックを入れると、作成されたアプリケーションのプロパティでは『テンプレートから設計を引き継ぐ』にチェックが入ります。逆にチェックを外して作成するとプロパティにもチェックが入りません。

また、『テンプレートから設計を引き継ぐ』にチェックがある場合、テンプレート名に値が入っています。

テンプレート名はテンプレート(ntf)のプロパティを開き『マスターテンプレートのデータベース』というチェックチェックボックスの下に記述されています。この名称とアプリケーションのテンプレート名が合致することにより、テンプレートとアプリケーションの引継ぎ関係が成立する仕組みになっています。

アプリケーション作成時には、テンプレートのタイトルとファイル名しか表示されないことから、テンプレート名との関連がわかりにくいので、覚えておきましょう。


引継ぎ時の動作

『テンプレートから設計を引き継ぐ』状態にあるアプリケーションでは、テンプレートの設計と同じになるように動作します。例えば、アプリケーションの設定を更新した場合はテンプレートの設計に戻ります。逆に、テンプレートの設計が更新された場合は、アプリケーションの設計にも反映されます。

Notes/Domino において、このような ”設計を引き継ぐ” 動作(操作)はいくつかあります。

  • デザインタスク(Domino サーバ)
  • アプリケーションの再設計(Notes クライアント)
  • アプリケーションの設計置換(Notes クライアント)

それぞれ少しずつ機能が違うので、次回以降に順次まとめます。

 

2024/02/20

アプリケーションの作成とテンプレート

Notes/Domino のデータファイルは次の 2 つに大別されます。

種類 拡張子
1 アプリケーション nsf
2 テンプレート ntf

アプリケーションは通常利用するアプリケーションそのもので、テンプレートはアプリケーションの設計要素(機能)だけを保持するアプリケーションの ”ひな形” にとなります。テンプレートの名称の通りですね。

では、順にテンプレートの利用方法を確認しつつ、テンプレートの役割を確認しましょう。


アプリケーションの新規作成

新規アプリケーションの作成時に、テンプレートを指定することができます。

例えば、『Domino ブログ(12)』というテンプレートを選択して、アプリケーションを作成すると、設計要素が入った文書が空っぽのアプリケーションが作成されます。『Domino ブログ(12)』テンプレートの設計要素を丸ごとコピーした新規アプリケーションが作成されたということですね。

テンプレートを指定しない(『-未入力-』を選択)場合、設計要素も空っぽのアプリケーションが作成されます。ですので、テンプレートの指定は必須ではありません。


テンプレートと ACL

新規作成したアプリケーションの ACL を確認すると初期設定ができていることが確認できます。『Domino ブログ(12)』テンプレートの場合だと、Anonymous のエントリが作成されていてアクセス権が「作成者」に設定されています。また、ロールが定義された状態になっています。

このコントロールはテンプレート側の ACL の設定で行われています。テンプレートもワークスペースにアイコンを表示することが可能です。例えば、『Domino ブログ(12)』テンプレートを追加するのであれば、アプリケーションを開く画面でサーバを指定して、ファイル名を手入力します。

ファイル名は、アプリケーションの新規作成画面でテンプレートを選択する画面やDomino Administrator クライアントなどで確認できます。


ワークスペースにテンプレートが追加されたら、ACL を確認します。すると [Anonymous] というエントリがあり、新規作成されたアプリケーションの Anonymous と同じ設定となっていることが確認できます。

テンプレートからアプリケーションを作成した場合、エントリの [ ] をとり、ACL の初期値として登録する機能があります。これを利用して、ACL の初期設定をしていたんですね。

ちなみに、[ ] のない通常のエントリは、テンプレート自身の ACL として利用されています。


テンプレートもアプリケーション!?

上記の通り、テンプレートに対しても ACL の設定があり、設定どおりに機能します。そして、テンプレート内にも文書を登録することはできます。ですので、通常と同じアプリケーションのように利用することは可能です。構造上はアプリケーションとテンプレートに差はありません。

だからといって、テンプレートをアプリとして利用しても、運用が不明瞭なるだけです。あくまで、サンプルデータを入れてテンプレートを機能を明示する程度のものと考えましょう。また、運用するアプリを作成する際は、nsf と ntf は似ているので打ち間違えないように注意が必要ですね。

機能は正しく使わないと使いにくくなるだけですから...


2024/02/18

リッチテキスト:#17)編集中のリッチテキスト

ノーツの特徴的な機能であるリッチテキストを LotusScript で操作する方法を紹介している連載『リッチテキストの基本操作』の 17 回目です。

今回は編集中のリッチテキストのアクセスについてまとめます。記事を書くにあたり検証した結果、私自身も新発見がありました。ですので、今回は解説というよりは、実験レポートのような位置づけとなります。ご了承ください。


入力チェック

編集中のリッチテキストに対してアクセスするシーンで最初に思い浮かぶのが入力チェックです。今回は事例として、添付用のフィールドに添付ファイルが存在するかを確認してみます。

機能的には、指定したリッチテキストフィールドに添付ファイルがあればそのサイズを返す関数を作成します。添付が複数ある場合、その合計サイズを返すものとし、0 を返した場合は添付ファイルが存在しないと判定できる関数です。

作成した関数は次の通りです。

Function xGetSize(Byval sFldName As String) As Long
   Dim nuiw As New NotesUIWorkspace
   Dim nuid As NotesUIDocument
   Dim nd As NotesDocument
   Dim ni As NotesItem
   Dim nrti As NotesRichTextItem
   Dim nemb As NotesEmbeddedObject

   Dim lSize As Long
   Dim vTmp As Variant
   Dim i As Integer

   Set nuid = nuiw.CurrentDocument
   Set nd = nuid.Document

   lSize = 0

   '添付ファイルを認識させる
   Call nuid.Refresh(True)

   'フィールドを取得
   Set ni = nd.GetFirstItem(sFldName)
   If ni.Type = RICHTEXT Then
      'リッチテキストなので添付ファイルをチェック
      Set nrti = ni
      vTmp = nrti.EmbeddedObjects
      If Not (IsEmpty(vTmp)) Then
         For i = 0 To UBound(vTmp)
            '埋め込みオブジェクトを順に取得
            Set nemb = vTmp(i)
            If nemb.Type = EMBED_ATTACHMENT Then
               lSize = lSize + nemb.FileSize
            End If
         Next
      End If
   End If

    xGetSize = lSize
End Function

続いてフォームにアクションボタンを作成して、上記関数とメインルーチンを記述します。

Sub Click(Source As Button)
   Msgbox xGetSize("Body"), 64, "RichText #17"
End Sub

実行すると次の通りフィールド内の添付ファイルのサイズの合計が返されます。8409 バイトのファイルを 2 つ貼り付けたので正しく取得できています。

Refresh メソッド

この関数でポイントとなるのが、リッチテキスト内部に Resresh メソッドをコールしている部分です。

   '添付ファイルを認識させる
   Call nuid.Refresh(True)

デザイナーヘルプによると Refresh メソッドには引数があります。

Refresh (NotesUIDocument - LotusScript®)

LotusScript では、UI に表示中のオブジェクトをフロントエンドと呼びます。Notes Object Class では、NotesUI????? クラスとして定義されています。そうでない場合はバックエンドと呼び、Notes????? クラス(UI が付かない)として定義されています。

NotesDocument クラスや NotesRichTextItem クラスなど一般の処理に利用するクラスばバックエンドです。入力チェックの作成などにおいては、NotesUIDocument から NotesDocument を取得することになります。

   Set nd = nuid.Document

リッチテキストフィールドでは、通常、バックエンドに渡されるのは文書として保存されている状態のものが渡されます。よって、編集前の状態であったり、入力しているのに空となったりします。

前置きが長くなりましたが、Refresh メソッドの引数で True を指定すると、リッチテキストのフロントエンドがバックエンドに反映されるというわけです。


エージェントでは使用できない?

ところでこのプログラムをエージェントに移設して、アクションボタンのシンプルアクションでエージェントを実行するようにしてみました。文書を編集してからボタンをクリックすると次のようなエラーが発生しました。

プログラムは全く同じなので、エージェント化した場合の制約だと思われます。

前回 発生したエラーもエージェント化が原因だったということですね...


まとめ

今回の実験で編集中のリッチテキストを扱うには、

  • Refresh(True) の実行が必要
  • エージェント化するとエラーが発生する

という点がポイントであることがわかりました。


ところで、私はつい先日まで『リッチテキストの編集はいったん保存しないとフロントエンドでは扱えない』と思い込んでいました。Refresh メソッドに引数が増えたことは、気が付いていたのですが、全く調べず放置していました。引数が増えたのは、リリース 5 か 6 のようなので、20 年以上も放置していたことになります。

気づいた時点で調べる!技術者にとって大切ですね。今後はこのようなことがないよう気を付けようと思います...

前回 リッチテキストの基本操作


2024/02/16

DXL Step-by-Step:#28)DXL を利用したサンプルアプリが公開されました!

本日、2024 年 2 月 16 日に東京で『ノーツコンソーシアム FESTA 2024』が開催されました。プログラムの1コマに私がお世話になっている地区別研究会である『大阪研究会』(大阪研)の成果発表を行いました。


その成果の一つとして、DXL を利用したサンプルアプリがあり、設計公開の nsf として公開されました。DXL 部分は私が担当し、この連載の内容が多く含まれますので、紹介させていただきます。


サンプルアプリ

まずサンプルアプリは、大阪研仲間の 林 哲司 さんのブログ『Notes開発者のためのXPagesデザインレシピ』で公開されており、nsf としてダウンロードが可能です(設計公開)。

公開されたサンプルアプリは、Youtube から動画の一覧を取得、動画のサムネイル付きでノーツのビューに表示するというアプリです。

機能的には、

  1. Youtube から動画の情報やサムネイル画像を取得
  2. イメージリソースにサムネイル画像を登録し、ビュー表示に利用
  3. イメージリソースをリッチテキストにインラインイメージで貼り付け

というようなことを実現しています。開発技術的には、

  • WebAPI 連携
  • DXL

を利用しており、1 と 2 で WebAPI 連携、2 と 3 で DXL を使用しています。

ビューなどノーツの基礎技術を使いつつ、ノーツ臭のしないアプリに仕上がっています。ぜひともダウンロードしてお試しください。


WebAPI 連携と DXL を使いこなせば、これまで ノーツだから... と諦めていた機能が実現できることがあります。そんな可能性を感じていただけるはずです。


DXL ライブラリの構成

私が担当したのは、DXL を使用した部分で、DXL_Tiny というスクリプトライブラリにまとまっています。このライブラリには、公開されている(Public の)関数が 3 つあり、それぞれ次のような機能を提供しています。

関数 役割
ImageResource_New イメージリソースの登録
ImageResource_Remove イメージリソースの削除
InlineImage_Replace リッチテキストにイメージリソースをインラインで表示

関数の内部は、この連載『DXL Step-by-Step』で紹介した内容を再構成して作成しました。関数ごとに参考となる回のリンクをまとめます。もちろん、全体的に関連するのですが、直接関連するものを 、間接的なものを △ で表しています。

コードの確認にあたり、参考にしてください。

# タイトル
8 イメージリソース(設計要素)の DXL
9 DXL 内の画像のダウンロード
10 イメージリソースの新規作成
11 イメージリソースの DXL の注意点
12 イメージの形式と DXL の関係
13 イメージの形式とサイズの取得
14 文書のフィールドと DXL
15 リッチテキストを構成する基本要素
21 段落内に入るオブジェクト
26 イメージリソースの DXL
27 イメージリソースの表示


前回 DXL Step-by-Step


2024/02/14

リッチテキスト:#16)テキストの抽出

ノーツの特徴的な機能であるリッチテキストを LotusScript で操作する方法を紹介している連載『リッチテキストの基本操作』の 16 回目です。

前回は、メールの本文からリッチテキストを抽出するサンプルプログラムを紹介しました。今回はテキスト成分を抽出する方法をまとめます。


サンプルの動作

リッチテキストに適当なコンテンツを入力した文書を用意し、サンプルのエージェントを実行するアクションボタンを配置します。

アクションをクリックすると次のように、リッチテキストからテキスト成分だけを抽出してメッセージボックスで表示します。


サンプルのエージェント

作成したコードは以下の通りです。

NotesRichTextItem オブジェクトの Text プロパティを取得しているだけです。たったこれだけのコードで、リッチテキストからテキスト成分を取得できています。改行や表も体裁よく再現されているのでなかなか高性能ですね。

Option Declare

Sub Initialize
   Dim nuiw As New NotesUIWorkspace
   Dim nuid As NotesUIDocument
   Dim nd As NotesDocument
   Dim ni As NotesItem
   Dim nrti As NotesRichTextItem

   Set nuid = nuiw.CurrentDocument
   Set nd = nuid.Document

   Set ni = nd.GetFirstItem("Body")
   If ni.Type = RICHTEXT Then
      Set nrti = ni
      MsgBox nrti.Text, 64, "RichText #16"
   End If
End Sub

前回、少し複雑なサンプルを紹介したので順序を間違えたかもしれませんね...


入力中は取得できない!?

ところで、この文書を編集し、文字を追加してから、このエージェントを実行すると『文書が保存されていません』とエラーが発生します。

テキストや数値フィールドは、入力したてのデータにも簡単にアクセスできます。しかし、リッチテキストフィールドには少し制限があるようですね。


前回 リッチテキストの基本操作 次回