Package de.chris_soft.fyllgen.export

Source Code of de.chris_soft.fyllgen.export.PdfExporter

/**
* FyLLGen - A Java based tool for collecting and distributing family data
*
* Copyright (C) 2007-2011 Christian Packenius
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
package de.chris_soft.fyllgen.export;

import java.awt.Graphics2D;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.itextpdf.text.Anchor;
import com.itextpdf.text.BadElementException;
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.ElementTags;
import com.itextpdf.text.Font;
import com.itextpdf.text.FontFactory;
import com.itextpdf.text.Image;
import com.itextpdf.text.Meta;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfWriter;
import de.chris_soft.fyllgen.Statics;
import de.chris_soft.fyllgen.data.OptionData;
import de.chris_soft.fyllgen.data.Person;
import de.chris_soft.fyllgen.data.Relationship;
import de.chris_soft.fyllgen.data.RelationshipPartners;
import de.chris_soft.fyllgen.utilities.FamilyUtilities;
import de.chris_soft.fyllgen.utilities.PersonListSort;

/**
* Export the instance data into a PDF file.
* @author Christian Packenius, 20100630.
*/
public class PdfExporter extends DataExporter {
  /**
   * Anhang A - �berschrift.
   */
  private static final String ANHANG_A_PERSONEN_SORTIERT_NACH_GEBURTSJAHR = "Personen, sortiert nach Geburtsjahr";

  /**
   * Anhang B - �berschrift.
   */
  private static final String ANHANG_B_VERSTORBENE_PERSONEN_SORTIERT_NACH_ALTER =
    "Verstorbene Personen, sortiert nach Alter";

  /**
   * Anhang C - �berschrift.
   */
  private static final String ANHANG_C_PERSONEN_SORTIERT_NACH_GEBURTSTAG_OHNE_JAHR =
    "Personen, sortiert nach Geburtstag (ohne Jahr)";

  /**
   * Anhang D - �berschrift.
   */
  private static final String ANHANG_D_BILDER = "Portraits";

  /**
   * Font for title on first page of the document.
   */
  private final Font fontHelvetica36Bold = FontFactory.getFont(FontFactory.HELVETICA, 36, Font.BOLD);

  /**
   * Font for title on first page of the document.
   */
  private final Font fontHelvetica24Bold = FontFactory.getFont(FontFactory.HELVETICA, 24, Font.BOLD);

  /**
   * Font for title on first page of the document.
   */
  private final Font fontHelvetica24BoldItalic = FontFactory
    .getFont(FontFactory.HELVETICA, 24, Font.BOLD | Font.ITALIC);

  /**
   * Font for title in appendix.
   */
  private final Font fontHelvetica18BoldItalic = FontFactory
    .getFont(FontFactory.HELVETICA, 18, Font.BOLD | Font.ITALIC);

  /**
   * Font for appendix number.
   */
  private final Font fontHelvetica18Bold = FontFactory.getFont(FontFactory.HELVETICA, 18, Font.BOLD);

  /**
   * Font for title of simple data in a single line.
   */
  private final Font fontHelvetica12Bold = FontFactory.getFont(FontFactory.HELVETICA, 12, Font.BOLD);

  /**
   * Font of simple text.
   */
  private final Font fontHelvetica12 = FontFactory.getFont(FontFactory.HELVETICA, 12);

  /**
   * Font of italic text.
   */
  private final Font fontHelvetica12Italic = FontFactory.getFont(FontFactory.HELVETICA, 12, Font.ITALIC);

  /**
   * Font for links [1].
   */
  private Font fontTimes12ItalicBlue = FontFactory.getFont(FontFactory.TIMES, 12, Font.ITALIC, new BaseColor(32, 32,
    192));

  /**
   * Font for links [2].
   */
  private final Font fontTimes12Blue = FontFactory.getFont(FontFactory.TIMES, 12, Font.NORMAL, new BaseColor(32, 32,
    192));

  /**
   * Font for links [2].
   */
  private final Font fontTimes10Italic = FontFactory.getFont(FontFactory.TIMES, 10, Font.ITALIC);

  /**
   * Font for page title with full name of the person.
   */
  private final Font fontCourier14Bold = FontFactory.getFont(FontFactory.COURIER, 14, Font.BOLD);

  /**
   * List of persons to export.
   */
  private List<Person> persons;

  /**
   * Liste der obigen Personen, die w�hrend des Exports in ihrer Reihenfolge nicht ver�ndert wird.
   */
  private final List<Person> personsSorted = new ArrayList<Person>();

  /**
   * PDF-Dokument, das erzeugt wird.
   */
  private Document document;

  /**
   * Der PDF-Writer.
   */
  private PdfWriter writer;

  /**
   * Erste Person in der Liste und damit die Person, deren Stammbuch hier aufgebaut wird.
   */
  private Person person0;

  /**
   * Name der Hauptperson.
   */
  private String person0Name;

  /**
   * Laufende Nummer des Dokumentes.
   */
  private long documentNumber;

  /**
   * Hinweis auf die jeweils n�chste Person, die mich einen Schritt weiter Richtung Hauptperson bringt.
   */
  private Map<Person, Person> nextPersons;

  /**
   * Liste aller Bilder, f�r den Anhang D.
   */
  private final List<Image> listImages_D = new ArrayList<Image>();

  /**
   * Liste aller Person obiger Bilder, f�r den Anhang D.
   */
  private final List<Person> listPersons_D = new ArrayList<Person>();

  /**
   * Menge aller Personen, deren Images angezeigt werden d�rfen. Eventuell werden auch andere Daten unterdr�ckt, wenn die Person
   * nicht in dieser Menge enthalten ist.
   */
  private Set<Person> imagingPersonsList = new HashSet<Person>();

  /**
   * @see de.chris_soft.fyllgen.export.DataExporter#export(java.util.List, java.io.OutputStream)
   */
  @Override
  public long export(List<Person> persons, OutputStream out) throws DocumentException, IOException {
    if (createPrintVersion) {
      fontTimes12ItalicBlue = FontFactory.getFont(FontFactory.TIMES, 12, Font.ITALIC);
    }
    else {
      fontTimes12ItalicBlue = FontFactory.getFont(FontFactory.TIMES, 12, Font.ITALIC, new BaseColor(32, 32, 192));
    }

    // Ein paar Listen f�llen / leeren.
    this.persons = persons;
    listImages_D.clear();
    listPersons_D.clear();
    personsSorted.clear();
    imagingPersonsList.clear();
    personsSorted.addAll(persons);
    person0 = persons.get(0);
    person0Name = person0.getValue(Person.NAME);
    documentNumber = DataExporter.createExportedFilesEntry(persons, this);

    // Liste von Personenlinks erzeugen.
    nextPersons = FamilyUtilities.createNextPersonsMap(person0, persons);

    // Liste der Bilder erzeugen.
    createImagingList();

    SimpleDateFormat dayForm = new SimpleDateFormat("dd.");
    SimpleDateFormat timeForm = new SimpleDateFormat("HH.mm");
    String day = dayForm.format(new Date());
    String date = day + " " + Statics.currentMonth + " " + Statics.currentYear;
    String time = timeForm.format(new Date());
    String datetime = "am " + date + " um " + time + " Uhr";

    document = new Document();
    writer = PdfWriter.getInstance(document, out);
    addMetaData();
    document.open();

    addTitlePages(datetime);
    addPersonPages();
    addAppendixPages();

    document.close();

    return documentNumber;
  }

  /**
   * Erzeugt eine Liste von Personen, deren Images mit angezeigt werden sollen. Bei allen anderen Personen werden keine Images
   * angezeigt.
   */
  private void createImagingList() {
    imagingPersonsList.clear();
    imagingPersonsList.add(person0);

    // 3-stufig alle direkten Verwandten �bernehmen.
    for (int i = 0; i < 4; i++) {
      ArrayList<Person> list = new ArrayList<Person>();
      list.addAll(imagingPersonsList);
      for (Person pp : list) {
        imagingPersonsList.addAll(Statics.getList(pp.getPartner()));

        // In der letzten Iteration nur noch die Partner �bernehmen.
        if (i < 3) {
          imagingPersonsList.addAll(Statics.getList(pp.getParents()));
          imagingPersonsList.addAll(Statics.getList(pp.getChildren()));
          imagingPersonsList.addAll(Statics.getList(pp.getSiblings()));
        }
      }
    }

    // Nun s�mtliche Kinder- und Elterngenerationen mit hinzunehmen (und deren
    // Partner).
    ArrayList<Person> list1 = new ArrayList<Person>();
    list1.addAll(imagingPersonsList);
    ArrayList<Person> list2 = new ArrayList<Person>();
    list2.addAll(imagingPersonsList);
    for (int i = 0; i < list1.size(); i++) {
      for (Person pp : list1.get(i).getParents()) {
        if (!list1.contains(pp)) {
          list1.add(pp);
          imagingPersonsList.add(pp);
        }
      }
      for (Person pp : list1.get(i).getPartner()) {
        if (!list1.contains(pp)) {
          list1.add(pp);
          imagingPersonsList.add(pp);
        }
      }
    }
    for (int i = 0; i < list2.size(); i++) {
      for (Person pp : list2.get(i).getChildren()) {
        if (!list2.contains(pp)) {
          list2.add(pp);
          imagingPersonsList.add(pp);
        }
      }
      for (Person pp : list2.get(i).getPartner()) {
        if (!list2.contains(pp)) {
          list2.add(pp);
          imagingPersonsList.add(pp);
        }
      }
    }

    // Alle Personen einf�gen, die entweder seit mindestens 40 Jahren tot sind.
    for (Person pp : persons) {
      if (!imagingPersonsList.contains(pp)) {
        // Todestag plus 40 Jahre.
        try {
          String sDeathday = pp.getValueView(Person.DEATHDAY);

          final int deathyear;
          if (sDeathday.endsWith("?")) {
            deathyear = pp.getBirthYear(true) + OptionData.instance.getInt(OptionData.MAX_PERSON_AGE);
          }
          else {
            int ik = sDeathday.lastIndexOf('.');
            deathyear = Integer.parseInt(sDeathday.substring(ik + 1));
          }
          if (Statics.currentYear >= deathyear + 40) {
            imagingPersonsList.add(pp);
          }
        } catch (NumberFormatException nfe) {
          // Okay, Person nicht im Detail anzeigen.
        }
      }
    }
  }

  // /**
  // * Erzeugt eine Map, in der je Person diejenige Person steht, die "mich" der
  // Hauptperson (der ersten in der Liste) n�her
  // * bringt.
  // */
  // private void createNextPersonsMap() {
  // nextPersons = new HashMap<Person, Person>();
  // nextPersons.put(person0, person0);
  // while (nextPersons.size() < persons.size()) {
  // boolean bSet = false;
  // Person[] keyPersons = nextPersons.keySet().toArray(new Person[0]);
  // for (Person person : keyPersons) {
  // List<Person> hpl = new ArrayList<Person>();
  // hpl.addAll(Arrays.asList(person.getChildren()));
  // hpl.addAll(Arrays.asList(person.getParents()));
  // hpl.addAll(Arrays.asList(person.getPartner()));
  // for (Person hp : hpl) {
  // if (nextPersons.get(hp) == null) {
  // nextPersons.put(hp, person);
  // bSet = true;
  // }
  // }
  // }
  //
  // // Darf zwar nicht vorkommen, aber man wei� ja nie...
  // if (!bSet) {
  // break;
  // }
  // }
  // }

  /**
   * F�gt der Seite ein paar Metadaten hinzu.
   */
  private void addMetaData() throws DocumentException {
    document.addCreator(Statics.producer);
    document.addCreationDate();
    document.add(new Meta(ElementTags.PRODUCER, Statics.producer));
    String title =
      "Familienstammbuch der Familie " + person0Name + " (" + Statics.shortProducer + " #" + documentNumber + ")";
    document.addSubject(title);
    document.addTitle(title);
    document.addKeywords("Personen, Verwandtschaft, Genealogie, Familie, Stammbaum, " + Statics.producer);
  }

  /**
   * Add one or more pages before the person data pages.
   * @param datetime
   * @throws DocumentException
   * @throws IOException
   */
  private void addTitlePages(String datetime) throws DocumentException, IOException {
    sign();

    // Die ersten Seiten sind mehr oder minder fix.
    addFirstPage(person0);
    addSecondPage(datetime);
    addThirdPage(person0);
  }

  /**
   * Erste Seite "Familienstammbaum der Familie XYZ".
   */
  private void addFirstPage(Person person) throws DocumentException {
    Paragraph paragraph = new Paragraph();
    paragraph.setAlignment(Element.ALIGN_CENTER);
    paragraph.add(new Chunk("\r\n\r\n\r\n\r\n\r\n\r\nFamilienstammbaum", fontHelvetica36Bold));
    document.add(paragraph);
    paragraph = new Paragraph();
    paragraph.setAlignment(Element.ALIGN_CENTER);
    paragraph.add(new Chunk("\r\n\r\n\r\n\r\nder Familie ", fontHelvetica24Bold));
    paragraph.setAlignment(Element.ALIGN_CENTER);
    paragraph.add(new Chunk("\r\n\r\n\r\n\r\n", fontHelvetica24BoldItalic));
    // paragraph.add(new Chunk(person.getValue(Person.NAME),
    // secondTitleFontItalic));
    Chunk chunk = new Chunk(person.getValue(Person.NAME), fontHelvetica24BoldItalic);
    Anchor anchor = new Anchor(chunk);
    anchor.setReference("#p-" + person.getXREFID());
    paragraph.add(anchor);
    document.add(paragraph);
  }

  /**
   * Zweite Seite: Belehrung.
   */
  private void addSecondPage(String datetime) throws DocumentException {
    Paragraph paragraph;
    newPage();
    paragraph = new Paragraph();
    paragraph.setFont(fontHelvetica12Italic);
    paragraph.setAlignment(Element.ALIGN_CENTER);
    paragraph.add(new Chunk("\r\n\r\n\r\n\r\n\r\n\r\nDiese PDF-Datei wurde\r\n"));
    paragraph.add(new Chunk(datetime));
    paragraph.add(new Chunk("\r\nvom Programm\r\n"));
    paragraph.add(new Chunk(Statics.producer));
    paragraph.add(new Chunk("\r\nf�r\r\n"));
    paragraph.add(new Chunk(person0Name));
    paragraph.add(new Chunk("\r\nerzeugt.\r\n\r\n"));
    paragraph.add(new Chunk(" Sie darf nur f�r private Zwecke "));
    paragraph.add(new Chunk("verwendet werden. Um die Wahrung der Privatsph�re "));
    paragraph.add(new Chunk("aller in dieser Datei genannten Personen sicher zu stellen, "));
    paragraph.add(new Chunk("ist eine Weitergabe oder Ver�ffentlichung dieser Datei (auch in Ausz�gen, "));
    paragraph.add(new Chunk("als Druck oder in anderer Form) nur an "));
    paragraph.add(new Chunk("innerhalb dieses Dokumentes genannte Personen erlaubt. "));
    paragraph.add(new Chunk("Eine Ausnahme von diesem Verbot ist nur gegeben, "));
    paragraph.add(new Chunk("wenn alle in diesem Dokument genannten noch lebenden Personen "));
    paragraph.add(new Chunk("ihre schriftliche Zustimmung zur Weitergabe oder Ver�ffentlichung gegeben haben. "));
    paragraph.add(new Chunk("(Entsprechendes gilt f�r die Weitergabe oder Ver�ffentlichung "));
    paragraph.add(new Chunk("eines Auszuges dieses Dokumentes.) "));
    paragraph.add(new Chunk("\r\n\r\n"));
    paragraph.add(new Chunk("F�r die Richtigkeit der hier gemachten Angaben wird keinerlei Gew�hr �bernommen. "));
    String eMail = OptionData.instance.getString(OptionData.EXPORT_MAIL);
    if (eMail != null && eMail.trim().length() > 0) {
      paragraph.add(new Chunk("Sollten Sie Fehler erkennen oder Erg�nzungen zu den gemachten Angaben haben, "));
      paragraph.add(new Chunk("senden Sie diese bitte an die folgende E-Mailadresse:\r\n\r\n"));
      Anchor iref = new Anchor(new Chunk(eMail, fontTimes12ItalicBlue));
      iref.setReference("mailto:" + eMail);
      paragraph.add(iref);
      paragraph.add(new Chunk("\r\n\r\nSie werden darauf hin eine aktualisierte Version dieser PDF-Datei "));
      paragraph.add(new Chunk("per E-Mail erhalten."));
      paragraph.add(new Chunk("\r\n\r\nVielen Dank!"));
    }
    paragraph.add(new Chunk("\r\n\r\nDie von " + Statics.shortProducer
      + " erzeugten PDF-Dateien werden durchgehend nummeriert.", fontHelvetica12));
    paragraph.add(new Chunk("\r\nDiese Datei besitzt die laufende Nummer " + documentNumber + ".", fontHelvetica12));
    document.add(paragraph);
  }

  /**
   * Dritte Seite: Links auf die Anh�nge und so.
   */
  private void addThirdPage(Person person) throws DocumentException {
    newPage();
    Paragraph paragraph;

    paragraph = new Paragraph();
    paragraph.setFont(fontHelvetica12);
    paragraph.add(new Chunk("\r\n\r\nIn dieser Datei stehen die Stammdaten von insgesamt "));
    paragraph.add(new Chunk(persons.size() + " Personen, "));
    paragraph.add(new Chunk("die erste aufgelistete Person ist "));
    Chunk chunk = new Chunk(person.getValue(Person.NAME), fontHelvetica12Italic);
    Anchor anchor = new Anchor(chunk);
    anchor.setReference("#p-" + person.getXREFID());
    paragraph.add(anchor);
    paragraph.add(new Chunk(". Alle weiteren Personen sind �ber ihre 'verwandtschaftliche N�he' sortiert. "));
    paragraph.add(new Chunk("Soweit dies m�glich ist, l�sst sich innerhalb dieser Dateien navigieren."));
    document.add(paragraph);

    paragraph = new Paragraph();
    paragraph.setFont(fontHelvetica12Italic);
    paragraph.add(new Chunk("\r\n\r\nAnh�nge:\r\n\r\n", fontHelvetica12Bold));
    anchor = new Anchor(new Chunk("Anhang A: " + ANHANG_A_PERSONEN_SORTIERT_NACH_GEBURTSJAHR));
    anchor.setReference("#anhang-a");
    paragraph.add(anchor);
    paragraph.add(new Chunk("\r\n"));
    anchor = new Anchor(new Chunk("Anhang B: " + ANHANG_B_VERSTORBENE_PERSONEN_SORTIERT_NACH_ALTER));
    anchor.setReference("#anhang-b");
    paragraph.add(anchor);
    paragraph.add(new Chunk("\r\n"));
    anchor = new Anchor(new Chunk("Anhang C: " + ANHANG_C_PERSONEN_SORTIERT_NACH_GEBURTSTAG_OHNE_JAHR));
    anchor.setReference("#anhang-c");
    paragraph.add(anchor);
    paragraph.add(new Chunk("\r\n"));
    anchor = new Anchor(new Chunk("Anhang D: " + ANHANG_D_BILDER));
    anchor.setReference("#anhang-d");
    paragraph.add(anchor);
    document.add(paragraph);
  }

  /**
   * Adds all pages for the single persons.
   */
  private void addPersonPages() throws DocumentException {
    for (Person person : persons) {
      newPage();
      fillPersonPage(person);
    }
  }

  /**
   * Fill a person page with all person data.
   */
  private void fillPersonPage(Person person) throws DocumentException {
    addPageTitle(person);
    addImages(person);
    addPersonData(person);
    addAllPersonLists(person);
    showConnectionToMainPerson(person);
  }

  /**
   * Falls f�r diese Person Bilder hinterlegt sind, werden diese mit angezeigt.
   */
  private void addImages(Person person) throws DocumentException {
    if (imagingPersonsList.contains(person)) {
      boolean first = true;
      for (File imageFile : person.getImageList()) {
        first = addImage(first, imageFile, person);
      }
      if (!first) {
        Paragraph paragraph = new Paragraph();
        document.add(paragraph);
      }
    }
    else {
      Paragraph paragraph = new Paragraph();
      paragraph.add(new Chunk("(Bilder und weitere Daten wurden eventuell unterdr�ckt.)", fontTimes10Italic));
      document.add(paragraph);
    }
  }

  /**
   * Setzt eine Bilddatei auf die Seite.
   */
  private boolean addImage(boolean first, File imageFile, Person person) throws BadElementException, DocumentException {
    try {
      Image image = Image.getInstance("images/" + imageFile.getName());
      image.setBorderWidth(5);
      if (first) {
        Paragraph paragraph = new Paragraph();
        paragraph.add(new Chunk("\r\n"));
        document.add(paragraph);
        first = false;
      }
      Chunk imageChunk = new Chunk(image, 0, 0, true);
      Anchor imageAnchor = new Anchor(imageChunk);
      imageAnchor.setReference("#anhang-d-" + listImages_D.size());
      // imageAnchor.setReference("#i-" + person.getXREFID());
      listImages_D.add(image);
      listPersons_D.add(person);
      document.add(imageAnchor);
      document.add(new Phrase(" "));
    } catch (MalformedURLException exception) {
      // Dann halt kein Image einbinden.
      System.err.println("Image " + imageFile.getAbsolutePath() + " konnte nicht in das PDF eingebunden werden:");
      exception.printStackTrace();
    } catch (IOException exception) {
      // Dann halt kein Image einbinden.
      System.err.println("Image " + imageFile.getAbsolutePath() + " konnte nicht in das PDF eingebunden werden:");
      exception.printStackTrace();
    }
    return first;
  }

  /**
   * Zeigt die Verbindung zur "Hauptperson" dieser PDF-Datei.
   */
  private void showConnectionToMainPerson(Person person) throws DocumentException {
    // Not for the first person!
    if (person == person0) {
      return;
    }

    // Eigenen Paragraph f�r die Verbindung zur Hauptperson erzeugen.
    Paragraph paragraph = new Paragraph();
    paragraph.add(new Chunk("\r\nVerbindung zu " + person0Name + ":\r\n"));
    while (person != person0) {
      Person currPerson = person;
      Person nextPerson = nextPersons.get(person);

      // Das hier passiert nur, wenn der Packenius/Vietor-Filter eingeschaltet
      // ist!
      if (nextPerson == null) {
        break;
      }

      int count = 0;
      String s = "";
      // Richtung Vorfahren.
      if (nextPerson.isChildOf(currPerson)) {
        while (currPerson != person0 && nextPerson.isChildOf(currPerson)) {
          currPerson = nextPerson;
          nextPerson = nextPersons.get(currPerson);
          count++;
        }
        if (count == 1) {
          if (person.isFemale()) {
            s += "Mutter";
          }
          else if (person.isMale()) {
            s += "Vater";
          }
          else {
            s += "Elternteil";
          }
        }
        else {
          for (int i = 2; i < count; i++) {
            s += "Ur-";
          }
          if (person.isFemale()) {
            s += "Gro�mutter";
          }
          else if (person.isMale()) {
            s += "Gro�vater";
          }
          else {
            s += "Gro�elter";
          }
        }
      }

      // Richtung Nachkommen.
      else if (nextPerson.isParentOf(currPerson)) {
        while (currPerson != person0 && nextPerson.isParentOf(currPerson)) {
          currPerson = nextPerson;
          nextPerson = nextPersons.get(currPerson);
          count++;
        }
        if (count == 1) {
          if (person.isFemale()) {
            s += "Tochter";
          }
          else if (person.isMale()) {
            s += "Sohn";
          }
          else {
            s += "Kind";
          }
        }
        else {
          for (int i = 2; i < count; i++) {
            s += "Ur-";
          }
          s += "Enkel";
        }
      }

      // Partner.
      else {
        currPerson = nextPerson;
        Relationship relship = person.getRelationship(currPerson);
        if (person.isFemale()) {
          if (relship.getType().equals(RelationshipPartners.MARRIAGE)) {
            s = "Gattin";
          }
          else {
            s = "Partnerin";
          }
        }
        else if (person.isMale()) {
          if (relship.getType().equals(RelationshipPartners.MARRIAGE)) {
            s = "Gatte";
          }
          else {
            s = "Partner";
          }
        }
        else {
          s = "Partner/in";
        }
      }
      Chunk chunk = new Chunk("  " + person.getValueView(Person.NAME), fontTimes12Blue);
      Anchor anchor = new Anchor(chunk);
      anchor.setReference("#p-" + person.getXREFID());
      paragraph.add(anchor);
      paragraph.add(new Chunk(" ist " + s + " von "));
      chunk = new Chunk(currPerson.getValueView(Person.NAME), fontTimes12Blue);
      anchor = new Anchor(chunk);
      anchor.setReference("#p-" + currPerson.getXREFID());
      paragraph.add(anchor);
      paragraph.add(new Chunk(".\r\n"));

      person = currPerson;
    }
    document.add(paragraph);
  }

  /**
   * Just show the full person name at the top of the page.
   */
  private void addPageTitle(Person person) throws DocumentException {
    String fullname = person.getValueView(Person.NAME);
    if (createPrintVersion) {
      fullname += "  [" + (personsSorted.indexOf(person) + 1) + "]";
    }
    Paragraph pdfParagraph = new Paragraph();
    Chunk fullnameChunk = new Chunk(fullname, fontCourier14Bold);
    Anchor zref = new Anchor(fullnameChunk);
    zref.setName("p-" + person.getXREFID());
    pdfParagraph.add(zref);
    document.add(pdfParagraph);
  }

  /**
   * Adds relevant and given person data.
   */
  private void addPersonData(Person person) throws DocumentException {
    // Einzelne Daten werden unterdr�ckt, falls Person nicht "nahe genug".
    boolean viewPersonDetails = imagingPersonsList.contains(person);

    // Read person data.
    String birthday = person.getValueView(Person.BIRTHDAY);
    String deathday = person.getValueView(Person.DEATHDAY);
    birthday = birthday == "?.?.?" ? "" : birthday;
    deathday = deathday == "?.?.?" ? "" : deathday;
    String birthplace = person.getValueView(Person.BIRTHPLACE);
    String deathplace = person.getValueView(Person.DEATHPLACE);

    // Create data paragraph.
    if (birthday.length() + deathday.length() + birthplace.length() + deathplace.length() > 0) {
      Paragraph paragraph = new Paragraph();
      // paragraph.add(new Chunk("\r\n"));
      PdfPTable table = createKeyValuePdfTable();
      addSingleDataLine(table, "Geburtstag", birthday, true, Statics.niceShortDate(birthday));
      if (viewPersonDetails) {
        addSingleDataLine(table, "Geboren in", birthplace, false, null);
      }
      addSingleDataLine(table, "Todestag", deathday, false, Statics.niceShortDate(deathday));
      if (viewPersonDetails) {
        addSingleDataLine(table, "Gestorben in", deathplace, false, null);
      }
      paragraph.add(table);
      document.add(paragraph);
    }

    // Add additional data.
    String job = person.getValueView(Person.JOB).trim();

    if (viewPersonDetails) {
      if (job.length() > 0) {
        Paragraph paragraph = new Paragraph();
        // paragraph.add(new Chunk("\r\n"));
        PdfPTable table = createKeyValuePdfTable();
        addSingleDataLine(table, "Beruf", job, false, null);
        paragraph.add(table);
        document.add(paragraph);
      }
    }
  }

  /**
   * Erzeugt f�r die PDF-Datei eine Tabelle mit zwei Spalten.
   */
  private PdfPTable createKeyValuePdfTable() throws DocumentException {
    PdfPTable table = new PdfPTable(new float[] {1f, 2f});
    table.setWidthPercentage(100);
    table.getDefaultCell().setPaddingLeft(0);
    table.getDefaultCell().setBorderWidth(0);
    table.setHorizontalAlignment(Element.ALIGN_LEFT);
    table.setWidths(new int[] {16, 100 - 16});
    table.addCell(new Phrase(" "));
    table.addCell(new Phrase(" "));
    return table;
  }

  /**
   * Adds a single data line, if the data is not empty.
   */
  private void addSingleDataLine(PdfPTable table, String title, String data, boolean withDateLinks, String niceData) {
    if (data != null && data.trim().length() > 0) {
      Phrase phrase = new Phrase(title + ": ", fontHelvetica12Bold);
      table.addCell(phrase);
      Phrase phrase2 = new Phrase();
      if (withDateLinks) {
        createTableCellWithDateLinks(data, niceData, phrase2);
      }
      else {
        phrase = new Phrase(niceData == null ? data : niceData);
        phrase2.add(phrase);
      }
      table.addCell(phrase2);
    }
  }

  /**
   * Create a table cell with a date as data.
   */
  private void createTableCellWithDateLinks(String data, String niceDate, Phrase phrase2) {
    Phrase phrase;
    int ndSpace1 = niceDate.indexOf(' ');
    int ndSpace2 = ndSpace1 < 0 ? -1 : niceDate.indexOf(' ', ndSpace1 + 1);
    String dayMonth = data.substring(0, data.lastIndexOf('.'));
    String year = data.substring(data.lastIndexOf('.') + 1);
    if (!dayMonth.contains("?")) {
      Anchor anchor = new Anchor(new Phrase(niceDate.substring(0, ndSpace2 < 0 ? niceDate.length() : ndSpace2)));
      anchor.setReference("#dm-" + dayMonth);
      phrase2.add(anchor);
    }
    else {
      phrase =
        new Phrase(year.contains("?") ? niceDate : niceDate.substring(0, Math.max(0, Math.max(ndSpace1, ndSpace2))));
      phrase2.add(phrase);
    }
    phrase = new Phrase(" ");
    phrase2.add(phrase);
    if (!year.contains("?")) {
      Anchor anchor = new Anchor(new Phrase(year));
      anchor.setReference("#y-" + year);
      phrase2.add(anchor);
    }
    else {
      phrase = new Phrase(year);
      phrase2.add(phrase);
    }
  }

  /**
   * Adds multiple person lists to the document.
   */
  private void addAllPersonLists(Person person) throws DocumentException {
    addPersonList(person, person.getParents(), Relationship.PARENTS);
    addPersonList(person, person.getChildren(), Relationship.CHILDREN);
    addPersonList(person, person.getPartner(), Relationship.PARTNERS);
    addPersonList(person, person.getSiblings(), Relationship.SIBLINGS);
  }

  /**
   * Adds a list of persons with the same header to the document.
   */
  private void addPersonList(Person currPerson, Person[] personList, String relationType) throws DocumentException {
    String personListHeader = getHeaderForPersonList(personList, relationType);
    String[] additionalTextList = new String[personList.length];
    sortPersonList(personList, relationType, additionalTextList, currPerson);
    if (personList.length > 0) {
      // Create pdf header of the person list.
      Paragraph paragraph = new Paragraph();
      paragraph.add(new Chunk("\r\n", fontHelvetica12));
      paragraph.add(new Chunk(personListHeader, fontHelvetica12));
      document.add(paragraph);

      // Person list.
      for (int i = 0; i < personList.length; i++) {
        Person person = personList[i];

        // Name extension.
        String nameExt = getNameExtension(currPerson, person);
        paragraph = new Paragraph();

        // Name of the person.
        String fullPersonName = person.getValue(Person.NAME) + nameExt;
        if (createPrintVersion) {
          fullPersonName += "  [" + (personsSorted.indexOf(person) + 1) + "]";
        }
        if (persons.contains(person)) {
          Chunk chunk = new Chunk(fullPersonName, fontTimes12ItalicBlue);
          Anchor iref = new Anchor(chunk);
          iref.setReference("#p-" + person.getXREFID());
          paragraph.add(iref);
        }
        else {
          Chunk chunk = new Chunk(fullPersonName, fontHelvetica12);
          paragraph.add(chunk);
        }

        // Extra text.
        if (additionalTextList[i] != null && additionalTextList[i].trim().length() > 0) {
          Chunk chunk = new Chunk("  " + additionalTextList[i].trim(), fontHelvetica12);
          paragraph.add(chunk);
        }

        paragraph.setIndentationLeft(8);
        document.add(paragraph);
      }
    }
  }

  /**
   * Sort the list of the person (by anything) and create additional text for it.
   * @param currPerson
   */
  private void sortPersonList(Person[] personList, String relationType, String[] additionalTextList, Person currPerson) {
    // Order persons by birthday and add birthday text.
    if (relationType.equals(Relationship.CHILDREN) || relationType.equals(Relationship.SIBLINGS)
      || relationType.equals(Relationship.PARENTS)) {
      List<Person> personList2 = Arrays.asList(personList);
      PersonListSort.sortByBirthday(personList2);
      for (int i = 0; i < personList.length; i++) {
        Person p0 = personList2.get(i);
        personList[i] = p0;
      }
    }

    // Order partners by marriage date.
    else if (relationType.equals(Relationship.PARTNERS)) {
      List<Person> personList2 = Arrays.asList(personList);
      PersonListSort.sortByMarriageDate(personList2, currPerson);
      for (int i = 0; i < personList.length; i++) {
        Person p0 = personList2.get(i);
        personList[i] = p0;
        Relationship relship = currPerson.getRelationship(p0);
        String relType = relship.getType();
        String marriageDate = relship.getValue(RelationshipPartners.STARTDATE);
        String marriageEnd = relship.getValue(RelationshipPartners.ENDDATE);

        if (relType.equals(RelationshipPartners.MARRIAGE)) {
          String divorceExtraText = "";
          if (i < personList.length - 1) { // Nicht die letzte Person.
            Relationship relship2 = currPerson.getRelationship(personList2.get(i + 1));
            String marriageDate2 = relship2.getValue(RelationshipPartners.STARTDATE);
            long lP0DeathDate = Statics.getMinYYYYMMDDlong(p0.getValueView(Person.DEATHDAY));
            if (Statics.getMinYYYYMMDDlong(marriageDate2) > lP0DeathDate && lP0DeathDate > 9999) {
              divorceExtraText = ", verwittwet";
            }
          }
          if (marriageDate != null) {
            if (marriageEnd == null) {
              additionalTextList[i] =
                " (verheiratet seit " + Statics.niceShortDate(marriageDate) + divorceExtraText + ")";
            }
            else {
              additionalTextList[i] =
                " (verheiratet vom " + Statics.niceShortDate(marriageDate) + " bis "
                  + Statics.niceShortDate(marriageEnd) + ")";
            }
          }
          else if (marriageEnd != null) {
            additionalTextList[i] = " (verheiratet bis " + Statics.niceShortDate(marriageEnd) + ")";
          }
          else {
            additionalTextList[i] = " (verheiratet)";
          }
        }

        if (relType.equals(RelationshipPartners.DIVORCE)) {
          if (marriageDate != null) {
            if (marriageEnd == null) {
              additionalTextList[i] = " (geheiratet am " + Statics.niceShortDate(marriageDate) + ", Ehe geschieden)";
            }
            else {
              additionalTextList[i] =
                " (verheiratet vom " + Statics.niceShortDate(marriageDate) + " bis "
                  + Statics.niceShortDate(marriageEnd) + ", geschieden)";
            }
          }
          else if (marriageEnd != null) {
            additionalTextList[i] = " (verheiratet bis " + Statics.niceShortDate(marriageEnd) + ", geschieden)";
          }
          else {
            additionalTextList[i] = " (geschieden)";
          }
        }
      }
    }
  }

  /**
   * Get extension for name.<br>
   * http://www.fileformat.info/info/unicode/char/26ad/index.htm<br>
   * http://de.wikipedia.org/wiki/Genealogische_Zeichen
   */
  private String getNameExtension(Person person1, Person person2) {
    String nameExt = "";
    Relationship relship = person1.getRelationship(person2);
    if (relship != null) {
      String type = relship.getValue(Relationship.TYPE, "option");
      if (type != null) {
        boolean isMarriage = type.equals(RelationshipPartners.MARRIAGE);
        if (isMarriage) {
          // Funktioniert leider nicht... :-(
          // nameExt = " (\u26AD)";
        }
      }
    }
    return nameExt;
  }

  /**
   * Add appendix pages with person list by first letter or some statistics.
   */
  private void addAppendixPages() throws DocumentException {
    addAppendix_A_PersonsListedByBirthday();
    addAppendix_B_PersonsListedByAge();
    addAppendix_C_PersonsListedByBirthdayDayMonth();
    addAppendix_D_Images();
  }

  /**
   * List the persons by birthday.
   */
  private void addAppendix_A_PersonsListedByBirthday() throws DocumentException {
    newPage();
    PersonListSort.sortByBirthday(persons);

    Paragraph paragraph = new Paragraph();
    Chunk chunk = new Chunk("Anhang A\r\n", fontHelvetica18Bold);
    Anchor anchor = new Anchor(chunk);
    anchor.setName("anhang-a");
    paragraph.add(anchor);
    document.add(paragraph);

    paragraph = new Paragraph();
    chunk = new Chunk("\r\n" + ANHANG_A_PERSONEN_SORTIERT_NACH_GEBURTSJAHR + "\r\n", fontHelvetica18BoldItalic);
    paragraph.add(chunk);
    document.add(paragraph);

    int lastYear = Integer.MIN_VALUE + 2;

    for (Person person : persons) {
      int currentBirthYear = person.getBirthYear(false);
      if (lastYear != currentBirthYear) {
        paragraph = new Paragraph();
        lastYear = currentBirthYear;
        String header = lastYear == Integer.MIN_VALUE ? "ohne Angabe" : Integer.toString(lastYear);
        chunk = new Chunk("\r\n" + header + "\r\n", fontHelvetica12Bold);
        anchor = new Anchor(chunk);
        anchor.setName("y-" + lastYear);
        paragraph.add(anchor);
        document.add(paragraph);
      }
      paragraph = new Paragraph();
      String fullname = person.getValue(Person.NAME);
      if (createPrintVersion) {
        fullname += "  [" + (personsSorted.indexOf(person) + 1) + "]";
      }
      chunk = new Chunk(fullname, fontTimes12ItalicBlue);
      anchor = new Anchor(chunk);
      anchor.setReference("#p-" + person.getXREFID());
      paragraph.add(anchor);
      document.add(paragraph);
    }
  }

  /**
   * List the oldest person first.
   */
  private void addAppendix_B_PersonsListedByAge() throws DocumentException {
    newPage();
    PersonListSort.sortByAge(persons);

    Paragraph paragraph = new Paragraph();
    Chunk chunk = new Chunk("Anhang B\r\n", fontHelvetica18Bold);
    Anchor anchor = new Anchor(chunk);
    anchor.setName("anhang-b");
    paragraph.add(anchor);
    document.add(paragraph);

    paragraph = new Paragraph();
    chunk = new Chunk("\r\n" + ANHANG_B_VERSTORBENE_PERSONEN_SORTIERT_NACH_ALTER + "\r\n", fontHelvetica18BoldItalic);
    paragraph.add(chunk);
    document.add(paragraph);

    long lastAge = Integer.MIN_VALUE;

    for (Person person : persons) {
      String birthday = person.getValueView(Person.BIRTHDAY);
      long b = Statics.getYYYYMMDDlong(birthday);
      String deathday = person.getValueView(Person.DEATHDAY);
      long d = Statics.getYYYYMMDDlong(deathday);
      if (b >= 0 && d >= 0) {
        long currAge = (d - b) / 10000;
        if (currAge != lastAge) {
          paragraph = new Paragraph();
          lastAge = currAge;
          String header = "Alter: " + lastAge + " Jahre";
          chunk = new Chunk("\r\n" + header + "\r\n", fontHelvetica12Bold);
          paragraph.add(chunk);
          document.add(paragraph);
        }

        paragraph = new Paragraph();
        String fullname = person.getValue(Person.NAME);
        if (createPrintVersion) {
          fullname += "  [" + (personsSorted.indexOf(person) + 1) + "]";
        }
        chunk = new Chunk(fullname, fontTimes12ItalicBlue);
        anchor = new Anchor(chunk);
        anchor.setReference("#p-" + person.getXREFID());
        paragraph.add(anchor);
        paragraph.add(new Chunk("  (" + person.getShortDate() + ")"));
        document.add(paragraph);
      }
    }
  }

  /**
   * List the persons by birthday day and month.
   */
  private void addAppendix_C_PersonsListedByBirthdayDayMonth() throws DocumentException {
    newPage();
    PersonListSort.sortByBirthdayDayMonth(persons);

    Paragraph paragraph = new Paragraph();
    Chunk chunk = new Chunk("Anhang C\r\n", fontHelvetica18Bold);
    Anchor anchor = new Anchor(chunk);
    anchor.setName("anhang-c");
    paragraph.add(anchor);
    document.add(paragraph);

    paragraph = new Paragraph();
    chunk =
      new Chunk("\r\n" + ANHANG_C_PERSONEN_SORTIERT_NACH_GEBURTSTAG_OHNE_JAHR + "\r\n", fontHelvetica18BoldItalic);
    paragraph.add(chunk);
    document.add(paragraph);

    int lastDay = Integer.MIN_VALUE + 2;
    int lastMonth = Integer.MIN_VALUE + 2;

    for (Person person : persons) {
      String birthday = person.getValue(Person.BIRTHDAY);
      int currDay = person.getBirthDay();
      int currMonth = person.getBirthMonth();
      int currYear = person.getBirthYear(false);
      if (currDay > 0 && currMonth > 0) {
        if (lastDay != currDay || lastMonth != currMonth) {
          paragraph = new Paragraph();
          lastDay = currDay;
          lastMonth = currMonth;
          String header = lastDay + ". " + Statics.months[lastMonth - 1];
          chunk = new Chunk("\r\n" + header + "\r\n", fontHelvetica12Bold);
          anchor = new Anchor(chunk);
          anchor.setName("dm-" + birthday.substring(0, birthday.lastIndexOf('.')));
          paragraph.add(anchor);
          document.add(paragraph);
        }
        paragraph = new Paragraph();
        String fullname = person.getValue(Person.NAME);
        if (createPrintVersion) {
          fullname += "  [" + (personsSorted.indexOf(person) + 1) + "]";
        }
        chunk = new Chunk(fullname, fontTimes12ItalicBlue);
        anchor = new Anchor(chunk);
        anchor.setReference("#p-" + person.getXREFID());
        paragraph.add(anchor);
        paragraph.add(new Chunk("  ("));
        anchor = new Anchor(new Chunk(Integer.toString(currYear)));
        anchor.setReference("#y-" + currYear);
        paragraph.add(anchor);
        paragraph.add(new Chunk(")"));
        document.add(paragraph);
      }
    }
  }

  /**
   * List all portraits of persons.
   */
  private void addAppendix_D_Images() throws DocumentException {
    newPage();

    Paragraph paragraph = new Paragraph();
    Chunk chunk = new Chunk("Anhang D\r\n", fontHelvetica18Bold);
    Anchor anchor = new Anchor(chunk);
    anchor.setName("anhang-d");
    paragraph.add(anchor);
    document.add(paragraph);

    paragraph = new Paragraph();
    chunk = new Chunk("\r\n" + ANHANG_D_BILDER + "\r\n", fontHelvetica18BoldItalic);
    paragraph.add(chunk);
    document.add(paragraph);

    if (!listImages_D.isEmpty()) {
      for (int i = 0; i < listImages_D.size(); i++) {
        if (i % 25 == 0) {
          if (i > 0) {
            newPage();
          }
          paragraph = new Paragraph();
          paragraph.add(new Chunk("\r\n"));
          document.add(paragraph);
        }
        Image image = listImages_D.get(i);
        Person person = listPersons_D.get(i);
        image.setBorderWidth(5);

        // Image anzeigen - dient gleichzeitig als Link und als Anchor.
        Chunk imageChunk = new Chunk(image, 0, 0, true);
        Anchor imageAnchor = new Anchor(imageChunk);
        imageAnchor.setName("anhang-d-" + i);
        imageAnchor.setReference("#p-" + person.getXREFID());
        document.add(imageAnchor);

        // Etwas Platz zum n�chsten Image lassen.
        document.add(new Phrase(" "));
      }
      document.add(new Paragraph());
    }
  }

  /**
   * Create a new page.
   */
  private void newPage() {
    document.newPage();
    sign();
  }

  /**
   * Sign the current page at the bottom left.
   */
  private void sign() {
    // Ist ausgeschaltet, weil nicht alle Seiten signiert werden...
    if (System.currentTimeMillis() > 1) {
      return;
    }

    PdfContentByte cb = writer.getDirectContent();
    Rectangle pagesize = document.getPageSize();
    float w = pagesize.getWidth();
    float h = pagesize.getHeight();

    Graphics2D g2 = cb.createGraphics(w, h);
    g2.setFont(new java.awt.Font("Arial", 0, 6));
    g2.setColor(new java.awt.Color(96, 96, 96));
    String produced =
      "(produced by " + Statics.shortProducer + " " + Statics.currentYear + ", #" + documentNumber + ", " + person0Name
        + ")";
    g2.drawString(produced, 20, h - 20);
    g2.dispose();
  }

  /**
   * @see de.chris_soft.fyllgen.export.DataExporter#getFileExtension()
   */
  @Override
  public String getFileExtension() {
    return "pdf";
  }

  /**
   * @see de.chris_soft.fyllgen.export.DataExporter#getLongTitle()
   */
  @Override
  public String getLongTitle() {
    return "PDF (Portable Document Format)";
  }

  /**
   * @see de.chris_soft.fyllgen.export.DataExporter#getShortTitle()
   */
  @Override
  public String getShortTitle() {
    return "PDF";
  }
}
TOP

Related Classes of de.chris_soft.fyllgen.export.PdfExporter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.