公開日 2023-06-11 更新日 -
前回、成績処理の印刷出力 その1ではドットマトリクス形式のプリンタで罫線まで印刷するところまでのお話でした。 ここではESC/Pageを使うようになる話からです。
当時学校に入っていたプリンタは NEC PC-PR201V2, EPSON HG-4000 でした。制御コードが異なりますので、出力部分をそれぞれ書いて N88-BASIC の CHAIN MERGE コマンドで切り替えていました。
さて、ページプリンタが導入されます。EPSON LP-8000 です。ESC/Page が本来の制御コード体系ですが、ESC/P モードでも動くということで選びました。ところが ビットイメージを繰り返して書いてくれるコマンド(ESC * 167)が使えなくなっていて、繰り返しの数だけデータを出力しなければならなくなって、印刷が極端に遅くなったという話は前回も書きました。
このときにページプリンタ用の制御コード(ESC/Page)で書き直すという気持ちもあったのですが、ESC/Pageのリファレンスマニュアルが別売りでなかなか入手できず、時間切れで繰り返しで対処しています。
そのプログラムの改変を追いかけたのですが、見つからない。そのまま F-BASIC のプログラムになってしまっています。F-BASIC は Windows の機能を呼び出す形になっているらしく、ESC/Pageを直接には使わないのです。
しかたなく他のプログラムを探しているうちに判明しました。ドットマトリクス形式で罫線を描くようにしたのは学期ごとの成績一覧表と、学年末に一回の出欠統計表だけでした。この後に罫線付きに機能アップしたプログラムが、すべてページプリンタを使用するものでした。先行した2つのプログラムは遅くなったけれども、印刷回数が少ないので改良を後回しにして、他のプログラムを優先したのでした。
ESC/Pageのリファレンスマニュアルがすでに手元にありませんので、残されたプログラムから拾ってみます。
用紙の設定です。罫線の説明としては外れますが、簡単にメモしておきます。@EJLで始まるコマンドはESC/Pageの上位に位置するものらしいです。
25660 *PGINT 'ESPER TYPE 25680 GS$=CHR$(29) ':グラフィック命令はESCの代わりにこれで始まるので変数に入れておきます 25700 GOSUB *WLP:IF I$=EC$ THEN I$="":RETURN ':ESCキーが押されていないか確認し押されていれば戻ります。 25720 DEF SEG=SEGPTR(7):MIO=PEEK(&H55B):POKE &H55B,1 ':漢字と英数字の幅の比を1.5:1にします。0なら2:1です。 25760 LPRINT EC$;CHR$(1);"@EJL " ':推測ですが"@EJL "という文字列以下をコマンドとして扱う指示だと思います 25762 LPRINT "@EJL SELECT LANGUAGE=ESC/PAGE" ':コマンド体系として ESC/Page を使用するという宣言 25764 IF TRAY THEN LPRINT "@EJL SET PAPERUNIT=3" ':用紙のトレイ選択。プリンタにより替わる。LP-8000で3は増設給紙ユニット 25765 IF NOT TRAY THEN LPRINT "@EJL SET PAPERUNIT=1" 25766 IF JOB$=SYUU$ THEN LPRINT "@EJL SET PAGESIZE=A4" ':用紙のサイズ。JOB$の値は前もって選択させている。(TRAYも) 25768 IF JOB$=SHIN$ THEN LPRINT "@EJL SET PAGESIZE=A3" ':ちなみに就職用、進学用、成績証明のみで用紙の大きさが異なるという迷惑な仕様 25769 IF JOB$=SHOU$ THEN LPRINT "@EJL SET PAGESIZE=B4" 25770 LPRINT "@EJL SET ZOOM=OFF" ':用紙に合わせて拡大縮小されることがあったのでしょう。 25772 LPRINT "@EJL ENTER LANGUAGE=ESC/PAGE" ':推測ですが、ここからは ESC/Page @EJL を特別扱いするのはおしまい。の指示 25780 LPRINT GS$;"2;1muE"; 'ミリ 1ミリ ':これも推測ですが、数値の単位をmmとし、1mm単位でということでしょうか 25860 IF JOB$=SYUU$ THEN LPRINT GS$;"1poE"; 'ヨコ ':縦長に使うか横長に使うかです。1は横長、0は縦長。 25862 IF JOB$=SHIN$ THEN LPRINT GS$;"1poE"; ':poはたぶんpaper_orientation 25864 IF JOB$=SHOU$ THEN LPRINT GS$;"0poE"; 25880 LPRINT GS$;"5.08;5.08loE"; ':印字の起点 logical origin 0.2*25.4=5.08mm 25882 CHRF$="" 25900 POKE &H55B,MIO ':漢字と英数字の幅の比を元に戻します。 25920 RETURN
ESC/Pでは文字も罫線も合わせて上から書いていくが、ESC/Pageでは用紙全体のどこでも好きなときにアクセスできるので、文字の印字と罫線の描画を分離することができる。
極めて自由度が高いが、線と文字の位置合わせもあるので、ある程度のブロックごとに処理をしていくことになる。
まず、線の描画はコントロール文字CHR$(29)で始まるが、プログラムに合わせてこれをGS$と書くことにすると
GS$X1;Y1;X2;Y2lG
とすると、(x,y)座標で、(X1,Y1)から(X2,Y2)までの線が引かれる。
x座標は左から右、y座標は上から下で、y座標は数学でよく使われる向きとは逆。原点は用紙の準備で指定する印字の起点(logical origin)。
数値はこれも用紙の準備で指定する単位に基づき、小数も使える。
例えば、
GS$11.1;22.2;33.3;44.4lG
という文字列をプリンタに送ると、(x,y)座標で (11.1,22.2)から(33.3,44.4)までの線が引かれる。
これをBASICのコマンドにすると、
LPRINT GS$;11.1;";";22.2;";";33.3;";";44.4;"lG"
;はLPRINTで連続して出力するための区切りです。";"はESc/Pageの座標の各値の区切りです。
実際には変数を使って座標を指定します。横線の場合はy座標が共通になります。(縦線の場合はx座標が共通になります)
LPRINT GS$;HB!;";";VL!;";";HE!;";";VL!;"lG";
!はBASICのお約束で浮動小数点数の数値変数であるという印。x座標はhorizontalからHではじまり、y座標はverticalでVで始まるとし、Bはbegin, Eはend, Lはlineかな。ローカルルールを作っています。
実際の調査書印字の一部です。HBB!=5 HBAS!=HBB!+3 は文字部分と罫線部分をそれぞれ上下左右に平行移動させて微調整するためのものです。今思えば、logical origin をいじれば一つは不要だったのかもしれません。Wはwidthから幅、PITはpitchから行間隔など繰り返しの幅を意味します。HW!(1)〜HW!(5)までは、教科名(縦書き), 科目名(横書き), 1学年評価, 2学年評価, 3学年評価 の欄の横幅でしょう。
50070 LPRINT GS$;"5.08;5.08loE"; 'logical origin 0.2*25.4=5.08mm 50080 HBB!=5 : VBB!=5 : RAJ!=-.3 'ROMANAJUST 'C 50100 HBAS!=HBB!+3 : VBAS!=VBB!+11 : VPIT!=10 'C 50120 HW!(1)=6:HW!(2)=31:HW!(3)=10:HW!(4)=10:HW!(5)=10 50140 HW!=0:FOR I=1 TO 5:HW!=HW!+HW!(I):NEXT 'C ・・・・ 略(ここには文字の印字部分が入る)・・・・ 53180 '-------------BOX1 53200 LPRINT GS$;"0;0lpE"; 'line pattern inner;jisssen 53220 LINE1$=GS$+".08;0;3lwG" 'line width CUT R SQ;n r p pin 53240 LINE2$=GS$+".25;0;3lwG" '3バイノ フトサ 53260 LINEM$=GS$+".16;0;3lwG" '2バイノ フトサ 53280 LPRINT GS$;"5.08;5.88loE"; 'logical origin 0.2*25.4=5.08mm 53300 'hori. lines 53320 LPRINT LINE2$; 53340 HB!=HBAS! :HE!=HBAS!+HW!*2:VPIT!=10 53360 VL!=VBAS! :LPRINT GS$;HB!;";";VL!;";";HE!;";";VL!;"lG"; 53380 VL!=VBAS!+VPIT!*4 :LPRINT GS$;HB!;";";VL!;";";HE!;";";VL!;"lG"; 53400 LPRINT LINE1$; 53420 VL!=VBAS!+VPIT!*2 :LPRINT GS$;HB!;";";VL!;";";HE!;";";VL!;"lG"; 53440 HE!=HBAS!+HW! 53460 VL!=VBAS!+VPIT!*3 :LPRINT GS$;HB!;";";VL!;";";HE!;";";VL!;"lG"; 53480 HB!=HBAS!+HW!-10:VL!=VBASEN!-YOMBAK!+1 53500 LPRINT GS$;HB!;";";VL!;";";HE!;";";VL!;"lG"; 53520 HB!=HE!+HW!(1):HE!=HBAS!+HW!*2 53540 VL!=VBAS!+VPIT!*3 :LPRINT GS$;HB!;";";VL!;";";HE!;";";VL!;"lG"; 53560 'vertical lines 53580 LPRINT LINE2$; 53600 VB!=VBAS! :VE!=VBAS!+VPIT!*4 53620 HL!=HBAS! :LPRINT GS$;HL!;";";VB!;";";HL!;";";VE!;"lG"; 53640 HL!=HBAS!+HW!*2 :LPRINT GS$;HL!;";";VB!;";";HL!;";";VE!;"lG"; 53660 LPRINT LINE1$; 53680 HL!=HBAS!+HW! :LPRINT GS$;HL!;";";VB!;";";HL!;";";VE!;"lG"; 53700 HL!=HL!+HW!(1) :LPRINT GS$;HL!;";";VB!;";";HL!;";";VE!;"lG"; 53720 VB!=VBAS! :VE!=VBAS!+VPIT!*2 53740 HL!=HBAS!+10 :LPRINT GS$;HL!;";";VB!;";";HL!;";";VE!;"lG"; 53760 HL!=HBAS!+HW!-10 :LPRINT GS$;HL!;";";VB!;";";HL!;";";VE!;"lG"; 53780 VB!=VE! :VE!=VBAS!+VPIT!*4 53800 HL!=HBAS!+HW!(1) :LPRINT GS$;HL!;";";VB!;";";HL!;";";VE!;"lG"; 53820 VB!=VBAS!+VPIT!*3 :VE!=VBAS!+VPIT!*4 53840 HL!=HL!+20 :LPRINT GS$;HL!;";";VB!;";";HL!;";";VE!;"lG"; 53860 HL!=HL!+6 :LPRINT GS$;HL!;";";VB!;";";HL!;";";VE!;"lG";
NEC PC-98シリーズがBASICがOSを兼ねていた時代から、MS-DOSになったときにはN88-BASICのMS-DOS版が用意されました。しかし、Windows版が発売されることはなく、MS-DOS版のコンパイラでコンパイルしたものも、PC-98シリーズ上のWindowsのDOS窓(linuxの端末エミュレータに相当)でしか動かず、Windows機ならどれでも動くというものではありませんでした。
BASIC以外の言語を含めて移植先を考えましたが、成績のEnterキーを使わない入力とか、出欠のマウスのクリックで増減する入力、プリンタの細かな位置制御ができるかというところがなかなか難しく、F-BASICを見つけて移植を果たすのは2004年でした。
F-BASICへの移行によってESC/Pageを直接使わなくても印刷できるようになります。本体もPC-98シリーズでなくても良くなります。制約はWindowsだけで、PC本体もプリンタもメーカーから自由になります。
N88-BASICはすべてグローバル変数でサブルーチンで分けてもそれは変わりません。F-BASICは基本ローカル変数なので、移植にあたってはグローバル変数を少なくするのに気を使いました。印刷ではPRINTEROBJECTを作ってそれに対して操作をしていく手はずになっていますが、プログラムを見ると、この変数のスコープをとゔするか迷っているようです。今作り直すとどうなのか考えてしまいますが、それをこらえていくつかのポイントだけ拾っておきます。
まずPRINTEROBJECTの生成。
PRINTEROBJECT P1
用紙の指定
'13:B5,12:B4,11:A5,9:A4,8:A3,1:tate,2:yoko '13:B5, 12:B4, 11:A5, 9:A4, 8:A3, 1:tate,2:yoko if JOB$=SHIN$ then P1.SETUPPRINTERMODE "",8,2 if JOB$=SYUU$ then P1.SETUPPRINTERMODE "",9,2 if JOB$=SHOU$ then P1.SETUPPRINTERMODE "",12,1
解像度やフォントサイズを指定しています。解像度は 1twip = 1/20point = 1/1440inch を選択
P1.SETMAPMODE 6 '1/1440inch 1440dot/inchです P1.SETFONTNAME "MS 明朝" ':フォント指定 fsize!=10 CPI!=7.2 P1.SETFONTSIZE fsize! ':フォントサイズは10ポイント
逆にPRINTEROBJECTから情報を得ます
PAPW!=P1.GETDEVICECAPS(4)*1440/25.4 'papar width (mm) to (1/1440) デバイス幅をmm単位で⇨twipに換算 CHRW!=P1.GETTEXTWIDTH("8") 'width of char 8 (1/1440) "8"一文字の幅(単位はMAPMODEによる)
また、mm単位の値をtwipsに換算する係数を用意しておきます。
MM2TW!=1440/25.4 :'tr. mm to TWIPSモード(1/1440inch)
ESC/Pageと考え方は同じで、
P1.LINE (X1,Y1)-(X2,Y2)
とすると、(x,y)座標で、(X1,Y1)から(X2,Y2)までの線が引かれます。P1は前もって宣言したPRINTEROBJECTです。
x座標は左から右、y座標は上から下で、y座標は数学でよく使われる向きとは逆。原点は指定しているところが見つからないので用紙の縁だったと思う。後のJavaがそうなので、混同しているかもしれないが。
数値はこれも用紙の準備で指定する単位に基づく。変数を使うときは浮動小数点数とする。
例えば、
P1.LINE (11.1,22.2)-(33.3,44.4)
という文字列をプリンタに送ると、(x,y)座標で (11.1,22.2)から(33.3,44.4)までの線が引かれる。
実際には変数を使って座標を指定します。横線の場合はy座標が共通になります。(縦線の場合はx座標が共通になります)
プログラム中では単位はmmで計算し、P1.LINE の引数にする時に MM2TW! を掛けてtwipの値にします。
P1.LINE (HB!*MM2TW!,VL!*MM2TW!)-(HE!*MM2TW!,VL!*MM2TW!)
!はBASICのお約束で浮動小数点数の数値変数であるという印。x座標はhorizontalからHではじまり、y座標はverticalでVで始まるとし、Bはbegin, Eはend, Lはlineかな。ローカルルールを作っています。
実際の調査書印字の一部です。ESC/Pageのときと同じ部分です。HBB!=5 HBAS!=HBB!+3 は文字部分と罫線部分をそれぞれ上下左右に平行移動させて微調整するためのものです。Wはwidthから幅、PITはpitchから行間隔など繰り返しの幅を意味します。HW!(1)〜HW!(5)までは、教科名(縦書き), 科目名(横書き), 1学年評価, 2学年評価, 3学年評価 の欄の横幅でしょう。
プログラム中の丸数字は下の印字例の画像のメモと対応しています。
HBB!=5 : VBB!=5 : RAJ!=0 'ROMANAJUST 'C HBAS!=HBB!+3 : VBAS!=VBB!+11 : VPIT!=10 'C HW!(1)=6:HW!(2)=31:HW!(3)=10:HW!(4)=10:HW!(5)=10 HW!=0:FOR I=1 TO 5:HW!=HW!+HW!(I):NEXT 'C ・・・・ 略(ここには文字の印字部分が入る)・・・・ '-------------BOX1 FUTO2=16 FUTOM=8 P1.SETDRAWWIDTH FUTO2 HB!=HBAS! :HE!=HBAS!+HW!*2:VPIT!=10 VL!=VBAS! p1.line (HB!*MM2TW!,VL!*MM2TW!)-(HE!*MM2TW!,VL!*MM2TW!) ':① VL!=VBAS!+VPIT!*4 p1.line (HB!*MM2TW!,VL!*MM2TW!)-(HE!*MM2TW!,VL!*MM2TW!) ':② P1.SETDRAWWIDTH 1 VL!=VBAS!+VPIT!*2 p1.line (HB!*MM2TW!,VL!*MM2TW!)-(HE!*MM2TW!,VL!*MM2TW!) ':③ HE!=HBAS!+HW! VL!=VBAS!+VPIT!*3 p1.line (HB!*MM2TW!,VL!*MM2TW!)-(HE!*MM2TW!,VL!*MM2TW!) ':④ HB!=HBAS!+HW!-10:VL!=VBASEN!-YOMBAK!+1 p1.line (HB!*MM2TW!,VL!*MM2TW!)-(HE!*MM2TW!,VL!*MM2TW!) ':⑤ HB!=HE!+HW!(1):HE!=HBAS!+HW!*2 VL!=VBAS!+VPIT!*3 p1.line (HB!*MM2TW!,VL!*MM2TW!)-(HE!*MM2TW!,VL!*MM2TW!) ':⑥ 'vertical lines P1.SETDRAWWIDTH FUTO2 VB!=VBAS! :VE!=VBAS!+VPIT!*4 HL!=HBAS! p1.line (HL!*MM2TW!,VB!*MM2TW!)-(HL!*MM2TW!,VE!*MM2TW!) ':⑦ HL!=HBAS!+HW!*2 p1.line (HL!*MM2TW!,VB!*MM2TW!)-(HL!*MM2TW!,VE!*MM2TW!) ':⑧ P1.SETDRAWWIDTH 1 HL!=HBAS!+HW! p1.line (HL!*MM2TW!,VB!*MM2TW!)-(HL!*MM2TW!,VE!*MM2TW!) ':⑨ HL!=HL!+HW!(1) p1.line (HL!*MM2TW!,VB!*MM2TW!)-(HL!*MM2TW!,VE!*MM2TW!) ':⑩ VB!=VBAS! :VE!=VBAS!+VPIT!*2 HL!=HBAS!+10 p1.line (HL!*MM2TW!,VB!*MM2TW!)-(HL!*MM2TW!,VE!*MM2TW!) ':⑪ HL!=HBAS!+HW!-10 p1.line (HL!*MM2TW!,VB!*MM2TW!)-(HL!*MM2TW!,VE!*MM2TW!) ':⑫ VB!=VE! :VE!=VBAS!+VPIT!*4 HL!=HBAS!+HW!(1) p1.line (HL!*MM2TW!,VB!*MM2TW!)-(HL!*MM2TW!,VE!*MM2TW!) ':⑬ VB!=VBAS!+VPIT!*3 :VE!=VBAS!+VPIT!*4 HL!=HL!+20 p1.line (HL!*MM2TW!,VB!*MM2TW!)-(HL!*MM2TW!,VE!*MM2TW!) ':⑭ HL!=HL!+6 p1.line (HL!*MM2TW!,VB!*MM2TW!)-(HL!*MM2TW!,VE!*MM2TW!) ':⑮
2016年についにJavaに移植しました。罫線については、F-BASICでのやり方と原理的には同じなので簡単に説明しておきます。
Java化にあたっては、罫線よりも印刷機構そのものや桁ぎめEnterなし入力、マウスクリックで増減入力、ランダムアクセスファイルの読み書きなどいろいろ話題がありますが、これについてはまたの機会にします。
ESC/Pageと考え方は同じで、(x,y)座標で、(X1,Y1)から(X2,Y2)までの線を引くときにはjava.awt.geom.Line2D.Floatのインスタンスを作って各座標をセットし、java.awt.Graphics2Dのdraw(Shape s)メソッドで描画します。
Line2D.Float line = new Line2D.Float(); line.setLine( 11.1f , 22.2f , 33.3f , 44.4f ); g2.draw(line);
これで、(11.1,22.2)から(33.3,44.4)までの線が引かれます。
実際には、横線と縦線のどちらかなので、Line2D.Floatを継承して線の縦位置と左右端、線の横位置と上下端を別々に設定するメソッドを入れました。左右端を指定しておいて、縦位置を変えるだけで複数の線を描画するということが楽にできます。
Line2Dmm lnmm = new Line2Dmm(); //drawline with extended class lnmm.setLR(hbas,hbas+hwhalf); lnmm.setY(vbas); g2.draw(lnmm); //Rectangle upper line
実際の調査書印字の一部です。ESC/Pageのときと同じ部分です。
boldStroke = new BasicStroke(1.0f); medmStroke = new BasicStroke(0.7f); fineStroke = new BasicStroke(0.0f); Line2Dmm lnmm = new Line2Dmm(); //drawline with new class float vbas=18.8f; float vtitle = vbas-4f; float vwyomi = 5.3f; float vwname = 13f; float vwsext = 7f; float vwsex = 15f; float vwbirth =20f; float vptch1 = 10f; float hbas = 11f; float hwaida = 5f; float hwhalf = 297f/2-hbas-hwaida/2; float hwnamet = 10f; float hwname = 47f; float hwsex = 10f; float hwttitle = 6f; float hwjusho = hwhalf-hwnamet-hwname-hwsex-hwttitle; float hwzennich = 20f; ・・・・ 略(ここには文字の印字部分が入る)・・・・ //氏名部罫線 g2.setStroke(boldStroke); lnmm.setLR(hbas,hbas+hwhalf); lnmm.setY(vbas); g2.draw(lnmm); //Rectangle up lnmm.setY(vbas+vwbirth+vptch1*2); g2.draw(lnmm); //Rectangle bottom lnmm.setTB(vbas,vbas+vwbirth+vptch1*2); lnmm.setX(hbas); g2.draw(lnmm); //Rectangle left lnmm.setX(hbas+hwhalf); g2.draw(lnmm); //Rectangle right g2.setStroke(fineStroke); lnmm.setX(hbas+hwhalf-hwjusho); g2.draw(lnmm); // 現住所| lnmm.setX(hbas+hwhalf-hwjusho-hwttitle); g2.draw(lnmm); // |現住所 lnmm.setTB(vbas,vbas+vwbirth); lnmm.setX(hbas+hwnamet+hwname); g2.draw(lnmm); // |性別 lnmm.setX(hbas+hwnamet); g2.draw(lnmm); // 氏名| lnmm.setTB(vbas+vwbirth,vbas+vwbirth+vptch1*2); lnmm.setX(hbas+hwttitle); g2.draw(lnmm); // 学校名| lnmm.setTB(vbas+vwbirth+vptch1,vbas+vwbirth+vptch1*2); lnmm.setX(hbas+hwttitle+hwzennich); g2.draw(lnmm); // |学科名 lnmm.setX(hbas+hwttitle+hwzennich+hwttitle); g2.draw(lnmm); // 学科名| lnmm.setLR(hbas+hwnamet+hwname,hbas+hwnamet+hwname+hwsex); lnmm.setY(vbas+vwsext); g2.draw(lnmm); // _性別_ lnmm.setLR(hbas,hbas+hwhalf); lnmm.setY(vbas+vwbirth); g2.draw(lnmm); // _生年月日_ lnmm.setLR(hbas,hbas+hwnamet+hwname+hwsex); lnmm.setY(vbas+vwbirth+vptch1); g2.draw(lnmm); // _学校名_ lnmm.setLR(hbas+hwnamet+hwname+hwsex+hwttitle,hbas+hwhalf); g2.draw(lnmm); // -在学期間-
Javaでは適当に切り抜いているので、説明不足もあるでしょうが、基本的な考え方が同じであることは解ると思います。
それよりも、ここで書いたことだけでは、ディスプレイへのグラフィック描画と同じです。これを使って紙に印刷する方法はウェブにも書籍にもほとんどありません。このサイトのJavaの項に書きました。また、codezine.jpにもJavaの標準機能だけで実現する帳票印刷の基本に書かせていただきましたが、それほど需要はなかったようです。