Drucken - Graphics?

flashray

Erfahrenes Mitglied
Hallo,

vor einiger Zeit habe ich mich eine Weile mit Drucken in Java beschäftigt gehabt und gesehen, dass man einfachen Text nicht ohne weiteres mit Java drucken kann. Auch das JPS seit Java 1.4 hilft da nicht großartig weiter, es gibt zwar Funktionen die Text drucken, diese werden aber in der Regel von den meisten Druckern nicht unterstüzt.

Es bleibt also nur noch die Möglichkeit über Graphics zu drucken. D.h. zuerst den Text dem Ausgabeformat entsprechend nach Bedarf den Text seitenweise aufteilen. Jeden Abschnitt dann einzeln auf eine Seite zu Zeichnen und drucken.

Genau das Zeichnen bereitet mir einige Mühe. Hierzu Bedarf es den Gebruach an Klassen wie LineBreakMeausurer, AttributedCharacterIterator, TextLayout und einigen anderen.

Einen Text zeichen mit Vernachlässigung von Tabs oder der Ausrichtung und anderen Formatierungen ist noch zu realisieren. Sollen diese auch beachtet werden, ist es um einiges komplizierter.

Wenn das zeichnen geklappt hat ist das Drucken an sich kein Thema.

Meine Frage hierzu wäre folgende: Ein JTextArea oder JTextPane oder JEditorPane haben doch alle die Methode paint. Tue ich diesen Text zu weisen und anzeigen zeichnen diese ja dann Text mit all ihren Formatierungen. Ich könnte diesen Komponenten dann auch die Größe des Ausgabeformats für das Drucken zuweisen.

Könnte ich dann nicht einfach die Daten aus der paint Mehtode einer dieser Textkomponenten weiterleiten und der paint Methode des Printable Interfaces übergeben?

Das Drucken einer JComponente gibt es natürlich auch in Java, dass meine ich aber nicht, denn so werden dann auch der Rahmen der Komponente oder der ScrollBalken mit ausgedruckt. Das möchte ich nicht.

Wäre das realisierbar wie ich das oben geschildert habe, oder führt kein Weg vorbei an LineBreakMeausurer, AttributetString, AttributedCharacterIterator, TextLayout, etc.?


Hinweis: Ich möchte keine Hilfslibrary für das Drucken verwenden.


Ich habe bisher folgende Klasse geschrieben in der ich Text auf eine JComponente zeichne. Zeilenumbrüche hatten schon funktioniert, bei den Tabs gibt es noch ein wenig Probleme. Ich möchte hier so vorgehen:
1. Ich zeichne den zu druckenden Text zunächst auf eine JComponente in Größe des Ausgabeformats.
2. Wenn der Text mehr als eine Seite in Anspruch nimmt, speichere ich die letzte Stelle einer Seite in ein array und zeichne dann die Nächste Seite und so weiter.
3. Anhand des Arrays weiss ich die Anzahl der Seiten und welcher Abschnitt des Textes auf welche Seite kommt. Jetzt tue ich dann für jede Seite eine Printable zeichnen und ausdrucken.

Code:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.font.LineBreakMeasurer;
import java.awt.font.TextAttribute;
import java.awt.font.TextLayout;
import java.text.AttributedCharacterIterator;
import java.text.AttributedString;
import java.util.ArrayList;

import javax.swing.JComponent;
import javax.swing.JFrame;

public class Try extends JComponent {

	// GUI //

	JFrame frame = new JFrame();

	Color col1 = new Color(255, 255, 205);

	Color col2 = Color.MAGENTA;

	Font f = new Font("SanSerif", Font.BOLD, 16);

	// String - AttributedString - AttributedCharacterIterator -
	// LineBreakMeasurer //

	String text = "\nHallo Peter wie geht es dir?\nMir geht es gut.\nDas sind drei Tabs\teins\t\tzwei\t\t\tdrei!";

	ArrayList list = new ArrayList();

	AttributedString[] aStyledText;

	AttributedCharacterIterator[] aParagraph;

	LineBreakMeasurer[] aLineMeasurer;

	// Hilfsvariablen //

	int number;

	int[] aParagraphStart;

	int[] aParagraphEnd;

	float as = 0, des = 0, lea = 0;

	// - //

	public static void main(String[] args) {
		Try t = new Try();
	}

	public Try() {

		// GUI //

		frame.getContentPane().setBackground(col1);
		frame.getContentPane().setForeground(col2);
		frame.setSize(400, 700);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setLocationByPlatform(true);
		frame.setAlwaysOnTop(true);
		frame.add(this);

		// Text splitten - \n - \t //

		for (int i = 0, j = 0; i < text.length(); i++) {
			if (text.charAt(i) == '\n' | text.charAt(i) == '\t') {
				if (i != j) {
					list.add(text.substring(j, i));
				}
				list.add("" + text.charAt(i));
				j = i + 1;
			} else if (i == text.length() - 1) {
				list.add(text.substring(j, i + 1));
			}
		}

		number = list.size();

		// AttributedString und AttributedCharacterIterator und
		// LineBreakMeasurer erzeugen //

		aStyledText = new AttributedString[number];
		aParagraph = new AttributedCharacterIterator[number];
		aParagraphStart = new int[number];
		aParagraphEnd = new int[number];
		aLineMeasurer = new LineBreakMeasurer[number];

		for (int i = 0; i < number; i++) {
			aStyledText[i] = new AttributedString(list.get(i).toString());
			aStyledText[i].addAttribute(TextAttribute.FONT, f);
			aParagraph[i] = aStyledText[i].getIterator();
			aParagraphStart[i] = aParagraph[i].getBeginIndex();
			aParagraphEnd[i] = aParagraph[i].getEndIndex();
		}

		// GUI anzeigen //

		frame.setVisible(true);
	}

	public void paint(Graphics g) {

		Graphics2D graphics2D = (Graphics2D) g;
		Dimension size = this.getSize();

		float formatWidth = (float) size.width;

		float drawPosY = 0;
		float drawPosX = 0;

		for (int i = 0; i < number; i++) {
			aLineMeasurer[i] = new LineBreakMeasurer(aParagraph[i], graphics2D
					.getFontRenderContext());
			aLineMeasurer[i].setPosition(aParagraphStart[i]);
		}

		for (int i = 0; i < number; i++) {

			// Positionskorrektur für Tab //
			if (list.get(i).equals("\t")) {
				drawPosX = drawPosX + 20;
			}

			// - //
			else {
				while (aLineMeasurer[i].getPosition() < aParagraphEnd[i]) {

					if (drawPosX > formatWidth) {
						drawPosX = 0;
						drawPosY += des + lea  + as;
					}
					TextLayout layout = aLineMeasurer[i].nextLayout(formatWidth
							- drawPosX);
					as = layout.getAscent();
					drawPosY += as;

					// Positionskorrektur für Tab //
					try {
						if (!list.get(i - 1).equals("\t")) {
							drawPosX = 0;
							drawPosY += des + lea;
						}
					} catch (Exception e) {
					}

					// - //
					layout.draw(graphics2D, drawPosX, drawPosY);
					des = layout.getDescent();
					lea = layout.getLeading();
					drawPosY += des + lea;
					drawPosX = drawPosX + layout.getAdvance();
				}

				// Positionskorrektur für Newline //

				if (!list.get(i).equals("\n"))
					drawPosY += -as - des - lea;
			}
		}
	}
}

Ich möchte jetzt nur wissen, wie oben geschildert ob ich die paint-Methode einer Textkomponente diese Zeichenarbeit machen lassen kann? Wenn ja würde ich mich auf einen Ansatz freuen. Wenn das nicht geht werde ich an meiner Klasse weiter tüfteln. Natürlich wenn jemand eine ganz andere Idee hat, wie man mit pure Java ohne extra Library mehrseitigen formatierten Text ausdrucken kann, der ist auch willkommen.


Hier ist ein Beispiel vom "Sun SDK 1.2 Printing API Tutorial" die eine Seite Text ohne Beachtung von Zeilenumbrüchen und Tabs und sonstiger Ausrichtung druckt.

http://java.sun.com/products/java-media/2D/forDevelopers/sdk12print.html#printable

Diese versuche ich so zu erweitern das man mehrseitig und mit Beachtung der Formatierungen drucken kann.


Vg Erdal
 
Deinen Ansatz mit dem "JEditorPane" kannst Du weiterverfolgen, indem Du nämlich genau dieses "JEditorPane" an die Druck-Methode gibst. Die ggf. umschließende Scrollbar dürfte damit nicht ausgedruckt werden.
 
Danke Steffen,

mit dieser frischen Motivation werde ich diesen Ansatz mal etwas genauer unter die Lupe nehmen.

Vg Erdal
 
Hallo Freunde,

in letzter Zeit habe ich mich über das Drucken von umgebrochenem Text aus einem JTextArea beschäftigt. Möchte den jetzigen Stand der Dinge mitteilen, und eure Meinung und Tips hören.

Vorgehensweise:
1. - getWrappedText()
Ich füge dem umgebrochenen Text an diesen Stellen ein newLine. So besteht der Text nur noch aus Zeilen, welche bis an den Rechten Rand der JTextArea reichen.

2. - FontMetrics
FontMetrics liefert die Zeilenhöhe. Die Höhe des TextAreas ist auch bekannt. Ich rechne die Anzahl der Zeilen und der Seiten aus.

3.
Nun füge ich den Text Abschnittsweise in ein TextArea, lasse ihn anzeigen, und speichere diesen in ein Bild.


Es fehlen noch, die Anpassung der Größe der TextArea an das gewünschte Papierformat. Und das Ausdrucken auf Papier statt das Umleiten in eine Bilddatei. Noch einwenig Feinschliff bedarf es auch, da noch Seitenränder und genaue Positionierung fehlen.


Würde mich über Hinweise und Verbesserungsvorschläge freuen.

Code:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;

import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JTextArea;
import javax.swing.text.BadLocationException;
import javax.swing.text.JTextComponent;
import javax.swing.text.Utilities;

public class PrintMultiplePages extends JFrame implements ActionListener {

	PrintStream o = System.out;

	Font f = new Font("Serif", Font.PLAIN, 22);

	JTextArea t1 = new JTextArea();

	JButton b = new JButton("Drucke mehrseitigen Text");

	Dimension d = new Dimension(350, 450);

	BufferedImage bufferedImage;

	FontMetrics fm = t1.getFontMetrics(f);

	int linesTotal = 0;

	int linesMax = 0;

	int numberOfPages = 0;

	int pageborders[][] = new int[999][2];

	public PrintMultiplePages() {
		super("PrintMultiplePagesOfText");
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		setAlwaysOnTop(true);
		setLocationByPlatform(true);

		b.setFocusable(false);
		b.addActionListener(this);
		t1.setFont(f);
		t1.putClientProperty(
				com.sun.java.swing.SwingUtilities2.AA_TEXT_PROPERTY_KEY,
				Boolean.TRUE);
		t1.setLineWrap(true);
		t1.setWrapStyleWord(true);
		t1.setSize(d);
		t1.setPreferredSize(d);
		t1.setMaximumSize(d);
		t1.setMinimumSize(d);
		t1.setText(text);

		add(t1, BorderLayout.CENTER);
		add(b, BorderLayout.SOUTH);

		pack();
		setVisible(true);
	}

	public static void main(String[] args) {
		new PrintMultiplePages();
	}

	public void actionPerformed(ActionEvent e) {
		t1.setText(getWrappedText(t1));
		t1.setPreferredSize(new Dimension(t1.getWidth() + 5, t1.getHeight()));
		this.pack();

		linesMax = getMaxLines();
		linesTotal = t1.getLineCount();
		numberOfPages = getNumberOfPages(linesMax, linesTotal);

		new Thread() {
			public void run() {
				try {
					for (int i = 0; i < numberOfPages; i++)
						pageborders[i][0] = t1.getLineStartOffset(i * linesMax);
					for (int i = 0; i < numberOfPages - 1; i++)
						pageborders[i][1] = pageborders[i + 1][0] - 1;
					pageborders[numberOfPages - 1][1] = t1
							.getLineEndOffset(linesTotal - 1);
					for (int i = 0; i < numberOfPages; i++)
						printToImage(t1.getText(pageborders[i][0],
								pageborders[i][1] - pageborders[i][0]), i);
				} catch (BadLocationException e1) {
					System.out.println(e1.getMessage());
				}
			}
		}.start();
	}

	public String getWrappedText(JTextComponent c) {
		int len = c.getDocument().getLength();
		int offset = 0;
		StringBuffer buf = new StringBuffer((int) (len * 1.10));
		try {
			while (offset < len) {
				int end = Utilities.getRowEnd(c, offset);
				if (end < 0) {
					break;
				}
				end = Math.min(end + 1, len);
				String s = c.getDocument().getText(offset, end - offset);
				buf.append(s);
				if (!s.endsWith("\n")) {
					buf.append('\n');
				}
				offset = end;
			}
		} catch (BadLocationException e) {
		}
		return buf.toString();
	}

	public void printToImage(String textToPrint, int page) {
		JDialog printPreview = new JDialog();
		JTextArea paperArea = new JTextArea(textToPrint);
		paperArea.setPreferredSize(new Dimension(d.width + 5, d.height));
		paperArea.setFont(f);
		paperArea.putClientProperty(
				com.sun.java.swing.SwingUtilities2.AA_TEXT_PROPERTY_KEY,
				Boolean.TRUE);

		printPreview.add(paperArea);
		printPreview.pack();
		printPreview.setLocationByPlatform(true);
		printPreview.setAlwaysOnTop(true);
		printPreview.setResizable(false);
		printPreview.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
		printPreview.setTitle("Printing Page: " + page);
		printPreview.setVisible(true);

		bufferedImage = new BufferedImage(d.width + 5, d.height,
				BufferedImage.TYPE_INT_RGB);

		paperArea.paint(bufferedImage.getGraphics());

		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			System.out.println(e.getMessage());
		}

		try {
			ImageIO.write(bufferedImage, "JPEG",
					new File("out" + page + ".jpg"));
		} catch (IOException e) {
			o.println(e.getMessage());
		}

		printPreview.removeAll();
		printPreview.dispose();
	}

	public int getMaxLines() {
		return t1.getHeight() / fm.getHeight();
	}

	public int getNumberOfPages(int lineMax, int lineTotal) {
		return (int) Math.ceil((double) lineTotal / (double) lineMax);
	}

	String text = "Four score and seven years ago our fathers brought forth on this "
			+ "continent a new nation, conceived in liberty and dedicated to the "
			+ "proposition that all men are created equal. Now we are engaged in "
			+ "a great civil war, testing whether that nation or any nation so "
			+ "conceived and so dedicated can long endure. We are met on a great "
			+ "battlefield of that war. We have come to dedicate a portion of "
			+ "that field as a final resting-place for those who here gave their "
			+ "lives that that nation might live. It is altogether fitting and "
			+ "proper that we should do this. But in a larger sense, we cannot "
			+ "dedicate, we cannot consecrate, we cannot hallow this ground."
			+ "The brave men, living and dead who struggled here have consecrated "
			+ "it far above our poor power to add or detract. The world will "
			+ "little note nor long remember what we say here, but it can never "
			+ "forget what they did here. It is for us the living rather to be "
			+ "dedicated here to the unfinished work which they who fought here "
			+ "have thus far so nobly advanced. It is rather for us to be here "
			+ "dedicated to the great task remaining before us--that from these "
			+ "honored dead we take increased devotion to that cause for which "
			+ "they gave the last full measure of devotion--that we here highly "
			+ "resolve that these dead shall not have died in vain, that this "
			+ "nation under God shall have a new birth of freedom, and that "
			+ "government of the people, by the people, for the people shall "
			+ "not perish from the earth.";
}

Vg Erdal
 
Zuletzt bearbeitet:
Hallo Freunde,

habe es nun endlich geschafft eine Klasse zu schreiben die ein beliebig formatiertes String mehrseitig ausdrucken kann.

Ich hätte aber noch eine einzige konkrete Frage:
1. Die Druckqalität gefällt mir noch nicht ganz. Wie könnte man diese verbessern? Irgendetwas stimmt an der Auflösung nicht.

Code:
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JTextArea;
import javax.swing.JWindow;
import javax.swing.text.BadLocationException;
import javax.swing.text.JTextComponent;
import javax.swing.text.Utilities;

public class PrintTool implements Printable {

	// Help //

	private PrintStream o = System.out;

	// - //

	private String textP;

	private PageFormat pf;

	private Font fontP;

	private String currentTextP;

	// - //

	private JWindow windowP = new JWindow();

	private JTextArea textAreaP = new JTextArea();

	private FontMetrics fm;

	private Dimension pageDim;

	private BufferedImage bufferedImage;

	// - //

	private int linesTotal = 0;

	private int linesMax = 0;

	private int numberOfPages = 0;

	private int pageborders[][] = new int[999][2];

	private List<String> lst = new ArrayList<String>();

	// - //

	public PrintTool(String textToPrint, PageFormat pageFormat, Font font) {
		this.textP = textToPrint;
		this.pf = pageFormat;
		this.fontP = font;

		if (textP == null)
			textP = "Das ist eine TestSeite";

		if (pf == null)
			pf = new PageFormat();

		if (font == null)
			font = new Font("Serif", Font.PLAIN, 16);

		pageDim = new Dimension((int) pf.getImageableWidth() - 5, (int) pf
				.getImageableHeight());

		textAreaP.setFont(fontP);
		textAreaP.putClientProperty(
				com.sun.java.swing.SwingUtilities2.AA_TEXT_PROPERTY_KEY,
				Boolean.TRUE);
		fm = textAreaP.getFontMetrics(fontP);
		textAreaP.setLineWrap(true);
		textAreaP.setWrapStyleWord(true);
		textAreaP.setPreferredSize(pageDim);
		textAreaP.setText(textP);

		windowP.add(textAreaP);
		windowP.pack();
		windowP.setLocation(2000, 2000);
		windowP.setVisible(true);

		textAreaP.setText(this.getWrappedText(textAreaP));
		pageDim = new Dimension((int) pf.getImageableWidth(), (int) pf
				.getImageableHeight());
		textAreaP
				.setPreferredSize(new Dimension(pageDim.width, pageDim.height));
		windowP.pack();

		linesMax = this.getMaxLines();
		linesTotal = textAreaP.getLineCount();
		numberOfPages = this.getNumberOfPages(linesMax, linesTotal);

		try {
			for (int i = 0; i < numberOfPages; i++)
				pageborders[i][0] = textAreaP.getLineStartOffset(i * linesMax);
			for (int i = 0; i < numberOfPages - 1; i++)
				pageborders[i][1] = pageborders[i + 1][0] - 1;
			pageborders[numberOfPages - 1][1] = textAreaP
					.getLineEndOffset(linesTotal - 1);
			for (int i = 0; i < numberOfPages; i++)
				lst.add(textAreaP.getText(pageborders[i][0], pageborders[i][1]
						- pageborders[i][0]));
		} catch (BadLocationException e) {
			e.printStackTrace();
		}

		windowP.dispose();
	}

	public static void main(String[] args) {
		PrintTool pt = new PrintTool(text, new PageFormat(), new Font("Serif",
				Font.BOLD + Font.ITALIC, 16));
		pt.printAll();
		System.exit(0);
	}

	public void printAll() {
		PrinterJob druckJob = PrinterJob.getPrinterJob();
		druckJob.setPrintable(this, pf);

		try {
			for (int i = 0; i < lst.size(); i++) {
				currentTextP = lst.get(i);
				druckJob.print();
			}
		} catch (PrinterException e) {
			e.printStackTrace();
		}
	}

	public int print(Graphics g, PageFormat pageFormat, int pageIndex)
			throws PrinterException {
		if (pageIndex > 0)
			return Printable.NO_SUCH_PAGE;

		textAreaP.setText(currentTextP);
		windowP.setVisible(true);

		bufferedImage = new BufferedImage(pageDim.width, pageDim.height,
				BufferedImage.TYPE_INT_RGB);
		
		textAreaP.paint(bufferedImage.getGraphics());

		g.drawImage(bufferedImage,(int) pageFormat.getImageableX(), (int)pageFormat.getImageableY(), textAreaP);

		windowP.setVisible(false);

		return Printable.PAGE_EXISTS;
	}

	private String getWrappedText(JTextComponent c) {
		int len = c.getDocument().getLength();
		int offset = 0;
		StringBuffer buf = new StringBuffer((int) (len * 1.10));
		try {
			while (offset < len) {
				int end = Utilities.getRowEnd(c, offset);
				if (end < 0) {
					break;
				}
				end = Math.min(end + 1, len);
				String s = c.getDocument().getText(offset, end - offset);
				buf.append(s);
				if (!s.endsWith("\n")) {
					buf.append('\n');
				}
				offset = end;
			}
		} catch (BadLocationException e) {
			o.println(e.getMessage());
		}
		return buf.toString();
	}

	private int getMaxLines() {
		return textAreaP.getHeight() / fm.getHeight();
	}

	private int getNumberOfPages(int lineMax, int lineTotal) {
		return (int) Math.ceil((double) lineTotal / (double) lineMax);
	}

	private static String text = "Four score and seven years ago our fathers brought forth on this "
			+ "continent a new nation, conceived in liberty and dedicated to the "
			+ "proposition that all men are created equal.\n\tNow we are engaged in "
			+ "a great civil war, testing whether that nation or any nation so "
			+ "conceived and so dedicated can long endure. We are met on a great "
			+ "battlefield of that war. We have come to dedicate a portion of "
			+ "that field as a final resting-place for those who here gave their "
			+ "lives that that nation might live.\nIt is altogether fitting and "
			+ "proper that we should do this. But in a larger sense, we cannot "
			+ "dedicate, we cannot consecrate, we cannot hallow this ground."
			+ "The brave men, living and dead who struggled here have consecrated "
			+ "it far above our poor power to add or detract. The world will "
			+ "little note nor long remember what we say here, but it can never "
			+ "forget what they did here.\nIt is for us the living rather to be "
			+ "dedicated here to the unfinished work which they who fought here "
			+ "have thus far so nobly advanced.\nIt is rather for us to be here "
			+ "dedicated to the great task remaining before us--that from these "
			+ "honored dead we take increased devotion to that cause for which "
			+ "they gave the last full measure of devotion--that we here highly "
			+ "resolve that these dead shall not have died in vain, that this "
			+ "nation under God shall have a new birth of freedom, and that "
			+ "government of the people, by the people, for the people shall "
			+ "not perish from the earth.";
}

Vg Erdal
 
Hallo Freunde,

ich glaube das Problem liegt bei der geringen Pixelanzahl von getImageableHeight und getImageableWidht (648.0, 468.0). Das ist doch für ein Din A4 Blatt etwas zuwenig. Ein Frame in dieser Größe ist auf meinem Bildschirm nicht mal halb so groß wie ein Din A4 Blatt. D.h. beim Drucken wird dann das Bild auf mehr als doppelte skaliert, wodurch, der Text verschwommen ist.


Vg Erdal
 
flashray hat gesagt.:
Hallihallo,

habs hinbekommen, hab ein 4 mal größeres Bild vom Text erzeugt und dann auf ein viertel runterskaliert, wie im Beispiel von Thorsten Horn. Wo der Unterschied jetzt liegt, zwischen, ein kleines Bild drucken und ein großen nehmen, runterskalieren, also verkleinern und drucken, weiß ich nicht, aber es klappt. Die Ausdruckqalität kann sich sehen lassen

http://www.torsten-horn.de/techdocs/java-print.htm#MyPrintableObject

Vg Erdal

Hallo Erdal,

kannst Du bitte Deinen komplette Code mal posten ?

DANKE !!
 
Zurück