PDF Generierung mit iText

6 Kommentare

Rückblick: In unserem letzten Sprint Planning Meeting stand die Aufgabe einer PDF-Generierung an. Wir hatten eine grobe Layout-Vorlage vom Kunden bekommen und die Service-Klassen zur Bereitstellung der benötigten Klassen waren ebenfalls schon vorhanden. Dennoch schätzten wir das zugehörige Ticket erstaunlich hoch. Kein Entwickler im Team hatte Erfahrung mit iText, gleichzeitig hatte jedoch jeder gruselige iText-Geschichten von anderen Entwickler gehört. Ein Zitat schwebte uns im Ohr: „Wenn du mit iText arbeitest, liest du früher oder später die Spezifikation des PDF-Formats.“

Nach einiger Recherche im Internet und mehreren Interviews anderer Projekt-Teams von codecentric möchte ich heute einen aus meiner Sicht sehr einfachen, robusten und vor allem schnellen Weg vorstellen, PDFs mit iText zu generieren. Das oben genannte Ticket wurde mit diesem Ansatz in einem Viertel der Zeit umgesetzt und die Angst der Team-Mitglieder vor iText ist deutlich gesunken.

iText bietet viele Möglichkeiten, PDFs komplett über die API zu erzeugen; mit allen Linien, Bildern, festen Texten, Farben uvm. Dieser Ansatz wird jedoch schnell sehr komplex und erzeugt sehr langen und fehleranfälligen Quellcode. Einfacher ist hier die Erzeugung eines „Vordrucks“ mit definierten Inhaltsfeldern. Dieser Vordruck wird in Form eines PDFs mit Formularfeldern gespeichert. Dieses Template-PDF kann jederzeit, z.B. im Adobe Reader, betrachtet werden und könnte so z.B. auch schon vor der Befüllung mit Daten mit dem Kunden abgestimmt werden.

Teil 1 – PDF-Formular erzeugen

PDF-Formulare lassen sich sehr einfach mit OpenOffice erzeugen. In meinem Beispiel habe ich OpenOffice Draw verwendet, der Ansatz funktioniert aber gleicherweise mit OpenOffice Writer oder Calc. Im jeweiligen Programm einfach ein neues Dokument anlegen und anschließend nach Belieben mit Texten, Farben oder Linien gestalten. Sobald das Layout fertig ist, müssen die Inhaltsfelder definiert werden. Dabei ist es hilfreich, wenn die Symbolleisten für Formular-Entwurf und Formular-Steuerelemente ausgewählt sind (Ansicht -> Symbolleisten). Mit diesen neuen Icons kann man unter anderem den Formular-Entwurfsmodus aktivieren. Jetzt kann z.B. aus den Formular-Steuerelementen ein Textfeld ausgewählt und in das Dokument eingefügt werden. Über einen Rechtsklick lassen sich die Eigenschaften des Textfelds definieren. Folgende Einstellungen sind interessant:

  • Name : Dieser Name wird später im Java Code zum Befüllen des Feldes referenziert.
  • Rahmen : Default ist 3D-Look – Normalerweise wird wohl ohne Rahmen besser passen.
  • Hintergrundfarbe, Ausrichtung, Schrift : Hier kann das Layout bestimmt werden. Layout-Angaben im Java Code sind damit überflüssig.
  • Text-Typ: Normalerweise einzeilig, möchte man jedoch im Java Code Text mit Umbrüchen einfüllen muss hier mehrzeilig ausgewählt werden.

Sobald die Formular-Felder keinen Rahmen haben, kann man diese im Dokument nicht mehr einfach erkennen. Hier hilft der Formular-Navigator.

Nach dem Layout und der Erstellung der Formular-Felder kann das Dokument in eine PDF-Vorlage exportiert werden. Die geht mittels Datei –> Exportieren als PDF. Im folgenden Dialog muss PDF-Formular erzeugen markiert sein.

OpenOffice

Editing a formular in OpenOffice

OpenOffice - PDF Template

Generated PDF Template

Teil 2 – PDF-Formular mit iText befüllen

Zum Befüllen des PDF-Formulars nutze ich in meinem Beispiel iText in der Version 2.1.7, da in späteren Versionen eine verschärfte Lizenz verwendet wurde.
Zunächst wird das PDF-Formular eingelesen…

PdfReader pdfTemplate = new PdfReader(pdfTemplateFile);

… und ein Stamper mit einem OutputStream erzeugt.

ByteArrayOutputStream out = new ByteArrayOutputStream();
PdfStamper stamper = new PdfStamper(pdfTemplate, out);

Damit im erzeugten PDF keine Formularfelder mehr enthalten sind, muss dem Stamper dies über folgendem Aufruf mitgeteilt werden.

stamper.setFormFlattening(true);

Zum Abschluß können jetzt die Felder gefüllt werden…

stamper.getAcroFields().setField("name", "Daniel Reuter");
stamper.getAcroFields().setField("adress", "Merscheider Str.1 – 42699 Solingen");
stamper.getAcroFields().setField("dates", "2008\n2009\n2010\n");
stamper.getAcroFields().setField("titles", "JAX\nDevoxx\nJavaOne\n");

…und natürlich die Resourcen geschlossen werden.

stamper.close();
pdfTemplate.close();

Fazit

Aus meiner Sicht ein sehr einfacher und leicht verständlicher Ansatz. Sicherlich wird es Szenarien geben, in denen der beschriebene Weg nicht passt. Insbesondere bei dynamischen Layouts stößt man hier an die Grenzen. Dennoch würde ich versuchen, als Grundlage immer ein PDF Template zu verwenden.



PDF Result

Daniel Reuter

Daniel gehört seit April 2009 zum Team der codecentric. Seine Schwerpunkte liegen in der Entwicklung von Enterprise-Anwendungen im Versicherungsumfeld. Daniel ist Allroundcodehacker, Architekturgedankenpfleger, Sauberkeitsprogrammierenthusiast, Teamzusammenarbeitshelfer und Versicherungsfachdomänenfreund.

Share on FacebookGoogle+Share on LinkedInTweet about this on TwitterShare on RedditDigg thisShare on StumbleUpon

Kommentare

  • Andreas Ebbert-Karroum

    Danke für den Tip mit OpenOffice, funktioniert super!

  • Fabian Broszat

    27. August 2010 von Fabian Broszat

    Das mit den PDFs klappt wirklich gut… 🙂

  • iText Neuling

    28. Februar 2011 von iText Neuling

    Ich dachte nach dem Durchlesen, DIE Lösung gefunden zu haben. Allerdings habe ich nach vielen Stunden das Problem, dass NICHTS passiert.

    Ich habe sogar ausschließlich mit den geposteten Vorlagen und dem Code gearbeitet.

    Gibt es vielleicht irgendwelche „implizit klaren“ Anweisungen die hier nicht aufgeführt wurden – oder ist der Code so vollständig ?

    Wäre für Hinweise und Ideen äußerst dankbar!

  • Daniel Reuter

    Hallo „iText Neuling“,

    der Code befüllt den ByteArrayOutputStream. Die Daten befinden sich danach in diesem Stream. Über out.toByteArray() erhalten Sie das PDF als byte[]. Falls Sie die Daten in eine Datei schreiben möchten, so können Sie das z.B. hiermit machen.

  • eagel

    29. Juli 2012 von eagel

    Tolle Anleitung!

    Leider wird bei mir die Tabelle, wenn ich am ende einer Seite bin nicht umgebrochen. Wo könnte mein Fehler sein?

    • Daniel Reuter

      Wenn ich mich recht erinnere, funktionieren automatische Zeilenumbrüche nicht mit der skizzierten Lösung. Die Templates sind wirklich als Druckvorlage zu sehen. Soll z.B. eine Tabelle auf einer zweiten Seite fortgesetzt werden, bräuchte man eine zweite Vorlage mit einer leeren Tabellenstruktur….

Kommentieren

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.