成績処理の印刷出力 その2

目次

公開日 2023-06-11 更新日 -

ページプリンタ用の制御コード(ESC/Page)への転換

前回、成績処理の印刷出力 その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?)

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/Pageでの罫線描画

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かな。ローカルルールを作っています。

ESC/Pageでの罫線描画の実際

実際の調査書印字の一部です。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";

F-BASICへの移行

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本体もプリンタもメーカーから自由になります。

用紙の準備(F-BASIC)

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)

F-BASICでの罫線描画

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かな。ローカルルールを作っています。

F-BASICでの罫線描画の実際

実際の調査書印字の一部です。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!) ':⑮

F-BASICでの印刷の一部

調査書印字例

Javaでの罫線描画

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

Javaでの罫線描画の実際

実際の調査書印字の一部です。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の標準機能だけで実現する帳票印刷の基本に書かせていただきましたが、それほど需要はなかったようです。