はじめに
業務システムでは、帳票などの定型書類の出力が必ずと言って良いほど要件に含まれます。方法はいろいろ考えられますが、本稿ではPDFによる出力方法を紹介します。とはいってもPDFを直接出力するのではなく、TeXをPDFに変換するプログラム(dvipdfmx)があるのでそれを利用します。TeXの出力にはテンプレートを用意し、必要な部分だけ書き換えて出力します。
対象読者
- .NETでシステム開発される方。
- PDF出力に興味のある方。
必要な環境
- Visual Studio .NET
- TeX(「platex.exe」「dvipdfmx.exe」が動作すること)
概要
作成するプログラムは、「文字列をパラメタで置き換えるプログラム」と「各プログラムをバッチ実行するプログラム」の2本です。入力ファイルは、「TeXテンプレート」と「変換パラメタXML」の2種類です。TeXテンプレートは1つですが、変換パラメタXMLの方は出力ファイルの数だけ用意します。バッチ実行プログラムは、変換パラメタXMLがあるフォルダから全てのファイル名を取得し、ファイルごとに決められた手順でプログラムを実行します。
なお、株式会社オンネット・システムズ(以下、弊社)では、UIを持たないプログラムのメイン処理は独立したThread
で実行し、Form
にログを出力しながら処理を進める形になっています。これは弊社での標準的な手法(以下、オンネット標準)なのでそのようにしていますが、通常のコンソールアプリケーションにしてしまっても問題はありません。メインの処理は、各cmp~
クラスのRun~()
メソッドに記述してあります。
文字列置換プログラム
このプログラムは以下のような動作をします。
- テンプレートファイルを読み、プレースホルダを検索する。
- 見つかったプレースホルダの名前に対応する値をパラメタから取得する。
- プレースホルダを対応するパラメタ値で置き換える。
- 全てのプレースホルダを置き換えた後、指定したファイルに出力する。
プレースホルダは「$$~$$」のように定義しています。「~」の部分がプレースホルダの名前にあたります。これを対応するパラメタで置き換えます。
System.Text.RegularExpressions.Regex regパラメタ = new System.Text.RegularExpressions .Regex(@"\$\$(?<PARAM>[^(\$\$)]+)\$\$"); for (int i = 0; i < list入力ファイル.Count; i++) { string strTemp = list入力ファイル[i].ToString(); strTemp = regパラメタ.Replace( strTemp, new System.Text.RegularExpressions .MatchEvaluator(Capパラメタ) ); if (strTemp.IndexOf("$$") >= 0) { throw ( new Exception( "対になる $$ が存在しません。[" + list入力ファイル[i].ToString() + "]") ); } list入力ファイル[i] = strTemp; }
list入力ファイル
はArrayList
です。テンプレートファイルの各行が文字列として入っています。ここでは正規表現を利用した置換を行っています。また、置換処理後に「$$」が残っている場合はテンプレートファイルのエラーとみなし、例外をthrow
しています。この部分のポイントは2つです。
まずは正規表現ですが、プレースホルダ名にあたる部分をグループ名にキャプチャしています。簡単に説明すると、丸型カッコで括った部分がグループ、「<?~>」の「~」の部分がグループ名になります。ソースコードを見ていただくと、グループ名にPARAM
と付けてあるのが分かると思います。グループのキャプチャについては、MSDNライブラリの『正規表現言語要素』に詳しい解説があります。キャプチャしたグループの取得については後述します。
次に置換処理ですが、Regex
のReplace()
メソッドにMatchEvaluator
の新しいインスタンスを渡しています。MatchEvaluator
は引数にMatch
オブジェクトを受け取りstring
を返すdelegate
です。正規表現のパターンにマッチした部分をメソッドを通して置換処理します。
private string Capパラメタ (System.Text.RegularExpressions.Match matchパラメタ) { string strパラメタ名 = matchパラメタ.Groups["PARAM"].Value; System.Data.DataRow[] dr変換パラメタ = ds変換パラメタ.Tables["パラメタ"].Select("パラメタ名 = '" + strパラメタ名 + "'"); string strパラメタ値 = ""; if (dr変換パラメタ.Length > 0) { strパラメタ値 = dr変換パラメタ[0]["パラメタ値"].ToString(); } return strパラメタ値; }
まずはプレースホルダ名ですが、パターン文字列でPARAM
と名付けたグループをプレースホルダ名として取得しています。ds変換パラメタ
はDataSet
です。パラメタ
という名前のDataTable
を含み、その中にはパラメタ名
、パラメタ値
の2つのDataColumn
があります。パラメタ名
がプレースホルダ名と一致するレコードを探し、パラメタ値
で置き換えます。DataTable
のSelect()
メソッドはSQLのwhere
句にあたる文字列を引数に渡すと、条件に当てはまるレコードをDataRow
の配列で返します。ここでは1行以上見つかったら最初に見つかったDataRow
のパラメタ値
を使用し、見つからなかった場合はEmpty
で置き換えることにしています。ds変換パラメタ
は前述した変換パラメタXMLから生成します。
<?xml version="1.0" encoding="shift_jis" ?>
<変換パラメタ>
<パラメタ>
<パラメタ名>辞令番号</パラメタ名>
<パラメタ値>000001</パラメタ値>
</パラメタ>
<パラメタ>
<パラメタ名>施行年月日</パラメタ名>
<パラメタ値>2005年10月1日</パラメタ値>
</パラメタ>
</変換パラメタ>
これをDataSet
のReadXml()
メソッドで読み込みます。すると、DataSet
内には次のようなDataTable
が生成されます。
パラメタ名 | パラメタ値 |
辞令番号 | 000001 |
施行年月日 | 2005年10月1日 |
これで、文字列置換プログラムの全容がつかめたかと思います。プレースホルダの検索に正規表現を使い、プレースホルダ名に対応するパラメタの取得にはDataSet
の機能を利用しています。
参考までに、オプションには以下のものが指定できます。
- /入力ファイル=入力ファイル名
- /変換パラメタ=変換パラメタファイル名
- [/出力ファイル=出力ファイル名]
- [/log=ログファイル名]
入力ファイル名と変換パラメタファイル名の指定は必須です。出力ファイル名が省略された場合は入力ファイル名に「_conv_」と日付を付加して出力します。
バッチ実行プログラム
このプログラムは変換パラメタXMLごとにプログラムを順次実行します。実行前に変換パラメタXMLを取得するパスやTeXテンプレートとTeXモジュールの存在チェックを行います。また、本プログラムは中間ファイル(.tex、.dvi)を出力するのでその出力先のパスもチェックします。全てのパス情報は「System.ini」ファイルに記述します(オンネット標準)。
[格納フォルダ] LogPath=Log\ TemplatePath=Template\ XmlPath=XML\ DocPath=Doc\ TeXPath=C:\usr\local\bin\ TeXWorkPath=C:\usr\local\work\
中間ファイルの出力先フォルダ(「TeXWorkPath」)はTeX関連のプログラムが正しく認識できる場所を指定します。2バイト文字やスペースが含まれると正常動作は期待できません。
本プログラムは
- 文字列変換プログラム
- pLaTex
- dvipdfmx
の順にプログラムを実行します。各プログラム実行時には、以前の処理による中間ファイルが残っていれば削除し、プログラム実行後に中間ファイルが出力されているかどうかを確認しています。中間ファイルが出力されていることを確認してから次のプログラムの実行に移っています。
各プログラムに対してはそれぞれ引数を組み立てています。ソースコードは非常にシンプルですが、あえていくつかポイントを挙げると、まずTeX関連のプログラムを実行する時はパスを「\」区切りから「/」区切りに直していることです。これは「System.ini」に「/」で表記すればすむ話ですが、うっかり「\」で設定してエラーになると原因が分かりづらくなるところですので、プログラム内で置換を行っています。
次に、TeXファイルの出力が成功した時点でその内容をログファイルに書き出している事です。本プログラムは途中でエラーが起きた場合には次ファイルの処理に移るようになっていますが、例えばTeXファイルが正しく出力できた後にエラーが起きたとすると、次の変換パラメタXMLの処理の際にそのTeXファイルは削除されてしまいます。そのような場合でもログファイルにはTeXの内容がテキストとして保存してあるので、エラーの原因を特定したり、またそのまま切り取って新しいテキストファイルに貼り付ければそのまま利用できるかもしれません。最終的に出力されたPDFファイルはドキュメント出力先フォルダ(「DocPath」)に移動しています。
利用方法
実際には、変換パラメタXMLは外部プログラムから出力することになります。出力書類の分だけ変換パラメタXMLを出力し、バッチ実行プログラムを起動/終了後には変換パラメタXMLを削除するか、別のフォルダに移動します(そのままにしておくと次回バッチ実行プログラムを起動したときに同じ書類が出力されてしまいます)。
ファイルの書き出しにはDataSet
のWriteXml()
メソッドを利用すると便利です。また、パラメタ値はTeXの文法に沿ったものにする必要があります。改行などがそのまま出力に反映されないなど不便な面もありますが、文字位置を微調整したりグラフィックスを埋め込んだりできます。
最後に
PDFのバッチ出力といいながらメインは文字列変換ということで、PDFのバイナリを出力するようなイメージを持った方はがっかりされたかもしれません。逆に、PDF出力というとサードパーティー製のモジュールなどを利用しなければならないというイメージを持っている方には、本稿の方法に関心を持っていただけたのではないでしょうか。
定型書類の出力で求められるのは文字や罫線がレイアウトされた外枠の定位置に文字、画像を埋め込むことぐらいです。これはWordやExcelでも可能ですが、テキストファイルであれば扱いが非常に簡単です。最終出力がPDFなのでAdobe Readerがあれば印刷できます。今後、帳票出力などの方法における選択肢の1つとして参考にしていただければ幸いです。
なお実は、サンプルのテンプレートは、TeXの文法を全く知らないまま調べながら書いたのでおかしな部分があるかもしれませんが、出力されるPDFファイルに問題がなかったのであえて修正していません。ご了解ください。