PDFを印刷するコンポーネントをつくってみる

紙に出力する機能はあまり必要とされていないのか、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コンポーネントを配置して、以下のようなフローを作成します。

flow.png

今回作成するコンポーネントは出力ストリームがありませんので、JavaInterpreterコンポーネントの「入力をそのまま出力」プロパティで「はい」を選択します。

through.png
コンポーネントのアイコンが変わりました。

次に作成するコンポーネントに必要なプロパティを、JavaInterpreterコンポーネントの「パラメーター」プロパティへ追加します。

parameter.png
4つのパラメーターを追加しました。それぞれ以下のように使う予定です。
PrinterName プリンターを検索するときの名前を指定します。
MediaSize プリンターを検索するときの用紙サイズを指定します。
Copies 印刷するときの部数を指定します。
PDFFile 印刷するPDFファイルのフルパスを指定します。


次にJavaInterpreterコンポーネントをダブルクリックして「ソースコード」プロパティを編集します。

source.png

追加した4つのパラメーターからValueオブジェクトを取得するところまで追加しました。

今回これ以降の処理はimport文も多くなりますので、IDEの助けを借りたいと思います。いったんコンポーネントのソースを出力します。
JavaInterpreterコンポーネントを右クリックして「カスタムコンポーネントの作成」を選択します。
すると、以下のようなコンポーネントウィザードが表示されますので、作成したいコンポーネントの情報を入力、選択していきます。

wiz1.png
こ こではコンポーネント名は、PDFPrintとしましたが、他のコンポーネントと名前が被らないようにするために<会社名>.<コン ポーネント名>のような名前を付けるようにしてください。コンポーネントのアイコンも好きなアイコン(32px×32px)を指定できます。

wiz2.png
コンポーネント用のJavaソースファイルのクラス名とパッケージ名を入力し、ソースファイルの保存フォルダーを指定します。
何を指定してもよいのですが、クラス名は○○Component、パッケージ名はcom.会社名.機能名とすると分かりやすいと思います。

wiz3.png
今回は、入力ストリームを使用せずコンポーネント単体で動作させますので、「入力ストリームをそのまま出力する」にチェックを入れます。

wiz4.png
作成するコンポーネントのプロパティを設定します。
すでにJavaInterpreterコンポーネントに追加した4つのパラメーターがプロパティとして設定されていますので、表示名、ツールチップなどを入力します。
PrinterName プリンターを探すときの名前(一部分でも可)ですので「必須」を「true」にします。
MediaSize 「型」を「choice」に変更して、A4かB5を選択するようにデフォルト値を改行区切りで指定します。「必須」を「true」に、「マッピング」を「false」にします。
Copies 「必須」を「true」にします。
PDFFile 「必須」を「true」にします。

wiz5.png
「次へ」をクリックします。

wiz6.png
jarファイル名は「pdfprint.jar」とします。ASTERIA WARP標準で提供されているコンポーネントのjarファイル名はすべて「fcXXXX.jar」のように「fc」で始まる名前になっていますので、それ以外のファイル名にしてください。
「build.xml」を作成するにチェックを入れて「完了」ボタンをクリックすると、ソースファイルなどコンポーネントを作成するために必要なファイル一式が作成されます。とっても簡単です。

では、ウィザードの最初で指定したソースファイルの保存フォルダーc:\blog\PDFPrintフォルダーを見てみます。

build.xmlができています。

すぐAntを実行したくなりますが、その前に2点、準備が必要です。

  1. コンパイル時に「[ASTERIA WARPインストールフォルダー]/common/endosed」の中のライブラリが必要になりますので、build.xmlを修正します。

    まず、buildxmlの一番上の方にwarp.libという名前で、ASTERIA WARPインストールフォルダー/common/endosedを定義しておきます。
    buildxml1.png

    コンパイルの部分に、上で追加したwarp.libと直下のlibフォルダーの中のjarを全て読むようにしておきます。また、直下のlibフォルダー、つまり、c:\blog\PDFPrint\libフォルダーはここで作成しておきます。
    buildxml2.png
    これでbuild.xmlの修正は完了です。

  2. 作成されているコンポーネント用のソースファイル
    「com.infoteria.blog.component.PDFPrintComponent.java」のdoExecute関数へreutrn null;を追加してください。
    doexecute.png


準備が完了したら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エレメントを追加します。

xsc.png

コンパイルに必要なPDFBoxのライブラリを、準備で作成したlibフォルダーにコピーします。
今回は、pdfbox-1.8.6.jarを追加します。

antを実行すれば、distフォルダーにpdfprint.jarが作成されます。


次の手順で、作成したコンポーネントをASTERIA WARPサーバーとフローデザイナーへ追加します。

  1. ASTERIA WARPを止めます。

  2. [ASTERIA WARPインストールフォルダー]/flow/lib/flowlibへpdfprint.jarをコピーします。
    [ASTERIA WARPインストールフォルダー]/flow/lib/userlibへダウンロードしたpdfbox-1.8.6.jarとfontbox-1.8.6.jarをコピーします。

  3. ASTERIA WARPを起動します。

  4. フローデザイナーを起動し、起動したASTERIA WARPへ接続します。

  5. ツールメニューから「コンポーネント/マッパー関数の取得」を選択します。
    designer-add.png

  6. サーバー上のJarファイル一覧を一番下までスクロールして「pdfprint.jar」を選択して、ダウンロードボタンをクリックします。
    designer-add2.png

  7. フローデザイナーを再起動すると、「ツール」へPDF印刷コンポーネントが追加されます。
    designer-add3.png

    designer-add4.png

フローを作成して、コンポーネントの動作を確認してみます。

  1. 次のようなフローを作成します。
    printflow.png

  2. PDF印刷1コンポーネントのプロパティを編集します。
    printflow2.png
    「プリンター名」プロパティには「Canon」と入力しました。
    私の使っているプリンターで「Canon」が名前に入っているものは1つしかありませんので、プリンター名の一部である「Canon」だけを指定しても大丈夫です。
    「用紙」プロパティはデフォルトのままのA4を指定します。あくまでプリンターを検索するときに使用するものです。
    「部数」プロパティはデフォルトのままの1を指定します。
    「PDFファイル」プロパティへは印刷するPDFファイルのフルパスを指定しますので、今回はTempフォルダー下のTest.pdfを指定しました。

  3. フローを実行します。
    flowrun.png

    Test.pdfが1部、無事プリントされました。

今回は、ApacheのPDFBoxというライブラリを使用して、PDFを印刷するコンポーネントを作ってみましたが、とても簡単にコンポーネントが作成できました。是非みなさんも独自のコンポーネントを作成してフローサービスの機能を拡張してみて下さい。

この記事は役に立ちましたか?
0人中0人がこの記事が役に立ったと言っています

他のキーワードで検索する