# stringWidth bei Graphics von JPanel und Printer unterschiedl



## Kipard (6. Apr 2008)

Hallo Forum,

ich schreibe an einem Programm, das in erste Linie einen Datensatz ausdrucken soll. Dazu möchte ich diesen Datensatz also Vorschau (über ein JPanel) anzeigen. Der Benutzer kann dann noch ein paar Optionen wählen und letztlich diesen Datensatz ausdrucken. Ich verwende eine Menge von Linien und Textblöcken, die ich auf ein Graphics2D Objekt zeichne. Bisher klappt das auch ganz gut, ich kann entweder auf ein Graphics eines JPanels malen (als Vorschau) oder auf das Graphics eines Druckers (um das Dokument tatsächlich auszudrucken). Leider stelle ich nun fest, dass die Methode FontMetrics.stringwidth("irgendeinTest") - unterschiedliche Längeninformationen zurück gibt. Die Schriftart und größe hab ich nicht verändern, ich vermute es liegt an den unterschiedlichen Graphics Objekten. 
Nun zu meiner Frage: wie kann ich dafür sorgen, dass stringwidth für beide Graphics-Objekte dieselbe Information zurück gibt? Denn wenn sich Vorschau und Druck doch unterscheiden macht das ganze keinen Sinn.

Hier mein Minimalbeispiel. Zuerst wird das Dokument auf einem Drucker ausgegeben, danach (wenn man auf den entsprechenden Knopf clickt) wird es auf ein JPanel gezeichnet.

======= Dokument ========


```
import java.awt.Font;
import java.awt.Graphics;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;

public class Dokument implements Printable {
	public int print(Graphics g, PageFormat pageFormat, int pageIndex) throws PrinterException {
		this.paint(g);
		return PAGE_EXISTS;
	}
	
	public void paint(Graphics g) {
		g.setFont(new Font("Arial", Font.PLAIN, 12));
		System.err.println(g.getFontMetrics().stringWidth("Hallo Welt"));
	}
}
```


======== GUI ===========

```
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.print.Book;
import java.awt.print.PageFormat;
import java.awt.print.Paper;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.lang.reflect.InvocationTargetException;
import javax.print.PrintService;
import javax.print.PrintServiceLookup;
import javax.print.attribute.HashPrintServiceAttributeSet;
import javax.print.attribute.PrintServiceAttributeSet;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Main extends JFrame {
	private static final long serialVersionUID = 112787250344916458L;
	public static JFrame frame;
	public static JPanel z;
	
	public static void main(String[] args) throws InterruptedException, InvocationTargetException {
		javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
            public void run() {
            	createAndShowGUI();
            }
        });		
		
		Book book = new Book();
		PrinterJob printJob = PrinterJob.getPrinterJob();
		PrintService private_druckerObjekt = null;
		
		//richtigen Drucker festlegen
		try {
			PrintServiceAttributeSet aset = new HashPrintServiceAttributeSet();
		    PrintService[] colorServices = PrintServiceLookup.lookupPrintServices(null, aset);
		    for (int i = 0; i < colorServices.length; i++) {
		    	if (colorServices[i].getName().equals("Win2PDF")) {
		    		private_druckerObjekt = colorServices[i];
		    		i = colorServices.length; //zum vorzeitigen Beenden der Schleife
		    	}
		    }
		    printJob.setPrintService(private_druckerObjekt);
	    } catch (PrinterException e) {
	    	System.err.println("Druckerfehler");	
	    }
		
	    
	    printJob.setJobName("test");
		PageFormat documentPageFormat = printJob.defaultPage();
		Paper papier = documentPageFormat.getPaper();
		papier.setImageableArea(0, 0, papier.getWidth(), papier.getHeight());
		documentPageFormat.setPaper(papier);
		
		book.append(new Dokument(), documentPageFormat);
		printJob.setPageable(book);
		try {
			printJob.print();
		} catch (PrinterException e) {
			e.printStackTrace();
		}
	}
	
	private static void createAndShowGUI() {
		JFrame.setDefaultLookAndFeelDecorated(true);
		frame = new JFrame("test");
		
		JButton laden = new JButton("testen!");
		ActionListener eventLaden = new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				Dokument d = new Dokument();
				d.paint(z.getGraphics());
			}
		};
		laden.addActionListener(eventLaden);
		
		frame.setSize(500, 600);
        frame.setLayout(new BorderLayout());
		frame.add(laden, BorderLayout.NORTH);
		
		//Display the window.
        frame.pack();
        frame.setVisible(true);
    }
}
```


Ich hoffe irgendwer kann mir jetzt sagen ich muss irgendein RenderingHint oder sonst was setzen, damit sich beide Graphics Objekte gleich verhalten. Im Moment bin ich ein bischen verzweifelt, denn ich finde keine brauchbare Hilfe im Netz. 

Vielen Dank schon im Voraus,
Stefan


----------



## Marco13 (6. Apr 2008)

Hm - kann's grad nicht testen (hab keinen Drucker und kein Win2PDF...) aber in der Doku zu stringWidth steht da so ein bißchen laberabarber, von wegen "vielleicht doch lieber http://java.sun.com/j2se/1.4.2/docs/api/java/awt/FontMetrics.html#getStringBounds(java.lang.String,%20java.awt.Graphics) " verwenden... hast du das schon getestet?


----------



## Guest (6. Apr 2008)

Hi Marco13,
schon mal vielen Dank für Deinen ersten Hilfsversuch. Um das klar zu stellen, das Problem ist unabhängig vom gewählten Drucker. Dass ich da jetzt "Win2PDF" drin stehen habe ist reiner Zufall. Da kann jeder andere installierte Drucker stehen.
Mir fällt auf, dass mein Beispiel zwei Fehler enthält: 
1. in der GUI Klasse hab ich vergessen das JPanel zu initialisieren, Zeile 21 sollte besser  
	
	
	
	





```
public static JPanel z = new JPanel();
```
 lauten und
2. hab ich vergessen das Panel auf das Frame zu adden: Man füge unter Zeile 80 
	
	
	
	





```
frame.add(z,BorderLayout.CENTER);
```
 ein. Beides ändert aber nichts am grundlegenden Problem.

Die Idee mit getStringBounds verhält sich ähnlich komisch wie stringWidth. Hier der Output:
Printer: java.awt.geom.Rectangle2D$Float[x=0.0,y=-11.05957,w=54.600002,h=13.798828]
JPanel: java.awt.geom.Rectangle2D$Float[x=0.0,y=-11.05957,w=56.0,h=13.798828]

Es gibt also weiterhin eine Diskrepanz zwsichen der Textlänge des Panels und des Druckers. Wie kann ich diese ausgleichen?


----------



## Marco13 (6. Apr 2008)

Hmja, ich hatte es gestartet, und nach ein bißchen tweaken "funktionierte" es auch, allerdings zeigt er bei mir eben beide male "55" als breite an... falls sich niemand meldet, versuch' ich morgen vielleicht nochmal, den Fehler wirklich zu reproduzieren...


----------



## Kipard (7. Apr 2008)

Ich habe nun folgenden RenderingHint hinzugefügt:

```
RenderingHints fractionalMetricON = new RenderingHints(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); 
internesG.setRenderingHints(fractionalMetricON);
```
Dadurch wird die Diskrepanz immerhin schon auf ein erträgliches Maß gesenkt, auch wenn es noch nicht exakt derselbe Wert der StringWidth für Printer und Panel ist.


----------

