# Rechnungen in Java erstellen & drucken



## Leroy42 (26. Apr 2007)

Hallo,

ich möchte Rechnungen wie diese Beispiel-Rechnung (wird auf vorgedruckten Seiten gedruckt)
in Java erstellen und auch ausdrucken.

Derzeit habe ich einen Großteil der Verwaltung (Kunden, Bestellungen, ...) in
Java via Zugriff auf einen MySql-Server realisiert.

Zur Erstellung der eigentlichen, auszudruckenden, Rechnung gebe ich
die Daten noch _zu Fuß_ in eine ODT (OpenOffice.org)-Datei ein(*)
und drucke sie aus.

Hierbei können (und tun es auch   ) allerlei Flüchtigkeitsfehler
(Doppelte Rechnungsnummer, falsches Einzahlungsdatum, ...)
auftreten.

Deshalb möchte ich auch die Erzeugung der Rechnungen via
Java erledigen um _alles aus einer Hand_ zu haben. Allerdings
habe ich mich bisher noch nie mit Druckausgaben beschäftigt.

Was würdet ihr für einen einfachen Weg vorschlagen; wo sollte
ich mich einarbeiten? 

Ich habe schon mal daran gedacht eine odt-Datei mit UNO zu
erstellen/bearbeiten, finde diese API aber schlicht und ergreifend, einfach K.....

Kennt ihr eine einfache Library, mit der man PDF-Dokumente erzeugen kann?
Da ich einen PDF-Druckertreiber habe, wäre es dann vielleicht einfacher, 
die Rechnungsdaten in ein JPanel _zu malen_ und dieses dann
auszudrucken(wie selektiert man in Java einen Drucker(-treiber) :shock: )?

Am liebsten jedoch, würde ich gerne die Möglichkeit haben,
direkt von einem Java-Programm (für unbedarfte User) auszudrucken.

Wie macht ihr das bei derartigen Anforderungen?

Im Voraus Danke für eure Vorschläge.




(*) Ja, lästert nur; hab's verdient...


----------



## AlArenal (26. Apr 2007)

Drucken mit Bordmitteln ist in Java ÄUßERST lästig!

Nachdem ich ein paar Woche mit Coden und Fluchen verbracht hatte,  stieß ich auf die JPDF Lib (hat nichts mit PDF zu tun  ) und die setzen wir nun im zweiten Jahr ein. Die kostet nicht viel und Henry vom Support ist echt knorke und schon in der Evaluierungsphase sehr hilfsbereit.

http://www.softframeworks.com/

Zu Fuß in Java drucken? Never ever again!


----------



## Wildcard (26. Apr 2007)

Eine LaTeX Lib wäre auch noch denkbar.


----------



## kleiner_held (26. Apr 2007)

PDF Dokumente kann man z.B. mit FOP erstellen, allerdings muss man sich dazu in XSL-FO einarbeiten. Einfacher ist meiner Meinung nach iText.


----------



## Der Müde Joe (26. Apr 2007)

Leroy42 hat gesagt.:
			
		

> Kennt ihr eine einfache Library, mit der man PDF-Dokumente erzeugen kann?



JasperReports mit iReports erstellt Pdfs...bei uns jedenfalls viele Reports

http://jasperforge.org/sf/projects/jasperreports
http://jasperforge.org/sf/projects/ireport

einfach...naja...habs nur so am rande mitbekommen....hat ein Praktikant gemacht...

EDIT:
Zitat Homepage
JasperReports is a powerful open source Java reporting tool that has the ability to deliver rich content onto the screen, to the printer or into PDF, HTML, XLS, CSV and XML files.


----------



## AlArenal (26. Apr 2007)

Der Müde Joe hat gesagt.:
			
		

> EDIT:
> Zitat Homepage
> JasperReports is a powerful open source Java reporting tool that has the ability to deliver rich content onto the screen, to the printer or into PDF, HTML, XLS, CSV and XML files.



Wenn es nur danach ginge, könnte ich dir dutzende Websites zeigen, auf denen Pridukte beworben sind, die alle im gleichen AMrktsegment zuhause sind und alle behaupten Marktführer zu sein!


----------



## padde479 (27. Apr 2007)

Ich würde auch TeX/LaTeX bevorzugen. Dann hast Du auch den Vorteil, dass es auch wirklich auf jeder Maschine läuft *und* gleich aussieht.


----------



## NTB (27. Apr 2007)

Ich würde mit iText ein PDF erzeugen.


----------



## SnooP (27. Apr 2007)

Nimm FOP (von Apache xml-graphics) bzw. FO-XSL... 

das Einstellen der dynamischen Informationen würde ich allerdings mit Velocity machen (nicht mit XSLT)... sprich ein Velocity-XML-FO Template erstellen mit entsprechenden Platzhaltern da wo es dynamisch wird und dann mit Hilfe von Java und nem Velocity-Context der die jeweiligen Daten enthält parsen... FOP bietet Renderer für PDF, PS usw. an... und wenn man mal nen Barcode drucken will - kein Problem mit barcode4j was ne Erweiterung für FOP anbietet und man den barcode als instream-objekt mitten in das FO-Dokument schmeißen kann und das ganze dann als svg geparsed wird... - supidupicool 

Das einzige was etwas nervig ist, ist das erstellen von komplexeren Dokumenten mit FO - bzw. FOP weil FOP nur eine Submenge von FO wirklich kann... bzw. manchmal in der Darstellung von Tabellen etwas seltsam im Verhalten ist... - da gehört also ein wenig Praxis dazu... aber ich bin jetzt etwa nen Monat dran und mache da diverse Dinge mit... echt ne ganz schöne Sache, wenn es dann mal läuft.

Tex kann man da natürlich auch gut nehmen und davor auch nen Velocity drüberjagen... aber gibt es gute Bibliotheken für java? Ich persönlich find die Gestaltungsmöglichkeiten von FO auch flexibler, sprich man kann Container absolut auf einer Seite positionieren, Header/Body/Footer-Bereiche angeben etc... sicher kann man mit Tex auch sehr viel machen, aber ich persönlich fand es immer etwas umständlich, wenn man ganz verrückte Dinge tun wollte 

bezüglich Drucker selektieren - hier mal ein wenig code mit der javax.print.* API:
du brauchst unter Umständen ein entsprechendes DocFlavor und für das Drucken verwende ich das SimpleDoc dem ich einfach den erzeugten Stream übergebe... im HashPrintServiceAttributeSet kann man noch spezielle Parameter einbauen, um den jeweiligen Drucker auszuwählen, ist bei uns aber blöd, brauchen nur nach Namen zu prüfen...

```
protected final DocPrintJob getPrintJob(String printerName, DocFlavor flavor) {
		//bei leerem Druckernamen Defaultprinter verwenden
		if (printerName == null || "".equals(printerName))
			return PrintServiceLookup.lookupDefaultPrintService().createPrintJob();
		
    	for (PrintService ps : PrintServiceLookup.lookupPrintServices(flavor, new HashPrintServiceAttributeSet())) {
    		if ( ps.getName().indexOf(printerName) != -1) 
    			return ps.createPrintJob();   			
    	}
    	//der in der config eingetragene Druckername konnte nicht gefunden werden - Standard verwenden
    	return PrintServiceLookup.lookupDefaultPrintService().createPrintJob();    	
	}
```


----------



## Leroy42 (27. Apr 2007)

Danke für die zahlreichen Antworten.



			
				NTB hat gesagt.:
			
		

> Ich würde mit iText ein PDF erzeugen.



Ich glaube ich mache das..  ???:L  :###


----------



## The_S (27. Apr 2007)

Leroy42 hat gesagt.:
			
		

> Danke für die zahlreichen Antworten.
> 
> 
> 
> ...



Jap, ist definitiv die einfachste Möglichkeit. So löse ich das privat wie auch (wir) beruflich auch immer. Ich hab mir mal ne kleine Lib geschrieben um PDF-Erstellung mit iText noch einfacher zu machen (leider nicht kommentiert, solltest du sie wirklich verwenden und fragen bzw. wünsche haben, kennst mich ja, einfach fragen  ):


```
public class PDFDocument implements Serializable{

	private static final long serialVersionUID = 1L;
	
	private int leftMargin = 5;
	private int rightMargin = 5;
	private int lineHeight = -1;
	
	private boolean compAddNotify = false;
	
	protected Document doc = null;
	protected Graphics2D g2d = null;
	protected PdfContentByte cb = null;
	protected PdfWriter writer = null;
	
	public static final float CENTER = 0;
	public static final float LEFT = 1;
	public static final float RIGHT = 2;
	
	public PDFDocument(File output) throws PDFException {
		this(output, PageSize.A4);
	}
	
	public PDFDocument(File output, Rectangle size) throws PDFException {
		
		doc = new Document(size);
		try {
			writer = PdfWriter.getInstance(doc, new FileOutputStream(output));
			doc.open();
			cb = writer.getDirectContent();
	    	g2d = cb.createGraphics(size.width(), size.height());
		}
		catch (DocumentException e) {
			PDFException excp = new PDFException("Can't create PDF Document");
			excp.setStackTrace(e.getStackTrace());
			throw excp;
		}
		catch (FileNotFoundException e) {
			PDFException excp = new PDFException("Can't write to File");
			excp.setStackTrace(e.getStackTrace());
			throw excp;
		}
	}
	
	public void drawImage(Image img, int x, int y) {
		g2d.drawImage(img, x, y, null);
	}
	
	public void drawImage(Image img, int y, float align) {
		drawImage(img, getHorizontalPosition(img.getWidth(null), align), y);
	}
	
	public void drawJavaComponent(Component comp, int x, int y) {
		
		initialComponent(comp);
		BufferedImage img = new BufferedImage(comp.getWidth(), comp.getHeight(), BufferedImage.TYPE_INT_ARGB);
		Graphics2D g = img.createGraphics();
		comp.paint(g);
		g.dispose();
		drawImage(img, x, y);
		if (compAddNotify) {
			comp.removeNotify();
			compAddNotify = false;
		}
	}
	
	public void drawJavaComponent(Component comp, int y, float align) {
		
		initialComponent(comp);
		drawJavaComponent(comp, getHorizontalPosition(comp.getWidth(), align), y);
	}
	
	public void drawSection(String text, int y, float align) {
		
		int space = (int)doc.getPageSize().width() - getLeftMargin() - getRightMargin();
		if (g2d.getFontMetrics().stringWidth(text) > space) {
			if (getLineHeight() < 0) {
				setLineHeight(g2d.getFontMetrics().getHeight() + 2);
			}
			int counter = 0;
			int lastCut = 0;
			int temp = 0;
			text += " ";
			while (true) {
				temp = counter;
				counter = text.indexOf(" ", counter + 1);
				if (counter == -1) {
					drawText(text.substring(lastCut, text.length() - 1), y, align);
					break;
				}
				if (g2d.getFontMetrics().stringWidth(text.substring(lastCut, counter)) > space) {
					counter = temp;
					drawText(text.substring(lastCut, counter), y, align);
					lastCut = counter + 1;
					y += getLineHeight();
				}
			}
		}
		else {
			drawText(text, y, align);
		}
	}
	
	public void drawText(String text, int x, int y) {
		g2d.drawString(text, x, y);
	}
	
	public void drawText(String text, int y, float align) {
		drawText(text, getHorizontalPosition(g2d.getFontMetrics().stringWidth(text), align), y);
	}
	
	public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) {
		g2d.drawArc(x, y, width, height, startAngle, arcAngle);
	}
	
	public int getHorizontalPosition(int elementSize, float align) {
		
		if (align == LEFT) {
			return getLeftMargin();
		}
		else if (align == RIGHT) {
			return (int)doc.getPageSize().width() - getRightMargin() - elementSize;
		}
		else {
			return ((int)doc.getPageSize().width() - getRightMargin()) / 2 - elementSize / 2 + getLeftMargin() ;
		}
	}
	
	public void newPage() {
		doc.newPage();
	}
	
	public void close() {
	//	g2d.dispose();
		doc.close();
	}
	
	private void initialComponent(final Component comp) {
		
		if (comp.getWidth() <= 0) {
			comp.setSize(comp.getPreferredSize());
			comp.addNotify();
			compAddNotify = true;
			comp.doLayout();
		}
	}
	
	public int getLeftMargin() {
		return leftMargin;
	}

	public void setLeftMargin(int leftMargin) {
		this.leftMargin = leftMargin;
	}

	public int getRightMargin() {
		return rightMargin;
	}

	public void setRightMargin(int rightMargin) {
		this.rightMargin = rightMargin;
	}

	public int getLineHeight() {
		return lineHeight;
	}

	public void setLineHeight(int lineHeight) {
		this.lineHeight = lineHeight;
	}

	public Graphics2D getGraphics2DObject() {
		return g2d;
	}
	
	public void setFont(Font font) {
		g2d.setFont(font);
	}
	
	public Font getFont() {
		return g2d.getFont();
	}
	
	public void setColor(Color color) {
		g2d.setColor(color);
	}
	
	public Color getColor() {
		return g2d.getColor();
	}
}
```


```
public class PDFException extends Exception {

	private static final long serialVersionUID = 1L;
	
	public PDFException(String message) {
		super (message);
	}
}
```


----------



## Leroy42 (27. Apr 2007)

Ich schaue mir g'rad deine Klasse an.

Ich bekomme verschiedene "ambigous"-Fehler. Z.b. bei Rectange, Image, ...

Könntest du mal deine Import-Anweisungen zu dieser Klasse posten, damit
ich nichts falsch mache?


----------



## SnooP (27. Apr 2007)

Also naja  ... ich hab mir das mal angeguckt - aber im Prinzip ist das ja auch nicht viel anders als auf Graphics zu malen und das dann zu drucken... vielleicht etwas komfortabler, aber naja 

irgendwie find ich die Gestaltungsmöglichkeiten bei externen Dokumenten mit Markup à la html eleganter bzw. auch flexibler... - aber naja - jedem das seine


----------



## The_S (27. Apr 2007)

@Leroy

hab die Original-Klasse nicht hier, aber importiert wird eigentlich immer die Klassen aus der itext lib

@SnooP

Graphics ist eine Möglichkeit, die ich zufällig hier benutze. Eine andere Möglichkeit ist so eine Art TableLayout mit itext Komponenten. Auch sehr schön. Zudem kann man mit der Lib auch PDF-Servlets kreieren


----------



## Guest (27. Apr 2007)

Die ganze Pinselei mit Graphics etc. könnt ihr euch sparen, wenn ihr JasperReports, BIRT oder vergleichbare 
Frameworks verwendet.


----------



## SnooP (28. Apr 2007)

was sind PDF-Servlets?


----------



## kleiner_held (28. Apr 2007)

SnooP hat gesagt.:
			
		

> was sind PDF-Servlets?



Servelts deren Ausgabe-Content-type "application/pdf" anstatt "text/html" ist.


----------



## The_S (28. Apr 2007)

kleiner_held hat gesagt.:
			
		

> SnooP hat gesagt.:
> 
> 
> 
> ...



richtig


----------



## SnooP (28. Apr 2007)

ahja okay  - jo... des geht mit fop genauso easy wie innerhalb "normaler" clients


----------



## Leroy42 (30. Apr 2007)

Weiß auch jemand, wo ich das iText - Tutorial herunterladen kann.

Oder gibt es da gar nix, weil der Herr Lowagie will, das man sich
das Buch kauft?   

(Recht hätte er ja, aber ....)


----------



## The_S (1. Mai 2007)

äh ...

http://itextdocs.lowagie.com/tutorial/

!?


----------



## java_net (2. Mai 2007)

SnooP hat gesagt.:
			
		

> Nimm FOP (von Apache xml-graphics) bzw. FO-XSL...
> 
> das Einstellen der dynamischen Informationen würde ich allerdings mit Velocity machen (nicht mit XSLT)... sprich ein Velocity-XML-FO Template erstellen mit entsprechenden Platzhaltern da wo es dynamisch wird und dann mit Hilfe von Java und nem Velocity-Context der die jeweiligen Daten enthält parsen...
> [/code]



kann man mit Hilfe von Velocity auch bei Rechnungen die Zwischensumme am Ende einer Seite automatisch erstellen? Ich versuche das gerade mit FOP und XSLT und da klappt das nicht.

Mit Latex und iText kann man das glaub ich auch nicht machen.

mfg
peter


----------



## SnooP (2. Mai 2007)

Woher hast du denn die Zahlen? Wenn das Werte aus entsprechenden Variablen sind, dann kann man per velocity in der iteration für die ausgabe einfach ne variable aufsummieren... also jo - das geht 

aber das würde auch mit allem anderen gehn, weil die daten ja meist wohl in java schon vorliegen oder? zumindest könnte man sie ja dort durchschleifen und dann wieder nach draußen ziehn...


----------



## Guest (2. Mai 2007)

SnooP hat gesagt.:
			
		

> Woher hast du denn die Zahlen?



Also die Daten kommen aus einer XML-Datei, aus der ich mit XSLT ein FO-Dokument generiere.

Im Report können nun aber auch mehrere dynamische Tabellen vorkommen, sodass die Durchzähl-Methode sehr schwierig ist, da man auch noch wissen muss wie weit es noch bis zum Seitenende ist. Deshalb wollte ich den Seitenumbruch FOP überlassen. FOP kann das aber nur sehr eingeschränkt. Am besten wäre so etwas wie ein Page-Break Event.


----------

