人気ブログランキング | 話題のタグを見る

PDFの連続印刷

 …というお題でぐぐっても結構な数がヒットするし、解決方法はいろいろあるようだ。フリーウェアでよさげなものもある。

 車輪の再発明というのは基本的に行わないのだが、金の絡んだ頼まれ仕事だと、「フリーウェア使って!」つーわけにもいかなかったりするし、事前・事後処理が必要だったりもするので、仕方が無いので自前でナントカすることにした。

 のだが。
 Webを漁ってみると、どうも思ったとおりにいかないという記録も多い。
 結局、引っかかるところはみな同じなわけである。

 対象ファイルがひとつふたつならいいのだが、数千ファイル想定の連続印刷の場合、前の印刷が完了してから、次の印刷を開始しないとキューが溜まり過ぎて破綻する恐れがある。
 特に俺の場合、後流でキューファイルを操作する必要があり、できれば「溜めたく」ない。

 またできるだけ安価にやりたいので、Acrobat Readerを使って…というセンが真っ先に思いつくが、コイツがなかなか難物である。
 Adobeのドキュメントを読むと、AcroRd32.exeにはコマンドラインオプションがあるので一瞬バッチファイルでずらずら並べりゃいいやシメシメと思うが、これが書いてある通りに動かない(/tオプションとか。バージョンにもよるのかもしれないが、Acrobat7でなきゃいけないという条件なので)。
 こちらの期待通りに動いてくれないので、印刷が終わったという情報が上手く得られず、印刷終了を待って次の印刷つーことが難しい。

 結局のところ、
  1. 印刷という挙動を監視して、前の印刷が終わったことを確認する
  2. Acrobat Readerで印刷開始。印刷終了を待つ。
  3. すべての処理が終わったら、AcrobatReaderのプロセスをきちんと始末する。
つーことが必要だ、たぶん。と結論した。

 Acrobat Reader自体はActiveXなのでCOMインタフェイスを持っているから、殆どの言語から生成してメソッドを呼べる。ところが印刷関係のメソッドは印刷終了の戻り値もなきゃコールバックも出来ないらしく、「印刷終了」の感知が難しいのである。

 うううううううむ。と10分ばかり考え込んでしまったが、仕方が無いのでこうすることにした。
 発想を変えて、Acrobat自体の終了報告には頼らずに、出した結果の方を監視することにしたのである。
  1. プリントキュー側を監視して、投入したジョブが消えたら終了したと<見なす>。共有プリンタではないので他からのアクセスについては考えない(爆)
  2. 基本的にはこれでAcrobatのプロセスがゾンビることはないと思うが、ダメ押しで、アプリ終了時にAcroRD32を強制的にKill(爆笑)
この線で手元にあるDelphi6で書いてみた。キモの部分はこんな感じ。

 まずは、出力しているプリンタの状態を見て登録されているジョブ数を確認する関数を用意する。Win API の GetPrinter を使うが、ここでは出し先がネットワークプリンタなので、PrinterInfo2構造体へ突っ込む。

//=============================================================================
// プリンタのステータスとジョブ数返す。
// PrinterName : プリンタ名
// JobCount(var): 登録されているジョブ数
// 戻り:ステータス
//=============================================================================
function PrinterStatus(const PrinterName: String; var JobCount: Cardinal): LongWord;
var
Count: LongWord;
hPrinter: THandle;
JobInfoCount: LongWord;
JobInfo2: PJobInfo2;
PrinterInfo2: PPrinterInfo2;
begin
Result := 0;

if OpenPrinter(PChar(PrinterName),hPrinter,nil) then begin
Count := 0;
GetPrinter(hPrinter,2,nil,0,@Count);
GetMem(PrinterInfo2,Count);
GetPrinter(hPrinter,2,PrinterInfo2,Count,@Count);
FreeMem(PrinterInfo2);
Count := 0;
EnumJobs(hPrinter,0,1,2,nil,0,Count,JobInfoCount);
GetMem(JobInfo2,Count);
EnumJobs(hPrinter,0,1,2,JobInfo2,Count,Count,JobInfoCount);
JobCount := JobInfoCount;
if (JobInfoCount > 0) and (JobInfo2.Status <> 0) then begin
if (JobInfo2.Status and JOB_STATUS_BLOCKED_DEVQ) <> 0 then
Result := Result or PRINTER_STATUS_ERROR;
if (JobInfo2.Status and JOB_STATUS_DELETING) <> 0 then
Result := Result or PRINTER_STATUS_PENDING_DELETION;
if (JobInfo2.Status and JOB_STATUS_ERROR) <> 0 then
Result := Result or PRINTER_STATUS_ERROR;
if (JobInfo2.Status and JOB_STATUS_OFFLINE) <> 0 then
Result := Result or PRINTER_STATUS_OFFLINE;
if (JobInfo2.Status and JOB_STATUS_PAPEROUT) <> 0 then
Result := Result or PRINTER_STATUS_PAPER_OUT;
if (JobInfo2.Status and JOB_STATUS_PAUSED) <> 0 then
Result := Result or PRINTER_STATUS_PAUSED;
if (JobInfo2.Status and (JOB_STATUS_PRINTING or JOB_STATUS_RESTART)) <> 0 then
Result := Result or PRINTER_STATUS_PRINTING;
if (JobInfo2.Status and JOB_STATUS_USER_INTERVENTION) <> 0 then
Result := Result or PRINTER_STATUS_USER_INTERVENTION;
end;
FreeMem(JobInfo2);
ClosePrinter(hPrinter);
end
end;

//=============================================================================
// PrinterNameのプリンタの印刷ジョブ数を返す関数
//=============================================================================
function TForm1.GetPrintJobCount(PrinterName: String): Cardinal;
var
aJobCount: Cardinal;
PrnStat: LongWord;
begin
aJobCount := 0;
PrnStat := PrinterStatus(PrinterName, aJobCount );
// ホントはステータス(PrnStat)をみるべきだが、めんどいので略
Result := aJobCount;
end;

こういう関数を用意し、大筋こんな感じでループ内で監視することにした。

PrintCount := GetPrintJobCount(Printername); // 印刷前の取得数を印字待ちなしと看做す
AcroPDF1.LoadFile(WideString(FileName)); // ファイルをロードして
AcroPDF1.printAllFit(True); // ダイアログなし印刷で

// データのポスト待ち
if GetPrintJobCount(Printername)=PrintCount then begin
while True do begin
Application.ProcessMessages;
if GetPrintJobCount(Printername)>PrintCount then break;
Sleep(1);
end;
end;

// 終了待ち
while True do begin
Application.ProcessMessages;
if GetPrintJobCount(Printername)<=PrintCount then break;
Sleep(1);
end;

どうもなんともカッコ悪いうえに、動作環境の仮定が多く、破綻するケースが容易に想像できる方法であるが(核爆)、しょせん一発モノなんで(笑)

いちおう、1万ファイル程度処理してみたが上手くいったのでよかったよかった。
by SIGNAL-9 | 2008-03-27 15:52 | TIPSとかKludgeとか
<< 漫画家・西原理恵子 家族が“ア... 花粉症…今年はラクだ。もしかし... >>