Wie lässt sich eine Tabelle als *.pdf ausgeben?

Die iText-Bibliotheken stellen umfangreiche Funktionen zur Bearbeitung von PDF-Dateien zur Verfügung. Zur Verwendung müssen sie als externe Bibliothek geladen werden. Siehe hierzu: Bibliotheken laden.

Das Beispiel enthält neben der main-Methode den Konstruktor und die Methode writePDF() , die für das Drucken der Tabelle in die PDF-Datei verantwortlich zeichnet. Im Konstruktor wird das Hauptfenster erzeugt, auf das eine einfache JTable und ein JButton zum Anstoßen der Druckfunktionalität geladen werden.
Die auszugebende Tabelle stellt zu Demonstrationszwecken nur ein einfaches Beispiel dar und wird wie üblich durch ein Model mit Inhalt versehen. Das DefaultTableModel wird hierbei durch ein eindimensionales String-Array für die Spaltentitel und ein zweidimensionales für den eigentlichen Inhalt gefüllt. Nach Erzeugen der Umrandung wird die Tabelle erst auf ein Scrollpane und mit diesem ins Zentrum des Frames geladen.
Ein Button im unteren Teil des Frames ruft in einer anonymen ActionListener -Klasse die Methode writePDF() auf, die für die Funktionalität der PDF-Ausgabe verantwortlich ist. Ihr werden drei Parameter übergeben: der Dateiname der Ausgabedatei, das Tabellen-Model und das Array der Spaltennamen.

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.text.DateFormat;
import java.util.GregorianCalendar;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;

import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Font;
import com.itextpdf.text.Font.FontFamily;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfWriter;

public class PDFTable extends JFrame {

    public PDFTable() {
        final String[] colNames = new String[] { "eins", "zwei", "drei" };
        String[][] data = new String[][] {
                new String[] { "1.1", "1.2", "1.3" },
                new String[] { "2.1", "2.2", "2.3" },
                new String[] { "3.1", "3.2", "3.3" } };
        final DefaultTableModel model = new DefaultTableModel(data, colNames);
        JTable table = new JTable(model);
        table.setShowGrid(true);
        table.setGridColor(Color.LIGHT_GRAY);
        this.add(new JScrollPane(table), BorderLayout.CENTER);

        JButton butt = new JButton("PDF drucken");
        butt.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                writePDF("test.pdf", model, colNames);
            }
        });
        this.add(butt, BorderLayout.SOUTH);

        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setSize(300, 300);
        this.setLocationRelativeTo(null);
        this.setTitle("PDFTable");
        this.setVisible(true);
    }

    public void writePDF(final String fileName, final DefaultTableModel model,
            final String[] colNames) {
        if (model == null) {
            System.err.println("Couldn't save table as *.pdf: model is null");
            return;
        }
        Thread t = new Thread() {
            public void run() {
                PdfPTable table = new PdfPTable(model.getColumnCount());
                table.setWidthPercentage(100);
                table.getDefaultCell().enableBorderSide(PdfPCell.BOX);
                // Titel
                DateFormat df = DateFormat.getDateTimeInstance(
                        DateFormat.SHORT, DateFormat.SHORT);
                Paragraph head = new Paragraph("Tabledemo - "
                        + df.format(new GregorianCalendar().getTime()),
                        new Font(FontFamily.HELVETICA, 14, Font.BOLD));
                head.setSpacingAfter(15f);

                // Spaltentitel
                for (int i = 0; i < colNames.length; i++) {
                    PdfPCell cell = new PdfPCell(new Phrase(colNames[i], new Font(
                            FontFamily.HELVETICA, 10)));
                    cell.setBackgroundColor(BaseColor.LIGHT_GRAY);
                    table.addCell(cell);
                }
                // Tabelleninhalt
                for (int clmCnt = model.getColumnCount(), rowCnt = model
                        .getRowCount(), i = 0; i < rowCnt; i++) {
                    for (int j = 0; j < clmCnt; j++) {
                        String value = model.getValueAt(i, j).toString();
                        table.addCell(new Phrase(value, new Font(
                                FontFamily.HELVETICA, 10)));
                    }
                }

                Document document = new Document(PageSize.A4.rotate(), 20, 15,
                        15, 15);
                try {
                    PdfWriter.getInstance(document, new FileOutputStream(
                            fileName));
                    document.open();
                    document.add(head);
                    document.add(table);
                    document.close();
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                } catch (DocumentException e) {
                    e.printStackTrace();
                }
            }
        };
        t.start();
    }

    public static void main(String[] args) {
        new PDFTable();
    }
}

Die Ausgabe des PDF wird in einen eigenen Thread ausgelagert, um bei langen Ausgabeprozessen den Programmfluss nicht zu blockieren. Er wird noch in der Methode selbst gestartet. Die run-Methode des Threads enhält die gesamte Funktionalität und beginnt mit der Erzeugung eines PdfPTable -Objektes, dem Objekt, das für die Darstellung der Tabelle in der PDF-Datei zuständig ist. Dem Konstruktor werden zwei Parameter übergeben: die Spaltenzahl und die Gesamtbreite der Tabelle, bezogen auf die Dokumentbreite.
Die Methode PdfPTable.getDefaultCell() liefert ein Objekt der 'Standard-Tabellenzelle', die in die Tabelle gesetzt wird, wenn dieser kein spezifisches PdfPCell -Objekt übergeben wird. Sie wird über die statische Variable BOX mit einem Rand versehen.

In der Folge wird die Seitenüberschrift gesetzt. Sie erfolgt im Rahmen eines Paragraph -Objektes, das etwa einem Abschnitt entspricht. Ihm wird ein String übergeben, der hier u.a. ein auf der Klasse GregorianCalendar beruhendes Datum enhält. SetSpacingAfter() erzeugt einen Abstand in Höhe des übergebenen float -Wertes nach dem zugehörigen Abschnitt.

Die beiden folgenden Quelltext-Absätze zeigen das Hinzufügen der Spaltentitel und des Tabelleninhaltes zum Tabellen-Objekt. Zentrale Methode ist hierbei PdfPTable.addCell() . Sie existiert in mehreren überladenen Varianten, von denen zwei hier demonstriert werden: Bei den Spaltentiteln wird ein PdfPCell -Objekt übergeben, das zunächst gebildet werden muss und hierbei als Parameter ein Phrase -Objekt erhält, das wiederum neben dem Inhalt als String noch den Schrifttyp genannt bekommt. Die Hintergrundfarbe kann zusätzlich durch setBackgroundColor() definiert werden.
Im zweiten Fall, der Erzeugung des Tabelleninhaltes, wird der Methode addCell direkt das Phrase -Objekt übergeben. Mit diesem Schritt ist die Bildung des Tabellen-Objektes abgeschlossen.

Den Abschluss bilden die Erstellung eines Document -Objektes und dessen Export. Ersteres erfolgt hier im Format DIN A4, das durch PageSize.A4.rotate() als Querformat spezifiziert wird. Die anderen Parameter des Konstruktors geben die Abstände des Dokument-Inhaltes vom Papierrand in Pixeln in der Reihenfolge links, rechts, oben, unten an. Der Export erfolgt durch den Aufruf eines PdfWriter -Singletons, dem das Dokument und ein FileOutputStream mit dem Ziel-Dateinamen übergeben wird.
Ist das Objekt gebildet, wird das Dokument geöffnet, die Textteile werden eingefügt und anschließend wird es wieder geschlossen. Der Export-Vorgang muss in einen try-catch-Block eingeschlossen werden, um Probleme beim Export und bei der Erzeugung des Dokumentes abzufangen.