iText Kopf- und Fußzeilen fehler auf der letzten Seiten

raikkonentk

Mitglied
Hallo,

ich habe das Problem, dass ich mit iText ein mehrseitiges PDF-Dokument erstelle und auf der letzten Seite werden keine Kopf- und Fußzeilen weder mit onEndpage() noch mit onCloseDocument() eingefügt. Ich hab auch schon eine leere Seite ans Ende angefügt, trotzdem ist dann die letzte Seite mit Inhalt immernoch ohne Kopf- und Fußzeile.

Das PDF-Dokument soll letztendlich einen Trainingsplan beinhalten. Das ganze ist ein Prototyp und deswegen nicht voll ausprogrammiert. Meine Java-Kentnisse halten sich auch in Grenzen, deswegen kann man sicher viele Dinge noch verbessern.

Die PDF-Datei wird in einem Servlet erzeugt. Den Quellcode hab ich ein bisschen kommentiert.

Servlet verkürzt:
Code:
package pdf;

import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Chunk;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.Font;
import com.itextpdf.text.Image;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.*;
import com.itextpdf.text.pdf.PdfWriter;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 *
 * @author felix
 */
public class PdfServlet_test extends HttpServlet {
    /**
     * Handles the HTTP <code>GET</code> method.
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
        doPost(request, response);
    }

    /**
     * Handles the HTTP <code>POST</code> method.
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {

        // Dokument starten
        Document document = new Document();

        try{
            // PDF als Dateityp festlegen
            response.setContentType("application/pdf");

            // Dateinamen festlegen
            response.setHeader("Content-Disposition"," attachment; filename=\"trainingplan.pdf\"");

            PdfWriter.getInstance(document, response.getOutputStream());
            // Output im Browser
            PdfWriter writer = PdfWriter.getInstance(document, response.getOutputStream());

            // Helper für Kopf- und Fußzeilen
            FootWriter event = new FootWriter();
            writer.setPageEvent(event);

            // Box für Kopfzeile positionieren
            writer.setBoxSize("header", new Rectangle(20, 20, 212, 818));

            event.onOpenDocument(writer, document);

            // Dokument öffnen
            document.open();

            // Neue Seite erstellen
            document.newPage();

            // Details für Trainingsplan laden
            String[][] trainingplan = db.trainingplans.getTrainingplanById(request.getParameter("id"));

            // Alle Übungen eines Trainingsplan findn
            String[][] trainingplanexercises = db.trainingplanexercises.getTraingplanExercisesByTrainingplanId(trainingplan[0][0]);

            // Zelle initiieren
            PdfPCell cell;

            // Zähler für die Übungen
            int lineCount = 1;

            // für jede Übung eine neue Tabelle erstellen
            for(String[] trainingplanexercise: trainingplanexercises){
                // Tabelle mit 2 Spalten
                PdfPTable exerciseTable = new PdfPTable(2);

                // Tabellen nicht auseinanderziehen, 100% breit und 70/30 Breite
                exerciseTable.setKeepTogether(true);
                exerciseTable.setWidthPercentage(100);
                exerciseTable.setWidths(new int[]{7,3});

                // Tabellenkopf mit Namen der Übungen erstellen
                cell = new PdfPCell(new Phrase(new Chunk(lineCount + ". Übung: " + db.exercises.getName(trainingplanexercise[0]),new Font(Font.FontFamily.HELVETICA, 12, Font.BOLD))));
                cell.setColspan(2);
                cell.setBorder(0);
                cell.setBackgroundColor(BaseColor.LIGHT_GRAY);

                // Zelle zur Tabelle hinzufügen
                exerciseTable.addCell(cell);

                // Übungsinhalte laden
                String[] exercise = db.exercises.getExerciseDetailsByExerciseId(trainingplanexercise[0]);

                // Beispiel anstatt Datenbankinhalt für die linke Spalte
                Phrase phrase = new Phrase("Beispiel\nBeispiel\nBeispiel\nBeispiel\nBeispiel\nBeispiel\nBeispiel\nBeispiel\nBeispiel\nBeispiel\nBeispiel\nBeispiel\n");
                cell = new PdfPCell(phrase);
                cell.setBorder(0);
                exerciseTable.addCell(cell);

                // Passendes Bild zur Übung laden, im Prototyp gibt es nur 1 Bild pro Übung
                String[][] images =  db.pictures.getAllPicturesByExerciseId(exercise[1]);

                // Prüfen ob ein Bild gefunden wurde
                if(images.length > 0){
                    // für jedes Bild
                    for(String image[]: images){
                        // suboptimal
                        String relativeWebPath = "pict/exercise/" + image[3];
                        String absoluteDiskPath = getServletContext().getRealPath(relativeWebPath);
                        // Bildobjekt
                        Image imghead = Image.getInstance(absoluteDiskPath);
                        // Bild in eine Zelle
                        cell = new PdfPCell(imghead, true);
                        cell.setBorder(0);
                        cell.setHorizontalAlignment(Element.ALIGN_RIGHT);
                        // Zelle in Tabelle einfügen
                        exerciseTable.addCell(cell);
                    }
                } else {
                     // Ersatzzelle, falls kein Bild vorhanden ist
                     cell = new PdfPCell(new Phrase("Kein Bild vorhanden"));
                     cell.setBorder(0);
                     cell.setHorizontalAlignment(Element.ALIGN_RIGHT);
                     exerciseTable.addCell(cell);
                }

                // Übungszähler inkrementieren
                lineCount++;

                // Übungstabelle in das Dokument übernehmen
                document.add(exerciseTable);

                //event.onEndPage(writer, document);
            }

            // Versuch onEndPage explizit auf der letzten Seite aufzurufen
            // --> bringt nichts
            event.onEndPage(writer, document);

            // Dokument finalisieren
            document.close();
        } catch (DocumentException ex) {
            Logger.getLogger(PdfServlet.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}
Merkwürdig ist auch, dass wenn ich in der Tabelle für eine Übung kein Bild, sondern eine Textzelle einfüge, erscheint auf keiner Seite mehr eine Kopf- und Fußzeile.

Klasse für die Kopf- und Fußzeilen
Code:
package pdf;

import com.itextpdf.text.*;
import com.itextpdf.text.pdf.*;

public class FootWriter extends PdfPageEventHelper
{
    // Seitenzähler
    public Integer pageCount = 1;

    // Seitenzahl ausgeben
    public Integer getPageCount() {
        return pageCount;
    }

    // Methode für Kopf- und Fußzeilen
    @Override
    public void onEndPage(PdfWriter writer, Document document)
    {
            // Box für Kopfzeile laden
            Rectangle rect = writer.getBoxSize("header");
            
            // Text in Kopfzeile schreiben
            ColumnText.showTextAligned(writer.getDirectContent(),
                Element.ALIGN_RIGHT, new Phrase("Trainingsplan | example.com"),
                rect.getRight(), rect.getTop(), 0);

            Rectangle page = document.getPageSize();
            
            // Tabelle für Fußzeile anlegen
            PdfPTable bottom = new PdfPTable(3);

            // leere Zelle
            PdfPCell tmp = new PdfPCell(new Phrase(" "));
            tmp.setBorder(Rectangle.NO_BORDER);
            tmp.setBorderWidthTop(1);
            tmp.setHorizontalAlignment(Element.ALIGN_CENTER);

            bottom.addCell(tmp);

            // Seitenzahl einfügen
            tmp = new PdfPCell(new Phrase("Seite: " + pageCount));
            tmp.setBorder(Rectangle.NO_BORDER);
            tmp.setBorderWidthTop(1);
            tmp.setHorizontalAlignment(Element.ALIGN_CENTER);

            bottom.addCell(tmp);

            // Werbung
            tmp = new PdfPCell(new Phrase("example.com"));
            tmp.setBorder(Rectangle.NO_BORDER);
            tmp.setBorderWidthTop(1);
            tmp.setHorizontalAlignment(Element.ALIGN_RIGHT);

            bottom.addCell(tmp);

            bottom.setTotalWidth(525);

            // Tabelle schreiben
            bottom.writeSelectedRows(0, -1, document.leftMargin(), document.bottomMargin(),writer.getDirectContent());
            
            // Seitenzähler erhöhen
            pageCount++;
    }

    // Beim schließen des Dokumentes nochmals die Kopf- und Fußzeilen einfügen
    @Override
    public void onCloseDocument(PdfWriter writer, Document document) {
        onEndPage(writer, document);
    }
}

Die Buch-Tutorials von iText hab ich mich auch schon alle angesehen und finde den Fehler trotzdem nicht.

Vielen Dank für eure Hilfe :)

// UPDATE: iText-Version: 5.0.6.
 
Zuletzt bearbeitet:
nur eine idee von mir

die Methode onPageEnd() wird erst aufgerufen wenn die Seite beendet wird
und du beendest die Seite vielleicht nicht.

kannst du die Kopf und Fußzeile nicht vorher erstellen? also mit onPageStart()

Das sollte keinen Einfluss auf die Seite haben
 
nur eine idee von mir

die Methode onPageEnd() wird erst aufgerufen wenn die Seite beendet wird
und du beendest die Seite vielleicht nicht.
Die Idee kam mir auch schon, deswegen hatte ich mir gedacht, dass ich vor dem Schließen des Dokumentes mit onCloseDocument nochmals die onEndPage-Methode aufrufe.

kannst du die Kopf und Fußzeile nicht vorher erstellen? also mit onPageStart()

Das sollte keinen Einfluss auf die Seite haben
Das habe ich gerade mal kurz probiert, aber da macht er überhaupt nichts, aber ich werds heute im Laufe des Tages noch weiter testen.

Danke trotzdem schon mal.
 
Für Alle die es interessiert.

Ich habs mit der anderen Variante wie hier beschrieben hinbekommen.

Also anstatt die Kopf- und Fußzeilen bei den onEndPage/onStartPage einzufügen, wird das Dokument, nachdem der Inhalt geschrieben wurde, noch einmal komplett durchlaufen und dabei auf jede Seite die Informationen geschrieben.
 
Zurück