Sebastian Hempel bio photo

Sebastian Hempel

Software Crafter, Clean-Code-Developer, JEE Professional, Puppet-Master, OpenSource Fanboy, Alfisti

Email Twitter Google+ XING Instagram Github

Das Erstellen von PDF Dateien aus Informationen die in einem Software-System vorliegen ist nichts ungewöhnliches. In der Geschäftswelt haben sich PDF Dokumente als Standard für den Austausch von Berichten, Artikeln oder Spezifikationen etabliert. Der Vorteil von PDF gegenüber anderen Formaten ist, dass Dokumente nachträglich nicht oder nur mit großen technischen Aufwand verändert werden können. Auch stehen für so gut wie jede Plattform entsprechende Programme zum Lesen der Dokumente zur Verfügung. Etwas außergewöhnlicher ist da schon das nachträgliche Ergänzen einer bestehenden PDF Datei um Informationen, die in verschiedenen Tabellen eines Software-Systems vorliegen.

In einem kleinen Projekt hatte ich die Aufgabe in ein vorhandenes Formular bereits erfasste Informationen aus verschiedenen Tabellen einer Datenbank einzutragen. Die Alternative wäre gewesen, dass gesamte Dokument neu zu erstellen und dabei die abgefragten Daten einzutragen. Warum aber das Dokument noch einmal neu erstellen, wenn das leere Formular bereits im PDF Format vorliegt?

Der Trick mit den Ebenen

Zur Bearbeitung / Erstellung der PDF Datei kommt die OpenSource Bibliothek iText zum Einsatz. Diese Bibliothek wird in der Java-Welt sehr gerne verwendet.

Die API von iText stellt zwei Ebenen für den Zugriff auf die “PDF Struktur” zur Verfügung. Einen “High-Level” der einem viel Arbeit beim Erstellen von Elementen im PDF Dokument abnimmt. Man hat dadurch nicht mehr die Möglichkeiten die Elemente zu beeinflussen (z.B. die absolute Position), dafür geht die Erstellung von Absätzen und Tabellen schnell von der Hand. Der “Low-Level” gibt einen mehr Einfluss auf die Position und Größe der Elemente. Dafür muss man aber auch mehr Informationen zur Verfügung stellen und die Code-Zeilen werden mehr.

Neben der Erstellung von komplett neuen PDF Dateien besteht mit iText die Möglichkeit eine bereits vorhandene PDF-Datei zu öffnen und zu erweitern. Beim Erweitern kann jedoch nicht auf die (komfortablere) “High-Level” API zurückgegriffen werden. Auch können weitere Elemente nicht direkt in eine bestehende Seite eingefügt werden. Der Trick besteht darin die neuen Elemente auf eine Ebene “oberhalb” der bereits existierenden Elemente einzufügen. In iText geschieht das über den OverContent.

Als Ergebnis erscheinen die neu eingefügten Elemente über den bereits vorhandenen Elementen. Wenn die neuen Elemente keine bereits vorhandenen überdecken, ändert sich am Aussehen für den Benutzer nichts. Auch können die neu hinzugefügten Elemente genauso wie die ursprünglichen Elemente behandelt (z.B. markiert) werden.

Neue Elemente einstempeln

In der Praxis sieht das Vorgehen beim Ergänzen einer bestehenden PDF Datei wie folgt aus.

Die vorhandene PDF Datei wird mit einem PdfReader geöffnet. Weiterhin wird ein OutputStream (z.B. FileOutputStream) als Ziel für das erweiterte PDF Dokument benötigt.

PdfReader reader = new PdfReader(fileName);
OutputStream outStream = new FileOutputStream(outputFileName);

Über den PdfReader kann nur lesend auf das PDF Dokument zugegriffen. Wir benötigten eine Instanz des PdfStamper um die Struktur erweitern zu können. Der PdfStamper schreibt die Änderungen nicht in die vorhandene Datei sondern schreibt das Ergebnis aus ursprünglicher Datei plus Erweiterungen in den oben angelegten OutputStream.

PdfStamper stamper = new PdfStamper(reader, outStream);

Der Stamper selbst bietet keine Methoden zur direkten Erstellung von neuen PDF Elementen zur Verfügung. Die Methoden des PdfStamper beschränken sich auf die Änderung der Meta-Informationen des Dokuments. Die Klasse bietet uns aber eine Methode für den Zugriff auf den OverContent.

PdfContentByte overContent = stamper.getOverContent();

Mit dem Objekt vom Typ PdfContentByte haben wir und direkten Zugriff auf die Ebene oberhalb der bereits vorhandenen Elemente. Das Hinzufügen von neuen Elementen ist aber so nur mit der “Low-Level” API möglich. Die hierzu notwendigen Methoden stellt die Klasse PdfContentByte selbst zur Verfügung.

Ein Text kann z.B. mit den folgenden Anweisungen in den OverContent eingefügt werden.

BaseFont font = BaseFont.createFont(BaseFont.HELVETICA, BaseFont.CP1250, false);
overContent.setFontAndSize(font, 12);
overContent.beginText();
overContent.setFontAndSize(
overContent.endText();

Weitere Informationen zu den Methoden zum Einfügen von weiteren Elementen sind der API Dokumentation zur PdfContentByte zu entnehmen. Die Positionierung der weiteren Elemente ist etwas mühselig, da Sie über das interne Koordinaten-System einer PDF Datei erfolgen muss. Durch entsprechende Hilfsmethoden kann man sich aber hier viel Arbeit ersparen.

Ich hoffe dem einen oder anderen mit dieser kurzen Beschreibung helfen zu können. Ich selbst konnte Informationen zu Ergänzen von PDF Dokumenten mit iText nur schwer finden.