紙に出力する機能はあまり必要とされていないのか、ASTERIA WARPのコンポーネント群には印刷するコンポーネントはありません。そこで、コンポーネントの作成方法の紹介しながらPDFを印刷するコンポーネントを作ってみます。
コンポーネントでPDFを処理するためにはJavaで使用できるPDF用ライブラリーが必要になります。そこで今回は、ApacheのプロジェクトにあるPDFBoxというライブラリーを使って作ります。
コンポーネントを作るといっても、どのようにすればコンポーネントを作ることができるのでしょうか?
実は、ASTERIA WARPには「フローサービス開発キット(SDK)」がありコンポーネントの開発についてのドキュメントが公開されています。
https://asteria.jp/documentation/warp/4.8.1/flow/sdk/index.html
このドキュメントを読むと、コンポーネントに対する理解が深まりますので、是非読んでみて下さい。
それではさっそくコンポーネントを作っていきます。
必要なものリスト
Java:JDKはコンポーネント開発に必須です。
Ant:コンポーネントをコンパイルするときに使います。
ライブラリ:PDFBoxと必要なライブラリ群を以下からダウンロードします。
https://pdfbox.apache.org/index.html
今回は、pdfbox-1.8.6.jarとfontbox-1.8.6.jarをダウンロードしました。
まず、ASTERIA WARPとフローデザイナーを起動します。
フローデザイナーでJavaInterpreterコンポーネントを配置して、以下のようなフローを作成します。
今回作成するコンポーネントは出力ストリームがありませんので、JavaInterpreterコンポーネントの「入力をそのまま出力」プロパティで「はい」を選択します。
コンポーネントのアイコンが変わりました。
次に作成するコンポーネントに必要なプロパティを、JavaInterpreterコンポーネントの「パラメーター」プロパティへ追加します。
4つのパラメーターを追加しました。それぞれ以下のように使う予定です。
PrinterName プリンターを検索するときの名前を指定します。
MediaSize プリンターを検索するときの用紙サイズを指定します。
Copies 印刷するときの部数を指定します。
PDFFile 印刷するPDFファイルのフルパスを指定します。
次にJavaInterpreterコンポーネントをダブルクリックして「ソースコード」プロパティを編集します。
今回これ以降の処理はimport文も多くなりますので、IDEの助けを借りたいと思います。いったんコンポーネントのソースを出力します。
JavaInterpreterコンポーネントを右クリックして「カスタムコンポーネントの作成」を選択します。
すると、以下のようなコンポーネントウィザードが表示されますので、作成したいコンポーネントの情報を入力、選択していきます。
何を指定してもよいのですが、クラス名は○○Component、パッケージ名はcom.会社名.機能名とすると分かりやすいと思います。
今回は、入力ストリームを使用せずコンポーネント単体で動作させますので、「入力ストリームをそのまま出力する」にチェックを入れます。
作成するコンポーネントのプロパティを設定します。
すでにJavaInterpreterコンポーネントに追加した4つのパラメーターがプロパティとして設定されていますので、表示名、ツールチップなどを入力します。
PrinterName プリンターを探すときの名前(一部分でも可)ですので「必須」を「true」にします。
MediaSize 「型」を「choice」に変更して、A4かB5を選択するようにデフォルト値を改行区切りで指定します。「必須」を「true」に、「マッピング」を「false」にします。
Copies 「必須」を「true」にします。
PDFFile 「必須」を「true」にします。
「次へ」をクリックします。
jarファイル名は「pdfprint.jar」とします。ASTERIA WARP標準で提供されているコンポーネントのjarファイル名はすべて「fcXXXX.jar」のように「fc」で始まる名前になっていますので、それ以外のファイル名にしてください。
「build.xml」を作成するにチェックを入れて「完了」ボタンをクリックすると、ソースファイルなどコンポーネントを作成するために必要なファイル一式が作成されます。とっても簡単です。
では、ウィザードの最初で指定したソースファイルの保存フォルダーc:\blog\PDFPrintフォルダーを見てみます。
build.xmlができています。
すぐAntを実行したくなりますが、その前に2点、準備が必要です。
- コンパイル時に「[ASTERIA WARPインストールフォルダー]/common/endosed」の中のライブラリが必要になりますので、build.xmlを修正します。
まず、buildxmlの一番上の方にwarp.libという名前で、ASTERIA WARPインストールフォルダー/common/endosedを定義しておきます。
コンパイルの部分に、上で追加したwarp.libと直下のlibフォルダーの中のjarを全て読むようにしておきます。また、直下のlibフォルダー、つまり、c:\blog\PDFPrint\libフォルダーはここで作成しておきます。
これでbuild.xmlの修正は完了です。 - 作成されているコンポーネント用のソースファイル
「com.infoteria.blog.component.PDFPrintComponent.java」のdoExecute関数へreutrn null;を追加してください。
準備が完了したらantを実行します。
とりあえず、何もしないコンポーネントが作成できました
次にPDFを印刷する部分のコードを追加します。
私はコードの修正をeclipse上で行いましたが、お好きなエディターを使って下さい。
PDFBox でPDFを印刷するには以下のようにします。
ファイルを読んで、
PDDocument doc = PDDocument.load(f);
プリンタ情報をジョブに渡して、
PrinterJob printerJob = PrinterJob.getPrinterJob();
printerJob.setPrintService(service);
ダイアログを出さないでサイレント印刷
doc.silentPrint(printerJob);
それではソースを編集します。
ごにょごにょごにょ・・・
以下の感じになりました。赤字の部分が編集、追加したソースです。importは必要な分追加してください。
package com.infoteria.blog.component;
import java.awt.print.PageFormat;
import java.awt.print.Paper;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.io.File;
import java.io.IOException;
import java.util.List;
import javax.print.PrintService;
import javax.print.PrintServiceLookup;
import javax.print.attribute.HashPrintRequestAttributeSet;
import javax.print.attribute.PrintRequestAttributeSet;
import javax.print.attribute.standard.MediaSizeName;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageable;
import com.infoteria.asteria.flowengine2.execute.ExecuteContext;
import com.infoteria.asteria.flowengine2.flow.InputConnector;
import com.infoteria.asteria.flowengine2.flow.OutputConnector;
import com.infoteria.asteria.flowlibrary2.FlowException;
import com.infoteria.asteria.flowlibrary2.component.ComponentException;
import com.infoteria.asteria.flowlibrary2.component.ComponentExceptionByMessageCode;
import com.infoteria.asteria.flowlibrary2.component.SimpleComponent;
import com.infoteria.asteria.flowlibrary2.property.IntegerProperty;
import com.infoteria.asteria.flowlibrary2.property.Property;
import com.infoteria.asteria.flowlibrary2.property.StringProperty;
import com.infoteria.asteria.flowlibrary2.property.ValueProperty;
import com.infoteria.asteria.flowlibrary2.stream.StreamDataObject;
import com.infoteria.asteria.flowlibrary2.stream.StreamException;
import com.infoteria.asteria.flowlibrary2.stream.StreamType;
import com.infoteria.asteria.value.Value;
public class PDFPrintComponent extends SimpleComponent
{
public static final String COMPONENT_NAME = "PDFPrint";
public String getComponentName() { return COMPONENT_NAME;}
private StringProperty _propPrinterName = new StringProperty("PrinterName", true, true);
private StringProperty _propMediaSize = new StringProperty("MediaSize", true, true);
private IntegerProperty _propCopies = new IntegerProperty("Copies", true, true);
private StringProperty _propPDFFile = new StringProperty("PDFFile", true, true);
private static final String FILE_NOT_EXIST = "1";
private static final String PRINTER_NOT_EXIST = "2";
private static final String PRINT_FAILED = "3";
public PDFPrintComponent()
{
int ia = StreamType.ALL;
int oa = StreamType.ALL;
getInputConnector().setAcceptType(ia);
getInputConnector().setAcceptLinkCount(1);
getOutputConnector().setAcceptType(oa);
registProperty(_propPrinterName);
registProperty(_propMediaSize);
registProperty(_propCopies);
registProperty(_propPDFFile);
}
public boolean execute(ExecuteContext context) throws FlowException {
Object result = doExecute(context, this);
passStream();
return true;
}
private Object doExecute(ExecuteContext context, PDFPrintComponent component) throws FlowException {
try {
Value pdfFile = component.getParameter("PDFFile");
Value printerName = component.getParameter("PrinterName");
Value mediaSize = component.getParameter("MediaSize");
Value copies = component.getParameter("Copies");
File targetPDF = new File(pdfFile.strValue());
if (!targetPDF.exists()) {
throw new ComponentExceptionByMessageCode(this, FILE_NOT_EXIST, targetPDF.getAbsolutePath());
}
PrintService printService = createPrintService(printerName.strValue(), mediaSize.strValue());
if (printService == null) {
throw new ComponentExceptionByMessageCode(this, PRINTER_NOT_EXIST, printerName.strValue());
}
try {
print(targetPDF, printService, copies.intValue());
} catch (IOException | PrinterException e) {
throw new ComponentExceptionByMessageCode(this, PRINT_FAILED, e.getMessage());
}
} catch (Exception e) {
throw new ComponentException(e);
}
return null;
}
// Utility Method
public Value getParameter(String name)
{
Property prop = getProperty(name);
if (prop instanceof ValueProperty)
return ((ValueProperty)prop).getValue();
else
return null;
}
public InputConnector getInputConnector()
{
return super.getInputConnector();
}
public OutputConnector getOutputConnector()
{
return super.getOutputConnector();
}
public void setOutputStream(StreamDataObject stream)
throws StreamException
{
super.setOutputStream(stream);
}
public void setOutputStream(StreamDataObject stream, boolean isSetProperties)
throws StreamException
{
super.setOutputStream(stream, isSetProperties);
}
private PrintService createPrintService(String printerName, String media)
{
//サイズで検索する
PrintRequestAttributeSet aset = createAttributeSet(media);
//プリントサービス検索
PrintService[] services = PrintServiceLookup.lookupPrintServices(null, aset);
if (services != null && services.length > 0) {
if (printerName == null || printerName.isEmpty()) {
return services[0];
}
//名前で検索
for (PrintService service : services) {
if (service.getName().contains(printerName)) {
return service;
}
}
}
return null;
}
private PrintRequestAttributeSet createAttributeSet(String media)
{
PrintRequestAttributeSet attrSet = new HashPrintRequestAttributeSet();
if (media.equals("B5")) {
attrSet.add(MediaSizeName.ISO_B5);
} else {
attrSet.add(MediaSizeName.ISO_A4);
}
return attrSet;
}
private void print(File f, PrintService service, int copy)
throws IOException, PrinterException
{
PDDocument doc = PDDocument.load(f);
try {
//ジョブ取得
PrinterJob printerJob = PrinterJob.getPrinterJob();
//プリントサービス設定
printerJob.setPrintService(service);
//コピー枚数設定
printerJob.setCopies(copy);
//印刷
doc.silentPrint(printerJob);
} finally {
doc.close();
}
}
}
コードの最初の方でエラーメッセージ用にFILE_NOT_EXIST、PRINTER_NOT_EXIST、PRINT_FAILEDを追加しましたので、XSCファイルにも対応するメッセージを追加します。
en、ja、zh_CNの3言語分ありますが、全て日本語でも問題ありません。3つのMessageエレメントを追加します。
今回は、pdfbox-1.8.6.jarを追加します。
antを実行すれば、distフォルダーにpdfprint.jarが作成されます。
次の手順で、作成したコンポーネントをASTERIA WARPサーバーとフローデザイナーへ追加します。
- ASTERIA WARPを止めます。
- [ASTERIA WARPインストールフォルダー]/flow/lib/flowlibへpdfprint.jarをコピーします。
[ASTERIA WARPインストールフォルダー]/flow/lib/userlibへダウンロードしたpdfbox-1.8.6.jarとfontbox-1.8.6.jarをコピーします。 - ASTERIA WARPを起動します。
- フローデザイナーを起動し、起動したASTERIA WARPへ接続します。
- ツールメニューから「コンポーネント/マッパー関数の取得」を選択します。
- サーバー上のJarファイル一覧を一番下までスクロールして「pdfprint.jar」を選択して、ダウンロードボタンをクリックします。
- フローデザイナーを再起動すると、「ツール」へPDF印刷コンポーネントが追加されます。
フローを作成して、コンポーネントの動作を確認してみます。
- 次のようなフローを作成します。
- PDF印刷1コンポーネントのプロパティを編集します。
「プリンター名」プロパティには「Canon」と入力しました。
私の使っているプリンターで「Canon」が名前に入っているものは1つしかありませんので、プリンター名の一部である「Canon」だけを指定しても大丈夫です。
「用紙」プロパティはデフォルトのままのA4を指定します。あくまでプリンターを検索するときに使用するものです。
「部数」プロパティはデフォルトのままの1を指定します。
「PDFファイル」プロパティへは印刷するPDFファイルのフルパスを指定しますので、今回はTempフォルダー下のTest.pdfを指定しました。 - フローを実行します。
Test.pdfが1部、無事プリントされました。
今回は、ApacheのPDFBoxというライブラリを使用して、PDFを印刷するコンポーネントを作ってみましたが、とても簡単にコンポーネントが作成できました。是非みなさんも独自のコンポーネントを作成してフローサービスの機能を拡張してみて下さい。