java.awt.print.PrinterJob

目的
Java2Dを使った印刷処理を使ってみる

関連クラス

今回のソース
import java.awt.*;
import java.awt.event.*;
import java.awt.font.*;
import java.awt.print.*;
import java.io.*;

class TextViewer
{
	public static void main(String args[])
	{
		new MainWindow();
	}
}

class MainWindow extends Frame
{
	// 改行文字の取得
	final String line_separator = System.getProperty("line.separator");

	// テキストエリア
	TextArea text_view;

	// ファイルの行数
	int num_line;

	public MainWindow()
	{
		super("Text Viewer");

		// ウインドウリスナの追加
		WindowListener listener = new WindowAdapter()
		{
			public void windowClosing(WindowEvent e)
			{
				System.exit(0);
			}
		};
		addWindowListener(listener);

		// メニューバー
		MenuBar mb = new MenuBar();

		// Fileメニュー定義
		Menu file = new Menu("File");
		MenuItem item_open = new MenuItem("Open");
		MenuItem item_print = new MenuItem("Print");
		MenuItem item_exit = new MenuItem("Exit");

		file.add(item_open);
		file.add(item_print);
		file.addSeparator();
		file.add(item_exit);

		// File->Openコマンド定義
		item_open.addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				// ファイルオープンダイアログの表示
				FileDialog dialog = new FileDialog(MainWindow.this,"File Load",FileDialog.LOAD);
				dialog.show();

				try
				{
					// ファイルからの読み込み
					FileInputStream file_in = new FileInputStream(dialog.getDirectory() + dialog.getFile());
					BufferedReader reader = new BufferedReader(new InputStreamReader(file_in));

					String buffer;
					text_view.setText("");
					num_line = 0;
					while((buffer = reader.readLine()) != null)
					{
						// テキストエリアへセット
						text_view.append(buffer + line_separator);
						num_line++;
					}
				}
				catch(IOException ioe)
				{
					System.err.println(ioe);
				}
			}
		});

		// File->Printコマンド定義
		item_print.addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				// プリンタージョブの構築
				PrinterJob printer = PrinterJob.getPrinterJob();
				PageFormat format = printer.defaultPage();
				printer.setPrintable(new TextPrinter(text_view.getText(),num_line));

				// プリンタ設定ダイアログの表示
				if(printer.printDialog())
				{
					try
					{
						// 印刷処理
						printer.print();
					}
					catch(PrinterException pe)
					{
						System.err.println(pe);
					}
				}
			}
		});

		// File->Exitコマンド定義
		item_exit.addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				System.exit(0);
			}
		});

		// メニューバーへFileメニューの追加
		mb.add(file);

		// テキストエリアのセット
		text_view = new TextArea();
		add(text_view,BorderLayout.CENTER);

		// メニューバーのセット
		setMenuBar(mb);

		// ウインドウの表示
		pack();
		setVisible(true);
	}
}

class TextPrinter implements Printable
{
	// 印刷するテキスト
	String text,text_lines[];

	// 一ページの印刷行数
	final int MAX_LINES_OF_PAGE = 35;

	// タブのスペース拡張数
	final int TAB_TO_SPACE = 4;

	// ファイルの行数
	int num_line;

	String line_separator = System.getProperty("line.separator");

	// コンストラクタ
	public TextPrinter(String text,int num_line)
	{
		super();

		this.text = text;
		this.num_line = num_line;
	}

	// 印刷処理メソッド
	public int print(Graphics g,PageFormat format,int page_index)
	{
		// テキストの取得
		text_lines = new String[num_line + 1];
		int index = 0,pre_index = 0,line = 0;

		while((index = text.indexOf(line_separator,index)) != -1)
		{
			text_lines[line] = expandTab(text.substring(pre_index,index),TAB_TO_SPACE);
			pre_index = ++index;
			line++;
		}

		// 文字列印刷処理
		int return_code;

		if(num_line >= MAX_LINES_OF_PAGE)
		{
			return_code = TextPrint(g,format,page_index,MAX_LINES_OF_PAGE);
		}
		else
		{
			return_code = TextPrint(g,format,page_index,num_line);
		}

		return(return_code);
	}

	// テキスト印刷処理
	protected int TextPrint(Graphics g,PageFormat format,int page_index,int num_line_of_page)
	{
		// Graphics2Dの構築
		Graphics g2d = (Graphics2D)g;

		// Font情報の取得
		Font font = g.getFont();
		LineMetrics font_info = font.getLineMetrics(text,new FontRenderContext(font.getTransform(),false,false));
		float font_height = font_info.getHeight();
		float font_leading = font_info.getLeading();

		// 印刷可能領域の取得
		float offsetX = (float)format.getImageableX();
		float offsetY = (float)format.getImageableY();

		// 印刷可能ページ数の設定
		if(page_index >= 5)
		{
			// 
			return(Printable.NO_SUCH_PAGE);
		}

		// 印刷要素の設定
		g2d.setFont(new Font("Dialog",Font.PLAIN,12));
		g2d.setColor(Color.black);

		for(int i=1;i<=num_line_of_page;i++)
		{
			int current_line = page_index * MAX_LINES_OF_PAGE + i - 1;
			float x_coodinate = offsetX;
			float y_coodinate = offsetY + i * (font_height + font_leading);
			g2d.drawString(text_lines[current_line],(int)x_coodinate,(int)y_coodinate);
		}

		// 正常応答
		return(Printable.PAGE_EXISTS);
	}

	// タブ拡張メソッド(ex.1つのタブ->4つのスペース)
	protected String expandTab(String src,int num_space)
	{
		// 指定数だけのスペースを並べた文字列を作る
		StringBuffer space = new StringBuffer();
		for(int i=0;i<num_space;i++)
		{
			space.append(' ');
		}

		// 置き換え処理
		StringBuffer buffer = new StringBuffer();
		for(int i=0;i<src.length();i++)
		{
			if(src.charAt(i) == '\t')
			{
				buffer.append(space);
			}
			else
			{
				buffer.append(src.charAt(i));
			}
		}

		return(buffer.toString());
	}
}
Source is here. (ZIP Format,2240Byte,Shift-JIS)

コンパイル&実行
javac TextViewer.java
java TextViewer

説明
(概略)

今回のプログラムは、結構苦戦しました(^^;
プリンターに、タブ文字が認識されなかったり、
StringTokenizerを用いて、改行文字で区切ると、
空白行が無視され、印刷に反映されない。
などなど、印刷処理を実装するのは楽じゃない
ということが、今回印刷処理プログラムを書いてみた感想です(^^;

概略は、普段Graphicsオブジェクトを用いて、
画面に文字列や、円などの図形や、イメージなどを描きますよね。
それにちょっと仕掛けを加えて、
画面では無く、紙の上に描き出すのが、
印刷処理だと考えれば良いでしょう。

今回は、テキストのみを印刷するようなプログラムでしたが、
イメージや図などを出力するのも、同じ要領で出来るので、
今回のプログラムを応用すれば、
ワープロなども作れるかもしれません。
(TextAreaじゃダメだけど...)

(サンプルプログラムの説明)

text_view.append(buffer + line_separator);
BufferedReader.readLineメソッドが返す文字列は、
行の終端コード(=改行文字)は含まれないので、
line_separatorを付加しています。
printer.setPrintable(new TextPrinter(text_view.getText()));
印刷処理を行う、Printableインタフェースを設定しています。
LineMetrics font_info = font.getLineMetrics(text,new FontRenderContext(font.getTransform(),false,false));
JDK1.1までは、FontMetricsクラスを用いて、
フォントの情報を取得していましたが、
Java2からは、Deprecatedになったので、
このような方法で取得するのが望まれるようです。
java.awt.font.*パッケージは、Java2Dの一部です。
印刷可能領域の取得
デフォルトでは、A4用紙の全体を使えるわけではありあせん。
上下左右には、余白が存在するということです。
つまり、0,0という座標に出力しようとしても、
出力されないということになります。
きちんと紙に出力されるように、
印刷可能領域の取得をしています。