iText Class Library
Transcription
iText Class Library
iText Class Library Fachhochschule Nordwestschweiz FHA University of Applied Sciences Aargau Autor: Filipe Luis [email protected] Studiengang: Fach: Informatik Seminar Enterprise Computing Reporting in Java Web Applications Prof. Dr. Dominik Gruntz 07. Mai 2004 Dozent Datum: 2 Abstract iText ist eine offene und frei erhältliche Java-Bibliothek zur Erzeugung von PDF Dokumenten. Mit dieser Bibliothek können PDFs auch aus Web-Applikationen programmatisch erzeugt werden. Dieser Seminarbericht soll einen Überblick über die Library geben und dadurch die Möglichkeiten von iText aufzeigen. Im Besonderen sollen dabei die Möglichkeiten der PDF-Erzeugung mittels XML Dokumenten und aus Servlets aufgezeigt werden. Filipe Luis Fachhochschule Aargau Inhaltsverzeichnis 3 Inhaltsverzeichnis 1. Was ist iText 4 2. Einführung: Einfache Dokumente 2.1. Das erste Dokument . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 5 3. Elemente eines Dokuments 3.1. Chunks . . . . . . . . . . 3.2. Phrase . . . . . . . . . . 3.3. Paragraphs . . . . . . . 3.4. Sprungmarken . . . . . . 3.5. Listen . . . . . . . . . . 3.6. Tabellen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 8 9 9 9 9 10 4. Positionierung & Writer 4.1. Automatische Anordnung 4.2. PdfWriter . . . . . . . . . 4.2.1. Interne Schichten . 4.2.2. Externe Schichten . 4.3. HtmlWriter . . . . . . . . 4.4. XmlWriter . . . . . . . . . 4.5. RtfWriter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 11 12 13 13 14 15 16 5. iText in Web-Applikationen 5.1. Servlet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 17 6. iText & XML 6.1. Tagmap für eigene XML Dokumente . . . . . . . . . . . . . . . . . . . . . . . . . 20 20 A. Source Code A.1. PdfCotnentByte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . A.2. Listen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . A.3. Servlets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 22 23 24 Filipe Luis Fachhochschule Aargau 4 1. Was ist iText iText ist eine Bibliothek zur dynamischen Erzeugung und Manipulation von Portable Document Format (PDF) Dateien. Das Projekt wird von Bruno Lowagie vorangetrieben und entwickelt. Die iText Bibliothek bietet dem Entwickler umfangreiche Möglichkeiten in Java PDF Dokumente zu erstellen. Seit kurzer Zeit wurde iText auch auf die C# Plattform portiert (einige Klassen stehen allerdings noch nicht zur Verfügung, weiterführende Informationen dazu unter [4]). Die iText Bibliothek umfasst im wesentlichen folgende Features: • Unterschiedliche Seitengrössen (Beispiele: A0 bis A10, Legal, Letter uvm. • Hoch- oder Querformat • Seitenabstände • Tabellen • Silbentrennung • Kopf- und Fusszeilen • Seitennummerierung • Barcodes • Schrifttypen • Dokument Verschlüsselung • Integration von Bilddateien (Formate: JPEG, GIF, PNG und WMF) • Geordnete und ungeordnete Listen • Dokumenttemplates • Erzeugung von PDF Dateien mittels XML Die gesamte Bibliothek ist unter [3] erhältlich und offen. Filipe Luis Fachhochschule Aargau 5 2. Einführung: Einfache Dokumente Ziel der Einführung ist es, die notwendigen Schritte zur Erzeugung einer PDF Datei aufzuzeigen und zu erläutern. 2.1. Das erste Dokument Die Erstellung eines PDF Dokuments mit der iText Bibliothek gliedert sich in fünf Schritte: 1. Erzeugung einer Document Instanz. 2. Erzeugung eines Writers, welcher als Obeserver des Document Objekts agiert und zugefügten Inhalt in die PDF Datei schreibt (siehe Kapitel 4.1 auf Seite 11). 3. Das Dokument muss, um Inhalt hinzufügen zu können explizit geöffnet werden. 4. Hinzufügen des Inhaltes. 5. Schliessen des Dokumentes (damit die OutputStreams geleert werden). Nachfolgend wurde der Programm-Code zur Erzeugung eines einfachen PDF Dokumentes aufgelistet. Im Code finden sich die oben beschriebenen Schritte wieder! Listing HelloWorld.java: import import import import java . i o . FileOutputStream ; j a v a . i o . IOException ; com . l o w a g i e . t e x t . ∗ ; com . l o w a g i e . t e x t . p d f . PdfWrite r ; public c l a s s HelloWorld { public s t a t i c void main ( S t r i n g [ ] a r g s ) { // − − 1. S c h r i t t : Document I n s t a n z Document doc = new Document ( ) ; try { // − − 2. S c h r i t t : Writer e r z e u g e n PdfWrite r . g e t I n s t a n c e ( doc , new F i l e O u t p u t S t r e a m ( ” HelloWorld . p d f ” ) ) ; // − − 3. S c h r i t t : Dokumentö f f n e n doc . open ( ) ; Filipe Luis Fachhochschule Aargau 2.1. Das erste Dokument 6 // − − 4. S c h r i t t : I n h a l t ü h i n z u f g e n Font f o n t = FontFactory . g e t F o n t ( FontFactory . HELVETICA BOLD, 3 4 ) ; Chunk c = new Chunk ( ” H e l l o World” , f o n t ) ; doc . add ( c ) ; // − − 5. S c h r i t t : Dokument s c h l i e s s e n doc . c l o s e ( ) ; System . out . p r i n t l n ( ” HelloWorld . p d f c r e a t e d ! ” ) ; } catch ( DocumentException e ) { e . printStackTrace ( ) ; } catch ( IOException e ) { e . printStackTrace ( ) ; } } } Die erzeugte HelloWorld.pdf Datei: Abbildung 2.1.: Output des ersten Beispiels. Seitengrösse. Das Objekt com.lowagie.text.Document besitzt mehrere Konstruktoren. Bei der Erzeugung einer Document Instanz wird ein Rectangle Objekt erwartet (Vorsicht: Ein com.lowagie.text.Rectangle und nicht ein java.awt.Rectangle Objekt), wird kein Parameter angegeben, so wird das statische Rectangle Objekt PageSize.A4 verwendet (default). Metadata. Über diverse Document Methoden können dem PDF Dokument Metadaten hinzugefügt werden. Folgende stehen zur Verfügung: public boolean addTitle(String title) public boolean addSubject(String subject) Filipe Luis Fachhochschule Aargau 2.1. Das erste Dokument 7 public boolean addKeywords(String keywords) public boolean addAuthor(String author) public boolean addCreator(String creator) Der Rückgabetyp boolean ist true wenn die entsprechende Operation erfolgreich ausgeführt werden konnte, sonst false. Verschlüsselung. Der PdfWriter kann mit der Methode public void setEncryption(byte[] userPassword, byte[] ownerPassword, int permissions, boolean strength128Bits) angewiesen werden, das Dokument zu verschlüsseln. Mit Ausnahme der Passwörter können alle Parameter mit den statischen Felder der Klasse PdfWriter angegeben werden. Unter anderem steht dem Entwickler das Feld PdfWriter.AllowPrinting zur Verfügung. Wird dieser Wert nicht angegeben, so kann der User ohne Angabe des korrekten Passworts, die PDF Datei nicht mehr drucken (zumindest nicht ohne gewisse Bemühungen). Einheiten. In der iText Bibliothek wird die typographische Einheit Punkt verwendet. 72 Punkte ergeben ein Inch oder ∼ 0.4cm. Eine A4 Seite hat somit 595 Punkte in der Breite und 842 Punkte in der Höhe. Filipe Luis Fachhochschule Aargau 8 3. Elemente eines Dokuments Um einen Eindruck zu vermitteln was in iText dargestellt werden kann, sollen einige elementare Bausteine erläutert werden. Diese Liste ist bei weitem nicht vollständig, sie soll lediglich eine Übersicht vermitteln. 3.1. Chunks Die ‘kleinste’ Einheit in iText ist das Chunk Objekt. Dieses Element besteht nur aus einem String mit einer bestimmten Schrift. Die weiteren Elemente Phrase und Paragraph bestehen aus lauter aneinander gehängten Chunk Objekten. Ein Beispiel für ein Chunk Objekt ist im Listing 2.1 auf Seite 5 zu sehen. Formatierung eines Chunks. Verschiedene Möglichkeiten stehen dem Entwickler zur Verfügung um einen Chunk zu formatieren. • Für ein Chunk Text der ‘Unterstrichen’ und ‘Kursiv’ gesetzt werden soll, genügt folgende Anweisung: Chunk chunk = new Chunk("Kursiv und unterschrichen", FontFactory.getFont(FontFactory.HELVETICA,12, Font.UNDERLINE | Font.ITALIC)); Weitere Möglichkeiten wie z.B. Durchstreichen des Texts sind vorhanden. • Mit der Methode setTextRise(float f) kann in Punkten definiert werden, ob der Text Hochgestellt oder Tiefgestellt gedruckt werden soll. Die Angabe erfolgt relativ zur Baseline, wobei ein positives Vorzeichen den Text nach oben verschiebt, ein negatives den Text nach unten versetzt. • Die Hintergrundfarbe kann mit der Methode setBackground(java.awt.Color color) gesetzt werden. Filipe Luis Fachhochschule Aargau 3.2. Phrase 9 3.2. Phrase Das Phrase Objekt stellt eine Sammlung von einem oder mehreren Chunk Objekten dar. Zusätzlich definiert das Phrase Objekt ein sog. leading (Zeilenabstand) und eine Main Font welche für alle beinhalteten Chunk Elemente gilt (es ist möglich einzelnen Elementen ein anderes Layout zuzuweisen). 3.3. Paragraphs Ein Absatz besteht aus einem oder mehreren Chunk und/oder Phrase Objekten. Ein Paragraph Objekt kann wie ein Phrase Objekt formatiert werden. Zusätzlich stehen jedoch noch die Möglichkeiten den Text einzurücken und auszurichten (links, rechts oder zentriert) zur Verfügung. 3.4. Sprungmarken iText bietet die Möglichkeit, ähnlich wie in HTML, Links zu definieren. Als Sprungziele können interne (innerhalb des Dokumentes) sowie auch externe Quellen referenziert werden (z.B. eine Website). Nachfolgend ist ein Anchor Objekt dargestellt, welches auf die Website der Fachhochschule Aargau verweist, und ein Anchor-Paar, welches eine Sprung- und eine Zielmarke darstellt. Das Konzept der Anchors entspricht demjenigen der Links in HTML. Der Entwickler definiert also ein String, im nachfolgenden Beispiel jeweils goto, welches beim Anklicken den Betrachter des Dokuments zur Zielmarke springen lässt. Die Zielmarke besteht ebenfalls aus einem String das für den Benutzer sichtbar ist (hier: destination) und einem Namen (NextPage) welches intern als Markierung des Sprungsziels dient, für den Betrachter jedoch unsichtbar ist. Anchor xref = new Anchor("goto"); xref.setReference("http://www.fh-aargau.ch/"); xref.setName("Website"); Anchor iref = new Anchor("goto", afont); iref.setReference("#NextPage"); //-- extern //-- intern Anchor zref = new Anchor("destination"); zref.setName("NextPage"); 3.5. Listen Mit dem List Objekt ist es möglich Listen zu erstellen. Der Kunstruktor erwartet ein boolean Flag, mit welchem angezeigt wird, ob die Liste geordnet oder ungeordnet ist. Verschachtelte Listen Filipe Luis Fachhochschule Aargau 3.6. Tabellen 10 sind ebenfalls möglich. Im Anhang A.2 auf Seite 23 ist ein Beispiel zu finden. 3.6. Tabellen Mit der Table Klasse können Tabellen erzeugt werden. Die Klasse stellt zwei Konstruktoren zur Verfügung: • Table(int spalten) • Table(int spalten, int zeilen) Mit folgenden Parametern kann das Aussehen der Tabelle formatiert werden: Cellspacing und Cellpadding: Mit der Methode setSpacing(float value) kann der Abstand vom Zelleninhalt und dem Zellenrand in Punkte angegeben werden. Um den Abstand der Zellen zum Tabellenrand (Cellpadding) zu definieren, wird die Methode setPadding(float value) aufgerufen. Alignment: Um den Inhalt in den Zellen auszurichten können folgende Methoden verwendet werden. cell.setHorizontalAlignment(Element.ALIGN_CENTER); cell.setVerticalAlignment(Element.ALIGN_MIDDLE); Color: Die Hintergrundfarbe wird mit der Methode setBackgroundColor(Color color) gesetzt. Um der Tabelle Inhalt zuzufügen, werden Instanzen der Cell Klasse verwendet. Die Cell Objekte können ein beliebiges Element aufnehmen. Nachdem der Inhalt einer Zelle bestimmt ist, kann die Zelle über die Methode addCell(...) der Tabelle eingefügt werden. Die Zellen werden von links nach rechts eingefügt. Beim Konstruktor kann die Zeilenanzahl weggelassen werden, da die Tabelle automatisch Zeilen einfügt wenn mehr benötigt werden sollten (dies gilt auch für den zweiten Konstruktor, obwohl die Zeilenanzahl angegeben werden kann). Informationen zum Umbruch der Tabellen bei einem Seitewechsel sind im Abschnitt 4.1 auf Seite 12 zu finden. Filipe Luis Fachhochschule Aargau 11 4. Positionierung & Writer Die iText Bibliothek definiert fünf Writer: DocWriter, PdfWriter, HtmlWriter, XmlWriter und RtfWriter. Die Writer werden wie bereits im ersten Beispiel 2.1 auf Seite 5 gesehen im zweiten Schritt erzeugt. Sie agieren als Listener und werden über die add() Methoden über Änderungen am Dokument notifiziert. Jedes Element muss also über die add(...) Methode der Document Klasse hinzugefügt werden, da die Writer ansonsten nicht über die Änderung notifiziert werden. Die Writer entscheiden dann, je nach eingefügtem Element, wie sie ihn darstellen. Die Writer sind dabei auch an die Grenzen des jeweiligen Datenformats gebunden. Nicht alles was in PDF Dokumenten darstellbar ist, kann beispielsweise auch in HTML umgesetzt werden (z.B. newPage() ). Der Writer ‘kennt’ also die Möglichkeiten des Formats und versucht diese entsprechend zu nutzen. Abbildung 4.1.: Die iText Writers. 4.1. Automatische Anordnung Die Writer sind auch für die Anordnung der Elemente zuständig, d.h. der Programmierer definiert nur die Struktur des Dokumentes (vergleichbar wie beim schreiben von HTML Seiten). Beim Entwickeln definiert der Programmierer ein Element nach dem anderen und fügt diesen dem Dokument mit der add(...) Methode hinzu. Die Writer werden von der Document Klasse über diese Änderung notifiziert. Der Writer nimmt diese Elemente entgegen und positioniert diese. Bei der Positionierung hängt die Anordnung u.a. vom Typ des Elementes ab: Chunk und Phrase Objekte werden beispielsweise nacheinander auf der gleichen Zeile von links nach rechts eingefügt Filipe Luis Fachhochschule Aargau 4.2. PdfWriter 12 (Die Zeilenumbrüche werden dabei von iText selbst gesteuert). Die Paragraph Objekte haben ein leading Parameter was heisst, dass die Absätze immer auf einer neuen Zeile beginnen. Der Entwickler kann mit gewissen Methoden auf die automatische Positionierung Einfluss nehmen (dies hängt allerdings auch vom Format ab, wie bereits erwähnt sind nicht alle Writer in der Lage alle Elemente korrekt darzustellen). Beispiele: • newPage() Methode der Document Klasse: Erwingt in jedem Falle einen Seitenumbruch. • Mit dem leading Parameter im Paragraph Konstruktor (Abstand zur vorangehende Zeile). • Mit der Methode setKeepTogether() der Paragraph Klasse, wird iText einen Absatz nicht durch einen Seitenumbruch trennen. In diesem Falle wird der ganze Absatz auf die nächste Seite verschoben. Seitenumbrüche & Tabellen. iText bestimmt die Seitenumbrüche automatisch. Dabei werden auch Tabellen problemlos umgebrochen. Es besteht sogar die Möglichkeit den Header der Tabelle auf jeder Seite zu wiederholen. Der Entwickler kann auf die Art wie die Tabelle umgebrochen wird mit folgenden zwei Methoden Einfluss nehmen: • setTableFitsPage(true): Die Tabelle wird, wenn möglich, auf eine Seite gesetzt. Dabei wird versucht, die Tabelle auf der aktuellen Seite unterzubringen, ansonsten wird mit newPage() die Tabelle auf die nächste Seite gesetzt. Falls diese Versuche scheitern, weil der Umfang zu gross für eine Seite ist, wird sie dennoch umgebrochen. • hasToFitPageCells(true): Die Tabelle wird so umgebrochen, dass ganze Zeilen umgebrochen werden. Ist diese Option nicht gesetzt, so kann es geschehen, dass ein Seitenumbruch innerhalb einer Zeile stattfindet. Daten Stream. Während dem Anordnen der Elemente erzeugen die Writers fortlaufend einen Daten Stream (darum sollte das Dokument auch immer explizit mit close() geschlossen werden, damit die Buffer geleert werden). Dies stellt vor allem bei der Erzeugung von dynamischen Inhalt (Inhaltsverzeichnisse, Referenzen usf.) eine Problematik dar und setzt der iText Bibliothek auch gewisse Grenzen. 4.2. PdfWriter Der PdfWriter unterscheidet sich in einer Eigenschaft von den anderen Writern: Der Entwickler besitzt die Möglichkeit, Elemente auch manuell an absolute Positionen zu setzten. Dazu wurde in der iText Library ein Modell mit vier hierarchischen Layers eingeführt. Die Hierarchie besteht aus vier PdfContentByte Objekten, welche je eine Schicht repräsentieren. Filipe Luis Fachhochschule Aargau 4.2. PdfWriter 13 Externer Layer oberhalb von Text und Grafik PdfContentByte cb = writer.getDirectContent() Interner Layer für Text Interner Layer für Grafik Externer Layer unterhalb von Text und Grafik PdfContentByte cb = writer.getDirectContentUnder() 4.2.1. Interne Schichten Zwei dieser vier Layers werden vom PdfWriter intern für die automatische Positionierung verwendet, auf diese hat der Programmierer keinen direkten Zugriff. Die Unterteilung der Layers in Text- und Grafik-Schichten dient u.a. der Darstellung von Tabellen: Die Ränder und Hintergrundfarben der Zellen werden im Grafik Layer gesetzt, der eigentliche Inhalt der Zellen wird im Text-Layer abgelegt. Alle Elemente, welche über die Methode add( Element element ) der Document Klasse dem Dokument hinzugefügt werden, werden über diese interne PdfContentByte Objekte gesetzt. 4.2.2. Externe Schichten Um auf die externen Schichten Zugriff zu erhalten, kann der Entwickler die Methoden getDirectContent() und getDirectContentUnder() der PdfWriter Klasse verwenden. Alle Schichten arbeiten völlig unabhängig voneinander, d.h. die Textfragmente können sich überlappen! Wird der Text in der obersten Schicht eingefügt, so überlappt er die restlichen Elemente, wird er in der untersten Schicht eingefügt so verschwindet er hinter den oberen Elementen. Absolute Positionierung von Text Nachfolgend wird ein Beispiel aufgezeigt wie man einen Text absolut positioniert. Beim Positionieren ist besonders der Ursprung des Koordinatensystems zu erwähnen, dieser befindet sich in der linken unteren Ecke. PdfContentByte cb = writer.getDirectContent(); BaseFont bf = BaseFont.createFont( BaseFont.HELVETICA, BaseFont.CP1252, BaseFont.NOT_EMBEDDED); cb.beginText(); cb.setFontAndSize(bf, 14); cb.setTextMatrix(0, 0); Filipe Luis Fachhochschule Aargau 4.3. HtmlWriter 14 cb.showText("Urspung Koodrinatensystem (0,0)."); cb.endText(); Bemerkungen. Zusammengefasst müssen folgende Schritte beachtet werden: 1. Instanz eins PdfContentByte Objektes erzeugen. 2. Erzeugen einer BaseFont. 3. Den Text-Bereich mit Aufruf der Methode beginText() eröffnen. 4. Die Schrift und die Grösse setzen. 5. Eine Text-Matrix an einer bestimmten Stelle öffnen. 6. Mit der Methode showText(...) den Text einfügen. 7. Den Text-Bereich durch Aufruf von endText() schliessen. Ein ausführliches Beispiel, in welchem u.a. auch Text unter bestehende Elemente gelegt wird ist im Anhang A.1 auf Seite 22 zu finden. Rotationen. Die iText Library bietet noch eine weitere Methode Text zu setzen an. Diese Methode setTextMatrix(float a, float b, float c, float d, float x, float y) erhält als Parameter die Koeffizienten einer Rotationsmatrix. Auch hierzu findet man im erwähnten Beispiel PdfContentByte im Anhang eine Anwendung. 4.3. HtmlWriter Der HtmlWriter arbeitet im wesentlichen gleich wie der PdfWriter. Bruno Lowagie empfiehlt den professionellen Einsatz dieses Packages nicht, da der HTML Code nicht sehr ‘schön’ ist aber vor allem noch nicht alle Features unterstützt werden. Für das Debugging von PDF Dokumenten hingegen, wird dieses Paket sehr empfohlen, da der HTML Quellcode wesentlich einfacher zu verstehen ist. Das Einbinden des HtmlWriters ist relativ einfach: Document document = new Document(); PdfWriter.getInstance(document, new FileOutputStream("beispiel.pdf")); HtmlWriter.getInstance(document, new FileOutputStream("beispiel.html")); Filipe Luis Fachhochschule Aargau 4.4. XmlWriter 15 Dadurch wird nun bei jedem add( Element element ) Aufruf gleich auch HTML Code erzeugt. Wenn man dem ersten Beispiel 2.1 auf Seite 5 noch einen HtmlWriter hinzufügen würde, erhält man folgenden HTML Code: <html> <head> <!-- Producer: iTextXML by lowagie.com --> <!-- CreationDate: Thu Apr 22 17:00:50 CEST 2004 --> </head> <body leftmargin="36.0" rightmargin="36.0" topmargin="36.0" bottommargin="36.0"> <span>Hello World</span> </body> </html> Weiterführende Informationen sind in der API Dokumentation unter [2, com.lowagie.text.html] zu finden. 4.4. XmlWriter Der XmlWriter wird im Kapitel 6 auf Seite 20 eingehender betrachtet und hier nur der Vollständigkeit halber erwähnt. Das Einbinden des XmlWriters erfolgt analog zur Einbindung des HtmlWriters. Zur Erzeugung des XML Dokuments wird die Document Type Declaration von iText (http://itext.sourceforge.net/itext.dtd) verwendet. Im wesentlichen stellt die DTD eine Abbildung der iText Elemente dar. So kann jedes Element wie z.B. ein Chunk Objekt in XML dargestellt werden. Wenn man dem ersten Beispiel 2.1 auf Seite 5 noch einen XmlWriter hinzufügen würde, erhält man folgenden XML Code: <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE ITEXT SYSTEM "http://itext.sourceforge.net/itext.dtd"> <itext creationdate="Thu Apr 22 17:16:57 CEST 2004" producer="iTextXML by lowagie.com"> <chunk font="Helvetica" size="34.0">Hello World</chunk> </itext> Filipe Luis Fachhochschule Aargau 4.5. RtfWriter 16 4.5. RtfWriter Mit der iText Library kommt ebenfalls noch ein RtfWriter mit. Das Einbinden verläuft gleich wie bei den vorangehenden Writer. Bemerkungen. Einige Features werden im RTF Format noch nicht unterstützt: z.B. Phrasen mit einem Leading, verschachtelte Tabellen und das Rotieren von Bildern. Filipe Luis Fachhochschule Aargau 17 5. iText in Web-Applikationen Um Dokumente aus einem Servlet heraus zu erstellen, gibt es zwei verschiedene Ansätze: 1. Das PDF Dokument wird auf dem Server erzeugt und gespeichert. Die Applikation verwendet ein java.io.FileOutputStream Objekt um das Dokument auf dem Filesystem abzulegen. In einem nächsten Schritt kann der Webuser das Dokument über das Hypertext Transfer Protocol (HTTP) anfordern (GET). 2. Das PDF Dokument wird auf dem Server erzeugt aber nicht gespeichert. Über die Angabe des Content Types (Type: Application, Subtype: pdf) kann dem Client direkt ein PDF Dokument über das HTTP Protokoll verschickt werden. Im Rahmen dieses Semniarberichtes wird nur auf die zweite Variante eingegangen, da sie gegenüber der ersten gleich mehrere Vorteile bietet: Für die erste Variante muss auf dem Server eine Datei gespeichert und dies macht eigentlich nur dann Sinn, wenn sie nach der Erzeugung für längere Zeit zur Verfügung stehen muss. Damit der Client auf das abgespeicherte Dokument zugreifen kann, muss ihm ein Link angegeben werden. Zusätzlich entsteht die Problematik des Wegräumens: das abgespeicherte Dokument muss wieder entfernt werden. All diese Probleme können mit der zweiten Variante vermieden werden. 5.1. Servlet Bevor irgend welche Daten an den Client zurückgeschickt werden, müssen die HTTP Response Headers gesetzt werden. In diesem Fall unterscheidet sich vor allem der Content-Type von den ‘herkömmlichen’ Servlets. Content-type Falls der Content-type nicht gesetzt wird, muss der Client selber herausfinden wie er das File handhaben will. Mit den Content-types kann das Format festgelegt werden. In diesem Fall wird der Content-type wie folgt gesetzt: response.setContentType("application/pdf");. Content-disposition Mit dieser Angabe kann dem Client der Filename des Dokuments mitgeteilt werden. Zusätzlich kann der Client angewiesen werden, das Dokument im Browser anzuzeigen Filipe Luis Fachhochschule Aargau 5.1. Servlet 18 oder aber eine externe Applikation zu verwenden. Hier: response.setHeader("Content-disposition", "inline; filename=response.pdf"); Content-length Die Grösse des Dokumentes muss in Bytes angegeben werden. Dieser Parameter muss stimmen, da der Browser sonst das Dokument nicht darstellen kann. Beispiel ITextServlet. Nachfolgend soll ein Beispiel eines Servlets besprochen werden. Den gesamten Code findet man im Anhang A.3 auf Seite 24. Dieses Beispiel greift auf eine PostgreSQL Datenbank zu. Die Verbindung zur Datenbank wird in der init(...) Methode aufgebaut, dieses Thema wird jedoch an dieser Stelle nicht ausführlicher besprochen (weitere Informationen dazu findet man auf den Webseiten [6] und [5]). In der doGet(...) Methode des Servlets wird ein ByteArrayOutputStream deklariert, dieser soll den Daten Stream, welcher vom PdfWriter erzeugt wird, entgegennehmen. Die Methode createPDFByteArrayStream() führt dann die entsprechenden Queries auf der Datenbank aus, um einige Daten für das PDF Dokument zu erhalten. Im weiteren wird in den bereits bekannten fünf Schritten ein PDF Dokument erzeugt. Dabei wird dieses mal nicht wie bisher in ein FileOutputStream geschrieben (es soll ja keine Datei erzeugt werden). Statt dessen lääst man den PdfWriter in ein ByteArrayOutputStream schreiben. Document doc = new Document(); ByteArrayOutputStream pdf_data; PdfWriter.getInstance(doc, pdf_data); Mit den Daten aus der Datenbank wird eine Tabelle zeilenweise erzeugt. Nachdem der Inhalt eingefügt wurde, wird mit doc.close() sichergestellt, dass der PdfWriter seine Daten vollständig im ByteArrayOutputStream abgelegt hat. Dieser wird nun an die doGet(...) Methode zurück gegeben. Die doGet(...) Methode definiert die HTTP Response Headers. Der erzeugte PDF Daten Stream wird danach wie folgt an den Client versendet. ServletOutputStream sos; sos = response.getOutputStream(); bstream.writeTo(sos); sos.flush(); Filipe Luis Fachhochschule Aargau 5.1. Servlet 19 Der Client erhält somit das PDF Dokument und wie in den HTTP Headers angegeben, wird das Dokument inline geöffnet: Abbildung 5.1.: Ein durch ein Servlet erzeugtes PDF Dokument. Weiterführende Informationen zur dynamischen Erstellung von PDFs aus Web Applikationen sind auf der Website [1] zu finden. Filipe Luis Fachhochschule Aargau 20 6. iText & XML Die iText Library liefert im Paket com.lowagie.text.xml diverse Klassen für die Erzeugung von PDF und RTF Dokumenten mit. Die Idee dabei ist recht einfach: Die Java Objekte wie zum Beispiel Paragraph, List oder Chunk sollen in XML abgebildet werden. Diese Abbildung der Elemente ist in der DTD http://itext.sourceforge.net/itext.dtd zu finden. Aus jedem XML Dokument welches diese itext.dtd referenziert, ist somit direkt ein PDF Dokument erzeugbar. 6.1. Tagmap für eigene XML Dokumente In der Regel möchte ein Entwickler jedoch ein spezifisches XML Dokument verwenden, welches eine eigene DTD referenziert. In diesem Fall muss der Programmierer eine Tagmap schreiben, welche die eigenen DTD Elemente in die itext DTD Elemente abbildet. Die Tagmap muss dem Parser angegeben werden (tagmap.xml). PdfWriter.getInstance(doc, new FileOutputStream("XML_doc.pdf")); SAXParser parser = SAXParserFactory.newInstance().newSAXParser(); parser.parse("XML_Document.xml", new SAXmyHandler(doc, new TagMap("tagmap.xml"))); Für ein Zeitungsartikel wurde folgende DTD geschrieben (itext_newspaper.dtd): < !ELEMENT NEWSPAPER ( ARTICLE+)> < !ELEMENT ARTICLE (AUTHOR, HEADLINE, BYLINE , BODY, NOTES)> < !ELEMENT HEADLINE (#PCDATA)> < !ELEMENT BYLINE (#PCDATA)> < !ELEMENT BODY (#PCDATA)> < !ELEMENT NOTES ( LINES ) ∗> < !ELEMENT LINES (#PCDATA)> < !ELEMENT AUTHOR (#PCDATA)> < !ATTLIST ARTICLE EDITOR CDATA #IMPLIED DATE CDATA #IMPLIED EDITION CDATA #IMPLIED > Ein XML Dokument (XML_Document.xml) welches diese DTD referenziert: ı̈¿ <?xml version=” 1 . 0 ” e n c o d i n g=”UTF−8” ?> < !DOCTYPE NEWSPAPER SYSTEM ” i t e x t n e w s p a p e r . dtd ”> <NEWSPAPER> <ARTICLE> Filipe Luis Fachhochschule Aargau 6.1. Tagmap für eigene XML Dokumente 21 <AUTHOR> F i l i p e L u i s</AUTHOR> <HEADLINE> S c h l a g z e i l e</HEADLINE> <BYLINE> U n t e r t i t e l</BYLINE> <BODY>H i e r f o l g t d e r A r t i k e l . . . </BODY> <NOTES> <LINES>N o t i z 1</LINES> <LINES>N o t i z 2</LINES> </NOTES> </ARTICLE> </NEWSPAPER> Und die Faltung der itext_newspaper.dtd auf die itext Elemente in der Tagmap: <tagmap> <t a g name=” i t e x t ” a l i a s=”NEWSPAPER” /> <t a g name=” n e w l i n e ” a l i a s=”ARTICLE” /> <t a g name=” p a r a g r a p h ” a l i a s=”AUTHOR” c o n t e n t=” Ã 41 K r z e l A u t o r : ”> <a t t r i b u t e name=” s i z e ” v a l u e=” 12 ” /> <a t t r i b u t e name=” a l i g n ” v a l u e=” L e f t ” /> </ t a g> <t a g name=” p a r a g r a p h ” a l i a s=”HEADLINE”> <a t t r i b u t e name=” l e a d i n g ” v a l u e=” 36 ” /> <a t t r i b u t e name=” s i z e ” v a l u e=” 18 ” /> <a t t r i b u t e name=” a l i g n ” v a l u e=” Ce n te r ” /> </ t a g> <t a g name=” p a r a g r a p h ” a l i a s=”BYLINE”> <a t t r i b u t e name=” l e a d i n g ” v a l u e=” 20 ” /> <a t t r i b u t e name=” s i z e ” v a l u e=” 14 ” /> <a t t r i b u t e name=” a l i g n ” v a l u e=” Ce n te r ” /> <a t t r i b u t e name=” s t y l e ” v a l u e=” i t a l i c ” /> </ t a g> <t a g name=” p a r a g r a p h ” a l i a s=”BODY”> <a t t r i b u t e name=” l e a d i n g ” v a l u e=” 18 ” /> <a t t r i b u t e name=” s i z e ” v a l u e=” 12 ” /> <a t t r i b u t e name=” a l i g n ” v a l u e=” L e f t ” /> </ t a g> <t a g name=” l i s t ” a l i a s=”NOTES”> <a t t r i b u t e name=” numbered ” v a l u e=” f a l s e ” /> <a t t r i b u t e name=” s y m b o l i n d e n t ” v a l u e=” 0 ” /> <a t t r i b u t e name=” l i s t s y m b o l ” v a l u e=” o ” /> <a t t r i b u t e name=” s y m b o l i n d e n t ” v a l u e=” 15 ” /> </ t a g> <t a g name=” l i s t i t e m ” a l i a s=”LINES”> <a t t r i b u t e name=” s i z e ” v a l u e=” 11 ” /> <a t t r i b u t e name=” a l i g n ” v a l u e=” L e f t ” /> <a t t r i b u t e name=” s t y l e ” v a l u e=” i t a l i c ” /> </ t a g> </ tagmap> Filipe Luis Fachhochschule Aargau 22 A. Source Code A.1. PdfCotnentByte Listing PdfContentByteDemo.java: import j a v a . i o . F i l e O u t p u t S t r e a m ; import j a v a . i o . IOException ; import import import import com . com . com . com . lowagie lowagie lowagie lowagie . . . . text text text text .∗; . p d f . BaseFont ; . p d f . PdfContentByte ; . p d f . PdfWrite r ; /∗ ∗ F i l i p e Luis , IA01 ∗ ∗ F a c h h o c h s c h u l e Aargau , 1 6 . 0 4 . 2 0 0 4 ∗ ∗/ public c l a s s ContentByteDemo { public s t a t i c void main ( S t r i n g [ ] a r g s ) { // − − 1. S c h r i t t : Document I n s t a n z Document doc = new Document ( ) ; try { // − − 2. S c h r i t t : Writer e r z e u g e n PdfWrite r w r i t e r = PdfWrite r . g e t I n s t a n c e ( doc , new F i l e O u t p u t S t r e a m ( ” ContentByteDemo . p d f ” ) ) ; // − − 3. S c h r i t t : Dokumentö f f n e n HeaderFooter header = new H e a d e r F o o t e r (new Phrase ( ” B e i s p i e l ContentByteDemo ” ) , f a l s e ) ; doc . s e t H e a d e r ( h e a d e r ) ; doc . open ( ) ; // − − 4. S c h r i t t : I n h a l t ü h i n z u f g e n PdfContentByte cb = w r i t e r . g e t D i r e c t C o n t e n t ( ) ; PdfContentByte cbu = w r i t e r . g e t D i r e c t C o n t e n t U n d e r ( ) ; BaseFont b f = BaseFont . c r e a t e F o n t ( BaseFont . HELVETICA, BaseFont . CP1252 , BaseFont .NOT EMBEDDED) ; cb . b e g i n T e x t ( ) ; cb . s e t F o n t A n d S i z e ( bf , 1 4 ) ; cb . s e t T e x t M a t r i x ( 0 , 0 ) ; Filipe Luis Fachhochschule Aargau A.2. Listen 23 cb . showText ( ” Urspung K o o d r i n a t e n s y s t e m ( 0 , 0 ) . ” ) ; cb . s e t T e x t M a t r i x ( 1 0 0 , 7 0 0 ) ; cb . showText ( ” Text a t p o s i t i o n 1 0 0 , 7 0 0 . ” ) ; f i n a l f l o a t p i 4 = ( f l o a t ) ( Math . s q r t ( 2 ) / 2 . 0 ) ; cb . s e t T e x t M a t r i x ( 2 , 0 , 0 , 2 , 2 0 0 , 5 0 0 ) ; cb . showText ( ” H e l l o Audience ! ” ) ; cb . s e t T e x t M a t r i x ( 1 . 5 f , p i 4 , − p i 4 , 1 . 5 f , 2 0 0 , 5 0 0 ) ; cb . showText ( ” H e l l o Audience ! ” ) ; cb . s e t T e x t M a t r i x ( 0 , 1 , − 1 , 0 , 2 0 0 , 5 0 0 ) ; cb . showText ( ” H e l l o Audience ! ” ) ; cb . s e t T e x t M a t r i x ( 1 0 0 , 4 5 0 ) ; cb . showText ( ” T e s s i n , Oktober 2003 ” ) ; //−− B i l d s o l l u n m i t t e l b a r nach Text e r s c h e i n e n cb . getYTLM ( ) . . . Image image = Image . g e t I n s t a n c e ( ” a b s o l u t e . j p g ” ) ; image . s c a l e P e r c e n t ( 2 0 ) ; f l o a t x = cb . getXTLM ( ) ; f l o a t y = cb . getYTLM ( ) − ( 6 + image . p l a i n H e i g h t ( ) ) ; System . out . p r i n t l n ( ” Absolute P o s i t i o n of Picture i s : ( ” + x + ” , ” + y + ” ) ” ) ; image . s e t A b s o l u t e P o s i t i o n ( x , y ) ; doc . add ( image ) ; cbu . s e t F o n t A n d S i z e ( bf , 1 6 ) ; cbu . s e t T e x t M a t r i x ( 1 0 , 4 0 0 ) ; cbu . showText ( ” H i n t e r dem B i l d ( T e s s i n , Oktober 2 0 0 3 ) ” ) ; cb . endText ( ) ; Font p f o n t = new Font ( Font . HELVETICA, 1 4 , Font . ITALIC ) ; Paragraph p = new Paragraph (new Phrase ( ” Durch i n t e r n e r Layer g e s e t z t ” , p f o n t ) ) ; p . setLeading ( 1 2 ) ; doc . add ( p ) ; // − − 5. S c h r i t t : Dokument s c h l i e s s e n doc . c l o s e ( ) ; System . out . p r i n t l n ( ” ContentByteDemo . p d f c r e a t e d ! ” ) ; } catch ( DocumentException e ) { e . printStackTrace ( ) ; } catch ( IOException e ) { e . printStackTrace ( ) ; } } } A.2. Listen Listing ListDemo.java: import j a v a . i o . F i l e O u t p u t S t r e a m ; import j a v a . i o . IOException ; import com . l o w a g i e . t e x t . ∗ ; import com . l o w a g i e . t e x t . p d f . PdfWrite r ; Filipe Luis Fachhochschule Aargau A.3. Servlets 24 /∗ ∗ F i l i p e Luis , IA01 ∗ ∗ F a c h h o c h s c h u l e Aargau , 1 6 . 0 4 . 2 0 0 4 ∗ ∗/ public c l a s s ListDemo { public s t a t i c void main ( S t r i n g [ ] a r g s ) { // − − 1. S c h r i t t : Document I n s t a n z Document doc = new Document ( ) ; try { // − − 2. S c h r i t t : Writer e r z e u g e n PdfWrite r . g e t I n s t a n c e ( doc , new F i l e O u t p u t S t r e a m ( ” ListDemo . p d f ” ) ) ; // − − 3. S c h r i t t : Dokumentö f f n e n doc . open ( ) ; // − − 4. S c h r i t t : I n h a l t ü h i n z u f g e n L i s t l i s t = new L i s t ( f a l s e , 1 5 ) ; l i s t . setListSymbol ( ”o” ) ; l i s t . add (new L i s t I t e m ( ” E r s t e s Element ” ) ) ; L i s t s u b l i s t = new L i s t ( true , 2 0 ) ; s u b l i s t . add (new L i s t I t e m ( ” E r s t e s Subelement ” ) ) ; s u b l i s t . add ( new L i s t I t e m ( ” Z w e i t e s Subelement : B e s o n d e r s ” + ” l a n g damit w i r sehen , wie d i e Z e i l e i n L i s t e n umgebrochen wird . ” ) ) ; s u b l i s t . add (new L i s t I t e m ( ” D r i t t e s Subelement ” ) ) ; l i s t . add ( s u b l i s t ) ; l i s t . add (new L i s t I t e m ( ” Z w e i t e s und l e t z t e s Element ” ) ) ; doc . add ( l i s t ) ; // − − 5. S c h r i t t : Dokument s c h l i e s s e n doc . c l o s e ( ) ; System . out . p r i n t l n ( ” ListDemo . p d f c r e a t e d ! ” ) ; } catch ( DocumentException e ) { e . printStackTrace ( ) ; } catch ( IOException e ) { e . printStackTrace ( ) ; } } } A.3. Servlets Listing ITextServlet.java: import j a v a . awt . C o l o r ; import j a v a . i o . ∗ ; import j a v a . s q l . ∗ ; import j a v a x . s e r v l e t . ∗ ; import j a v a x . s e r v l e t . h t t p . ∗ ; Filipe Luis Fachhochschule Aargau A.3. Servlets import import import import import import import import import com . com . com . com . com . com . com . com . com . 25 lowagie lowagie lowagie lowagie lowagie lowagie lowagie lowagie lowagie . . . . . . . . . text text text text text text text text text . Document ; . DocumentException ; . Font ; . FontFactory ; . Paragraph ; . Phrase ; . pdf . PdfPCell ; . p d f . PdfPTable ; . p d f . PdfWrite r ; public c l a s s I T e x t S e r v l e t extends H t t p S e r v l e t { private C o n n e c t i o n dbcon ; // Connection f o r s c o p e o f ShowBedrock //−− I n i t Methode : Aufbau e i n e r D a t e n b a n k v e r b i n d u n g public void i n i t ( S e r v l e t C o n f i g c o n f i g ) throws S e r v l e t E x c e p t i o n { //−− Login angaben String loginUser = ” postgres ” ; String loginPasswd = ” i t e x t ” ; String loginUrl = ” j d b c : p o s t g r e s q l : / / d a 8 2 6 0 i . c s . fh−a ar ga u . ch : 5 4 3 2 / i t e x t ” ; //−− Laden d e s PostgreSQL T r e i b e r s try { C l a s s . forName ( ” o r g . p o s t g r e s q l . D r i v e r ” ) ; dbcon = DriverManager . g e t C o n n e c t i o n ( l o g i n U r l , l o g i n U s e r , l o g i n P a s s w d ) ; } catch ( ClassNotFoundException ex ) { System . out . p r i n t l n ( ” ClassNotFoundEx ” ) ; System . e r r . p r i n t l n ( ” ClassNotFoundException : ” + ex . g e t M e s s a g e ( ) ) ; throw new S e r v l e t E x c e p t i o n ( ” C l a s s not found E r r o r ” ) ; } catch ( SQLException ex ) { System . out . p r i n t l n ( ”SQLEx” ) ; System . e r r . p r i n t l n ( ” SQLException : ” + ex . g e t M e s s a g e ( ) ) ; } } //−− I m p l e m e n t a t i o n d e s GET R e q u e s t s public void doGet ( H t t p S e r v l e t R e q u e s t r e q u e s t , H t t p S e r v l e t R e s p o n s e r e s p o n s e ) throws IOException , S e r v l e t E x c e p t i o n { ByteArrayOutputStream bstream = createPDFByteArrayStream ( ) ; r e s p o n s e . setContentType ( ” a p p l i c a t i o n / p d f ” ) ; //−− Response Mime Typ r e s p o n s e . s e t H e a d e r ( ” Content−d i s p o s i t i o n ” , ” i n l i n e ; s t u d e n t s . p d f ” ) ; r e s p o n s e . s e t C o n t e n t L e n g t h ( bstream . s i z e ( ) ) ; ServletOutputStream sos ; s o s = r e s p o n s e . getOutputStream ( ) ; bstream . w ri te To ( s o s ) ; sos . flush ( ) ; } private ByteArrayOutputStream createPDFByteArrayStream ( ) { ByteArrayOutputStream bstream = n u l l ; try { //−− S t a t e m e n t v o r b e r e i t n e Statement s t a t e m e n t = dbcon . c r e a t e S t a t e m e n t ( ) ; S t r i n g query = ”SELECT ∗ ” ; query += ”FROM employee ” ; Filipe Luis Fachhochschule Aargau A.3. Servlets 26 //−− Query ü a u s f h r e n R e s u l t S e t r s = s t a t e m e n t . e x e c u t e Q u e r y ( query ) ; //−− PDF Dokument e r z e u g e n Document doc = new Document ( ) ; doc . addAuthor ( ” F i l i p e L u i s ” ) ; bstream = new ByteArrayOutputStream ( ) ; PdfWrite r w r i t e r = P d f W r i t e r . g e t I n s t a n c e ( doc , bstream ) ; doc . open ( ) ; Phrase p h r a s e = new Phrase ( ” Studenten ” , FontFactory . g e t F o n t ( FontFactory . HELVETICA, 32 , Font .BOLD | Font . ITALIC ) ) ; PdfPTable t a b l e = new PdfPTable ( 3 ) ; P d f P C e l l c e l l = new P d f P C e l l ( p h r a s e ) ; c e l l . setColspan ( 3 ) ; table . addCell ( c e l l ) ; i n t count = 0 ; S t r i n g [ ] s = { ”name” , ” dept ” , ” j o b t i t l e ” } ; Font f o n t = new Font ( Font . HELVETICA, 1 8 , Font . BOLDITALIC ) ; //−− DB ä E i n t r g e i n T a b e l l e ü e i n f g e n while ( r s . n e x t ( ) ) { f o r ( i n t i = 0 ; i < s . l e n g t h ; i ++) { String entry = rs . getString ( s [ i ] ) ; p h r a s e = new Phrase ( e n t r y , f o n t ) ; c e l l = new P d f P C e l l ( p h r a s e ) ; i f ( count % 2 == 0) c e l l . s e t B a c k g r o u n d C o l o r (new C o l o r ( 0 , 1 5 5 , 2 5 5 ) ) ; table . addCell ( c e l l ) ; } count++; } doc . add ( t a b l e ) ; doc . add ( new Paragraph ( 1 8 , ” C r e a t e d on : ” + new j a v a . u t i l . Date ( ) , f o n t ) ) ; doc . c l o s e ( ) ; } catch ( DocumentException e ) { System . e r r . p r i n t l n ( e . g e t M e s s a g e ( ) ) ; } catch ( E x c e p t i o n ex ) { System . e r r . p r i n t l n ( ex . g e t M e s s a g e ( ) ) ; } return bstream ; } } Filipe Luis Fachhochschule Aargau Literaturverzeichnis [1] Dynamically Crating PDFs in a Web Application. Article on http://www.onjava.com/, Zuletzt geprüft im Mai 2004. [2] iText Tutorial. http://itext.sourceforge.net/docs/, Zuletzt geprüft im Mai 2004. [3] iText Website. http://www.lowagie.com/iText/download.html, Zuletzt geprüft im Mai 2004. [4] iTextSharp. http://itextsharp.sourceforge.net, Zuletzt geprüft im Mai 2004. [5] Java Servlets, JSP, Jakarta-Tomcat, Database, Apache and Linux. http://www.yolinux.com/TUTORIALS/, Zuletzt geprüft im Mai 2004. Article on [6] The PostgreSQL Database and Linux. Article on http://www.yolinux.com/TUTORIALS/, Zuletzt geprüft im Mai 2004. 27