de.extra.xtt.util.pdf.PdfCreatorImpl.java Source code

Java tutorial

Introduction

Here is the source code for de.extra.xtt.util.pdf.PdfCreatorImpl.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package de.extra.xtt.util.pdf;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Queue;

import org.apache.log4j.Logger;

import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Chapter;
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.FontFactory;
import com.itextpdf.text.Image;
import com.itextpdf.text.List;
import com.itextpdf.text.ListItem;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.Section;
import com.itextpdf.text.pdf.ColumnText;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfPageEventHelper;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfWriter;
import com.sun.xml.xsom.XSAnnotation;
import com.sun.xml.xsom.XSAttGroupDecl;
import com.sun.xml.xsom.XSAttributeDecl;
import com.sun.xml.xsom.XSAttributeUse;
import com.sun.xml.xsom.XSComplexType;
import com.sun.xml.xsom.XSDeclaration;
import com.sun.xml.xsom.XSElementDecl;
import com.sun.xml.xsom.XSModelGroup;
import com.sun.xml.xsom.XSModelGroup.Compositor;
import com.sun.xml.xsom.XSParticle;
import com.sun.xml.xsom.XSSchema;
import com.sun.xml.xsom.XSSchemaSet;
import com.sun.xml.xsom.XSType;
import com.sun.xml.xsom.XSWildcard;
import com.sun.xml.xsom.impl.ElementDecl;

import de.extra.xtt.util.schema.MySchemaWriter;
import de.extra.xtt.util.tools.Configurator;

/**
 * Implementierung fr die Erzeugung einer PDF-Dokumentation
 * 
 * @author Beier
 */
public class PdfCreatorImpl implements PdfCreator {

    private static Logger logger = Logger.getLogger(PdfCreatorImpl.class);

    private final String fontNameStandard = FontFactory.HELVETICA;

    private final int fontSizeChapter = 18;
    private final int fontSizeElement = 14;
    private final int fontSizeText = 11;
    private final int fontSizeFooter = 9;
    private final int fontSizeTextHalf = 5;

    // Aufzhlungszeichen fr die erste und zweite Ebene
    private final String bulletMain = "\u2022";
    private final String bulletSub = "-";

    private final Configurator configurator;
    private Document docPdf;
    private int chapterCounter;
    private String dateiname;
    private String bezVerfahren;

    private java.util.List<ContPdfEntry> listEntries;
    private int currPageNumber;

    /**
     * Konstruktor mit der Initialisierung des Configurator-Objekts
     * 
     * @param configurator
     *            Objekt zum Zugriff auf Properties und Einstellungen
     */
    public PdfCreatorImpl(Configurator configurator) {
        this.configurator = configurator;
    }

    /**
     * {@inheritdoc}
     */
    @Override
    public void erzeugePdfDoku(String filePath, XSSchemaSet xmlSchemaSet, String bezVerfahren)
            throws PdfCreatorException {
        try {
            this.dateiname = filePath;
            this.bezVerfahren = bezVerfahren;
            this.listEntries = new java.util.LinkedList<ContPdfEntry>();

            // Erzeuge PDF-Doku ohne Inhaltsverzeichnis
            erzeugePdfDatei(xmlSchemaSet, false);

            // Erzeuge temp. Inhaltsverzeichnis
            int anzPagesInhalt = erzeugeInhaltsverzeichnis(null, true);
            // Seitenzahlen aktualisieren
            for (ContPdfEntry currEntry : listEntries) {
                currEntry.setPageNumber(currEntry.getPageNumber() + anzPagesInhalt);
            }

            // Dokument inkl. Inhaltsverzeichnis neu schreiben
            erzeugePdfDatei(xmlSchemaSet, true);

            if (logger.isInfoEnabled()) {
                logger.info("PDF-Dokumentation '" + filePath + "' erfolgreich gespeichert.");
            }
        } catch (Exception e) {
            throw new PdfCreatorException("Fehler beim Erzeugen der PDF-Doku.", e);
        }
    }

    /**
     * Erzeugt die PDF-Doku fr das bergebene SchemaSet und optional mit
     * Inhaltsverzeichnis
     * 
     * @param xmlSchemaSet
     *            SchemSet mit allen Elementen und Typen
     * @param writeInhaltsverzeichnis
     *            Gibt an, ob ein Inhaltsverzeichnis erstellt werden soll
     * @throws DocumentException
     * @throws MalformedURLException
     * @throws IOException
     */
    private void erzeugePdfDatei(XSSchemaSet xmlSchemaSet, boolean writeInhaltsverzeichnis)
            throws DocumentException, MalformedURLException, IOException {
        // PDF-Writer und PDF-Dokument initalisieren
        docPdf = initPdfWriterAndDocument(dateiname, true);

        // Titelseite
        erzeugeTitelSeite();

        // Inhaltsverzeichnis
        if (writeInhaltsverzeichnis) {
            erzeugeInhaltsverzeichnis(docPdf, false);
        }

        // Queues fr Schemata anlegen
        Map<String, Queue<XSElementDecl>> schemaQueues = new HashMap<String, Queue<XSElementDecl>>();

        // Erstes Element in allen Schemata suchen und Queue-Objekte anlegen
        String strRootElement = configurator
                .getPropertySystem(Configurator.PropBezeichnungSystem.SCHEMA_ROOT_ELEMENT);
        for (XSSchema currSchema : xmlSchemaSet.getSchemas()) {
            // Neue Queue fr aktuelles Schema anlegen
            String nsPrefix = getNsPref(currSchema.getTargetNamespace());
            schemaQueues.put(nsPrefix, new LinkedList<XSElementDecl>());

            // Prfen, ob erstes Element in diesem Schema enthalten ist
            XSElementDecl currFirstElement = currSchema.getElementDecl(strRootElement);
            if (currFirstElement != null) {
                // Element als erstes Element dieser Queue hinzufgen
                schemaQueues.get(nsPrefix).add(currFirstElement);
            }
        }

        // Queues der Reihe nach abarbeiten
        for (String currPrefix : configurator.getSchemaPrefixSorted()) {
            behandleSchemaQueue(currPrefix, schemaQueues, xmlSchemaSet);
        }

        // Dokument schlieen
        docPdf.close();

    }

    /**
     * Erzeugt das Inhaltsverzeichnis aus den bereits vorhandenen Elementen in
     * der Liste <code>listEntries</code>.
     * 
     * @param docPdf
     *            Zieldokument, falls Inhaltsverzeichnis nicht temporr erzeugt
     *            wird
     * @param temp
     *            Gibt an, ob das Inhaltsverzeichnis temporr in einer neuen
     *            Datei/Dokument erzeugt werden soll
     * @return Anzahl der Seiten
     * @throws DocumentException
     * @throws IOException
     */
    private int erzeugeInhaltsverzeichnis(Document docPdf, boolean temp) throws DocumentException, IOException {

        int anzPages = 0;
        Document docInhalt = docPdf;
        String filePathTempInhaltString = "";

        if (temp) {
            // temp. Dateinamen bestimmen
            File fileDokuFile = new File(dateiname);
            filePathTempInhaltString = fileDokuFile.getParent() + "/tmp_inhalt.pdf";
            // Neues Dokument erzeugen
            docInhalt = initPdfWriterAndDocument(filePathTempInhaltString, false);
        }

        // berschrift
        Chapter currChapter = new Chapter(getParagraphChapter("Inhaltsverzeichnis"), 0);
        // 0, damit keine Nummerierung
        currChapter.setNumberDepth(0);
        docInhalt.add(currChapter);
        // eine Zeile Abstand
        docInhalt.add(getEmptyLineTextHalf());

        for (ContPdfEntry currEntry : listEntries) {

            // Eintrag erzeugen inkl. Abstand
            String strEintrag = currEntry.getBezeichnung() + "  ";
            Chunk chunkBezeichnung;
            Chunk chunkSeitenzahlChunk;
            if (currEntry.getParentEntry() == null) {
                // 1. Ebene => fett, Abstand davor einfgen
                docInhalt.add(getEmptyLineTextHalf());
                chunkBezeichnung = getChunkTextBold(strEintrag);
                chunkSeitenzahlChunk = getChunkTextBold("" + currEntry.getPageNumber());
            } else {
                // 2. Ebene
                chunkBezeichnung = getChunkText(strEintrag);
                chunkSeitenzahlChunk = getChunkText("" + currEntry.getPageNumber());
            }
            // Referenz setzen
            chunkBezeichnung.setLocalGoto(currEntry.getDestination());
            chunkSeitenzahlChunk.setLocalGoto(currEntry.getDestination());
            // Abstandzeichen generieren, Breite auffllen
            float widthAbstand = docInhalt.getPageSize().getWidth() * 0.81f;
            ;
            while (chunkBezeichnung.getWidthPoint() <= widthAbstand) {
                chunkBezeichnung.append(".");
            }

            // Tabelle erzeugen und formatieren
            PdfPTable currTable = new PdfPTable(2);
            currTable.setWidthPercentage(100f);
            currTable.setWidths(new int[] { 96, 4 });

            // Inhalte einfgen
            // Zelle Bezeichnung
            PdfPCell currCellBezeichnung = new PdfPCell(new Phrase(chunkBezeichnung));
            currCellBezeichnung.setBorder(0);
            currCellBezeichnung.setHorizontalAlignment(Element.ALIGN_JUSTIFIED_ALL);

            // Zelle Seitennummer
            PdfPCell currCellPageNumberCell = new PdfPCell(new Phrase(chunkSeitenzahlChunk));
            currCellPageNumberCell.setBorder(0);
            currCellPageNumberCell.setHorizontalAlignment(Element.ALIGN_RIGHT);

            // Zellen zur Tabelle hinzufgen
            currTable.addCell(currCellBezeichnung);
            currTable.addCell(currCellPageNumberCell);

            docInhalt.add(currTable);
        }

        if (temp) {
            // Dokument schlieen
            docInhalt.close();

            // Anzahl der Seitenzahlen bestimmen
            PdfReader reader = new PdfReader(filePathTempInhaltString);
            anzPages = reader.getNumberOfPages();
            reader.close();

            // temp. Datei lschen
            File currFileInhaltFile = new File(filePathTempInhaltString);
            currFileInhaltFile.delete();
        }
        return anzPages;
    }

    /**
     * Alle Elemente, die sich fr das angegebene Schema in der entsprechenden
     * Queue befinden, werden behandelt.
     * 
     * @param currSchemaPrefix
     *            Prfix des zu behandelnden Schemas
     * @param schemaQueues
     *            Map enthlt Queues fr jedes Schema (Key ist der
     *            Namepace-Prfix)
     * @param xmlSchemaSet
     *            SchemaSet, in dem alle Typen und Elemente definiert sind
     * @throws DocumentException
     */
    private void behandleSchemaQueue(String currSchemaPrefix, Map<String, Queue<XSElementDecl>> schemaQueues,
            XSSchemaSet xmlSchemaSet) throws DocumentException {

        // Liste mit allen bereits behandelten Elementen
        java.util.List<XSElementDecl> listElementsInserted = new LinkedList<XSElementDecl>();

        // Aktuelle Queue
        Queue<XSElementDecl> currQueue = schemaQueues.get(currSchemaPrefix);

        if (currQueue != null) {

            // zur Sicherheit nochmal alle Elemente dieses Schema in Queue
            // einfgen
            // damit ist sichergestellt, dass auch nicht direkt verknpfte
            // Elemente (any => Plugins) behandelt werden
            XSSchema currSchema = xmlSchemaSet.getSchema(configurator.getPropertyNamespace(currSchemaPrefix));
            for (Entry<String, XSElementDecl> currElement : currSchema.getElementDecls().entrySet()) {
                currQueue.add(currElement.getValue());
            }

            if (currQueue.peek() != null) {
                // Kapitel inkl. Sprungmarke erstellen
                String targetNsUrl = currQueue.peek().getTargetNamespace();
                String chapterTitle = getNsPref(targetNsUrl) + ":" + targetNsUrl;
                chapterCounter++;
                Chunk chunkChapter = getChunkChapter(chapterTitle);
                chunkChapter.setLocalDestination(chapterTitle);
                Chapter currChapter = new Chapter(new Paragraph(chunkChapter), chapterCounter);

                // Kapitel dem Dokument hinzufgen
                docPdf.add(currChapter);

                // Eintrag fr Inhaltsverzeichnis (Chapter)
                ContPdfEntry currEntry = new ContPdfEntry(null, currChapter.getTitle().getContent(), chapterTitle,
                        currPageNumber);
                listEntries.add(currEntry);

                while (currQueue.peek() != null) {
                    // Aktuelles Element aus Queue holen
                    XSElementDecl currElement = currQueue.poll();
                    if (!listElementsInserted.contains(currElement)) {
                        behandleElement(currElement, currChapter, currEntry, schemaQueues);
                        listElementsInserted.add(currElement);
                    }
                }
            }
        }
    }

    /**
     * Das Element wird mit Datentyp, seinen Attributen und Kindelementen
     * geschrieben.
     * 
     * @param currElement
     *            Zu behandelndes Element
     * @param currChapter
     *            Kapitel, zu dem das Element hinzugefgt wird
     * @param chapterEntry
     *            Eintrag des Kapitels fr das Inhaltsverzeichnis
     * @param schemaQueues
     *            Queues mit allen Elementen fr die einzelnen Schemas
     */
    private void behandleElement(XSElementDecl currElement, Chapter currChapter, ContPdfEntry chapterEntry,
            Map<String, Queue<XSElementDecl>> schemaQueues) throws DocumentException {
        // Fr jedes Element eine Section; Titel ist der Elementname

        // eine Zeile Abstand
        docPdf.add(getEmptyLineText());

        // Referenz des Elements
        String currReferenzString = getReferenceForElement(currElement);

        // Titel des Elements inkl. Sprungmarke
        Chunk chunkElem = getChunkElement(currElement.getName());
        chunkElem.setLocalDestination(currReferenzString);

        Paragraph currElemPara = new Paragraph(getChunkElement(""));
        currElemPara.add(chunkElem);
        Section currSection = currChapter.addSection(currElemPara);

        // Eintrag fr Inhaltsverzeichnis (Element)
        listEntries.add(new ContPdfEntry(chapterEntry, currSection.getTitle().getContent(), currReferenzString,
                currPageNumber));

        // Sektion dem Dokument (vorlufig) hinzufgen (wichtig fr das
        // Aktualisieren der Seitenzahl)
        currSection.setComplete(false);
        docPdf.add(currSection);

        // Anmerkung zum Element
        XSAnnotation ann = currElement.getAnnotation();
        if (ann != null) {
            // Abstand
            currSection.add(getEmptyLineTextHalf());

            String beschreibung = ann.getAnnotation().toString();
            currSection.add(getParagraphTextItalic(beschreibung));
        }

        // Abstand
        currSection.add(getEmptyLineTextHalf());

        // Datentyp
        Paragraph paraTyp = new Paragraph();
        XSType currType = currElement.getType();
        paraTyp.add(getPhraseTextBold("Datentyp: "));
        String strTyp = getNameWithPrefix(currType);
        if (currType.isLocal()) {
            strTyp = "<lokal definiert>";
        }
        paraTyp.add(getPhraseText(strTyp + "\n"));

        // Basistyp
        if ((currType.getDerivationMethod() == XSType.RESTRICTION)
                || (currType.getDerivationMethod() == XSType.EXTENSION)) {
            paraTyp.add(getPhraseTextBold("basiert auf: "));
            paraTyp.add(getPhraseText(getNameWithPrefix(currType.getBaseType()) + "\n"));
        }
        currSection.add(paraTyp);

        // Attribute (falls vorhanden)
        behandleAttribute(currType, currSection);

        // Kind-Elemente (falls vorhanden)
        if (currType.isComplexType()) {
            // Gruppentyp (sequence oder choice)
            String strCompositor = getCompositorStr(currType.asComplexType());
            if (strCompositor.length() > 0) {
                // Abstand
                currSection.add(getEmptyLineTextHalf());

                currSection.add(getParagraphTextBold(strCompositor + "-Elemente:"));
                behandleKindElemente(currElement, currSection, schemaQueues);
            }
        }

        // Sektion dem Dokument (endgltig) hinzufgen
        currSection.setComplete(true);
        docPdf.add(currSection);
    }

    /**
     * Die Kindelemente (Sequence oder Choice) des bergebenen Elements werden
     * behandelt
     * 
     * @param currElementMain
     *            Element, dessen Kindelemente gesucht und behandelt werden
     * @param currSection
     *            Sektion des PDF-Dokuments
     * @param schemaQueues
     *            Queues mit allen Elementen fr die einzelnen Schemas
     */
    private void behandleKindElemente(XSElementDecl currElementMain, Section currSection,
            Map<String, Queue<XSElementDecl>> schemaQueues) {

        // Kind-Elemente bestimmen
        XSParticle[] childs = null;
        XSType currType = currElementMain.getType();
        if (currType.isComplexType()) {
            XSParticle currParticle = null;
            if (currType.asComplexType().getExplicitContent() != null) {
                currParticle = currType.asComplexType().getExplicitContent().asParticle();
            } else if (currType.asComplexType().getContentType() != null) {
                currParticle = currType.asComplexType().getContentType().asParticle();
            }
            if (currParticle != null) {
                XSModelGroup modelGroup = currParticle.getTerm().asModelGroup();
                childs = modelGroup.getChildren();
            }
        }

        if ((childs != null) && (childs.length > 0)) {
            // Fr jedes Kind wird ein Listeneintrag erstellt
            List currList = erzeugeListeFuerKindElemente(childs, schemaQueues, bulletMain);
            currSection.add(currList);
        }
    }

    /**
     * Fr die angegebenen Kindlemente wird ein Listeneintrag fr das PDF
     * erzeugt.
     * 
     * @param childs
     *            Kindelemente
     * @param schemaQueues
     *            Queues mit allen Elementen fr die einzelnen Schemas
     * @param strBullet
     *            Zeichen, das als Aufzhlunsgzeichen fr die Liste verwendet
     *            wird
     * @return PDF-Liste mit den Kindelementen
     */
    private List erzeugeListeFuerKindElemente(XSParticle[] childs, Map<String, Queue<XSElementDecl>> schemaQueues,
            String strBullet) {

        List currList = new List(false, 12);
        currList.setListSymbol(strBullet);

        for (XSParticle currChild : childs) {

            if (currChild.getTerm() instanceof ElementDecl) {
                // Elemente von sequence bzw. choice abarbeiten
                XSElementDecl currElement = currChild.getTerm().asElementDecl();

                Phrase currPhrase = new Phrase();

                // Name, inkl. Referenz auf das Element
                currPhrase.add(getChunkTextBoldWithReference(getNameWithPrefix(currElement),
                        getReferenceForElement(currElement)));

                // Auftreten (direkt hinter dem Namen)
                currPhrase.add(getChunkText(" (" + getMinMaxStr(currChild) + ")\n"));

                // Beschreibung
                XSAnnotation currAnn = currChild.getAnnotation();
                if ((currAnn == null) && currElement.isLocal() && (currElement.getAnnotation() != null)) {
                    currAnn = currElement.getAnnotation();
                }
                if (currAnn != null) {
                    currPhrase.add(getChunkTextItalic(currAnn.getAnnotation().toString() + "\n"));
                }

                currList.add(new ListItem(currPhrase));

                // Element in Queue einfgen
                String currPrefix = getNsPref(currElement.getTargetNamespace());
                schemaQueues.get(currPrefix).add(currElement);

            } else if (currChild.getTerm() instanceof XSModelGroup) {
                // Element von sequence/choice kann wieder eine ModelGroup
                // (sequence/choice) sein
                XSModelGroup mg = currChild.getTerm().asModelGroup();
                XSParticle[] childChilds = mg.getChildren();
                if ((childChilds != null) && (childChilds.length > 0)) {

                    // Art der Gruppe
                    String strCompositor = "";
                    Compositor compositor = mg.getCompositor();
                    if (compositor.equals(com.sun.xml.xsom.XSModelGroup.Compositor.SEQUENCE)) {
                        strCompositor = "Sequence:";
                    } else if (compositor.equals(com.sun.xml.xsom.XSModelGroup.Compositor.CHOICE)) {
                        strCompositor = "Choice:";
                    }
                    currList.add(new ListItem(getPhraseTextBold(strCompositor)));

                    // neue Liste fr aktuelle Kindelemente erzeugen
                    List subList = erzeugeListeFuerKindElemente(childChilds, schemaQueues, bulletSub);

                    // Als Subliste hinzufgen
                    currList.add(subList);
                }

            } else if (currChild.getTerm() instanceof XSWildcard) {
                // Element von sequence/choice kann ein Wildcard-Objekt sein,
                // z.B. 'xs:any'
                XSWildcard wildCard = currChild.getTerm().asWildcard();
                String currNamespaceStr = wildCard.apply(MySchemaWriter.WILDCARD_NS);

                Phrase currPhrase = new Phrase();
                currPhrase.add(getChunkTextBold("any"));
                currPhrase.add(getChunkText(" (" + getMinMaxStr(currChild) + ")\n"));
                if (currNamespaceStr.length() > 0) {
                    currPhrase.add(getChunkText(currNamespaceStr));
                }
                currList.add(new ListItem(currPhrase));
            }
        }
        return currList;
    }

    /**
     * Fr die Attribute des aktuellen Typs wird eine Liste erzeugt und der
     * angegebenen Sektion hinzugefgt.
     * 
     * @param currType
     *            Schemtyp, fr den die Attribute bestimmt werden
     * @param currSection
     *            Aktuelle Sektion des PDF-Dokuments
     */
    private void behandleAttribute(XSType currType, Section currSection) {
        if (currType.isComplexType()) {
            @SuppressWarnings("unchecked")
            Iterator<XSAttGroupDecl> itr1 = (Iterator<XSAttGroupDecl>) currType.asComplexType().iterateAttGroups();
            @SuppressWarnings("unchecked")
            Iterator<XSAttributeUse> itr2 = (Iterator<XSAttributeUse>) currType.asComplexType()
                    .iterateAttributeUses();

            if (itr1.hasNext() || itr2.hasNext()) {
                // Abstand
                currSection.add(getEmptyLineTextHalf());

                currSection.add(getParagraphTextBold("Attribute:"));

                List currList = new List(false, 12);
                currList.setListSymbol(bulletMain);

                while (itr1.hasNext()) {
                    XSAttGroupDecl currAttr = itr1.next();
                    String strAttribute = currAttr.getName();
                    currList.add(new ListItem(getPhraseText(strAttribute)));
                }

                while (itr2.hasNext()) {
                    XSAttributeDecl currAttr = itr2.next().getDecl();
                    String strAttribute = currAttr.getName();
                    // Typ kann lokal sein
                    if (!currAttr.getType().isLocal()) {
                        strAttribute += " (" + getNameWithPrefix(currAttr.getType()) + ")";
                    }
                    currList.add(new ListItem(getPhraseText(strAttribute)));
                }
                currSection.add(currList);
            }
        }
    }

    /**
     * Diese Methode erzeugt die Titelseite fr das PDF-Dokument mit der
     * Bezeichnung des Verfahrens.
     * 
     * @param bezVerfahren
     *            Bezeichnung des profilierten Verfahrens
     * @throws DocumentException
     * @throws MalformedURLException
     * @throws IOException
     */
    private void erzeugeTitelSeite() throws DocumentException, MalformedURLException, IOException {
        // Symbol, falls vorhanden
        String pathImage = "./logo_doku.png";
        try {
            // Image image =
            // Image.getInstance(PdfCreatorImpl.class.getResource(pathImage));
            Image image = Image.getInstance(pathImage);
            image.setAlignment(Element.ALIGN_RIGHT);
            docPdf.add(image);
        } catch (Exception e) {
            if (logger.isDebugEnabled()) {
                logger.debug("Fehler beim Einbinden der Grafik '" + pathImage + "'.", e);
            }
        }
        docPdf.add(getEmptyLineText());

        // Restliche Texte werden im Event-Handler geschrieben

        // Seitenumbruch
        docPdf.newPage();
    }

    private String getCompositorStr(XSComplexType type) {
        XSParticle currParticle = null;
        if (type.asComplexType().getExplicitContent() != null) {
            currParticle = type.asComplexType().getExplicitContent().asParticle();
        } else if (type.asComplexType().getContentType() != null) {
            currParticle = type.asComplexType().getContentType().asParticle();
        }
        if (currParticle != null) {
            XSModelGroup modelGroup = currParticle.getTerm().asModelGroup();
            Compositor compositor = modelGroup.getCompositor();
            if (compositor.equals(com.sun.xml.xsom.XSModelGroup.Compositor.SEQUENCE)) {
                return "Sequence";
            } else if (compositor.equals(com.sun.xml.xsom.XSModelGroup.Compositor.CHOICE)) {
                return "Choice";
            }
        }
        return "";
    }

    private String getMinMaxStr(XSParticle currParticle) {
        // minOccurs
        int minOccursChild = currParticle.getMinOccurs().intValue();
        String strminOccurs = "minOccurs: " + minOccursChild;
        // maxOccurs
        int maxOccursChild = currParticle.getMaxOccurs().intValue();
        String strMaxOccurs = "maxOccurs: " + maxOccursChild;
        if (maxOccursChild == -1) {
            strMaxOccurs = "maxOccurs: unbounded";
        }
        return strminOccurs + ", " + strMaxOccurs;
    }

    private Document initPdfWriterAndDocument(String filePath, boolean addHandlerHeaderFooter)
            throws FileNotFoundException, DocumentException {
        chapterCounter = 0;
        currPageNumber = 1;
        Document document = new Document();
        document.setMargins(40, 40, 55, 55);
        PdfWriter pdfWriter = PdfWriter.getInstance(document, new FileOutputStream(filePath));
        if (addHandlerHeaderFooter) {
            // Eventhandler fr die Fuzeile
            HeaderFooter hf = new HeaderFooter();
            pdfWriter.setPageEvent(hf);
        }
        document.open();
        return document;
    }

    private Paragraph getEmptyLineText() {
        return getEmptyLinesText(fontSizeText, 1);
    }

    private Paragraph getEmptyLineTextHalf() {
        return getEmptyLinesText(fontSizeTextHalf, 1);
    }

    private Paragraph getEmptyLinesText(int fontSize, int countLines) {
        Paragraph pg = new Paragraph("\n", FontFactory.getFont(fontNameStandard, fontSize, Font.NORMAL));
        for (int i = 1; i < countLines; i++) {
            pg.add(new Paragraph("", FontFactory.getFont(fontNameStandard, fontSize, Font.NORMAL)));
        }
        return pg;
    }

    private Chunk getChunkChapter(String text) {
        return new Chunk(text, FontFactory.getFont(fontNameStandard, fontSizeChapter, Font.BOLD));
    }

    private Chunk getChunkElement(String text) {
        return new Chunk(text, FontFactory.getFont(fontNameStandard, fontSizeElement, Font.BOLD));
    }

    private Chunk getChunkText(String text) {
        return new Chunk(text, FontFactory.getFont(fontNameStandard, fontSizeText, Font.NORMAL));
    }

    private Chunk getChunkTextBold(String text) {
        return new Chunk(text, FontFactory.getFont(fontNameStandard, fontSizeText, Font.BOLD));
    }

    private Chunk getChunkTextBoldWithReference(String text, String reference) {
        Chunk chunkText = new Chunk(text, FontFactory.getFont(fontNameStandard, fontSizeText, Font.BOLD));
        // Referenz setzen
        chunkText.setLocalGoto(reference);
        // Unterstreichen
        chunkText.setUnderline(new BaseColor(0x00, 0x0f, 0xFF), 0.5f, 0.0f, -4f, 0.0f,
                PdfContentByte.LINE_CAP_BUTT);
        return chunkText;
    }

    private Chunk getChunkTextItalic(String text) {
        return new Chunk(text, FontFactory.getFont(fontNameStandard, fontSizeText, Font.ITALIC));
    }

    private Phrase getPhraseText(String text) {
        return new Phrase(text, FontFactory.getFont(fontNameStandard, fontSizeText, Font.NORMAL));
    }

    private Phrase getPhraseTextBold(String text) {
        return new Phrase(text, FontFactory.getFont(fontNameStandard, fontSizeText, Font.BOLD));
    }

    private Phrase getPhraseChapter(String text) {
        return new Phrase(text, FontFactory.getFont(fontNameStandard, fontSizeChapter, Font.BOLD));
    }

    private Phrase getPhraseChapterItalic(String text) {
        return new Phrase(text, FontFactory.getFont(fontNameStandard, fontSizeChapter, Font.BOLDITALIC));
    }

    private Phrase getPhraseHeaderFooter(String text) {
        return new Phrase(text, FontFactory.getFont(fontNameStandard, fontSizeFooter, Font.NORMAL));
    }

    private Paragraph getParagraphChapter(String text) {
        return new Paragraph(text, FontFactory.getFont(fontNameStandard, fontSizeChapter, Font.BOLD));
    }

    private Paragraph getParagraphTextBold(String text) {
        return new Paragraph(text, FontFactory.getFont(fontNameStandard, fontSizeText, Font.BOLD));
    }

    private Paragraph getParagraphTextItalic(String text) {
        return new Paragraph(text, FontFactory.getFont(fontNameStandard, fontSizeText, Font.ITALIC));
    }

    private String getNameWithPrefix(XSDeclaration decl) {
        return getNsPref(decl.getTargetNamespace()) + ":" + decl.getName();
    }

    private String getReferenceForElement(XSElementDecl element) {
        return element.hashCode() + "_" + getNameWithPrefix(element);
    }

    private String getNsPref(String nsUrl) {
        return configurator.getPropertyNamespace(nsUrl);
    }

    class HeaderFooter extends PdfPageEventHelper {

        String currChapterTitle = "";

        /**
         * Initialize one of the headers, based on the chapter title; reset the
         * page number.
         * 
         * @see com.itextpdf.text.pdf.PdfPageEventHelper#onChapter(com.itextpdf.text.pdf.PdfWriter,
         *      com.itextpdf.text.Document, float, com.itextpdf.text.Paragraph)
         */
        @Override
        public void onChapter(PdfWriter writer, Document document, float paragraphPosition, Paragraph title) {
            currChapterTitle = title.getContent();
        }

        /**
         * Adds the header and the footer.
         * 
         * @see com.itextpdf.text.pdf.PdfPageEventHelper#onEndPage(com.itextpdf.text.pdf.PdfWriter,
         *      com.itextpdf.text.Document)
         */
        @Override
        public void onEndPage(PdfWriter writer, Document document) {
            float posTopLine = document.top() + 10;
            float posTopText = document.top() + 15;
            float posBottomLine = document.bottomMargin() - 10;
            float posBottomText = document.bottomMargin() - 22;
            float posLeft = document.leftMargin();
            float posRight = document.getPageSize().getRight() - document.rightMargin();

            if (writer.getPageNumber() == 1) {
                // Titelseite

                PdfContentByte contentByte = writer.getDirectContent();

                // Titel des Verfahrens (ungefhr in der Mitte der Seite)
                float posTitleX = document.getPageSize().getWidth() / 2;
                float posTitleY = document.getPageSize().getHeight() / 2;

                Phrase phraseTitle = getPhraseChapter("Schnittstellenspezifikation");
                ColumnText.showTextAligned(contentByte, Element.ALIGN_CENTER, phraseTitle, posTitleX,
                        posTitleY + 30, 0);
                phraseTitle = getPhraseChapterItalic(bezVerfahren);
                ColumnText.showTextAligned(contentByte, Element.ALIGN_CENTER, phraseTitle, posTitleX, posTitleY, 0);

                // Datumszeile 'erstellt am' am Ende der Seite
                DateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss");
                Phrase phraseDatum = getPhraseText("erstellt am " + dateFormat.format(new Date()));
                ColumnText.showTextAligned(contentByte, Element.ALIGN_RIGHT, phraseDatum, posRight,
                        document.bottomMargin(), 0);

            } else if (writer.getPageNumber() > 1) {
                // Kopf- und Fuzeile erst ab der zweiten Seite ...

                PdfContentByte contentByte = writer.getDirectContent();

                // Kopfzeile, Linie
                contentByte.moveTo(posLeft, posTopLine);
                contentByte.lineTo(posRight, posTopLine);
                contentByte.stroke();

                // Kopfzeile, Dateiname
                File currFile = new File(dateiname);
                Phrase phraseDateiname = getPhraseHeaderFooter(currFile.getName());
                ColumnText.showTextAligned(contentByte, Element.ALIGN_LEFT, phraseDateiname, posLeft, posTopText,
                        0);

                // Kopfzeile, Erstellungsdatum
                DateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy");
                Phrase phraseDatum = getPhraseHeaderFooter(dateFormat.format(new Date()));
                ColumnText.showTextAligned(contentByte, Element.ALIGN_RIGHT, phraseDatum, posRight, posTopText, 0);

                // Fuzeile, Linie
                contentByte.moveTo(posLeft, posBottomLine);
                contentByte.lineTo(posRight, posBottomLine);
                contentByte.stroke();

                // Fuzeile, aktuelles Kapitel
                if (currChapterTitle.length() > 0) {
                    Phrase phraseFooterChapterTitle = getPhraseHeaderFooter(currChapterTitle);
                    ColumnText.showTextAligned(contentByte, Element.ALIGN_LEFT, phraseFooterChapterTitle, posLeft,
                            posBottomText, 0);
                }

                // Fuzeile, Seitenzahl
                Phrase phraseFooter = getPhraseHeaderFooter("" + writer.getPageNumber());
                ColumnText.showTextAligned(contentByte, Element.ALIGN_RIGHT, phraseFooter, posRight, posBottomText,
                        0);
            }

            // Seitenzahl hochzhlen
            PdfCreatorImpl.this.currPageNumber++;
        }
    }

}