4D-jp 4D Japan Technical Support Team

プリンタードライバーとの相性

2023-02-03

一般的に言われるところのアプリケーションのプリンターとの相性が良い/悪いは、実はプログラミングで決まります。

4Dで作成したアプリケーションに限らず、アプリケーションとプリンタードライバーとの相性が問題視されることは良くあります。 Knowledge Baseで「印刷」や「printer」というようなキーワードで検索すると、多くの記事を見つけることができます。 こうした多くの記事が書かれている背景には、プリンタードライバーの存在があります。

4Dに限らずアプリケーションが直接プリンターをコントロールしているわけではなく、実際に印刷を行うのはプリンタードライバーなので、プリンタードライバーと上手にお付き合いすることが肝心です。

プリンタードライバーは、アプリケーションのメモリー内にロードされて実行されます。 そのため、プリンタードライバーが問題を起こすと、その影響はアプリケーション全体の問題に発展し、時としてアプリケーションが異常終了することもあります。 異常終了すると、例えプリンタードライバーに問題があるとしても、アプリケーション側の問題として捉えられてしまいますし、データファイルに異常をもたらすこともあるので避けたいところです。

ところで、他のアプリケーションで問題なく印刷できるのに、4Dが印刷時に異常終了するような環境では、プリンタードライバーではなく4Dが疑われてしまいます。 では印刷で問題のないアプリケーションと、問題を起こすアプリケーションの違いは何でしょうか。

相性が悪い原因その1

アプリケーションがプリンターに印刷するとき、スプールと呼ばれる一種のキューとして印刷データはまとめられ、そのキューを順番に処理して印刷物が生成されます。 スプールが利用されることで、キューさえ用意すれば印刷物の生成を待つこと無くアプリケーションは印刷の動作から開放されるので、アプリケーションは印刷しながら他の処理をこなうことができます。 ですが、アプリケーション側の印刷作業は、実のところキューを作るところで終了している言えます。

キューは実際にはスプールファイルと呼ばれるファイルとして生成され、処理されます。 スプールファイルはバイナリーのデータであることが多く、またその書式は公開されているものもありますが、プリンターメーカー独自の書式で非公開のものもあります。 スプールファイルを作成するのは、プリンタードライバーの役目なので、書式や存在そのものを意識せずとも、印刷するプログラムを書くことができます。

4Dのプログラムに目を向けると、スプールを全く意識しなくとも、たった1行のプログラムで印刷することが可能なので、ここまで説明したことを考える必要なく印刷物を手に入れることができます。 例えば、次のようなコードで複数のレコードに関しての印刷を行えます。

PRINT SELECTION([SmpleTable])

この1行が実行されるだけで、スプールファイルが用意されて印刷されます。 確かに印刷されるので間違いではありませんが、プリンタードライバー側からすると乱暴な印刷要求かもしれません。

プリンタードライバーとしては、名前のついたプリントスプールに印刷要求をまとめて受け取りたいところです。 ですが、先程の1行では名前を指定するということはしていません。 ここに相性の問題が生まれます。

プリンタードライバーに対して、丁寧な頼み方をすることが、相性の問題を避ける第一歩になります。 そのためには、先程のコードに手を入れる必要があります。

SET PRINT OPTION(Spooler document name option; "Spoole name")  //スプール名に"Spoole name"を指定
OPEN PRINTING JOB  //ここから1つのスプールになる
PRINT SELECTION([SmpleTable])  //印刷
CLOSE PRINTING JOB  //スプールの終了

これで、プリンタードライバーが望むスタイルで印刷データをドライバーに渡すことができます。 スプール名は重複しても問題ないはずですし、日本語でも問題ないはずです。 ですが、”はずです”と微妙な表現をしたのは、スプール名を扱うドライバー側で問題があるかもしれないのです。 日本語ができないドライバーがあるかもしれないので、英数字のスプール名にするのが相性を高める秘訣のひとつだと言えそうです。

相性が悪い原因その2

印刷するためには、印刷するデータに添えて、用紙設定や時にはトレーの指定などをしなければなりません。 ここにも相性の問題が潜んでいます。

用紙設定などの印刷時に係る情報は、構造体にまとめられています。 構造体は大まかに2つの部分から成り立っています。

  1. OSで定義された構造
  2. ドライバー独自のバイナリーデータ

OSで定義された部分には用紙設定などが含まれ、ドライバー独自のバイナリーデータ部分にはプリンター特有の例えばトレーの情報などが収められています。 OSで定義された部分は、4DのGET PRINT OPTIONコマンドで設定内容を得たり、SET PRINT OPTIONコマンドで設定することができます。 また、SET PRINT PREVIEWのような特定のプロパティを扱うコマンドもあります。 一方、ドライバー独自のバイナリーデータ部分の内容を得る、または設定する方法はありません。

しかしこうした構造体をひとまとめにして、GUIで表示したり設定するコマンドはあります。 また、メモリ上に展開されている構造体を変数に取り込むコマンドはあります。

  • PRINT SETTINGS
  • Print settings to BLOB
  • BLOB to print settings

こうしたコマンドを使うと、プリンタードライバーと仲の良いプログラムを作成することができます。

また、PAGE SETUPコマンド(_o_PAGE SETUP)も構造体をまとめてフォームデータとして扱かう点では、上記の3つのコマンドと同じ仲間と言えます。

ですが、4Dの長い歴史の中で利用されてきたPAGE SETUPは、設定をするためにはデザイナーモード、つまりプログラムデザイナーがプログラムを作成するときにしか構造体を設定することができません。 つまり、構造体はプログラムを作成した環境に依存した内容になるので、開発環境と運用環境が同じであるときにのみ利用できる特殊なものと言えます。

PAGE SETUPは、数十年前に4Dが誕生したとき、開発しながら運用する形態が求められていたような時代の遺産的なコマンドだと言っても過言ではないでしょう。

さて、ここからが本題です。

もし、PAGE SETUPを利用してプログラムしているようなとき、何が起きるでしょうか。

まったく問題なく利用できるかもしれませんし、ときどき異常終了するかもしれません。 なぜそのようになるかは、これまでの説明で理解できるかと思いますが、構造体のドライバー独自のバイナリーデータに係る問題になります。

ドライバー独自のバイナリーデータの部分はアプリケーションから見てブラックボックスです。 ドライバーだけが、このブラックボックスを扱うことができます。 ですが、同じメーカーのドライバーでもプリンターの型番が違う、あるいはプリンタードライバーのバージョンが違うと、ブラックボックスの内部書式が合わなくなり取り扱えないかもしれません。 また、PCのチップセットが異なると、ブラックボックスに違いがあるかもしれません。 ドライバーがブラックボックスの一部のバイナリーが取り扱えないときの振る舞いを予測することは、ドライバーの作成者以外予測できません。

また、運用環境でOSをアップデートしただけで、ドライバーがブラックボックスを正しく扱えなくなるかもしれません。

このように構造体を利用すると、運用環境に変化があったような時、構造体を作り直すことが必要になるかもしれません。 しかし、プリンター特有のトレーを切り替えて印刷するなどの利便性を考えると、構造体を利用するしかありません。 ですが、PAGE SETUPを利用してプログラムしてしまうと、構造体を入れ替える必要があるとき、プログラムを扱える者が印刷設定をフォームに保存し直す必要があります。 現実的な運用方法では無いと言えます。

そこで先程紹介した、3つのコマンドを利用して、印刷設定をほどこした構造体をファイルとして保存、さらにそのファイルから構造体をメモリーにロードして利用することが求められます。

印刷前に必要な印刷設定があればロードして利用、なければ印刷設定をして保存するコードを実験的に作成してみました。

//印刷設定用保管ファイル
$file:=File(Folder(fk user preferences folder).path+"myApplication/printset.json")

If ($file.exists=False)
	
	CONFIRM("プリンター設定がされていません。プリンター設定を行いますか?"; "設定する"; "今はしない")
	If (OK=1)
		
		//印刷設定をする
		PRINT SETTINGS
		If (OK=1)
			//印刷設定を変数に取り出す
			$result:=Print settings to BLOB($settingsBlob)
			If ($result=1)
				//バイナリーの印刷設定をテキスト化する
				var $settingsText : Text
				BASE64 ENCODE($settingsBlob; $settingsText)
				//設定を保存用に組み立てる
				$settings:=New object
				$settings.printer:=Get current printer
				$settings.settings:=$settingsText
				//ファイルに保存
				$file.setText(JSON Stringify($settings; *))
			End if 
		End if 
		
	End if 
	
Else 
	
	//印刷設定を取り出す
	$settings:=JSON Parse($file.getText())
	
	If (Get current printer=$settings.printer)
		
		//印刷設定をメモリにロードする
		var $settingsBlob : Blob
		BASE64 DECODE($settings.settings; $settingsBlob)
		$result:=BLOB to print settings($settingsBlob)
		
	Else 
		
		//利用できないとき
		ALERT("保存されている印刷設定と現在のプリンターが異なるため、印刷設定を利用できません\r\r現在プリンター:Get current printer"+$settings.printer+"\r保存プリンター:"+$settings.printer)
		
	End if 
	
End if 

このコードをメソッドとして作成してPRINT SETTINGSの代わりに利用すると、初めて印刷するときだけ印刷設定のダイアログが表示され、次回からは印刷設定ダイアログは表示されずに印刷されるようになります。

このコードは、printset.jsonというファイルをPCのログインユーザーのプレファレンスとして保存して活用しています。 保存した構造体が利用可能なものかを判定するのに、プリンターの名称を利用していますが、他にも細かい情報を考慮した方が良いかもしれません。 printset.jsonのフォーマットは、JSONなので、プリンター名称以外の情報を構造的に保存することも可能ですから、他に考慮するべき情報を付加して埋め込むことも可能です。

このコードは、そのまま利用可能ですが、色々と応用できると思います。

例えば、帳票毎に異なる印刷設定用のファイルを用意するなどして、印刷物の目的に合わせて用紙設定を組み込むようなことも可能でしょう。 また、印刷設定用ファイルに収められたプリンター名で示されたプリンターに切り替えるようにプログラムを変更すれば、複数のプリンターを切り替えて運用することも可能でしょう。

その他の原因

Windows 64ビット版の4Dは,画面と印刷の両方にDirectXを使用しています。しかし一貫した表示と印刷の仕組みが用意されているMacとは異なり、Windowsでは表示と印刷で別々のメカニズムが用いられています。そのためWindowsでは、表示で問題なくとも印刷で問題が発生することは十分に考えられます。また、32ビット版では問題がなく64ビット版で問題が起きるのであれば、DirectXではなく32ビット版のようにGDIでの印刷を試す価値があるかもしれません。

DirectXでの印刷に問題があると疑われるようなときには、SET PRINT OPTIONLegacy printing layer optionセレクタを用いてGDIエンジンでの印刷を試す価値があるかもしれません。

まとめ

プリンタードライバーとの相性の問題を避けるためには、プリンタードライバーの気持ちになって、情報を送り出すことに務めることが重要です。

上手に、プリンタードライバーをエスコートできるプログラミングを心がけることで、プリンターの相性問題は解決できるはずです。